标签: CRC

  • opendbc 车型校验算法族:同名 CRC8 不可替换,RX/TX 共用函数

    结论:opendbc 的车型校验不是「一种 CRC8 走天下」,而是按品牌/平台硬绑定的一族函数:Honda 用半字节补码、Toyota 把帧长本身算进和、Chrysler 用 CRC8 J1850(初值与末异或均为 0xFF)、Hyundai CAN FD 用 CRC16 XMODEM、VW MQB/MEB 用 CRC8 H2F 再追加地址常量。同名「CRC8」只代表多项式查找表相同,地址、帧长、字节顺序与最终 XOR 各不相同,不构成车型间可替换的插件接口。更关键的是,RX 校验与 TX 生成共用同一个函数,任何「只改发送端」的假设都必须重新核对。

    每个校验函数都写死了覆盖字节与校验位位置

    在固定源码快照里,get_checksum_state() 按 DBC 名称与信号名把每帧绑定到一个具体函数。所谓「算法」其实由四件事共同决定:哪些字节进入计算、校验字段在哪个字节或半字节、初始值与末异或是什么、帧长是否参与。源文档明确指出,帧长度和字段位置本身就是协议的一部分,不能当作可省略的装载细节。

    下面的函数目录来自源文档的逐函数静态核对。注意「参与计算的内容」一列——同一种「CRC8」在 Chrysler、VW、body 三处覆盖的字节范围完全不同。

    家族 / 函数 实际参与计算的内容 重要前提
    honda_checksum() 地址各半字节、每个载荷字节的高低半字节;末字节仅取高半字节,结果为 8 - sum,扩展 ID 再加 3,截 4 位。 假定校验位在最后字节低半字节;扩展地址判据是 address > 0x7FF。
    toyota_checksum() len(d)、地址各字节和除最后一字节外的载荷字节,相加截 8 位。 假定校验在末字节,帧长本身参与计算。
    subaru_checksum() 地址各字节与除第一字节外的载荷,相加截 8 位。 Global 版本校验位默认在首字节;和 pre-global 手动辅助函数不是同一算法。
    chrysler_checksum() 对末字节以外数据查 CRC8 J1850 表,初值和末异或均为 0xFF。 不把地址计入此函数;checksum 按末字节处理。
    fca_giorgio_checksum() 同表、初值 0,末异或按地址 0xDE/0x106/0x122/其他 分别为 0x10/0xF6/0xF1/0x0A。 未识别地址落入通用 0x0A 分支;不能把四组样本扩大为所有 FCA 报文。
    hkg_can_fd_checksum() 从第 2 字节到末尾做 CRC16 XMODEM,再加入地址低、高字节;仅 8/16/24/32 字节追加特定末异或常量。 前两字节视作校验字段;其它长度不加表中常量,但函数自身不会拒绝。
    volkswagen_mqb_meb_checksum() 初值 0xFF,对第 1 字节起载荷做 CRC8 H2F;已知地址再按第二字节低半字节选 16 项常量追加一步,最终异或 0xFF。 首字节为校验位;未在常量表中的地址跳过附加常量步骤。
    tesla_checksum() 地址低、高字节加上除 checksum 字节外的所有载荷,截 8 位。 校验位置由 sig.start_bit 决定,不限固定末字节。
    body_checksum() 从倒数第 2 字节向首字节反向迭代做 CRC8 BODY,初值 0xFF。 仅最后一字节不进入计算,函数不检查其字段含义。
    byd_checksum() 前七字节之和按位取反再截 8 位。 固定覆盖前七字节;不是通用「跳过任意校验字段」的算法。
    psa_checksum() 地址选择 4 位初值,先在传入 bytearray 中清除校验半字节,再把全帧高低半字节求和,从初值中减去并截 4 位。 函数就地修改传入缓冲;RX 解析器传副本,TX packer 传当前构帧缓冲。

    CRC8H2F、CRC8J1850、CRC8BODY、CRC16_XMODEM 只是按多项式生成的查找表。查找表可以共享,但函数外层的地址、帧长、字节顺序和最终 XOR 无法共享,这正是「同名不同义」的根源。

    RX 校验与 TX 生成共用同一个函数

    源文档反复强调一个契约:RX 验证和 TX 生成走的是同一份实现,而不是「接收一套、发送另一套」。这带来两个直接后果。第一,校验位的位置往往由信号元数据决定,例如 tesla_checksum() 的校验位置由 sig.start_bit 决定,并非固定在末字节。第二,某些函数带有副作用,例如 psa_checksum() 会就地清除并回写传入缓冲里的校验半字节。

    副作用的差别决定了移植风险:接收解析器传入的是 bytearray(dat) 副本,因此就地修改不影响原始帧;而发送打包器传入的是当前工作缓冲。把这类函数当成无副作用纯函数重用,会直接改变构帧结果。同时,VW MLB 的 XOR 变体以 sig.start_bit // 8 定位整字节,说明「校验在哪个字节」不是全局常量,而是逐信号给定的。

    按「接收帧已有校验字节」选择变体,而不是按算法优劣

    VW MEB 提供了一个容易被误读的变体函数 volkswagen_meb_alt_crc_checksum():它对备用表内六个地址先按指定长度算另一组 CRC,只有候选值等于输入 d[0] 时才返回,否则退回标准 MQB/MEB CRC。这个语义是按接收帧里已经带着的校验字节来「认领」变体,并不代表发送端会主动优先采用备用值。

    源文档用三个隔离探针把边界固定下来,结果如下表。它们都使用原版函数与真实查找表,但只在纯函数层面运行,未加载完整车型 DBC、未发 CAN、未测 ECU。

    探针 输入 观察结果 结论边界
    MEB 双 CRC 选择 地址 0x0DB、64 字节零缓冲 备用长度 42,备用结果 0xEB、标准结果 0xED;d[0]=0 时选 0xED,改成 0xEB 时才选 0xEB CANPacker.pack() 默认从零缓冲开始,故不能凭接收端可识别备用 CRC 就推断发送端会按备用算法生成
    Hyundai 长度分支 长度 8/12/16/24/32 的全零缓冲 各长度均给出结果;12 字节路径不会被拒绝,只是不使用四种已列长度常量 仅验证函数分支,不代表 12 字节是该平台合法帧长
    PSA 就地修改 0x452、start_bit=0、输入 AB CD 返回校验半字节 1,同时把首字节原地改为 A0 移植时不能把该函数当无副作用纯函数

    把这几条合起来看:一个「接收端能认」的校验并不自动意味着「发送端会这么写」。当前实际发送函数是否涉及 MEB 那六个地址,需要按车型另做核对,本篇探针不说明已有车型发生过错误发送。

    「跳过校验字节」不是通用能力

    Subaru 有两个函数常被混为一谈:subaru_checksum() 是 Global 版本,校验位默认在首字节;subaru_preglobal_checksum() 则先用 packer 生成字节,再对除 checksum_byte 指定字节(默认第 7 字节)外的字节求和。后者由 pre-global 构帧函数显式填写 Checksum,不由 DBC.get_checksum_state() 自动绑定。同理,byd_checksum() 固定覆盖前七字节,也是一种特定布局,而不是「跳过任意校验字段」的通用规则。

    这些差异说明:校验算法必须和 DBC 名称、报文地址、DLC、校验字段位位置、覆盖字节范围、counter 位置一起版本化。只声明「用了 CRC8」在车型之间没有任何可迁移性。

    给协议插件的验收底线

    新车型的协议插件应把下列字段作为一个整体来管理和验收,缺一项都可能造成接收能过、发送写错,或反之。

    必须版本化的字段 为什么不能省
    DBC 名称、报文地址、DLC 决定帧长与字段位置,帧长本身参与部分算法(如 Toyota)。
    校验字段位位置、覆盖字节范围 Tesla 由 sig.start_bit 决定、Honda 在低半字节,不能固定假定末字节。
    counter 位置 与 checksum 同级参与健康判定,不能只看数值。
    CRC 多项式 / 初值 / 末异或 Chrysler 与 FCA Giorgio 同表不同初值与末异或。0x10/0xF6/0xF1/0x0A
    长度变体与 RX/TX 选择规则 MEB 备用 CRC 只在匹配接收字节时启用;Hyundai 只在 8/16/24/32 追加常量。

    测试层面,应在参考载荷上直接比对计算值与输出字节,并确认解析器确实接受本帧;还要覆盖坏 checksum、截短/超长帧、counter 回绕与未知地址。需要提醒的是,checksum 只检测某类传输错误,不提供发送身份认证,也不构成执行器安全约束——这些仍由可信的车型逻辑、panda safety 和物理台架验收承担。

    常见问题

    为什么同为「CRC8」,不同车型的校验函数不能互相替换?

    因为查找表相同不代表算法相同。Chrysler 用 CRC8 J1850 且初值与末异或均为 0xFF,VW MQB/MEB 用 CRC8 H2F、初值 0xFF 并追加地址常量,body 则从倒数第二字节反向迭代。地址、帧长、字节顺序和最终 XOR 各不相同,源文档明确指出相同「CRC8」名称不构成车型间可替换的插件接口。

    接收端能校验通过的备用 CRC,发送端也会照此生成吗?

    不会自动如此。MEB 备用函数只有候选值等于输入 d[0] 时才返回,否则退回标准 CRC,这是「按接收帧已有校验字节选择变体」的语义;而 CANPacker.pack() 默认从零缓冲开始,不会凭接收可识别就优先采用备用值。当前发送函数是否涉及那六个地址需按车型另核。

    PSA 校验函数可以直接当纯函数复用吗?

    不可以。源文档探针显示,psa_checksum() 对 0x452、start_bit=0、输入 AB CD 返回校验半字节 1 的同时,把首字节原地改成了 A0。接收解析器传的是副本,发送打包器传的是原始工作缓冲,移植时必须区分。

    checksum 通过是否等于报文安全可信?

    不等于。checksum 只检测某类传输错误,不提供发送身份认证或执行器安全约束。源文档明确这些仍由可信的车型逻辑、panda safety 和物理台架验收承担,并且本专题没有实板证据。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/OPENDBC_CHECKSUM_ALGORITHM_FAMILY_RX_TX_CONTRACT_DEEP_DIVE.md(取用了校验函数目录、RX/TX 共用函数的语义、三个隔离探针结果与插件验收字段清单)。

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

    更新记录

    首次发布 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