结论:Honda 控制输出分 Bosch 与 Nidec 两条构帧路径,并叠加三层节拍:转向每轮一条候选、纵向 50 Hz、仪表 10 Hz。Nidec 的正加速并不在 50 Hz 制动帧里,而由 10 Hz ACC_HUD 携带 PCM_SPEED/PCM_GAS,所以仪表链本身就是纵向执行链的一部分。Python 侧的限幅不等于 MCU 的安全保证:CarController 的 STEER_MAX 与变化率处理、panda honda_tx_hook() 的独立检查、继电器故障阻断是不同层次。下述 100/50/10 Hz 均以目标 100 Hz、每轮调用控制器一次且无丢周期为前提,只表示软件名义节拍;未做 DBC 回放、台架或实车验证。
关键函数与组件
| 入口 | 源码 | 作用 |
|---|---|---|
CarController.update() |
carcontroller.py |
读取控制目标与车况缓存,限幅、调度并返回候选 CAN 帧和软件回报值。 |
compute_gas_brake() |
carcontroller.py |
Nidec 把加速度转换成 gas/brake;Bosch 分支当前返回两个零,实际纵向另由 ACC 帧表示。 |
actuator_hysteresis() / brake_pump_hysteresis() |
carcontroller.py |
Nidec 制动去抖与泵请求;泵计时基于 frame * DT_CTRL。 |
CarControllerParams / CanBus |
values.py、hondacan.py |
车型扭矩上限、变化率及 PT/radar/camera/LKAS 路由。 |
create_steering_control() / create_acc_commands() |
hondacan.py |
转向和 Bosch 纵向字段编码。 |
create_brake_command() / create_acc_hud() |
hondacan.py |
Nidec 制动、PCM 加速及仪表字段编码。 |
honda_tx_hook() / honda_nidec_fwd_hook() |
honda.h |
MCU 独立检查候选帧并决定相机制动帧转发。 |
公共转向与回报语义
每轮先对归一化 actuators.torque 以 STEER_DELTA_UP/DOWN * DT_CTRL 限变化率,再取负号、夹到 [-1, 1] 并乘车型 STEER_MAX 构造 CAN 扭矩。create_steering_control() 始终生成一条 STEERING_CONTROL 候选;CC.latActive 为假时其扭矩和请求位都为零,BOSCH_TJA_CONTROL 还设置 STEER_DOWN_TO_ZERO。这段 Python 限制不替代 MCU 的未使能零转向规则,也不能从 latActive 为真推定车辆接受转向。
new_actuators 中的 torque 是软件限速后的归一化值,torqueOutputCan 是编码前的整数;accel/gas/brake/speed 有部分字段只在对应分支更新,可能保持上轮值。它们是软件目标回报,不是 panda 发送回执或车辆执行测量。控制器仅在车控存活检查通过时才被调用并发布 sendcan;消息的 valid 使用 CS.canValid,具体总线发送仍需再经过 pandad/panda。
纵向与按钮分支
| 分支 | 触发条件 | 生成的候选帧 |
|---|---|---|
| 原车纵向 | 未启用自控纵向 | 偶数轮 BOSCH_SUPPLEMENTAL_1;取消或恢复请求时 SCM_BUTTONS(radarless 发相机总线,其余用 PT 总线) |
| 自控纵向 Bosch | 启用自控纵向 | 偶数轮 ACC_CONTROL;有雷达版再加 ACC_CONTROL_ON |
| 自控纵向 Nidec | 启用自控纵向 | 50 Hz BRAKE_COMMAND + 10 Hz ACC_HUD 携带 PCM_SPEED/PCM_GAS |
若未启用自控纵向,偶数轮对非 radarless/CAN FD 平台尝试生成 BOSCH_SUPPLEMENTAL_1,取消或恢复请求则生成 SCM_BUTTONS;radarless 把按钮发到相机总线,其他平台用 PT 总线。源码的 supplemental 条件没有显式限定 Bosch,不能据此推定所有进入该分支的车型都获安全白名单许可。spam_buttons_command() 只生成一条候选,调用处没有独立按钮节流,实际发送还受当前模式的 TX 白名单和按钮安全门制约。
启用自控纵向时偶数轮生成纵向候选。Bosch 把加速度夹在 BOSCH_ACCEL_MIN/MAX,用独立查表得到 gas;有雷达版同时生成 ACC_CONTROL_ON 与 ACC_CONTROL,无雷达版只生成 ACC_CONTROL。gas_command 在不活动或低于阈值时为 -30000,ACCEL_COMMAND 在不活动时为零;STANDSTILL 由 LongControlState.stopping 连续计数而来,无雷达版 IDLESTOP_ALLOW 要计数超过 200 个 50 Hz 周期。enabled、longActive、stopping 是不同条件,不能合并成单一「已启用」位。
Nidec 的 compute_gb_honda_nidec() 在低于 2.3 m/s 时引入蠕行制动,再经刹车迟滞、每轮变化率限制、风阻扣减和 NIDEC_BRAKE_MAX - 1 上界量化为 BRAKE_COMMAND。泵请求在制动增大或超过 20 秒后重置计时,正制动下保持至少 0.2 秒。编码器把 AEB 执行相关字段显式置零,FCW 只映射警告和原车 chime。Nidec 正加速不是每个 50 Hz 制动周期的独立油门帧,而是在 10 Hz ACC_HUD 中携带 PCM_SPEED/PCM_GAS,因此 HUD 链也是其纵向执行链的一部分,应分别回放替代与普通分支。
HUD、总线与 MCU 边界
每 10 轮:自控纵向才生成 ACC_HUD,所有模式生成一个或两个 LKAS_HUD 候选;有雷达 Bosch 自控纵向还生成 RADAR_HUD,特定 Civic Bosch 补 LEGACY_BRAKE_COMMAND。原车纵向且 BOSCH_EXT_HUD 时 LKAS_HUD_A/B 分开发送。hud_v_cruise 在不可见时取 255,可见时除以 CS.v_cruise_factor,此值依赖 CarState 所选单位。create_acc_hud() 对 Bosch/Nidec 使用不同字段,Nidec 还复制原车 acc_hud 的若干 FCM/图标位;无新鲜度检查时不应将复制成功当作原车功能健康。
CanBus 不能假定 PT 永远是 bus 0:有雷达 Bosch 的 PT 为 offset+1、radar 为 offset,LKAS 依据自控纵向决定去 PT 还是 radar;radarless/CAN FD 与 Nidec 的 PT 为 offset。panda honda_nidec_init() / honda_bosch_init() 选择地址、总线、长度白名单及 RX 检查;Bosch 自控纵向的 safetyParam 分支仍受 ALLOW_DEBUG 编译门约束,不能因 Python 能构帧就推定量产固件可发。
honda_tx_hook() 对 Nidec BRAKE_COMMAND 的制动幅度、AEB 位和 ACC_HUD 的速度/gas 作检查;对 Bosch ACC_CONTROL 的加速度、gas 和 AEB 位作检查;未允许控制时转向输出必须为零。诊断地址只允许固定 tester-present 载荷,Bosch 原车纵向的按钮在控制不允许时只能取消。Nidec honda_nidec_fwd_hook() 还按相机原车 AEB 制动力与自控制动请求比较动态转发 0x1FA,不是简单的「总是屏蔽原车制动」。
插件与硬件验收
车型输出插件应分别声明转向、纵向、按钮、HUD、诊断保活和原车 ADAS 影响权限;每项绑定 DBC、车型指纹、物理总线、消息周期、数据年龄和 panda safety 模式。只允许插件产生候选控制帧,最终 TX 白名单、力矩/加速度/AEB 约束与超时失效仍在独立 MCU。对 Nidec 要单测 10 Hz ACC_HUD 与 50 Hz 制动的耦合、泵保持和原车 AEB 转发;对 Bosch 要单测雷达/无雷达、原车/自控纵向、ALLOW_DEBUG 配置和 ECU 静默失败。当前没有可用实板、目标车型回放或车辆执行证据,本文不能作为上车许可。
常见问题
Nidec 的加速信号在哪条帧里?
不在 50 Hz 制动帧里。Nidec 正加速是在 10 Hz ACC_HUD 中携带 PCM_SPEED/PCM_GAS,因此 HUD 链也是其纵向执行链的一部分,不能只看 BRAKE_COMMAND。
Bosch 的 gas 是怎么算出来的?
自控纵向时 Bosch 把加速度夹在 BOSCH_ACCEL_MIN/MAX,再用独立查表得到 gas;gas_command 在不活动或低于阈值时为 -30000。compute_gas_brake() 的 Bosch 分支当前返回两个零,实际纵向另由 ACC 帧表示。
PT 总线一定是 bus 0 吗?
不是。有雷达 Bosch 的 PT 为 offset+1、radar 为 offset,LKAS 依据自控纵向决定去 PT 还是 radar;radarless/CAN FD 与 Nidec 的 PT 为 offset,因此不能假定固定编号。
Python 的转向限幅算安全保证吗?
不算。CarController 的 STEER_MAX 与变化率处理只是软件层;MCU 的 honda_tx_hook() 独立检查候选帧,未允许控制时转向输出必须为零,最终还受继电器故障阻断。
延伸阅读
- Honda CarState 的车速、刹车与巡航输入 —— 本文输出侧的输入前提与缓存来源。
- Honda panda safety:RX 检查表与 Nidec AEB 转发 —— 候选帧最终如何被 MCU 独立裁决。
- 横向目标曲率到执行器:来源仲裁、时序与限幅 —— 转向目标进入构帧前的限幅链。
- 从使能到车轮:执行链上的八道门 —— 「能构帧」与「车轮执行」之间还有哪些门。
参考资料
- 项目 02 ——
openpilot-research/HONDA_CONTROLLER_CAN_OUTPUT_DEEP_DIVE.md(Bosch/Nidec 构帧、50/10 Hz 节拍、HUD 与 MCU 边界)。 - 项目 02 ——
openpilot-research/同目录 Honda CarState 与接口专题(输入侧与雷达通信控制作为相邻边界参考)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复