标签: panda safety

  • 大众 MQB panda safety:双总线按钮、冗余制动与纵向编译门

    结论:Volkswagen MQB 的 panda safety 用同一张 RX 表区分两种模式,差异几乎全在 TX 白名单:Stock ACC 模式允许在 bus 0 与 bus 2 发 GRA_ACC_01,自控纵向模式则完全不发该帧,改为允许 ACC_02/06/07。纵向配置依赖 FLAG_VOLKSWAGEN_LONG_CONTROL,且只有 ALLOW_DEBUG 构建才读取它,所以上层 CarParams 设置了纵向能力,并不能单独证明发布固件允许发送 ACC。本文为固定源码快照 c8fb906 的静态阅读,bus 号是单个 safety 实例的本地编号。

    模式与总线矩阵

    volkswagen_mqb_init() 初始化共享状态与同一张 RX 表;仅在 ALLOW_DEBUG 构建中读取纵向参数位,从而选择 Stock 或 Long 的 TX 白名单。两种模式都没有专属转发钩子,通用转发只在 bus 0 与 bus 2 之间,check_relay 地址在目标总线上被静态阻断。

    一个容易误读的点是方向:LH_EPS_03 的 RX 在 bus 0,候选 TX 在 bus 2,不能因为 ID 相同就把两个方向混为一个。

    模式 RX 关键 TX 使能来源
    Stock ACC 七组均在 bus 0 HCA_01、LDW_02 在 bus 0;LH_EPS_03 在 bus 2;GRA_ACC_01 在 bus 0 及 bus 2 TSK_06 原车巡航接合沿;主开关关闭或 GRA Cancel 退出
    自控纵向 同一七组均在 bus 0 HCA_01、LDW_02、ACC_02/06/07 在 bus 0;LH_EPS_03 在 bus 2;无 GRA_ACC_01 TX TSK_06 主开关可用时,GRA Set/Resume 释放沿进入;Cancel 退出

    RX 完整性与状态来源

    volkswagen_mqb_rx_hook() 更新移动状态、驾驶员扭矩、巡航许可、按钮、油门和两种制动观测。制动状态是 Motor_14 踏板开关或 ESP_05 压力检测的组合,之后由共享安全层处理制动退出。ESP_19 的四轮原始值求和大于零即判移动,它不形成第二车速估计。

    需要注意,忽略 checksum 与计数器的两帧仍留在 RX 存活表里,因此不能写成「完全不检查」。频率是检查配置,并非对每帧的实时 deadline 保证。

    RX 帧 检查配置 钩子消费
    ESP_19 0x0B2 8 字节、100 Hz;忽略 checksum/计数 四轮原始值求和大于 0 判移动
    LH_EPS_03 0x09F 8 字节、100 Hz;CRC + 4 位计数 带符号驾驶员扭矩采样
    ESP_05 0x106 8 字节、50 Hz;CRC + 4 位计数 制动压力超过门限的布尔值
    TSK_06 0x120 8 字节、50 Hz;CRC + 4 位计数 ACC 状态 3/4/5 视为接合,2 视为主开关可用
    Motor_20 0x121 8 字节、50 Hz;CRC + 4 位计数 原始油门字段非零
    Motor_14 0x3BE 8 字节、10 Hz;忽略 checksum/计数 踏板开关位
    GRA_ACC_01 0x12B 8 字节、33 Hz;CRC + 4 位计数 纵向 Set/Resume 释放沿,任意模式 Cancel 退出

    TX 字段门与空白

    volkswagen_mqb_tx_hook() 检查 HCA_01 的扭矩与请求位、ACC_06/07 的加速度,以及在未允许控制时 GRA 的设置/恢复位。HCA_01 由共享扭矩钩子检查最大 300、每帧上升 4 与下降 10、实时差 75、驾驶员扭矩余量 80 和请求位;LH_EPS_03 bus 2 虽在 TX 表中用于 Emergency Assist 兼容,但本模式 TX 钩子没有该帧的专属扭矩或载荷检查,其准入只受白名单和继电器故障约束。

    GRA_ACC_01 在 Stock 模式允许 bus 0/2 与 8 字节;未允许控制时只禁止字节 2 的 Set/Resume 对应位,钩子并不要求 Cancel 必须置位,也不全面约束其余按钮位。已允许控制时,这一专属按钮门不再限制这些位。ACC_06/07 只在纵向 TX 表中,两帧主加速度都用 -3.5 至 2.0 m/s² 活动范围与 3.01 m/s² 未激活编码,ACC_07 的另一加速度字段固定要求 3.02 m/s²。

    TX 帧 专属检查 空白与边界
    HCA_01 最大 300、每帧上升 4/下降 10、实时差 75、驾驶员扭矩余量 80、请求位 共享扭矩钩子生效
    LH_EPS_03(bus 2) 仅白名单与继电器故障约束 本模式无专属扭矩/载荷检查
    GRA_ACC_01 Stock 模式;未允许时禁字节 2 Set/Resume 位 不要求 Cancel 置位,不全面约束其余按钮位
    ACC_06/07 仅纵向表;主加速度 -3.5 至 2.0,未激活 3.01 ACC_07 另一字段固定要求 3.02
    LDW_02 / ACC_02 无本模式专属载荷分支 白名单不证明仪表语义或 ECU 接受

    面向自制硬件的插件契约

    车型插件应提交 MQB 平台、DBC、网关或相机线束、固件构建及纵向授权证据,未知组合保持只读。总线插件要分别标注 LH_EPS_03 的 bus 0 RX 与 bus 2 TX,以及 Stock 模式 GRA_ACC_01 的 bus 0/2 TX,不能按单一 ID 推断方向。

    转向与纵向插件只提供意图;可信任网关负责编码、节拍、HUD 与 LH_EPS_03 模板及 ECU 静默回执,最终 TX 仍由独立 MCU safety 决定。故障注入须覆盖两路制动输入失效、主开关或按钮沿错序、CRC 与计数故障、拒帧、错总线,以及纵向固件不支持时的降级。

    证据与缺口

    三项模式离线探针直接运行编译版 safety:调试构建的 Stock 模式允许 bus 2 的 GRA_ACC_01,纵向模式拒绝它;用 DBC 打包的未激活加速度 ACC_06=3.01 m/s² 仅在调试纵向模式通过。以同一 LONG 参数但无 ALLOW_DEBUG 编译后,bus 2 的 GRA_ACC_01 再次可通过、该合法未激活 ACC_06 被拒,说明该测试构建实际退回 Stock TX 表。

    这里说的「无调试宏」只表示本机 C 测试库的预处理条件,不等于目标 MCU 固件已验证。现有 test_volkswagen_mqb.py 覆盖扭矩、冗余制动、Stock 按钮、纵向加速度与按钮使能;本机运行该模块 65 项,9 项跳过,其余通过,但仍没有目标板或实车数据,不能宣称车载可用。

    常见问题

    Stock ACC 与自控纵向模式的 TX 白名单差在哪?

    Stock 模式在 bus 0 与 bus 2 发 GRA_ACC_01;自控纵向模式完全不发该帧,改为在 bus 0 发 ACC_02/06/07。两种模式的 RX 都是同一七组、都在 bus 0。

    为什么 LH_EPS_03 的 RX 在 bus 0、TX 却在 bus 2?

    这是该实例的本地总线角色分工:RX 取自 bus 0,候选 TX 在 bus 2,用于 Emergency Assist 兼容。不能因为 ID 相同就把两个方向混为一个。

    忽略 checksum 与计数器的两帧是不是等于完全没检查?

    不等于。ESP_19 与 Motor_14 虽然忽略 checksum 与计数,但仍留在 RX 存活表中,仍受地址、总线、长度与存活检查约束。

    上层 CarParams 开了纵向能力就代表固件允许发 ACC 吗?

    不代表。纵向配置依赖 FLAG_VOLKSWAGEN_LONG_CONTROL,且只有 ALLOW_DEBUG 构建才读取它;上层能力位不能单独证明发布固件允许发送 ACC。探针显示无调试宏编译时会退回 Stock TX 表。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/VOLKSWAGEN_MQB_PANDA_SAFETY_DEEP_DIVE.md(取用了双模式总线矩阵、RX 完整性表、TX 字段门与空白、模式离线探针结论)。
    • 项目 02 —— openpilot-research/VOLKSWAGEN_MEB_PANDA_SAFETY_DEEP_DIVE.md(同品牌关联专题,用于对照 MQB 与 MEB 的差异,本文未展开其 Gen2 CRC 细节)。

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

    更新记录

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

  • Honda panda safety:RX/TX 白名单与 AEB 转发

    结论:Honda panda safety 的规则分四段:RX 检查表(地址/总线/长度/频率 + nibble checksum 与二位计数器)、使能状态(主开关、PCM/SET-RESUME 使能沿)、TX 白名单与内容钩子、以及转发钩子。honda_tx_hook() 的 Honda 内容钩子在 controls_allowed=False 时拒绝非零转向,但源码并未在该钩子内对使能时的转向幅度或变化率作额外限制,那部分由主机 CarController 承担。拒帧还可能产生状态副作用:本地 C 探针显示,一帧被拒的主机 0x1FA 仍会先写入全局 honda_brake,从而影响下一次原车 AEB 转发决策。以下为固定源码快照 c8fb906 与本地 C 探针的静态分析,不证明目标板固件编译选项、实际 CAN 总线或整车安全性。

    关键函数与组件

    入口 源码 作用
    honda_nidec_init() / honda_bosch_init() honda.h 按参数选择 RX 期望、TX 地址/总线/长度白名单和平台状态。
    honda_get_checksum() / honda_compute_checksum() / honda_get_counter() honda.h 解码 Honda checksum/计数器,供通用接收检查使用。
    safety_rx_hook() / honda_rx_hook() safety.h、honda.h 通用验证通过且属于 RX 检查表后,才更新 Honda 使能、踏板与 AEB 状态。
    generic_rx_checks() / safety_tick() safety.h 踩刹车等通用退出,以及每秒检查关键 RX 消息是否超时/失效。
    safety_tx_hook() / honda_tx_hook() safety.h、honda.h 通用白名单加 Honda 内容检查;最终还需无继电器故障。
    safety_fwd_hook() / honda_nidec_fwd_hook() safety.h、honda.h 通用阻断冲突帧后,Nidec 对相机原车制动执行动态转发决策。

    RX 与使能状态

    Honda RX 检查表把 SCM_BUTTONS 两种地址、ENGINE_DATA、POWERTRAIN_DATA 纳入核心检查;多数模式还检查 SCM_FEEDBACK,备用刹车模式加 BRAKE_MODULE,Nidec 还检查相机总线 BRAKE_COMMAND。每项声明地址、总线、长度和频率,部分声明最大计数器。Honda 钩子提供 nibble checksum 和二位计数器解码;通用 safety_rx_hook() 仅在消息匹配、校验有效且属于 RX 表时才调用 honda_rx_hook()。safety_tick() 每秒检查迟到/无效关键消息并清除 controls_allowed,不能把 25/100 Hz 配置频率误读为毫秒级 fail-safe deadline。

    honda_rx_hook() 从 ENGINE_DATA 更新是否行驶,从 SCM 更新 ACC 主开关,主开关关闭直接禁止控制。Bosch 原车巡航和 Nidec 使用 PCM 巡航上升沿允许控制;Bosch 巡航退出会撤销许可,但 Nidec 允许在 PCM 退出后继续制动至零。Bosch 自控纵向改用 SET/RESUME 的释放沿使能,MAIN/CANCEL 则退出。用户制动有普通 POWERTRAIN_DATA 的连续开关样本判据或备用 BRAKE_MODULE 来源;随后通用 generic_rx_checks() 再按制动上升沿/车辆运动状态撤销控制。主机侧 CarState 字段不替代 MCU 的这一路独立采样。

    TX 白名单与内容规则

    通用 safety_tx_hook() 先核对当前模式 TX 表中的地址、总线和长度,再调用 Honda 内容钩子,最终还受 relay_malfunction 阻断。Nidec 允许指定转向、BRAKE_COMMAND、ACC_HUD 和 LKAS_HUD;Bosch 普通、纵向、radarless、CAN FD 各有不同表。Bosch 纵向 safetyParam 只在 ALLOW_DEBUG 编译条件下转成纵向模式能力,不能由 Python CarParams 位推断实板支持。

    内容钩子在 controls_allowed=False 时拒绝非零转向;源码未在该钩子内对使能时的转向扭矩幅度、变化率或驾驶员反向扭矩作额外限制。主机 CarController 自身会做变化率和车型 STEER_MAX 处理,但这是与 MCU TX 规则不同的层。当前 Honda 安全测试的转向专项只断言未使能零值可发、非零值被拒,不能把通用车型的转向扭矩安全测试结果自动套给 Honda。Nidec 对 ACC_HUD 的 PCM 速度/gas 和 BRAKE_COMMAND 的制动量做纵向许可检查,并阻止主机设置 AEB 执行位;Bosch 有雷达的 ACC_CONTROL 检查加速度、gas 和 AEB 位,radarless 的加速报文走另一地址与编码。所有这些规则只约束通过当前 TX 表的候选,不能保证 ECU 实际接受。

    Nidec AEB 转发及状态顺序

    相机总线 0x1FA 通过 RX 检查后,如果未设置「禁用原车 AEB」的替代体验位,Honda 钩子比较原车制动力与最近记录的主机制动力:原车 AEB 不活动时清掉转发锁存;AEB 活动且原车制动力不小于主机记录值时置位;较小时保持之前状态。通用转发层先按继电器故障和静态冲突表阻断,Nidec 专属钩子再决定是否允许相机 0x1FA 走向另一总线。

    honda_tx_hook() 读取 Nidec 主机 0x1FA 时,先写全局 honda_brake,后检查制动上限、是否正在转发原车 AEB 和 AEB 位。本地拒帧探针直接编译仓库的 libsafety/safety.c:在 Nidec 模式下,相机 AEB 0x1FA 请求制动 100,基线转发;重新初始化后,先送主机 0x1FA 请求制动 200 且带 AEB 执行位,该帧被 TX 钩子拒绝,再送相同的相机帧,RX 有效但转发被阻断。严格编译和 UBSan 运行输出:

    场景 相机帧 RX 转发 说明
    基线 有效(rx=1) 允许(forward=1) 无先前主机帧干扰。
    先送被拒主机帧 — 候选未被接受(accepted=0) 主机制动 200 且带 AEB 位,被 TX 钩子拒绝。
    随后相机帧 有效(rx=1) 被阻断(forward=0) RX 有效但转发被阻断,证明拒帧有状态副作用。

    这证明当前测试库实现中拒帧可影响下一次 AEB 转发决策,尚不证明目标实板的编译配置、真实消息时序或车辆制动后果。

    自制硬件与插件验收

    车型插件只能提交受限候选,MCU 必须独立持有模式、RX 有效性、TX 白名单、超时与转发规则。Honda 的不同模式要分别验证 PT/camera 总线、备用刹车、Nidec 原车 AEB、Bosch ALLOW_DEBUG 门、诊断固定载荷和失联默认状态。探针应扩展为正式回归测试:对越界、带 AEB 位、因原车 AEB 锁存而被拒的主机 0x1FA,都断言下一帧的转发状态只依据已准许发送的主机制动记录。还需检查使能时转向幅度由哪些独立物理/软件约束承担,不能把 Python 限幅写成 MCU 的保证。本文未运行完整 safety 测试或实板故障注入。

    常见问题

    Honda 的转向扭矩在 MCU 侧会被限幅吗?

    Honda 内容钩子只在 controls_allowed=False 时拒绝非零转向,源码未在该钩子内对使能时的幅度、变化率或驾驶员反向扭矩作额外限制。变化率与车型 STEER_MAX 由主机 CarController 处理,属于不同层次。

    Nidec 用哪种沿使能,Bosch 自控纵向呢?

    Nidec 与 Bosch 原车巡航用 PCM 巡航上升沿允许控制;Bosch 自控纵向改用 SET/RESUME 的释放沿使能,MAIN/CANCEL 退出。Nidec 还允许在 PCM 退出后继续制动至零。

    一帧被拒的主机 0x1FA 会影响后续转发吗?

    会。主机 0x1FA 被 TX 钩子读取时先写全局 honda_brake 再检查,探针显示被拒帧仍能改变下一次相机 AEB 0x1FA 的转发决策,输出为 baseline rx=1 forward=1; rejected candidate accepted=0; stock rx=1 forward=0。

    safety_tick() 是毫秒级 fail-safe 吗?

    不是。它每秒才检查迟到/无效关键消息并清除 controls_allowed,不能把 25/100 Hz 配置频率误读为毫秒级安全时限。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HONDA_PANDA_SAFETY_RX_TX_FORWARD_DEEP_DIVE.md(RX/TX 白名单、使能状态、Nidec AEB 转发与拒帧探针)。
    • 项目 02 —— openpilot-research/ 同目录 Honda 接口与输出专题(模式参数与控制构帧作为相邻边界参考)。

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

    更新记录

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

  • Volkswagen MEB panda safety:Gen2 CRC 边界

    结论:Volkswagen MEB 的 panda safety 不是「按品牌名放行」,而是一组可逐帧核对的约束:MEB 选择 volkswagenMeb 安全模式与曲率转向,MEB_GEN2 额外设置 MEB_ALT_CRC;所有必需 RX 在 bus 0,使用 CRC-8H2F、四位计数器与存活检查;发送侧只放行 HCA_03、ACC_18、LDW_02、KLR_01、ACC_19、TA_01 六类地址。其中最容易被误读的一点是 Gen2 的替代 CRC:它不是「Gen2 一律不校验尾部」,标准整帧 CRC 仍可能被接受。

    一、车型资格与 RX 格式

    CarInterface._get_params() 为 MEB 选择 volkswagenMeb 安全模式和曲率控制;MEB_GEN2 设置 MEB_ALT_CRC。网关接线还由指纹判断,不能从品牌名推断物理总线。volkswagen_meb_init() 初始化 Gen1/Gen2 的 RX 长度与统一 TX 白名单,其中 Gen2 的 Motor_51 由 32 字节变为 48 字节,ESC_51 由 48 字节变为 64 字节。

    RX 报文 频率 Gen1 / Gen2 长度 作用
    LH_EPS_03 100 Hz — 驾驶员扭矩。
    Motor_14 10 Hz — 制动状态,再由通用 RX 规则参与退出。
    GRA_ACC_01 33 Hz — Set/Resume 释放沿且主开关开时使能;Cancel 位使其退出。
    QFK_01 50 Hz 32 字节 测量曲率;本模式 max_curvature_error=0,不运行相对误差限幅。
    ESP_21 50 Hz — 独立速度源。
    Motor_51 50 Hz 32 / 48 字节 ACC 状态 2 表示主开关可用,3/4/5 表示巡航接合;否则撤销 controls_allowed。
    ESC_51 50 Hz 48 / 64 字节 四轮原始速度判断移动并形成主速度。

    频率是检查配置,并非对每帧的实时 deadline 保证;计数器从字节 1 低四位、报文 CRC 从字节 0 读取。双车速这条链尤其要谨慎:ESC_51 形成一个速度源,ESP_21 提供独立速度源,曲率安全 helper 调用 speed_mismatch_check(),但不能用任一消息单独证明双源一致。

    二、Gen2 替代 CRC 的真实边界

    Gen2 替代 CRC 只影响 QFK_01、ESC_51 和 Motor_51。其逻辑是:先按整帧算标准 CRC;若开启替代标志,再按 28/60/44 字节前缀和另一组 salt 计算,只有前缀计算值等于字节 0 时才返回该值。因此标准整帧 CRC 仍可能被接受,前缀算法的接受范围则要结合车辆实报文确认。

    源文档给出的三项离线探针把这条边界说得很具体:对 QFK_01 的 32 字节帧,写入前缀 CRC 后修改索引 30 仍被接受,修改索引 20 则被拒;写入整帧 CRC 的帧修改索引 30 被拒。原因是 QFK_01 前缀长度 28 含校验字节,因此前缀算法不保护尾部索引 28..31。该结论只针对本机构造的 QFK_01 报文,不能外推为 Gen2 所有消息都忽略尾部或真实 ECU 会产生这种变异。

    三、TX 白名单与安全门

    地址 bus/长度 专属载荷检查
    0x303 HCA_03 0/24 曲率、请求状态为 4 时的激活位、转向功率;共享曲率 helper。
    0x14D ACC_18 0/32 加速度、HMS 停车状态、起步/保持位与 ACC 活动状态。
    0x397 LDW_02 0/8 无本模式专属载荷分支。
    0x25D KLR_01 0/8 与 2/8 无本模式专属载荷分支;仅 bus 2 项带 check_relay。
    0x300 ACC_19 0/48 无本模式专属载荷分支。
    0x26B TA_01 0/8 无本模式专属载荷分支。

    通用 safety_tx_hook() 先按地址、bus、长度白名单过滤,再调用车型 TX 钩子,最终还受继电器故障阻断;静态转发只在 bus 0↔2 间,阻断目的总线中带 check_relay 的地址。TX 准入不是 ECU 已接受、仪表内容正确或原车 AEB 不受影响的证明。

    ACC_18 的加速度量纲为 0.001 m/s²,活动范围 -3.5 至 2.0,未激活专用码为 3.01;停车请求 HMS=1(保持)或 4(起步)仅在允许控制时可发,0(无请求)与 5(坡道释放)可在退出后发。HCA_03 采用有符号曲率、请求位和功率三类输入,最大绝对曲率 29105、功率 125、名义频率 50 Hz;未请求转向时曲率必须为 0、功率必须为 0。一个关键细节:控制许可撤销后,带请求位的严格递减功率可作为退出时的降功率路径,不能把它误写成「失能后所有请求位帧必拒」。

    四、自制硬件与插件边界

    车型插件须提交 MEB 代际、DBC、接线位置和 CRC 变体的证据;未经确认时保持只读,不能仅按 ID.4/Born 名称自动授予 TX。感知/定位插件应分别报告 ESC_51 与 ESP_21 的来源、时间戳及健康;控制插件不能自行合并双车速再宣称冗余检查已通过。纵向控制插件应输出带语义的「加速度 / 保持 / 起步 / 坡道释放 / ACC 状态」,由可信任网关负责编码与状态序列,MCU 保留独立的字段检查和失联门。

    HUD、TA 与 KLR 白名单帧缺专属载荷检查,插件网关需给这些帧最小模板、速率和所有权,不能开放任意原始 CAN 写入。验证顺序应包含 Gen1/Gen2 CRC 尾部变异、错长度/计数、双车速失配、按钮沿、失联时保持/释放与功率降阶。现有 test_volkswagen_meb.py 覆盖两代配置、计数/校验、双车速、曲率功率、加速度与停车状态;本机运行该模块 90 项,7 项跳过,其余通过,但它不代替上述 Gen2 前缀/整帧 CRC 尾部变异探针,也未验证目标硬件。

    常见问题

    Gen2 是不是「不校验尾部 CRC」?

    不是。Gen2 替代 CRC 只影响 QFK_01、ESC_51 和 Motor_51,且先按整帧算标准 CRC;只有前缀计算值等于字节 0 时才返回该值,所以标准整帧 CRC 仍可能被接受。QFK_01 前缀长度 28 含校验字节,故前缀算法不保护尾部索引 28..31,这只是一个具体探针结论,不能外推为所有消息都忽略尾部。

    两条车速报文能互相证明冗余吗?

    不能。ESC_51 从四轮原始速度形成主速度,ESP_21 提供独立速度源,曲率 helper 调用 speed_mismatch_check(),但源文档明确不能用任一消息单独证明双源一致。控制插件也不能自行合并双车速再宣称冗余检查已通过。

    失能后所有带请求位的转向帧都会被拒吗?

    不是。源文档指出控制许可撤销后,带请求位的严格递减功率可作为退出时的降功率路径,相邻测试 test_power_without_control() 为此设了专门断言,不能把它误写成「失能后所有请求位帧必拒」。

    按车型名 ID.4/Born 就能自动授予发送权限吗?

    不能。车型插件须提交 MEB 代际、DBC、接线位置和 CRC 变体的证据,未经确认时保持只读;源文档明确不能仅按 ID.4/Born 名称自动授予 TX。网关接线位置还由指纹判断,不能从品牌名推断物理总线。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/VOLKSWAGEN_MEB_PANDA_SAFETY_DEEP_DIVE.md(取用了 MEB 车型资格、RX 格式与使能链、Gen2 替代 CRC 边界、TX 白名单与安全门、硬件插件边界)。

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

    更新记录

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

  • Hyundai 经典 CAN panda safety:三模式权限矩阵

    结论:Hyundai 经典 CAN panda safety 把「标准雷达 SCC / 标准相机 SCC / Legacy」三种模式做成一张权限矩阵:同一辆车的按钮 RX 固定在 bus 0,而 TX 可能落在 bus 0 或 bus 2,纵向能力只在调试构建生效。Legacy 也不是全面关闭 RX 校验,放宽的只有 0x386 与 0x394,0x260、0x4F1、0x421 仍保留各自的校验或计数门。以下为固定源码快照 c8fb906 的函数级静态分析,不是目标固件或实车验证;bus 号指单个 safety 实例的本地总线编号,车型、线束与 ECU 版本需另行确认。

    初始化与模式选择

    CarInterface._get_params() 在 hyundai 与 hyundaiLegacy 之间选择,并设置相机 SCC 与纵向能力。需要注意的是 openpilotLongitudinalControl 只是上层请求,不是固件准入证明。hyundai_init() 再依据纵向、相机 SCC、FCEV 标志选择 RX 检查表与 TX 白名单;hyundai_legacy_init() 使用 Legacy RX 表,并强制关闭纵向和相机 SCC,仅保留基础 TX 表。

    共享的 hyundai_common_init() 在非调试构建中强制 hyundai_longitudinal=false。也就是说纵向链在源码层面就带着 ALLOW_DEBUG 编译门,不能由 Python 侧 CarParams 位推定量产固件可发。

    模式 SCC12 RX CLU11 TX 纵向/附属 TX 关键差异
    标准雷达 SCC、非纵向 bus 0 bus 0,4 字节 无 SCC TX 巡航接合沿使能;按钮只许受门控的 Resume/Cancel。
    标准相机 SCC、非纵向 bus 2 bus 2,4 字节 无 SCC TX 按钮 RX 仍是 bus 0,不能把 TX 所在总线误当按钮输入来源。
    标准雷达 SCC、纵向 不列为必需 RX bus 0,4 字节 SCC11/12/13/14、FRT_RADAR11、FCA11/12、0x7D0 在 bus 0 LONG 仅 ALLOW_DEBUG 有效;不经原车 SCC 接合沿使能。
    标准相机 SCC、纵向 不列为必需 RX bus 2,4 字节 SCC11/12/13/14、FRT_RADAR11 在 bus 0 相机 SCC 纵向不使用雷达静默诊断白名单。
    Legacy bus 0 bus 0,4 字节 无 SCC TX 强制非纵向、非相机 SCC;仅部分 RX 的完整性检查放宽。

    所有模式均可发送 bus 0 的 0x340 LKAS11 与 0x485 LFAHDA_MFC。纵向 TX 表中的 SCC11/12/13/14 对 bus 0 标记 check_relay=true;若原 ECU 在该总线继续发送同地址帧,共享继电器故障逻辑可阻断 TX。safety_fwd_hook() 只提供通用 bus 0↔2 转发,并静态阻断将 check_relay 地址送往本机 TX 总线,这不是 ECU 已被成功静默的证明。

    RX 完整性与状态消费

    各地址使用不同的计数位与半字节校验:0x386 使用位计数/XOR,其余适用帧使用半字节和。RX 完整性检查先于车型钩子执行,通用 safety_rx_hook() 只在消息匹配、校验有效且属于 RX 表时才调用 hyundai_rx_hook()。

    帧 检查表 实际消费
    0x260 EMS16 或 0x371 E_EMS11 同一备选组;0x260 检查 2 位计数和校验,0x371 忽略二者 ICE 从 0x260 读油门,EV/HEV 从 0x371 读油门。
    0x386 WHL_SPD11 标准模式校验加 4 位计数;Legacy 忽略二者 只用左前、右后轮速判断移动,阈值原始值 12。
    0x394 TCS13 标准模式校验加 3 位计数;Legacy 忽略二者 字节 5 的 DriverOverride 等于 2 判制动。
    0x251 MDPS12 忽略校验/计数,但监视存活 11 位驾驶员扭矩进入共享采样窗口。
    0x4F1 CLU11 忽略校验、检查 4 位计数 bus 0 上解析按钮和主开关,更新近期交互或纵向按钮边沿。
    0x421 SCC12 只在非纵向为必需;校验加 4 位计数 按相机 SCC 标志从 bus 2 或 bus 0 读取 ACC 模式。
    0x91 FCEV 油门 仅特定标准雷达 SCC 或纵向分支附加;忽略校验/计数 FCEV 标志下读取字节 6;标准相机 SCC 非纵向表没有该附加检查。

    FCEV 分支中 0x91 更新油门,但 0x260 的 ICE 回退条件仅排除 EV/HEV;若车辆也出现 0x260,后续该帧仍可能改写油门状态,须用目标车报文核对,不能把单帧观测当成最终状态。

    TX 字段边界与使能条件

    转向 0x340 从 11 位字段还原扭矩与请求位:普通/ALT_LIMITS/ALT_LIMITS_2 的最大扭矩分别为 384/270/170,每帧上升分别为 3/2/2、下降为 7/3/3,均使用实时差 112、驾驶员扭矩余量 50 与请求位节拍容忍;两种替代标志同时存在时 ALT_LIMITS_2 优先。非纵向的 0x4F1 只允许 controls_allowed 时 Resume,或 cruise_engaged_prev 时 Cancel;纵向模式跳过整个按钮 TX 分支,白名单内的 0x4F1 不受该专属按钮码限制,这是插件权限设计必须额外收紧的差异。

    纵向加速度 0x421 同时核对 aReqRaw 与 aReqValue,单位是 0.01 m/s²,活动范围为 -350 到 200,并禁止 AEB 减速度命令、AEB 请求和停止请求。共享加速度检查允许未激活编码,不能仅用数值落在范围内推断准入。雷达 SCC 纵向表包含 0x38D FCA11,专属钩子禁止其减速度/AEB 激活字段;0x483 FCA12 仅有地址/总线/长度白名单;0x7D0 仅雷达 SCC 纵向可发送,载荷必须是 02 3E 80 00 00 00 00 00。SCC11/13/14、FRT_RADAR11 和 HUD 没有该模式的逐字段校验,白名单准入不代表 ECU 接受,也不代表原车 AEB、FCW 或仪表状态保持不变。

    面向自制硬件的插件契约

    源文档给出的是一组建议设计而非已实现能力:车型身份插件应输出带证据的标准/Legacy、SCC 来源、动力类型与转向限制,未知或冲突组合不授予 TX;总线插件同时报告 RX/TX 的物理接口与本地 bus,尤其记录相机 SCC 模式下按钮 RX bus 0、TX bus 2 的差异。

    纵向插件只能提交控制意图;可信任网关需核对构建类型、MCU mode/param 读回、ECU 静默回执、原车 AEB 功能与失联降级,再交由独立 MCU safety 门裁决。对纵向 CLU11、FCA12、HUD 与 SCC 附属帧应另设最小载荷模板和速率配额,不能因原模式白名单存在就向普通插件开放原始 CAN 写入。现有 test_hyundai.py 覆盖标准/Legacy、替代转向限值、相机 SCC、FCEV 与纵向 AEB/诊断路径,但本文未运行这些单测,模型实体、目标固件、台架及实车行为仍未验证。

    常见问题

    Hyundai Legacy 模式是不做 RX 校验吗?

    不是。Legacy 只放宽 0x386 和 0x394 的校验与计数检查;0x260、0x4F1、0x421 仍保留各自的校验或计数门。同时它强制关闭纵向和相机 SCC。

    相机 SCC 模式下按钮输入在哪个总线?

    按钮 RX 仍是 bus 0,只是 CLU11 的 TX 落在 bus 2。源码明确说明不能把 TX 所在总线误当成按钮输入来源。

    Hyundai 纵向控制是不是打开参数就能用?

    不是。LONG 仅在 ALLOW_DEBUG 编译条件下有效,共享 hyundai_common_init() 在非调试构建中强制关闭纵向。Python 侧 CarParams 位不能作为量产固件可发的证明。

    0x7D0 的载荷能自由填吗?

    不能。源文档说明 0x7D0 仅雷达 SCC 纵向可发送,载荷必须是固定的 02 3E 80 00 00 00 00 00。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HYUNDAI_CLASSIC_PANDA_SAFETY_DEEP_DIVE.md(模式权限矩阵、RX 完整性表、TX 字段边界与插件契约)。
    • 项目 02 —— openpilot-research/ 同目录 Hyundai/Honda 品牌总览(SCC 与纵向能力作为相邻边界参考)。

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

    更新记录

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

  • 横向限幅四种算法:主机候选与 panda 判决为什么不是一件事

    结论:横向限幅分成扭矩、转角、曲率三条路径,共享默认横向加速度约 3.6 m/s²、jerk 约 3.6 m/s³(含道路横滚容差)。它们只是主机侧的候选值计算,不能等同于 panda 的发送许可;同一场景下主机函数输出并不保证每个瞬时输入同时满足加速度与 jerk 两项物理限值。插件可以输出带单位的候选扭矩/角度/曲率,但固件必须保留独立最终许可。

    三类配置与四条算法路径

    AngleSteeringLimits、AngleSteeringLimitsVM、CurvatureSteeringLimits 分别对应速度表转角、车辆模型转角和曲率。它们的共同特点是:配置类不检查车型身份、传感器健康或 CAN 权限,具体值由车型提供。下面是四条主要路径:

    入口 主机行为 主要消费者
    apply_driver_steer_torque_limits() 据驾驶员力矩与 allowance/factor/multiplier 算上下限,再对上次请求作非对称上升/下降速率限制,最后取整 Hyundai、GM、Subaru、Mazda、MG、Rivian、Volkswagen 等扭矩车型
    apply_dist_to_meas_limits() / apply_meas_steer_torque_limits() 先约束命令与实测量的误差带和绝对上限,再按上次请求限制每帧变化;误差带以 STEER_ERROR_MAX 给零附近保留范围 Toyota、Chrysler 等 EPS 电机力矩测量
    apply_std_steer_angle_limits() 按请求是否同向增大绝对值,在两张车速-角速度表间选择;插值后限单步变化,停用横向时改用实测转角,再限绝对角度 Nissan、PSA、Toyota 角度路径
    apply_steer_angle_limits_vm() 用 MAX_LATERAL_JERK/v² 反算每次可变转角,用 MAX_LATERAL_ACCEL/v² 反算绝对角;车速至少取 1 m/s Tesla、BYD 以固定安全车型参数建模

    注意 apply_driver_steer_torque_limits() 里的 steer_max 可动态覆盖静态上限。VM 路径还会用 STEER_STEP*DT_CTRL 将角速度换算为每次发送增量——调用周期由车型控制器决定,不是函数自己假设的常量。

    非扭矩路径的数值顺序

    曲率路径 CurvatureSteeringLimits.apply_limits() 的裁剪顺序固定,且这个顺序会产生反直觉的结果:

    步骤 动作
    1 按 a_max/v² 限曲率
    2 按 jerk_max/v² * steer_step*DT_CTRL 限相对上次请求的增量
    3 停用时以调用方传入的当前曲率替换
    4 最后限固定 CURVATURE_MAX

    当上次命令在当前车速下已经超出新的加速度界限时,jerk 区间可能与加速度区间不相交;后一道 np.clip 会把先前的加速度限值「拉回」上次命令附近。源文档给了一个可复算的例子:用 Ford 量级的 CURVATURE_MAX=0.02 1/m、MAX_LATERAL_ACCEL=MAX_LATERAL_JERK=3、车速 20 m/s、上次与本次请求均为 0.02 1/m、steer_step=1,本地原函数输出 0.019925 1/m——它高于当前加速度导出的 3/20²=0.0075 1/m,但仍低于固定绝对曲率上限。

    这说明了什么?主机此函数不保证每个瞬时输入同时满足两项物理限值。源文档很克制地补了一句:这并非证明真实 Ford 配置或 panda 固件会放行该帧;实际车型的速度、加速度配置、帧步长及固件独立检查需另核对。停用时的行为也值得记住:曲率和两种转角函数先前的动态限幅结果会被当前测量值替换,再只受最终绝对上限约束。

    主机不等于固件:同一场景的 safety helper 对照

    板端 steer_curvature_cmd_checks() 对启用的曲率命令分别 OR 累积固定绝对曲率、按板端车速推导的横向加速度、每帧 jerk 和实时发报频率等违例——加速度检查不会被后续 jerk 检查覆盖。这与主机的顺序差异,正是「主机候选」和「固件判决」不是同一件事的根源。

    Ford FORD_STEERING_LIMITS 将 0.02 1/m 映射为 1000 CAN 单位,20 Hz 发报;主机 CarController 每五个 100 Hz 周期调用一次共享曲率限幅。跨层探针的对照结果是:车速 20 m/s、上次曲率 0.02 1/m 的候选值被拒;同速度下界内的 0.008 1/m 候选通过。源文档强调这是共享函数与独立板端 helper 的对照证据,不是实际整车拒帧率或驾驶性能证明。

    辅助逻辑:故障规避与摩擦前馈

    common_fault_avoidance() 只在连续 request && fault_condition 时累计;超过阈值暂时拉低请求位,到 max_above_limit_frames+max_mismatching_frames 重置计数。Hyundai 的大转角、Toyota/Subaru 的高转角速度等复用此逻辑。它有两条边界:返回的是新计数和请求位,不清除同帧扭矩,也不自行核验 MCU 对请求位不匹配的容忍度。

    摩擦侧,apply_center_deadzone() 在死区内把误差归零,get_friction() 在 +/-FRICTION_THRESHOLD 之间线性插值摩擦前馈、区间外饱和。摩擦输出按 friction*latAccelFactor 缩放,不是原始扭矩命令——把它当作扭矩值会算错整整一个量纲。

    插件与硬件建议

    源文档给出的插件契约是:把横向算法插件限制为输出带单位的候选扭矩/角度/曲率;宿主负责车型身份和参数绑定、消息新鲜度、活动门及主机限幅,panda 固件保留独立最终许可。每个参数包应带车型、总线布局、固件安全模式、版本和发帧周期,拒绝跨车型或不完整配置;动态标定不能自动改变固件许可。

    在验证缺口上,test_lateral_limits.py 只对非 mock、非 notCar 的扭矩平台做静态估算,且明确跳过非扭矩平台,不直接执行限幅函数,也不验证 CAN 帧或 panda 判决;CarController.update() 全路径、NaN、真实车辆 CAN、目标固件和实车反馈均尚未验证。硬件验证的顺序应是:离线回放/隔离台架比较主机候选值与固件判决,覆盖驾驶员抢方向、车速跳变、实测量失联、请求位切断以及停用瞬间,再讨论受控场地测试。

    常见问题

    主机限幅通过就等于车辆会执行吗?

    不等于。主机函数只产生候选值;steer_torque_cmd_checks() 与 steer_curvature_cmd_checks() 在 panda 内另有状态化检查(固件车速、双车速差、曲率绝对值、加速度、jerk、实测误差等)。源文档明确主机函数不是这些状态化检查的替身。

    为什么曲率限幅可能不满足加速度门?

    因为 CurvatureSteeringLimits.apply_limits() 的顺序是加速度 → 相对上次命令的 jerk → 停用时实测曲率 → 固定绝对曲率。当上次命令已超出新的加速度界限时,后一道 np.clip 会把先前的加速度限值拉回上次命令附近,因此不保证每个瞬时输入同时满足两项限值。

    3.6 m/s² 和 3.6 m/s³ 是通用限制吗?

    它们是三类配置共享的默认量级(含道路横滚容差),但具体值由车型提供。例如 VM 路径用固定安全车型参数,Ford 量级例子用的是 CURVATURE_MAX=0.02 1/m 与 MAX_LATERAL_ACCEL=MAX_LATERAL_JERK=3。所以不能把它当成跨车型的通用上限。

    摩擦前馈的输出是扭矩命令吗?

    不是。get_friction() 的输出按 friction*latAccelFactor 缩放,由 LatControlTorque.update() 消费,不是原始扭矩命令。混淆两者的单位会得到错误结果。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/LATERAL_LIMITS_HOST_SAFETY_PLUGIN_CONTRACT_DEEP_DIVE.md(取用了四条算法路径、曲率裁剪顺序与数值例子、板端 helper 对照与插件建议)。
    • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(仅用于确认 safety_tx_hook 与 CANPacker 在横向候选之后的分工)。

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

    更新记录

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