设备功耗验收:hwmon 增量不是逐进程瓦特计

作者:

在

结论:设备功耗测试 test_power_draw.py 测的是同一 hwmon 节点读数的阶段增量,不是逐进程独立瓦特计:每个阶段把当前 get_power(1) 均值与前一阶段比较,prev 每阶段更新。因此它测到的是「依次加负载后的边际电源通道读数」,可能混入相机预热、GPU 频率、风扇、后台进程或上个阶段尚未稳定的影响。

本文基于固定源码快照的静态分析及五项原测试类方法/假时钟探针;test_power_draw.py 依赖特定硬件、固定 sysfs 功率节点、真实进程和消息总线,本次没有运行设备测试、读取 hwmon、启动相机/模型或修改设备电源状态。底层功率采样的窗口边界属于相邻专题。

硬件读数与函数索引

read_power() 读取固定的 hwmon 功率节点,数值除 1e6 转为瓦特;节点缺失或读数不合法直接抛异常,没有探测或热插拔恢复。sample_power() 用 Ratekeeper(123) 读取约 123 × seconds 个样本,未返回每个样本时间戳;get_power() 对指定秒数样本取算术平均,测试基线默认 5 秒,候选窗口按每次 1 秒测。

函数/数据 静态契约
read_power() 读固定 hwmon 节点,除 1e6 转瓦;缺失或不合法直接抛异常,无恢复
sample_power() Ratekeeper(123) 读约 123 倍秒数样本,未返回每样本时间戳
get_power() 对指定秒数样本取算术平均;基线默认 5 秒,候选窗口每次 1 秒
Proc 与 PROCS 进程名、期望功率增量、需要消息名及 np.isclose 容差;顺序为 camerad、modeld、dmonitoringmodeld、encoderd
valid_power_draw() 比较同一 hwmon 节点读数的增量,不是进程独立瓦特计
get_expected_messages() / valid_msg_count() 各服务标称频率乘 2 秒求和;仅比较总条数
get_power_with_warmup_for_target() 为当前进程预订其消息并先清队列;最多 30 秒反复测 1 秒功率,保留最近两个样本及消息数
test_camera_procs() 先测基线再逐个启动进程,当前平均功率作为下一阶段 prev;函数名虽指 camera,实际涵盖模型和编码器

阶段增量与容差

每一阶段比较 get_power(1) 的最近两次均值与前一阶段读数,prev 在每阶段完成后更新。测试既不把单个进程功耗隔离,也不减去同一时刻的独立空载对照。绝对容差分别是 camerad 0.4 W、modeld 0.2 W、dmonitoringmodeld 0.35 W、encoderd 0.12 W;相对容差默认 5%,实际 np.isclose 取两者合成范围。

阶段进程 期望功率增量 绝对容差 消息门
camerad 1.65 W(三相机消息) 0.4 W 按标称频率合计条数
modeld 1.5 W(modelV2) 0.2 W 按标称频率合计条数
dmonitoringmodeld 0.65 W(driverStateV2) 0.35 W 按标称频率合计条数
encoderd 0.23 W(无消息门) 0.12 W msgs=[],期望数与实收数都为 0,恒通过

valid_msg_count() 允许约 2% 相对误差或绝对 2 条,不逐主题核对;某一路缺失若被其它路超量抵消,仍可能通过总数门。

测试真实覆盖与失效路径

TestPowerDraw 继承 OpenpilotTestCase,标记为硬件测试。其 setup_method() 写入演示 CarParams;继承的 setUp() 会初始化硬件、关闭省电并终止 athena,teardown_method() 和 run() 的 finally 还会做 manager 清理。mock_messages(['deviceMotion']) 在测试期间另起后台发布线程。因此这项测试不是无副作用的算法单元测试。

失效/边界 行为 后果
30 秒超时退出 仍返回最后的 now/msg_counts 返回的 warmup_time 不是成功标志
首样本就越过截止 循环在不足两个样本后退出 返回变量尚未赋值而抛 UnboundLocalError
encoderd 的 msgs=[] 期望数与实收数都为 0 消息门恒通过,只靠功率增量间接判断
合计消息门 只比较总条数 单主题缺失可被其它主题超量抵消

隔离执行原 TestPowerDraw 方法后,假服务各 10 Hz、两秒窗口的 left=0 与 right=40 仍通过 40 条合计消息门;空主题列表也通过。假时钟在第一份功率样本返回后越过 30 秒,使 now/msg_counts 尚未赋值而抛 UnboundLocalError。已有两份样本但功率不达标时,超时返回最近两份均值而非错误;若两份样本达标则提前退出。该探针未执行 test_camera_procs(),不能证明真实服务频率、传感器功率或 manager 清理行为。

manager_cleanup() 在测量尾部、测试 teardown 及继承的 run() 清理路径多次调用,主要作用是停止所有受管进程;若中途抛异常,测试体末尾的清理不会到达,但外层测试辅助类的 finally 仍尝试清理。它不能回滚硬件电源、温度或持久参数的外部状态。

面向硬件插件的验收建议

自研计算板/相机/推理加速器要把功耗基线、传感器/模型负载和安全 MCU 电源域分开测量,记录电压、电流、温度、频率、环境与样本时间戳,并对稳态/启动峰值/异常回退分别设门。

  1. 插件的「功率达标」不能替代图像帧有效性、模型输出数值一致性、消息新鲜度或 CAN 发送安全。
  2. 固定路径 hwmon 工具只适用于已知设备布局,不应作为跨硬件插件通用驱动接口。
  3. 验收应把电源通道、消息条数和进程启动结果分开记录,避免用单一通过条件覆盖多类风险。

验证范围上,本专题已静态核对测试全部函数、hwmon 工具、消息模拟器和 manager 清理链,但未读取实际电源节点、运行硬件或统计噪声分布;探针只复现 Python 控制流,不表示已在具体设备上测得这些失效。

常见问题

这个测试能测出单个进程的独立功耗吗?

不能。valid_power_draw() 比较的是同一 hwmon 节点读数的增量,不是进程独立瓦特计;每阶段把当前均值与前一阶段读数比较,可能混入相机预热、GPU 频率、风扇、后台进程或上个阶段尚未稳定的影响。

warmup_time 达标就代表测试成功吗?

不是。get_power_with_warmup_for_target() 到 30 秒退出后仍返回最后的 now/msg_counts,再由测试函数的最终断言决定失败;返回的 warmup_time 不是成功标志。

为什么 encoderd 阶段的消息门形同虚设?

因为该阶段 msgs=[],期望数与实收数都为 0,消息门恒通过;这一阶段只靠功率增量和进程启动无异常间接判断,没有核对编码帧、输出文件或码流有效性。

消息条数达标就能说明各服务都正常吗?

不能。门控只比较所列服务的合计条数,某一路缺失若被其它路超量抵消仍可能通过;valid_msg_count() 允许约 2% 相对误差或绝对 2 条,不逐主题核对。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/POWER_DRAW_HW_MONITOR_PROCESS_INCREMENT_TEST_DEEP_DIVE.md(取用了 hwmon 读数索引、阶段增量与容差表、失效路径及五项探针结论)。
  • 项目 02 —— openpilot-research/POWER_SAMPLE_WAIT_WINDOW_CONTRACT_DEEP_DIVE.md(仅用于确认底层功率采样的窗口边界,未取用其未公开内容)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注