4. 功耗测量方法:板级功耗测量、电流钳与功率监视器、软件功耗统计

做功耗优化,第一步不是动手改代码,而是先搞清楚——你的功耗到底是多少。

我见过不少工程师,上来就调 DVFS、关 sensor,结果改了半天发现功耗根本没降。为什么?因为测量方法不对,数据本身就是错的。所以这一章,咱们把测量这件事彻底聊透。

功耗测量分三个层次:板级、硬件仪表、软件统计。每个层次都有自己的场景和坑。我个人习惯是:板级做基准,仪表做定位,软件做趋势。三者配合,才能把功耗问题看清楚。

核心原则:没有准确的测量,就没有有效的优化。测量误差超过 5%,优化方向就可能跑偏。

4.1 板级功耗测量:最准,但最麻烦

板级测量,说白了就是直接在手机主板上串入精密电阻,用万用表或数据采集卡测电压降,再换算成功率。这是最接近真实值的办法,误差可以控制在 1% 以内。

具体怎么做?

  1. 找到供电 rail:Camera 通常有独立的 PMIC rail,比如 VDD_CAM_IO、VDD_CAM_CORE、VDD_CAM_AF。翻原理图找到对应的电感或测试点。
  2. 断开原有走线:在 rail 上串联一个 10mΩ 或 100mΩ 的精密采样电阻。
  3. 接上差分探头:用高精度万用表或数据采集卡,测量电阻两端的电压差。
  4. 计算功耗:P = V × I,其中 I = V_diff / R_sense。

我在项目中遇到过一个问题:采样电阻选太大,导致 rail 电压被拉低,Camera 直接启动失败。后来换成 10mΩ 才搞定。所以这里有个经验值——采样电阻不要超过 100mΩ,否则会影响电路正常工作。

供电 rail 典型电压 典型电流 采样电阻建议
VDD_CAM_IO 1.8V 10-50mA 100mΩ
VDD_CAM_CORE 1.1V 100-500mA 10mΩ
VDD_CAM_AF 2.8V 50-200mA 10mΩ

注意:板级测量需要焊接或飞线,操作不当可能烧板。建议先在废弃主板上练手,再动真机。

4.2 电流钳与功率监视器:灵活,但精度有限

如果不想动主板,或者需要快速测量多个场景,电流钳和功率监视器是更好的选择。

电流钳(比如 Keysight N2783B)利用霍尔效应,夹在电源线上就能测电流。优点是非接触、无侵入,缺点是精度一般(3-5%),而且对低频电流响应慢。

功率监视器(比如 Monsoon Solutions 的 Power Monitor)是专门为移动设备设计的。它直接替代电池,给手机供电的同时实时记录电流和电压。采样率可以到 10kHz,能捕捉到毫秒级的功耗尖峰。

我建议你准备一套 Monsoon。为什么?因为它能导出 CSV 数据,配合 Python 脚本做后处理,非常方便。我曾经用 Monsoon 抓到过一个 50ms 的 ISP 电流尖峰,板级测量根本看不到。

小技巧:用 Monsoon 时,把手机屏幕亮度调到最低,关闭 Wi-Fi 和蓝牙,排除干扰项。这样测出来的 Camera 功耗才干净。

4.3 软件功耗统计:Perfetto 与 Systrace

硬件测量只能告诉你「功耗是多少」,但没法告诉你「功耗是谁产生的」。这时候就需要软件工具上场了。

Perfetto 和 Systrace 是 Android 平台最常用的两个工具。它们通过内核的 ftrace 和电源管理驱动,把每个进程、每个线程的 CPU 占用、唤醒次数、锁状态都记录下来。配合功耗模型,就能估算出每个模块的功耗占比。

我个人更推荐 Perfetto。为什么?因为 Systrace 已经快被 Google 弃用了,Perfetto 是它的下一代,支持更长的 trace 时间、更丰富的分析视图,而且可以直接在浏览器里打开。

使用步骤很简单:

  1. 抓取 trace:adb shell perfetto -c /path/to/config.pbtx -o /data/misc/perfetto-traces/trace.perfetto-trace
  2. 拉取到本地:adb pull /data/misc/perfetto-traces/trace.perfetto-trace
  3. 打开分析:在 Chrome 浏览器中打开 ui.perfetto.dev,加载 trace 文件。

在 Perfetto 界面里,重点关注这几个指标:

  • CPU 频率和负载:Camera 线程是否跑在大核上?频率是否过高?
  • 唤醒锁(wake lock):Camera 是否长时间持有唤醒锁,阻止系统休眠?
  • Binder 调用:Camera HAL 和 Framework 之间的通信是否频繁?每次调用耗时多少?
  • GPU 活动:预览和拍照时,GPU 渲染是否过度?

经验之谈:我曾经在 Perfetto 里发现一个 Camera 服务线程每 10ms 就唤醒一次 CPU,持续了整整 30 秒。原因是一个 while 循环里没有加 sleep。改完之后,预览功耗直接降了 15%。

4.4 三种方法的对比与选择

三种方法各有优劣,我整理了一张对比表,方便你快速决策:

方法 精度 侵入性 时间分辨率 能否定位模块 适用场景
板级测量 ±1% 高(需焊接) ms 级 否 基准标定、硬件验证
电流钳/功率监视器 ±3-5% 低(夹线/替换电池) μs-ms 级 否 快速测量、场景对比
Perfetto/Systrace ±10-20%(估算) 无(纯软件) μs 级 是 问题定位、趋势分析

你想想看,如果只靠软件统计,误差 20% 的数据你敢信吗?反过来,只靠板级测量,你连是哪个线程在耗电都不知道。所以我的建议是:先用板级或 Monsoon 拿到总功耗,再用 Perfetto 拆解到模块和线程。两条腿走路,才走得稳。

功耗测量方法知识体系 板级功耗测量 电流钳/功率监视器 软件功耗统计 精密电阻 + 差分探头 精度 ±1%,需焊接 霍尔效应 / 替代电池 精度 ±3-5%,非接触 Perfetto / Systrace 精度 ±10-20%,可定位模块 推荐组合:板级做基准 + 仪表做定位 + 软件做趋势 三者配合,才能把功耗问题看清楚

4.5 实操:用 Perfetto 抓一次 Camera 功耗 trace

光说不练假把式。咱们走一遍完整的流程。

首先,准备一个 Perfetto 配置文件。这是我常用的一个模板:

# camera_trace_config.pbtx
buffers: {
    size_kb: 65536
    fill_policy: RING_BUFFER
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "power/cpu_frequency"
            ftrace_events: "power/cpu_idle"
            ftrace_events: "power/suspend_resume"
            ftrace_events: "sched/sched_switch"
            ftrace_events: "sched/sched_wakeup"
            ftrace_events: "binder/binder_transaction"
            ftrace_events: "binder/binder_transaction_received"
        }
    }
}
duration_ms: 10000

然后执行抓取命令:

adb shell perfetto -c /data/local/tmp/camera_trace_config.pbtx \
  -o /data/misc/perfetto-traces/camera_trace.perfetto-trace

抓取期间,打开 Camera 应用,做一次预览→拍照→预览的操作。10 秒后 trace 自动结束。

拉取到电脑:

adb pull /data/misc/perfetto-traces/camera_trace.perfetto-trace

打开 ui.perfetto.dev,加载这个文件。你会看到类似这样的界面:

  • 顶部是 CPU 频率和负载曲线
  • 中间是每个线程的调度时间线
  • 底部是唤醒锁和电源状态

重点关注 Camera 相关线程(比如 cameraserver、HAL3、ISP)的 CPU 占用和唤醒频率。如果某个线程频繁唤醒但实际工作时间很短,那就是一个优化点。

避坑指南:我曾经抓了一次 trace,发现 CPU 频率一直在最高频,以为是 Camera 的问题。后来才发现是后台有个 App 在跑 AI 模型。所以抓 trace 前,记得清理后台进程,或者用 adb shell am force-stop 关掉无关应用。

4.6 小结

功耗测量这件事,没有银弹。板级测量最准但最麻烦,电流钳最灵活但精度有限,Perfetto 最能定位问题但误差大。我的建议是:三种方法都掌握,根据场景灵活切换。