结论:硬件状态的信任缺口集中在两处:缺测被静默当成 0(ThermalZone.read() 在温度文件不存在时返回 0,HardwareBase 多数方法是空操作或零值默认),以及慢速链没有采样时间戳(hw_state_thread() 消费端无采样时间,旧网络/USB 状态可能继续出现在 deviceState)。此外驻车容量是估算值而非车载 BMS 实测电量。以下基于固定源码快照 c8fb906 的静态分析与离线探针,不是实板验证。
平台抽象与 hardwared 线程
平台选择在导入时就固定:有 /AGNOS 文件则用 HardwareComma,否则用 HardwarePc,并非运行时可插拔发现。HardwareBase 除 get_device_type() 外多数方法有空操作或零值默认实现——移植时不能把「继承成功」等同于「能力可用」。
| 组件 / 函数 | 输入与输出 | 实际职责 |
|---|---|---|
HARDWARE 选择 |
/AGNOS 文件 → HardwareComma 或 HardwarePc |
导入时固定平台实例,并非运行时可插拔发现 |
ThermalZone.read()、ThermalConfig.get_msg() |
sysfs 温区 → *TempC 字段 |
按温区名称查找并读取;温度文件不存在返回 0,不是显式失效标志 |
hw_state_thread() |
网络、调制解调器、USB → 容量 1 的队列 | 每约 10 秒或 USB 拓扑变化时抓取慢速状态;队列满或采集失败会消耗本次触发,主线程沿用旧值 |
hardware_thread() |
pandaStates、peripheralState、温度、Params → deviceState |
点火边沿和约 2 Hz 周期更新,处理启动门、热状态、风扇目标、功耗与关机请求 |
FanController.update() |
温度、点火 → 目标风扇百分比 | 积分 PID 加前馈,点火变化重置 |
PowerMonitoring.calculate() |
车端电压、点火 → 累计耗电 / 剩余容量估计 | 驻车按设备功率积分,点火时按固定 45 W 充电率估计;约 10 秒保存 CarBatteryCapacity |
点火、热状态与行车资格
pandaStates 中任一已知 panda 的点火线或 CAN 点火信号可建立点火;超过 5 秒未收到状态则清除点火,但线束连接状态 in_car 仅在收到 panda 状态时更新。行车资格分两层:已有行车状态要求点火、无 onroad cycle、未达到 critical;从驻车新启动还要求条款/培训、空间、设备已启动、温度低于 overheated、非卸载等启动条件。
因此热状态迟滞允许「过热但未严重」的在途状态暂时保持,不能把「禁止进入行车」误写成「立即退出行车」。温度源分两组:驻车危险检查用内存、CPU、GPU 的最大值,行车热状态再加入 PMIC,两者都经 5 秒一阶滤波;mici 与其他设备采用不同热带阈值,驻车高温可直接升到 critical。风扇目标在温度告警时被强制为 100%,随后经 pandad 发给 panda;panda 回报的转速另由 selfdrived 检查。
风扇 PID 与温度带
风扇与温度构成一条闭环:温度决定目标百分比,panda 执行并回报转速,故障事件则由 selfdrive 侧产生。关键参数如下。
| 项目 | 参数 / 行为 |
|---|---|
| 风扇控制 | 积分 PID 加前馈,点火变化时重置 |
| 行车风扇范围 | 30–100% |
| 驻车风扇范围 | 0–30% |
| 温度告警 | 风扇目标强制 100% |
| 温度滤波 | 5 秒一阶滤波 |
| 温度源 | 驻车用内存/CPU/GPU 最大值;行车再加入 PMIC |
电源估算与关机条件
普通关机不是单条件触发,而是「前置状态 + 多个触发之一」的组合;ForcePowerDown 能绕过普通条件,但仍受末尾约束。
| 条件 | 阈值 / 机制 |
|---|---|
| 普通关机前置 | 驻车、连接车辆、未禁用关机、驻车超过 300 秒 |
| 触发之一 | 驻车超过 30 小时 |
| 触发之二 | 低通电压低于 11.8 V |
| 触发之三 | 估计容量耗尽 |
| 点火充电率 | 按固定 45 W 估计 |
ForcePowerDown |
绕过上述普通条件,但仍受末尾的 started_seen / 最短开机时间约束 |
hardwared 只写 DoShutdown;真正调用关机的是 manager.main()。
缺测的零值风险与插件契约
两个具体风险值得单独记住:hw_state_thread() 消费端无采样时间戳,旧网络/USB 状态可能继续出现在 deviceState;ThermalZone.read() 把缺失温区当 0。这类慢速状态之所以能继续沿用旧值,正是因为消费端没有采样时刻可供判断新鲜度;新平台应给每条状态附上采样时间与失效哨兵。这些是移植设计风险,需要在目标平台上显式处理,而不是沿用零值默认。
因此新平台要显式声明能力与缺测状态:标定 sysfs 温区名、单位、采样频率、失效哨兵;定义电压测点、功率方向与关机电气动作;点火、温度、关机和风扇属于可信任设备服务,需要时效性、故障降级和唯一发布者。硬件插件不得自行宣布 deviceState.started 或绕开独立 panda 安全门。
外置计算模块与触摸记录
同一条 hardwared 线程里还挂着外置计算模块与触摸记录两条支线。Chestnut.update() 在驻车时限次刷固件,并对外置 GPU 的丢电、链路、过热、模型失败分别生成不同告警;chestnut_state_thread() 读取供电与 PCIe 状态,并按有效位合并 GPU 状态。也就是说「外置模块可用」本身也是一组需要区分失效类型的判定位,而不是一个布尔值。
触摸记录则只属诊断路径:touch_thread() 仅在 comma 硬件启动,按事件帧分组发布到原始 touch 主题,UI 交互另由 raylib 采集,两者不应混为一谈。此外,hardware_thread() 里的 ign_edge 是「已启动状态」与 onroad 条件的持续差异,不一定是单次点火边沿:启动条件受阻而 onroad 条件仍真时,它会持续绕过约 2 Hz 的周期门,device_booted 一旦为真也会在本进程锁存。
常见问题
温度读不到会怎样?
ThermalZone.read() 在温度文件不存在时返回 0,这不是显式失效标志。缺测不能被静默当作 0 °C,新硬件平台必须明确失效处理。
普通关机需要满足哪些条件?
前置是驻车、连接车辆、未禁用关机且驻车超过 300 秒;再由超过 30 小时、低通电压低于 11.8 V、或估计容量耗尽三者之一触发。
驻车容量是车载 BMS 实测电量吗?
不是。它是估算值:无点火时按平台报告的设备功率积分,有点火时按固定 45 W 充电率估算,并非测得的真实电量。
过热会立即退出行车吗?
不一定。热状态迟滞允许「过热但未严重」的在途状态暂时保持;「禁止进入行车」与「立即退出行车」是两条不同的判定。
延伸阅读
- 外置计算模块的 USB 与固件回退:刷写、供电与模型失败 —— 同一 hardwared 线程里的外置 GPU 状态链
- pandad 主循环与活性遥测:风扇命令与心跳是怎么下发的 —— 风扇目标到 panda 的执行与回报
- Ed25519 签名加 96 字节事务记录:让 MCU 升级不怕中途断电 —— 掉电场景下的事务与恢复设计
- 21 块板卡的去留:A/B/C 分级、三个产品族与四道阶段门 —— 硬件能力分级与阶段门思路
参考资料
- 项目 02 ——
openpilot-research/HARDWARE_STATE_DEEP_DIVE.md(硬件状态、供电与热管理,含源码行号索引) - 项目 02 ——
openpilot-research/probes/test_power_monitoring_contract.py(电源估算与关机条件的离线探针) - 项目 02 ——
openpilot-research/probes/test_fan_controller_contract.py(风扇 PID 上下限与积分的离线探针)
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复