PowerMonitoring 电量估算与 11.8 V 低压关机契约

作者:

在

结论:PowerMonitoring 的「电量」是估算值而非 BMS 读数:容量单位是 uWh,代码里的 30,000,000 uWh 只是设定的 30 Wh 上限,不是车辆电池实际容量;Params 缺失或过低时按 3 Wh 起步。估算器的失败路径不向调用方返回失效状态——它在积分前裁剪容量、缺测时不更新低通电压、写盘失败时延后积分时钟,而关机判定表达式不检查这些输入的有效位与年龄。它只写一个 DoShutdown 请求位,本身不切断电源;真正断电由 manager 接力。固定快照基线为 c8fb906。

输入、容量估算与数据来源

估算器读 CarBatteryCapacity,初始容量至少 3,000,000 uWh;电压滤波器初值 12,000 mV。电量积分的核心式子是 power_used = power_W * 1,000,000 * elapsed_h:驻车时按硬件功率积分耗电并减少估算容量,点火时按固定的 45 W 增加估算容量,并把本次驻车累计耗电清零。

功率来源因平台而异。comma 平台从 hwmon1/power1_input 读微瓦并转瓦;read_param_file() 读取失败默认返回 0,PC 基类也返回 0。这意味着「功率缺测」和「真实零功率」在数值上无法区分——这是估算偏差风险的来源之一,而不代表设备上已测到该偏差。

入口 实际行为
PowerMonitoring.__init__() 读 CarBatteryCapacity,容量至少 3,000,000 uWh;电压滤波器初值 12,000 mV。
calculate() 电压可用时做固定增益低通、容量裁剪与约 10 秒保存;点火按 45 W 充电,驻车按硬件功率积分;异常只记录,不返回失效状态。
_perform_integration() 用单调时间差把瓦换算为 uWh,增加驻车耗电并减少估算容量;负耗电抛错后在内部被捕获。
should_shutdown() 根据驻车时长、低通电压、容量、点火、线束、Params 开关和启动历史返回布尔判定。
hardware_thread() peripheralState.pandaType=unknown 时传 voltage=None;把估算值写 deviceState,判定后写 DoShutdown。
manager.main() 看到 DoShutdown 后清理受管进程,再调用 HARDWARE.shutdown()。

采样时序与低通滤波的实际行为

calculate() 在积分之前把容量裁剪至 [0, 30 Wh],所以一次积分之后可能暂时高于 30 Wh 或低于 0;现有上下限测试也按一帧越界的行为断言。发布到 deviceState.carBatteryCapacityUwh 时又用 max(0, ...) 遮掉负值,这不代表内部估算已回到零。

当 voltage=None,函数清空 last_measurement_time、next_pulsed_measurement_time 和 power_used_uWh,但不更新容量、低通电压或瞬时电压;下一次有效电压只重新设置积分时间,不补算缺测区间。离线探针确认旧的低通电压可在缺测后继续触发 should_shutdown(),因为关机表达式不检查电压年龄。

电压低通系数固定 0.011,每次有效 calculate() 调用推进一次,代码不按实际 dt 重算系数;探针把两次有效样本间隔拉长到 1000 秒,第二次仍只推进一次固定系数。注释的约 45 秒时间常数依赖近似稳定的调用频率。hardware_thread() 名义上按约 2 Hz 更新,但点火边沿会额外触发,调度延迟也会改变低通的实际物理时间。新平台若更换采样频率或电压测点,11.8 V 阈值和滤波参数必须重新标定。

关机布尔门与边界

若 offroad_timestamp is None,函数立即返回 False,即使 ForcePowerDown=True 也不走后续判定。普通关机要求:驻车超过 300 秒、未点火、已连接车端线束、DisablePowerDown=False,并且三个触发条件至少一项为真。

条件 阈值 / 备注
驻车时长 超过 300 秒(普通门共同前提)
触发条件一 驻车超过 30 小时
触发条件二 低通电压低于 11.8 V 且驻车超过 60 秒
触发条件三 估算容量不大于零
强制位 ForcePowerDown 以 OR 加入,可绕过普通门,但仍受最终启动门约束
最终启动门 started_seen 或系统单调时间已超过 3600 秒

ForcePowerDown 在这些普通门之后以 OR 方式加入,故可绕过点火、线束、禁用关机、五分钟和三个触发条件;但仍受最末尾的 started_seen or now > 3600 约束。离线探针确认强制位在点火中、无车端线束且禁用普通关机时仍可通过,但无驻车时间或未过最终启动门时不能通过。这里的 now 是系统单调时钟绝对值,不是本进程或本次开机管理器的局部计时;启动后已超过一小时的设备即使 started_seen=False 也通过该门。

两条失败路径都不提供失败区间的功率历史:put() 抛错时积分时刻都不推进,下次成功时把当下功率应用到从上次积分起的整段间隔;负功率会让 _perform_integration() 记录错误并保留旧积分时刻,后续正功率同样覆盖这段间隔。hardwared 写入 DoShutdown 后由 manager 接力;该 Params 位是动作请求,不是电源已经切断的回执。

面向新硬件与插件化的接口建议

新硬件电源插件应返回 {voltage, current_power, sample_time, valid, source},明确瓦数符号、供电位置和校准精度;缺测不能静默映射为 0 W。估算器与关机策略应分离,低压门使用有界年龄的有效电压、实际 dt 和可追踪阈值,强制关机另设授权与审计。

台架至少测试点火抖动、USB / 线束脱落、功率传感器读失败、低压瞬降、长时间驻车、manager 重启、请求已写但硬件未关机,以及真实 12 V 电气负载下的最坏关机延迟。现有单测覆盖无 peripheral、点火充电、驻车积分、上下限、低压、禁用关机、线束与延迟门;本轮未运行。

常见问题

30 Wh 是车辆电池的真实容量吗?

不是。30,000,000 uWh 是代码里的设定上限,不是车辆电池 BMS 读数;若 Params 缺失或过低,初始化按 3 Wh 起步。容量单位是 uWh,且积分前会裁剪到 [0, 30 Wh],一次积分后可能暂时越界。

为什么功率读不到时估算不会停?

因为两种平台行为不同:comma 读 sysfs 失败默认 0 W,有效电压但功率缺测会被当成零耗电;若平台实现直接抛错,calculate() 捕获后不推进积分时刻,恢复后的一个功率读数会被应用到整个失败间隔。两条路径都不提供失败区间的功率历史。

电压缺测后还能触发低压关机吗?

可能。当 voltage=None,函数不更新低通电压;离线探针确认旧的低通电压可在缺测后继续参与 should_shutdown(),因为关机表达式不检查电压年龄。反过来,缺功率读数会让容量消耗被低估。

写了 DoShutdown 就等于断电了吗?

不等于。DoShutdown 是一个动作请求位,PowerMonitoring 本身不切断电源;hardwared 写入后由 manager 看到并清理受管进程,再调用 HARDWARE.shutdown()。请求已写但硬件未关机属于必须单独台架验证的场景。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/POWER_MONITORING_CAPACITY_SHUTDOWN_CONTRACT_DEEP_DIVE.md(取用了容量估算公式、采样时序、低通滤波行为、关机布尔门与硬件插件建议)。

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

更新记录

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

评论

发表回复

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