标签: 安全边界

  • OSCC 开源车辆控制:归一化命令、200 ms 保活与三层安全边界

    结论:OSCC 的价值在于它选了一条最难但最可辩护的路线:不欺骗 CAN 报文、不改原厂安全用例、不破解 ADAS 功能,而是把控制命令注入到转向盘扭矩传感器、油门位置传感器与制动位置传感器这三个物理接口上。代价是必须自己补上原厂本来承担的安全机制——文档里能明确数出来的有三层:enable/disable 门控、每 200 毫秒必须调用一次的看门狗、以及默认开启的操作者超控。任何把这三层当可选项的实现,都偏离了它的设计前提。

    接口定位:低层注入而不是报文欺骗

    这套系统由软件 API、固件与硬件设计三部分构成,通过车载通信网络与控制系统的模块化接口实现计算机控制。它的能力边界可以拆成三件事:向车辆发送控制命令、从车辆的 OBD-II CAN 网络读取控制报文、转发当前车辆控制状态报告(如转向角与轮速)。控制命令并不直接操纵执行器,而是作用于部件 ECU 的传感器输入端——转向盘扭矩传感器、油门位置传感器、制动位置传感器。汽油版起亚 Soul 不具备线控制动能力,因此额外加装了一套辅助执行器来实现制动。

    这种低层接口的意义在于完整保留原厂安全用例:原车的失效检测、冗余与机械链路都还在,注入的只是一个「驾驶员意图」。这与在总线上伪造一帧转向命令有本质区别——后者绕过了原厂所有校验,前者仍然在原厂设计的信号链里。目前官方支持的车型是 2014 年及以后的起亚 Soul(汽油版与纯电版),但 API 与固件在设计时就为新增车型留出结构:按既有车型头文件的格式新增一个头,并补充一个构建选项即可。

    版本矩阵:四件事必须同时对齐

    需要跟踪的版本有四件:传感器接口板(油门与转向,版本印在板正面)、车辆控制模块(纯电版制动)、执行器控制板(汽油版制动),以及共用同一版本号的 API 与固件。文档对版本更新给的评价很重——每次有新版本都必须更新,以确保没有使用带有已知安全问题的版本。这不是礼貌性提醒,因为板卡版本与固件版本的配对关系会直接影响启动自检能否通过。

    板卡 板卡版本 要求的固件版本 说明
    传感器接口板 大于等于 1.0.0 大于等于 1.0.0 承担油门与转向
    车辆控制模块 0.1.0 大于等于 1.1.0 纯电版制动
    执行器控制板 大于等于 1.2.0 大于等于 1.0.0 汽油版制动
    执行器控制板 小于 1.2.0 大于等于 1.0.1 需额外关闭启动自检

    最后一行是唯一需要额外操作的组合:较新的执行器控制板利用了新的引脚做启动阶段的附加安全检查,旧板没有这些引脚,因此构建时要显式关闭制动启动测试,否则自检会失败。这是硬件演进与安全检查相互耦合的直接例子。

    命令契约:归一化取值范围与 200 毫秒保活

    API 只暴露三个发布函数,参数全部归一化,与具体车辆的电压标定解耦:制动与油门位置取 0.0 到 1.0,转向扭矩取负 1.0 到正 1.0,其中负为逆时针、正为顺时针。API 负责把归一化值换算成需要注入的欺骗电压,并内置安全检查以确保不会发出超出车辆预期范围的电压。这个换算层是安全边界的一部分——应用层永远碰不到原始电压。

    保活要求是这套接口里最容易被忽略的硬约束:三个发布函数每一个都必须至少每 200 毫秒被调用一次,否则模块会判定与上位机失去通信并自行禁用。它把「上位机死机」和「主动松开控制」统一收敛成同一种失效结果,这正是看门狗该有的行为。反过来说,一个只在上位机有输出时才发命令的控制循环,一旦进入长时间稳定状态就会自己把系统关掉。

    接口 方向与取值 约束
    oscc_open / oscc_close 通道编号 建立与终止到固件的 CAN 通道连接
    oscc_enable / oscc_disable 无参数 禁用时仍可接收报告,但无法发送命令
    publish_brake_position 0.0 至 1.0 每 200 毫秒至少调用一次
    publish_throttle_position 0.0 至 1.0 每 200 毫秒至少调用一次
    publish_steering_torque 负 1.0 至正 1.0 负为逆时针、正为顺时针,同样需 200 毫秒保活
    subscribe_to_brake_reports 等 回调函数 制动、转向、油门、故障四类报告各自独立订阅
    subscribe_to_obd_messages 回调函数 转发非本系统的 CAN 帧,可按 ID 自行解析

    还有一条实现层面的注意事项:API 会捕获 CAN 帧到达时产生的信号并为其安装处理函数,把有效报文转发给应用。因此应用中对中断敏感的代码段,应在该段执行期间临时阻塞该信号、结束后再解除,否则回调可能在临界区中被触发。

    三层安全边界

    把这些约束放在一起看,OSCC 的安全设计是分层的,任何一层单独失效都不会立刻导致失控。

    层级 机制 失效时的行为
    应用层 oscc_enable 与 oscc_disable 门控 未使能时命令发不出去,但报告照常接收
    通信层 三个发布函数各需 200 毫秒内再次调用 超时即判定通信丢失,模块自行禁用
    信号层 API 内置电压范围安全检查 换算后的欺骗电压被限制在车辆预期范围内
    操作层 操作者超控检测,默认全部开启 驾驶员施加扭矩即退出,转向灵敏度可调
    硬件层 启动自检(新板用新引脚做附加检查) 自检不通过则不上线
    流程层 强制跟随版本更新 避免停留在带已知安全问题的版本

    转向超控为什么默认开启且不建议关闭

    所有模块的操作者超控检测默认开启,但文档对转向给出了额外警告:系统激活时试图抓住转向盘可能导致严重伤害,因此推荐的转向超控方式是制动踏板或急停按钮,而不是去扳方向盘。若确实需要关闭,可在构建时传入关闭标志,此时灵敏度由对应车型头文件中的扭矩差阈值控制。

    这个阈值是一个典型的双向权衡:调低会让模块对超控更敏感,但在转向齿条机械极限或快速反复变向等高扭矩区域产生误判;调高则误判更少,但人工接管转向变得更困难。文档没有给出默认值之外的推荐值,因为最优解取决于具体的转向系统与应用场景,这正是需要在实车上标定的参数。

    构建、刷写与测试命令

    固件用 CMake 构建而非 Arduino IDE,目的是绕开 IDE 的限制并支持命令行刷写。前置条件是 Arduino Core 1.8.5 及以上与 CMake 2.8 及以上。构建时通过车型标志选择目标:汽油版 Soul、纯电版 Soul 与 Niro 各有对应标志。生成的构建目标可以整体构建,也可以按模块单独构建,四个模块分别是制动、CAN 网关、转向与油门。

    环节 命令或标志 说明
    选择车型 构建时传入车型标志 汽油版 Soul、纯电版 Soul、Niro 三选一
    关闭转向超控 追加关闭超控标志 默认开启,不建议关闭
    关闭制动启动测试 追加关闭启动测试标志 仅旧版执行器控制板需要
    发行构建 指定构建类型为 Release 关闭调试符号并开启优化
    单模块构建 制动 / 网关 / 转向 / 油门四个目标 可只构建所需模块
    刷写 单模块上传目标与全量上传目标 按字母顺序连接模块可获得稳定端口分配
    单元测试 打开测试标志后运行单元测试目标 依赖 Cucumber 2.0.0 与 Boost
    属性测试 打开测试标志后运行属性测试目标 依赖 Rust、Cargo 与 libclang

    测试分两类且覆盖角度不同。单元测试以行为描述的方式验证报文处理,例如油门模块的「接收命令」用例会给出传感器读数与两路 DAC 输出值的对照表,四个样例点分别是 0 对应 0、256 对应 1024、512 对应 2048、1024 对应 4096。属性测试则验证控制器库的不变式,一组运行包含积分项、微分项、比例项、反转输入、相同输入产生相同控制、以及清零共七项。两类测试都有分模块的独立运行入口,也有一次性运行全部测试的便捷入口。

    许可证分工:硬件与软件不是同一种授权

    这一点常被忽略但对集成决策影响很大:硬件源材料——原理图、电路板布局、接线图、数据手册、物理安装文档、三维模型等——采用 CC BY-SA 4.0 授权;固件与软件源则采用 MIT 授权,除非第三方依赖另有声明。也就是说,照着板卡设计做改动后的再分发义务,与修改代码后的义务并不相同。此外文档整体声明按现状分发、不提供任何担保。

    组成部分 许可证 主要义务
    硬件源材料(原理图、布局、接线图、安装文档、三维模型) CC BY-SA 4.0 署名并以相同方式共享衍生作品
    固件与软件源(源码、头文件、文档、构建脚本) MIT 保留版权与许可声明
    第三方依赖 以各自声明为准 需逐项核对

    项目还提供商用的整车控制接口方案作为交钥匙选项,覆盖纯电版 Soul 与 Niro 混动,包含集成控制模块、车规级线束与急停开关。对研究者而言,开源版本与商业版本的区别在于支持范围与工程化程度,接口契约本身是一致的。

    常见问题

    OSCC 是在 CAN 总线上伪造报文吗?

    不是。它把控制命令作用于部件 ECU 的传感器输入端——转向盘扭矩传感器、油门位置传感器与制动位置传感器,属于低层接口,因此能在不改动原厂安全用例、不欺骗 CAN 报文、不破解 ADAS 功能的前提下实现全量程控制。纯电版以外的汽油车因不具备线控制动能力,需额外加装辅助执行器。

    为什么控制命令必须每 200 毫秒发一次?

    这是模块侧判定上位机是否存活的看门狗:三个发布函数中任意一个超过 200 毫秒未被调用,模块就认定与上位机失去通信并自行禁用。它把上位机死机与停止控制统一成同一结果。因此控制循环不能只在数值变化时才发命令,长时间稳态下也必须持续调用。

    可以关闭转向超控吗?

    可以在构建时传入相应标志关闭,但不建议。文档明确警告系统激活时抓握转向盘可能造成严重伤害,推荐的转向超控方式是制动踏板或急停按钮。若保留开启,灵敏度由车型头文件中的扭矩差阈值控制:调低会增加高扭矩区域的误判,调高会让人工接管更困难。

    板卡版本与固件版本不匹配会怎样?

    会直接影响启动自检。较新的执行器控制板用新引脚做附加的启动安全检查,旧板没有这些引脚,必须用关闭制动启动测试的构建标志处理。其余组合按官方配对表选择固件版本即可,文档同时强调每次发布新版本都应及时更新,以避开已知安全问题。

    硬件设计和软件代码的授权一样吗?

    不一样。原理图、电路板布局、接线图、数据手册、物理安装文档与三维模型等硬件源材料采用 CC BY-SA 4.0,衍生作品需署名并以相同方式共享;固件与软件源采用 MIT,只需保留版权与许可声明;第三方依赖以各自声明为准。集成前应按组成部分分别核对义务。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/polysync-oscc/README.md(接口定位、版本矩阵、API 契约与 200 ms 保活、构建与测试命令、许可证分工)
    • 项目 01 —— public-can-dbc-datasets/additional/polysync-oscc/LICENSE.md(硬件源材料的 CC BY-SA 4.0 条款与固件软件的 MIT 条款全文,含免责与责任限制)

    免责声明

    本文所述内容来自公开开源项目的说明文件,不代表车辆制造商的官方数据,文中数字均摘自上述文件,未经独立实车验证。该项目按现状分发、不提供任何担保:硬件源材料(原理图、电路板布局、接线图、数据手册、物理安装文档与三维模型)采用 CC BY-SA 4.0,衍生作品须署名并以相同方式共享;固件与软件源采用 MIT,须保留版权与许可声明;第三方依赖以各自声明为准,集成前应逐项核对。对车辆进行任何控制改装都涉及人身与财产安全,本文仅作技术资料梳理,不得作为上路或载人测试的依据;实际操作须在封闭场地、由具备资质的人员在急停开关与完善的失效处理机制下进行,并以官方资料与实际测量为准。

    更新记录

    • 2026-10-15 初稿

  • 沃尔沃 C30 嗅探记录:35 个 29 位 ID 与 462 条 VIDA 请求

    结论:2011 款沃尔沃 C30 T5 是少数可以直接从诊断口同时够到两条总线的车:它的高速总线 500 kbit/s、低速总线 125 kbit/s,全部使用 29 位扩展 ID、每帧固定 8 字节,且诊断口与整车之间不存在网关,两条总线都被引到 OBD 连接器上。配套研究留下两份清单——35 个广播 ID 的嗅探记录(24 条带功能注释)与 462 条 VIDA 请求条目,其中 147 个是唯一的 ECM 故障码。真正需要警惕的是请求表里混着执行器动作、ECU 复位、擦除与全总线静默这几类命令,它们和读数据命令只有第三个字节的差别。

    双总线拓扑:为什么这台车的嗅探门槛很低

    这台车有两条 CAN 网络。高速总线挂 ECU、转向模块、制动及其他模块,低速总线挂收音机、车门模块、仪表组合及附属电器,中央电子模块 CEM 在两者之间做网关。关键在于:诊断口与整车其余部分之间没有额外网关,两条总线都被引到了 OBD 连接器上,因此不需要任何中继设备就能同时观察两侧流量。

    代价是物理层和寻址方式都比常见乘用车更特殊:29 位扩展 ID、每帧一律 8 字节、两条总线速率差四倍。作者也说明,同年代更早的车型需要先通过 K 线激活诊断继电器才能从诊断口访问 CAN,这台车不需要,因此那份资料对老车型不适用。兼容性方面的表述很克制——其他采用同款博世 ME9 实现的车型大概率无需改动即可工作,但有报告称广播 ID 会随车辆变化,需要自行调整。

    总线 速率 主要节点 接入方式
    高速总线 500 kbit/s ECU、转向模块、制动模块 诊断口直连,29 位扩展 ID
    低速总线 125 kbit/s 收音机、车门模块、仪表组合 诊断口直连,29 位扩展 ID
    网关 — 中央电子模块 CEM 诊断口与整车之间无额外网关
    帧长度 — 所有 ECU 的所有帧 一律 8 字节
    前置条件 — 更老车型 需经 K 线激活诊断继电器

    VIDA 请求帧:与 UDS 的差别只在开头两个字节

    VIDA 是与 UDS 类似的诊断协议,但帧结构不同:第一个字节是数据长度码,第二个字节才是 ECU 地址。这一点决定了它不能直接用标准 UDS 工具发。以读取增压压力为例,请求帧 ID 为 0x000FFFFE,数据为 CD 7A A6 12 9D 01 00 00:首字节 CD 等于 C8 加上后续有效字节数,7A 是发动机 ECU 的地址,A6 是按标识符读当前数据,12 9D 是增压压力参数,01 大概是「只回送一次」,其余补零凑满 8 字节。

    响应帧 ID 为 0x00400021,数据 CD 7A E6 12 9D 95 00 00:E6 是对 A6 的正响应,12 9D 回显所请求的参数,95 才是返回值。也就是说,解析时要先按首字节裁出有效长度,再按「地址—命令—参数—值」的顺序取值,不能假定整帧都是有效载荷。

    字节位置 请求帧示例 响应帧示例 含义
    帧 ID 0x000FFFFE 0x00400021 请求与响应使用不同 CAN 地址
    字节 0 CD CD C8 加后续有效字节数
    字节 1 7A 7A ECU 地址,此处为发动机 ECU
    字节 2 A6 E6 按标识符读当前数据及其正响应
    字节 3–4 12 9D 12 9D 参数标识,此处为增压压力
    字节 5 01 95 请求侧为回送次数,响应侧为返回值
    字节 6–7 00 00 00 00 补零至 8 字节

    命令字节全集与风险分档

    第三个字节决定动作类型,资料里列出的取值及其含义如下。需要特别注意的是其中几类并非只读:跳转执行、运行代码段、启动引导、擦除、复位,以及以 FF 为 ECU 标识的全局命令「全总线静默」与「全总线复位」。这些命令与读数据命令在帧结构层面完全相同,只差一个字节的值,因此任何把请求表整体接入自动化脚本的做法都必须先做白名单过滤。

    命令字节 含义 风险档
    A6 按标识符读当前数据 只读
    AE 发送数据(资料中主要用于读故障码) 只读
    B9 读 ECU 硬件/软件件号与序列号、防盗通信计数 只读
    A3 防盗安全访问:发送或重锁防盗 PIN 写入
    A8 设置引导段结束 写入
    AF 清除全部已存储的 2 字节故障码 写入
    B1 执行器动作,如逐缸喷油器开启 动作
    B2 启停泄漏快检 动作
    B4 结束取数与校验和 写入
    B8 写防盗 PIN 与安全码 写入
    9C / A0 / C0 跳转地址 / 运行跳转点代码段 / 启动引导 高危
    C8 / F8 复位 ECU / 擦除 高危
    86 / C8(ECU 为 FF) 全总线静默 / 全总线复位 高危

    462 条请求条目:规模与命令分布

    请求清单共 507 行,其中 462 行是带分号分隔的有效条目,按命令字节分布为:A6 读数据 236 条、AE 读故障码 152 条、B1 执行器动作 59 条,其余十余条分散在 B9、A3、B2、B8、AF 上。也就是说,这张表约八成是只读请求,但剩下的两成里有 59 条会直接驱动执行器。

    每条只读请求附带换算式,这是它比起单纯的参数列表更有价值的地方。换算形态有三类:线性乘系数、含偏移的线性式(如温度统一按乘 0.75 减 48 得摄氏度)、以及按位掩比较(用于开关量与状态位)。同一参数在不同条目里出现不同换算式的情况也存在,例如喷油时间在两处分别给乘 0.002666 与除 375,使用时应以实车回归为准。

    参数 请求数据 换算式 单位
    增压压力 A6 12 9D 01 乘 10 hPa
    增压压力传感器电压 A6 12 9C 01 乘 5.0/1024 V
    发动机转速 A6 10 1D 01 乘 40 rpm
    车速 A6 11 40 01 乘 1.25 km/h
    冷却液温度 A6 10 D8 01 乘 0.75 减 48 摄氏度
    进气温度 A6 10 CE 01 乘 0.75 减 48 摄氏度
    点火提前角 A6 10 36 01 乘 191.25/255 BTDC
    空气质量流量 A6 10 29 01 乘 4 kg/h
    蓄电池电压 A6 10 0A 01 乘 0.07 V
    油门踏板(模拟输入) A6 10 04 01 乘 100/65535 %

    故障码清单:147 个唯一码与它的取法

    读故障码统一使用 AE 11,即按 2 字节故障码请求,清单里共出现 147 个唯一的 ECM 前缀码,覆盖增压压力传感器、点火线圈逐缸信号、喷油器逐缸、节气门单元内部故障、控制模块内部故障、前后氧传感器、失火逐缸与催化器损伤、涡轮控制阀、燃油泵继电器、系统继电器、5 V 供电、配置故障等。清单中还有一条写作 ECM-XXXX,标注为「当前控制模块版本下的未知故障码」,这是解读时的一个重要提示:同一数值在不同软件版本上可能对应不同含义。

    清码使用 AF 11,描述为「清除全部已存储的 2 字节故障码」。这是一次性的 bus-write 动作,在只读采集脚本里不应出现。与此相对,B9 F0、B9 F5、B9 F8 分别读取 ECU 硬件件号、软件件号与硬件序列号,是确认所连车辆软件版本最安全的方式,排在读故障码之前更合理。

    35 个广播 ID 的嗅探记录

    广播侧的记录共 35 条,字段为 ID、数据长度码、8 字节数据、周期、观测计数与注释;其中 24 条带功能注释,11 条留空。周期列取值从 30 到 1088,观测计数从 8029 到 184057。注释里最有价值的是「CRITICAL FOR CLUSTER」这一类标记——它指出哪些帧是仪表正常工作所依赖的,一旦在总线上缺失或篡改,仪表会出现可见异常;这类帧在实验里应只观察、不注入。

    ID 周期 观测计数 注释要点
    021204B8 30 184057 转向灯
    03C3F7FC 31 170711 点火状态
    09C050B8 52 91376 仪表与里程计数;字节 1 的第二个半字节为亮度,字节 4 与 5 为转速,标记「仪表关键」
    0EE0500A 101 50799 气囊灯,标记「仪表关键」
    1093F7FC 248 8029 标记「仪表关键」,无进一步说明
    1760E008 153 29186 仪表车速与告警灯
    1AD10068 600 12205 字节 1 为油量;字节 2 在仪表开时为 0x04、关时为 0x07
    19F010FC 201 25299 巡航控制按键
    16124008 97 38201 胎压告警灯
    16801028 233 32269 ABS 与制动失效灯

    这份记录也说明了作者的实现策略:优先从广播帧取数,因为开销和总线占用都低;只有在广播里根本不出现的量(增压压力就是典型)才改用请求。仪表亮度、点火状态、方向盘按键都在后台循环里持续更新全局变量,供显示循环使用——代码采用一种伪多任务结构,接收循环在其他循环不运行时始终在跑,因此广播变化不会漏掉。此外还记录了一个交互细节:长按巡航取消键超过 2 秒切换显示页面。

    硬件侧也有参考价值:控制器用 Arduino Due 配合其双 CAN 接口的库,显示屏是一块 2.2 英寸圆形串口屏,两者都挂在诊断口上而不经过任何改装线束。显示项最终定为增压压力、冷却液温度、进气温度与点火提前角四个,其中三项要靠请求才拿得到。仪表指针还做了模拟化处理:数值跳变超过 1 时不允许瞬时跨越,必须逐级走完中间值,以避免数字仪表的跳变感。

    常见问题

    这台车的诊断口能直接读到两条总线吗?

    能。它的高速总线 500 kbit/s、低速总线 125 kbit/s,诊断口与整车之间不存在网关,两条总线都被引到 OBD 连接器上。但要注意更老的同品牌车型需要先通过 K 线激活诊断继电器才能访问 CAN,这份资料不适用。

    VIDA 协议能不能用标准 UDS 工具发?

    不能。它与 UDS 的差别在帧开头:第一个字节是数据长度码,第二个字节才是 ECU 地址,而 UDS 的寻址方式不同。解析响应时也要先按首字节裁出有效长度,再按地址、命令、参数、返回值的顺序取值。

    增压压力为什么必须靠请求而不是广播?

    因为这类量在广播帧里根本不出现。该研究优先使用广播帧取数以降低总线开销,只有广播中不存在的数据才走请求路径,增压压力就是其中之一,对应请求数据为 A6 12 9D 01、换算为乘 10 得百帕。

    请求表里有哪些命令不能随便发?

    约两成条目不是只读。59 条 B1 会直接驱动执行器(如逐缸喷油器开启),AF 清除全部故障码,A3 与 B8 涉及防盗 PIN,9C、A0、C0 涉及跳转与引导,C8、F8 是复位与擦除,而以 FF 为地址的 86 与 C8 会作用于整条总线。自动化脚本应先做命令白名单过滤。

    这些广播 ID 能套用到其他车吗?

    不能默认可以。作者明确表示与其他车辆的兼容性未知,采用同款博世 ME9 实现的车型大概率无需改动,但有报告称广播 ID 会随车辆变化。此外清单里存在 ECM-XXXX 这类条目,标注为当前控制模块版本下的未知故障码,说明同一数值在不同软件版本上含义可能不同。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/alfaa123-volvo-c30-research/README.md(车辆年份与型号、双总线速率、29 位扩展 ID、硬件构成与兼容性说明)
    • 项目 01 —— public-can-dbc-datasets/additional/alfaa123-volvo-c30-research/Codes.txt(VIDA 请求帧结构、命令字节全集与 462 条带换算式的请求条目)
    • 项目 01 —— public-can-dbc-datasets/additional/alfaa123-volvo-c30-research/Sniffing/RX List.txt(35 个广播 ID 的数据、周期、观测计数与功能注释)

    免责声明

    本文所述的总线参数、请求帧结构与故障码清单均为社区逆向工程的研究笔记,不代表车辆制造商的官方数据,文中数字均来自上述公开仓库的说明文件,未经独立实车验证。该仓库快照中未发现任何许可证,仅可作私有离线研究副本,再分发或集成前须先行确认权利。文中列出的执行器动作、故障码清除、ECU 复位与擦除、全总线静默等命令不得在未知车辆或公共道路上使用;诊断请求与主动 CAN 流量不得假定对另一辆车安全。涉及安全相关用途时请以官方资料与实际测量为准。

    更新记录

    • 2026-10-15 初稿

  • 特斯拉 iBooster Gen1 台架解码:双总线、已锁存故障与先验证清单

    结论:这份 Tesla iBooster Gen1(件号 1037123-00-B,适配 2016 至 2020 年 Model S)的台架笔记给出三条硬结论:只给 12 V 常电、地和点火三根线,助力就会出现,因此纯只听的监视方案成立;两条 500 kbit/s 总线的引脚和极性都已完成实测;踏板行程可以换算到毫米。但这些结论有一条严厉的前置要求——作者在专门的「先自行验证」文件里写了:踏板传感器引脚、件内部终结电阻、峰值工作电流等项目必须先在台架上量出来,未经核实不得写进固件、DBC 或线束。

    台架前提:哪些已经量过,哪些只是假设

    区分「已测得」和「来自其他车型的社区资料」是这份档案的第一原则。电源与 CAN 引脚是实测量;踏板传感器那几组引脚属于借用其他 iBooster 变体(不同厂商平台)的推论,置信度低。

    引脚 功能 状态 如何收口
    1 常电 12 V,40 A 熔断 已确认 配合 9 与 20 即可出现助力
    9 地 已确认 与壳体接地螺柱连通验证
    20 点火 12 V,5 A 熔断 已确认 只有在它带电时才出现助力
    25 / 16 车辆总线 CAN-H / CAN-L 已确认,含极性 该极性下抓到干净的 0x39D 帧
    18 / 10 YAW 总线 CAN-H / CAN-L 已确认,含极性 该极性下抓到 0x38E 与 0x38F
    17 常电 12 V,5 A 熔断 低置信度 Gen1 应有此针,本件未量
    2 / 8 / 22 / 23 踏板行程传感器 低置信度 社区对同代也有分歧,需台架确认
    24 / 4 制动信号输出 / CLIN 低置信度 可能本件根本不存在
    连接器 26 针 EuCon 中等置信度 按壳体标记与针腔数量核对

    另外三条至今没有量:助力时的峰值电流(社区说法是「几十安」,保险丝规格 40 A,未测前一律按电瓶供电准备);只接引脚 1 与 9 不给点火时是否还发报文;上电自检是否会让推杆移动(在证明不会之前按会来处理)。

    两条总线与关键报文的定位

    两条总线同为 500 kbit/s、11 位 ID,都不带内部终结电阻,因此适配器侧的 120 欧终结跳线必须闭合,并在部件端并一颗松脱的 120 欧电阻,形成两节点总线;上电前的高低速线间拓扑检查应读到约 60 欧。

    总线 引脚(H / L) 静态帧率 报文 ID 承载内容
    车辆总线 25 / 16 36.4 帧每秒(静态 4 个 ID,踏板动作时 11 个) 以 0x39D 为主 16 位踏板行程
    YAW 总线 18 / 10 149.5 帧每秒 0x38E(99.7 Hz)、0x38F(49.8 Hz) 12 位位置、状态与两个制动标志

    实际作业中用两台适配器时,USB 索引会重排,识别哪台在哪条总线上必须看内容——谁看到 0x39D 谁就在 25/16 上,绝不能按索引或 USB 地址判断。还有一个非常具体的陷阱:这条总线只有两个节点,只听模式下没有节点发应答位,发送错误计数会以每次 8 递增,约 32 次尝试就进总线关闭状态,再自动恢复,如此循环。同一器件、同一 12 秒窗口的对照结果是:只听模式抓到 1,006 帧每秒但只有 1 个唯一 ID,而应答模式是 37 帧每秒却有 4 个 ID。所以捕获一律用应答模式。

    0x39D:踏板行程的字节定义与两套标定

    这条报文是 500 kbit/s、DLC 4、25.0 Hz。它的 b0 是校验和,算法为 b1 + b2 + b3 + 0xA0 取低字节,在 2,250 帧上 100% 通过;b1 是半字节活跃计数器(每次加 1,模 16,高四位恒为 0);b2:b3 是小端无符号 16 位行程值,静态约 262,观测到的最大值为 14070。

    样例 解析
    B7 0E 08 01 计数器 14,行程 264(静止)
    85 00 C8 1D 计数器 0,行程 7624(行程中段)
    D4 0B F8 31 计数器 11,行程 12792(接近全行程)

    筛选方法值得一提:按「平滑度」而不是极差排序候选字段,判据是相邻差值均值除以值域。校验和与计数器铺满 00 到 FF 但帧间跳变随机,物理量则平滑移动;b2:b3 小端组合得分 0.009,计数器与校验和那对约 0.12。

    位置 实测原始值 样本数
    静止 263.1 319
    21 mm 6114.0 161
    42 mm 13125.2 80
    终点限位脉冲峰值 13606(合 43.44 mm) 无

    第一次跑给出 counts = 333.866 × mm − 897.2,即约 0.0030 mm 每计数。但静止点不落在这条直线上:263 计数外推得 3.475 mm 而不是零,合理解释是传感器啮合前约有 3.5 mm 自由间隙,低于该点读数被压在约 263 的底值上。第二次跑保持在 10、20、30 mm,结果与第一次不自洽——20 mm 读到 6886.4,反而高于第一次 21 mm 的 6114.0。单调传感器不可能如此,因此至少有一组物理位置是错的。

    组合 计数每毫米
    第一次跑(21、42 mm) 333.87
    第二次跑(10、20、30 mm) 361.93
    五个点全用 320.68

    结论很干脆:瓶颈是尺子而不是 CAN 数据。单次保持内的总线读数离散为约正负 20 计数(0.062 mm),而两次跑之间的物理位置偏差可达 4.4 mm——信号比它的测量手段精确约 70 倍。要真正标定,需要百分表配固定基准,并且一次跑完整个量程(因为误差出现在两次跑之间,而不是一次跑的内部)。用于显示和记录已足够,档案给出的组合拟合是 mm = (counts − 3.3) / 320.68。

    0x38E 与 0x38F:状态、两个制动标志与校检

    0x38E 是 500 kbit/s、DLC 8、99.7 Hz,静止值为 77 2B 00 40 11 00 00 00。它的 b0 不是数据,而是覆盖 b1 到 b7 的 CRC-8/SAE-J1850。b3 只是位置的低字节,b4 的低四位才是高字节位——这是这份档案纠正过的最重要一处。

    字节 字段 说明
    b0 CRC-8 覆盖 b1 至 b7,无 数据
    b1 活跃计数器 0x20 至 0x2F,高四位固定为 2
    b3 位置低字节 静止稳定值为 0x40,会因进位而回绕
    b4 低四位 位置高字节位 与 b3 组成 12 位位置,静止 320,全行程约 3052
    b4 高四位 状态 0 初始化中、1 正常助力、2 故障
    b6 制动标志 0 或 1,带约 0.55 mm 回差

    把 b4 高位从位置里剥离是人为诱发的 sensor 故障验证的:拔掉行程传感器后该 nibble 从 1 跳到 2,而 b3 完全没动,旧的 16 位读法整整偏移了 0x1000。健康工况下它恒为 1,所以两种解释只差一个常数,这也是早先的 16 位模型能拟合所有健康捕获、相关性仍达 0.9999 的原因。上电轨迹还能独立 佐证这个拆分:约 1.47 秒前 b4 读 0x01,之后读 0x11,而两侧的 12 位位置都是 320。

    标志 触发位置 回差 次数
    0x38E b6 建立 2.18 至 2.42 mm 约 0.55 mm 4 次上电周期
    0x38E b6 释放 1.72 至 1.75 mm 释放阈值在四次上电中重复到 2 个计数内
    0x38F b2 bit0 建立 1.45 至 1.66 mm 释放边沿未被采样间隔分辨 跨 4 次上电
    0x38F b2 由 0xE3 转 0xDB 39.75 至 40.16 mm 回程 38.77 至 39.51 mm 4 次

    0x38F 是同一总线的另一条报文,49.8 Hz,静止值 3F 2D E2 53 02 00 00 00,同样用 b0 承载同一套参数的 CRC-8。它的 b2 是状态字节:跨四次上电只见到六种取值,0xE2 为就绪且制动释放、0xE3 为制动施加、0xDB 为接近行程末端、0xCC 为故障,另三个只在启动前两秒依次出现。b3 虽然常变,但与位置的相关性只有 0.009、与踏板速度 0.000,也不是计数器,档案刻意留着不解码。两套制动标志对应两个阈值,形状上像稳定程序期望的冗余开关对;而 0x38F 的释放边沿在 49.8 Hz 下常只有一两帧落在过渡区,观测点散布在 1.45 到 4.11 mm,不要引用它的释放值。

    整份档案还有一条反向但有用的结论:独立的故障状态报文并不存在,状态就藏在位置报文的一个字段里。完整性方案则分两种:车辆总线的 0x39D 用加性校验和,YAW 两条报文用 CRC-8/SAE-J1850(多项式 0x1D、初值 0x00、异或输出 0x0A,输入输出均不反射),在四次上电周期的 46,960 帧 0x38E 与 23,478 帧 0x38F 上 100% 通过,合计 70,438 帧零失败;两条 ID 共用一套参数,说明 CRC 并未用报文 ID 做种子。

    故障行为:会锁存,且两条总线会不一致

    拔掉行程传感器可以诱发故障,助力随之消失,三个信号在约 10 ms 内一起变化。这里最关键的一条是它的可恢复性边界。

    步骤 状态 nibble 0x38E 位置 0x39D 行程 助力
    正常 1 实时更新 实时更新 有
    拔掉传感器 2 无 钉在 16354(0x3FE2) 无
    重新插回传感器 仍为 2 恢复实时(320 到 401) 仍钉在 16354 仍无
    断电重启 1 实时 实时 有

    两件事由此定下来。第一,钉住值带有效校验和,16354 在物理上不可达(终点限位才 13606),所以消费者不必认识这个哨兵值也能按量程拒绝。第二,状态位是「助力可用性」标志,不是「数据有效性」标志:插回传感器后位置已经有效并持续更新,状态却仍是 2、助力依旧关闭,只有断电重启才恢复。锁存期间的另一条后果是两条总线不再一致——YAW 总线报告真实位置,车辆总线仍钉在哨兵值,因此要位置就得用 0x38E。

    对实车的影响值得提示:插头瞬时接触不良(振动、腐蚀、压接不良)会把助力器锁进无助力状态,直到点火循环才恢复——踏板变硬并持续如此,而原车仪表没有任何东西能解释原因。制动仍然可用,只是需要大得多的踏板力。

    刹车后的突发报文:行为数据与推理教训

    最后一组可用数据是事件性的。0x31D、0x34D、0x36D、0x37D、0x38D 每次各发一帧、集中在约 120 ms 内,0x33D 也属于这一组。合并六份日志得到 12 次完整突发,其中 10 次跟在制动释放之后 0.64 至 2.20 秒,没有一次踏板施加会漏掉;另外 2 次是上电突发,约在累计运行 6.8 秒时出现,与踏板无关。

    字段 含义 标度 相关性
    0x38D b5:b6 小端 u16 该次施加的推杆行程峰值 129.3 计数每毫米 0.99975
    0x37D b0:b1 小端 u16 制动施加持续时间 27.50 计数每秒 0.99877
    0x31D b2 高四位 同上持续时间的对数分桶 1 到 7,约每翻倍一级 0.884
    0x31D b0:b1、0x3AD b0:b1 累计运行时间,非事件数据 9.99 计数每秒(100 ms 时基) 无

    0x38D 的峰值与标定过的行程在 34 mm 量程内相差在 0.66 mm 以内,比尺标定的 2 mm 不确定度还紧;三次上电中四次打到终点限位的读数彼此相差在 8 个计数内。但这一节所有数字都建立在 10 个事件上,候选字节远多于事件数:两个 0.999 级的拟合通过了 20,000 次置换检验,而相关性在 0.9 附近及以下的都只当线索。峰值与持续时间在这批标定扫描中部分耦合(踩得深通常也踩得久),还需要 20 到 30 次独立控制深度、速度与保持时间的施加才能收口。

    这节还留了两条反面教训。一是量程规则会被误用:针对消费者的「行程超出 13700 即拒」对分析是错的——终点限位读数 13606,但快速撞击会过冲到 14070,套用会剪掉真实行程,曾因此把系数写成 129.8(恢复过冲后为 129.3,且拟合更好)。二是运行时间会伪造相关性:后期事件往往更深更久,于是运行时间足以冒充测量量,0x37D b5 与峰值相关 0.90 却和运行时间相关 0.82,0x34D b5 与运行时间相关 0.88,两条都没敢主张。

    常见问题

    这份解码能不能直接写进 DBC 或线束?

    踏板行程、位置、状态与两条 ID 的校验部分是实测量,可以用;但标注为低置信度的行不能落地。作者在「先自行验证」文件里点名了必须先在台架上量出来的项目:踏板传感器引脚(2、8、22、23)、17 号针、24 与 4 号针是否存在、两总线是否真的没有内部终结电阻、助力时的峰值电流、以及上电自检是否会让推杆移动。未按日期把假设替换成实测之前,不要写进固件、DBC 或线束。

    为什么 0x38E 的 b4 高位不能当作数据有效位?

    因为它是助力可用性标志,不是数据有效性标志。人为诱发故障后把传感器插回,0x38E 的 12 位位置已经恢复实时更新(320 到 401),而该状态位仍为 2、助力依旧关闭,只有断电重启才恢复。位置本身的合法性应当另按量程判断:故障 Sentinel 是 16354,这个值带有效校验和且在物理上不可达。

    为什么捕获必须用 ACK 模式而不是只听?

    因为这条总线只有两个节点,只听模式下没人发应答位,发送错误计数以每次 8 递增,约 32 次尝试就进入总线关闭并自动恢复,循环往复。同一器件、同一 12 秒窗口的对照是:只听模式 1,006 帧每秒但只有 1 个唯一 ID,应答模式 37 帧每秒却有 4 个 ID——那 1,006 帧是重传循环而不是信号,而且它把其余三个 ID 全部隐藏了。

    踏板行程为什么会有两套换算系数?

    第一次跑只有 21 与 42 mm 两个点,得到计数等于 333.866 倍毫米减 897.2;第二次跑取了 10、20、30 mm,得到 361.93 计数每毫米。两次不自洽的标志是 20 mm 的读数高于 21 mm 的读数,单调传感器不可能这样,所以问题出在物理位置测量而不是总线数据。五个点合并后的系数是 320.68,用于显示与记录足够,而要把精度提高到毫米级则需要百分表加固定基准,并且一次跑完整个量程。

    传感器插头瞬时接触不良会发生什么?

    会把助力器锁进无助力状态:状态 nibble 变为 2,车辆总线的行程值钉在 16354,助力关闭。即使插头立刻恢复接触,YAW 总线的位置已经恢复更新,状态仍保持 2、助力也仍不回来,必须等到点火循环才恢复。实际后果是踏板突然变硬并持续变硬,而原车仪表上没有任何东西能说明原因;制动仍然可用,只是需要大得多的踏板力。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/pelosim-tesla-ibooster/docs/DECODE.md(两条总线的引脚与帧率、0x39D 与 0x38E、0x38F 字节定义、校验方案与故障行为)
    • 项目 01 —— public-can-dbc-datasets/additional/pelosim-tesla-ibooster/docs/PINOUT.md(引脚假设表、置信度分级、台架接线与实测结果)
    • 项目 01 —— public-can-dbc-datasets/additional/pelosim-tesla-ibooster/docs/BENCH_LOG.md(首次点亮记录、只听陷阱、二次离线分析的推行数据与推理教训)
    • 项目 01 —— public-can-dbc-datasets/additional/pelosim-tesla-ibooster/VERIFY_FIRST.md(必须先自行验证的清单与「打勾即等于用实测替换假设」的要求)

    免责声明

    本文内容涉及制动助力部件。所有 CAN ID、字节定义、系数与阈值均来自单一零件号的公开台架笔记,未在任何独立实车上复现,不构成装机、改装或维修依据。文中明确区分了已实测量与借用其他 iBooster 变体的假设,凡标注低置信度或未打勾的项目,在自行测量之前不得进入固件、DBC 或线束。制动系统属于安全相关件:助力失效时踏板力会显著上升,任何介入都应在具备夹持、限位与断电条件的台架上进行,并由具备资质的人员确认。

    更新记录

    • 2026-10-15 初稿

  • 比亚迪 / Proton 的 openpilot 移植:指纹规模与功能替换边界

    结论:bukapilot 是 openpilot 之上的维护型补丁集,目标是把 ADAS 带到马来西亚在售车型(尤其 Perodua 与 Proton),并在既有能力上补齐完整的右舵支持。落到 CAN 适配层,判断一个车型「算不算移植完成」要看两件事:DBC 字典有没有映射、指纹有没有登记。按这个口径,BYD ATTO 3 有 2 组指纹(144 与 110 个地址),Proton X50 有 1 组(179 个地址),而 Proton S70 与 X90 只有 DBC 映射、没有指纹,属于未完成的端口条目。

    车型移植的两张表:DBC 映射与指纹

    移植数据集中在两个 values.py 里:一份按品牌给出 CAR 常量、DBC 字典与指纹字典,另一份给出 HUD 倍率。DBC 字典走 openpilot 的 dbc_dict 形式,第二项为 None,即未登记雷达侧 DBC。HUD 倍率直接影响仪表与抬头显示上的速度呈现,BYD 侧为 0.718,Proton 侧为 1.035 且在源码里留着「Todo」标记,说明它仍是待标定的值。

    品牌 车型常量 DBC 字典 指纹组数 HUD 倍率
    BYD ATTO3 = "BYD ATTO 3" byd_general_pt(雷达侧 None) 2 0.718
    Proton S70 = "PROTON S70" proton_general_pt(雷达侧 None) 0 1.035(Todo)
    Proton X50 = "PROTON X50" proton_general_pt(雷达侧 None) 1 1.035(Todo)
    Proton X90 = "PROTON X90" proton_general_pt(雷达侧 None) 0 1.035(Todo)

    指纹规模与非 8 字节 DLC

    指纹记录的是「地址到长度」的映射,是车型识别的第一道判据。三组的规模差异很大,而真正有价值的信号藏在那些长度不等于 8 的地址里。

    指纹 地址数 长度非 8 的地址 地址范围
    ATTO 3 指纹一 144 204:20、287:5、385:3、682:2、1542:2、1798:3 85 – 2027
    ATTO 3 指纹二 110 287:5、385:3、682:2、1542:2、1798:3 85 – 1832
    Proton X50 指纹 179 385:3、1542:2、1798:3 32 – 2025

    把三组放在一起比对,能读出三条结论。其一,ATTO 3 的两组指纹共有 107 个地址,第一组独有 37 个、第二组独有 3 个,说明第二组几乎是第一组的子集,多数组差异来自配置或年款导致的报文增减。其二,三组指纹共有 17 个地址,其中 385(长度 3)、1542(长度 2)、1798(长度 3)长度都不是 8,这类跨品牌稳定的短帧通常是通用或诊断相关报文,不应作为车型特征。其三,X50 的 179 个地址里有 159 个不在 ATTO 3 两组指纹的并集(147 个地址)之内,两个品牌的报文空间基本不重叠,因此 DBC 也必须分成 byd_general_pt 与 proton_general_pt 两套。

    另有一个需要单独确认的点:ATTO 3 第一组指纹里出现 204:20,长度 20 超出经典 CAN 的 8 字节上限。它可能来自 CAN FD 采集,也可能来自采集工具的合并口径,在把它写进匹配规则之前必须先确认采集链路。

    功能替换边界:哪些被替换,哪些叠加

    移植不只是接上 CAN,还要说清接管了哪些原厂功能。README 把这件事分成「所有受支持车型」与「特定受支持车型」两层,处理逻辑完全不同。

    功能 所有受支持车型 特定受支持车型
    LKA 原厂 LKA 被替换,仅在用户启用后生效 同上
    LDW 原厂 LDW 被替换 同上
    ACC 不替换 原厂 ACC 被替换
    FCW 不声明 在原厂 FCW 之外叠加运行
    AEB、自动远光、盲区警示、侧向碰撞警示 原厂功能一律保留 原厂功能一律保留

    这套划分的工程含义是:横向控制(LKA/LDW)是移植的必选项,纵向控制(ACC)只在完成标定的车型上接管,前向碰撞预警采取「叠加而非替换」以保留原厂兜底,其余主动安全功能不动。开发车端口时,也应按同样的层次逐项确认,而不是一次性接管全部。

    数据上传与责任边界

    默认情况下驾驶数据会上传到项目方服务器。日志范围按项列出,可退出性并不一致,做合规评估时要逐项核对。

    数据项 默认状态 可否关闭
    路面相机、CAN、GPS、IMU、磁力计、热传感器、崩溃与操作系统日志 记录并上传 仅在设置中显式退出驾驶员相机一项,其余未说明可关闭
    驾驶员朝向相机 默认记录 可在设置中显式 opt-out
    麦克风 不录制 不适用
    数据使用权 授予项目方不可撤销、永久、全球范围的使用权 使用即视为同意

    责任侧同样写得很硬:软件以 MIT 许可发布(部分组件按各自许可),但被明确定性为 alpha 质量的 research-only 软件,不是产品;使用者须自行遵守当地法律法规,无明示或默示担保,并须就使用该软件产生的一切索赔向项目方作出赔偿与免责。性能限制也被逐条列出:恶劣能见度、相机被遮挡或损坏、车身贴膜贴纸或橡胶涂层造成的遮挡、设备安装不当、急弯与匝道路口的转向扭矩上限、受限车道与施工区、大横坡或强侧风、极热极冷、强光直射、坡道窄路与蜿蜒道路。它不会自动驾驶,也不减轻驾驶者必须付出的注意力;变道时它无法查看盲区,确认安全后由人工轻推方向盘发起。

    常见问题

    bukapilot 与 openpilot 是什么关系?

    bukapilot 是 openpilot 的维护型补丁集,目标是支持马来西亚在售车型(尤其 Perodua 与 Proton),使用为这些车型专门设计的硬件,并在 openpilot 既有能力之上补齐完整的右舵支持。它实现 ACC、LKA、FCW、LDW,并在启用时叠加基于相机的驾驶员监测;作为 openpilot 的补丁集,它也继承了 openpilot 的软件安全模型,并针对补丁部分逐步补充增量测试。

    为什么 Proton S70 与 X90 不能算已适配?

    因为它们在 values.py 里只有 DBC 映射(proton_general_pt)而没有指纹条目。指纹是车型识别的第一道判据,缺失意味着无法在实车上完成确定的车型匹配;同为 Proton 的 X50 则有 1 组含 179 个地址的指纹。此外 Proton 侧的 HUD 倍率 1.035 在源码里仍带 Todo 标记,属于待标定值。

    指纹里的 204:20 该怎么理解?

    它表示地址 204 的帧长度为 20 字节,超出经典 CAN 的 8 字节上限,可能来自 CAN FD 采集或采集工具的合并口径。在把它写进车型匹配规则之前必须先确认采集链路与总线类型,否则会在不支持该长度的接口上造成误匹配。

    默认会上传哪些数据,能不能关掉?

    默认上传驾驶数据,日志包括路面朝向相机、CAN、GPS、IMU、磁力计、热传感器、崩溃与操作系统日志;驾驶员朝向相机默认记录但可在设置中显式退出;麦克风不录制。使用即视为授予项目方不可撤销、永久、全球范围的数据使用权,除驾驶员相机外其余项的关闭方式在文档中未作说明。

    bukapilot 会接管原厂 AEB 和盲区警示吗?

    不会。文档要求保留车辆其他全部原厂功能,明确列举的包括 FCW、自动紧急制动(AEB)、自动远光、盲区警示与侧向碰撞警示。被替换的是原厂 LKA 与 LDW;在受支持的特定车型上,原厂 ACC 被替换,而 bukapilot 的 FCW 是在原厂 FCW 之外叠加运行,不是替换。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/alienware-sa-bukapilot-byd-proton/README.md(项目定位、功能替换层次、限制清单、数据上传与许可责任条款)
    • 项目 01 —— public-can-dbc-datasets/additional/alienware-sa-bukapilot-byd-proton/selfdrive/car/byd/values.py(ATTO 3 的 DBC 映射、HUD 倍率与两组指纹)
    • 项目 01 —— public-can-dbc-datasets/additional/alienware-sa-bukapilot-byd-proton/selfdrive/car/proton/values.py(S70/X50/X90 的 DBC 映射、HUD 倍率与 X50 指纹)

    免责声明

    本文所述内容为公开开源项目的移植数据与文档摘录,不代表车辆制造商或项目方的官方承诺。素材自身已声明:该软件为 alpha 质量的 research-only 版本,不是产品,无明示或默示担保,使用者须自行遵守当地法律法规并承担相应责任;它不会自动驾驶,也不减轻驾驶者必须付出的注意力。文中指纹地址与长度均按公开文件统计,未经实车复现,不同年款、市场与配置可能存在差异,涉及行车安全的改装请务必在封闭场地与专业监督下进行。

    更新记录

    • 2026-10-14 初稿

  • Palisade 只读 CAN 解码器:9 条报文、28 信号与三条边界

    结论:这份 Palisade 2020 解码器不是通用 DBC 解析器,而是被压缩到 9 条报文、28 个信号的窄口径只读实现:未知帧静默忽略,已知报文的 payload 长度不符则直接返回错误。决定它可用性的不是那张报文表,而是三条边界——它是 opendbc 的 MIT 授权只读子集、不引入上游任何车辆控制与发送代码、未用实车日志按年款与市场确认前不得把新信号提升为 canonical 状态。

    解码授权清单:只有 9 条报文

    解码器被固定在 dbc 仓库的 HYUNDAI_PALISADE_2020 provenance manifest 上,只直接解析下表中的报文,其余一律视为未知帧。六条报文在素材里给出了十六进制 ID,另外三条以 DBC 报文名引用。用途分两类:少数信号进入 canonical 状态(车速、转向角、纵向加速度、挡位、轮速、SCC 状态),其余只作为 raw diagnostics 呈现。

    DBC 报文 CAN ID 信号 解码后用途
    CLU11 未给出 CF_Clu_Vanz、CF_Clu_SPEED_UNIT 仪表车速与单位,canonical
    SAS11 未给出 SAS_Angle 转向角,canonical
    TCS13 未给出 ACCEL_REF_ACC、DriverOverride 纵向加速度与驾驶员制动状态,canonical
    CGW1 0x541 近光、驾驶席车门、前排安全带、雨刮与灯光开关 近光映射为 night_mode,其余为 raw diagnostics
    GW_DDM_PE 0x521 四门状态 raw diagnostics
    DATC12 0x042 驾驶席/副驾摄氏设定温度 raw diagnostics
    LVR12 0x367 CF_Lvr_Gear 换挡杆挡位状态,canonical
    WHL_SPD11 0x386 WHL_SPD_FL/FR/RL/RR 四轮轮速,canonical
    SCC14 0x389 ACCMode SCC 激活状态,canonical

    raw diagnostics:可以读,不能判定

    素材明确写明当前没有实车捕获,因此这组信号以 manifest 固定版本的 DBC 所载 message ID、bit 位置与 scale 为唯一依据。数值以 MESSAGE.SIGNAL 为键写入 VehicleState.raw_signals,在 HMI Diagnostics 中只读展示。硬规则是:不得自行解释 DBC 枚举值的含义,也不得把这些值用于车门、安全带、空调的 canonical 状态或任何车辆控制。

    raw 键 DBC 物理值 换算
    CGW1.CF_Gway_DrvDrSw 驾驶席车门开关 原始枚举,不解释
    CGW1.CF_Gway_DrvSeatBeltSw、CGW1.CF_Gway_AstSeatBeltSw 前排安全带开关 原始枚举,不解释
    GW_DDM_PE.C_DRVDoorStatus、C_ASTDoorStatus、C_RLDoorStatus、C_RRDoorStatus 四门状态 原始枚举,不解释
    DATC12.CR_Datc_DrTempDispC、CR_Datc_PsTempDispC 驾驶席/副驾摄氏设定温度 raw × 0.5 + 14

    唯一给出换算式的只有空调温度这一组,其余全部停在「原始枚举」层级。这不是偷懒,而是把「DBC 说有这个信号」和「这个枚举在这个年款上代表开或关」两件事分开——后者只能由实车日志证明。

    0x541 近光位的实车验证判据

    CF_Gway_HeadLampLow 只表示近光是否点亮,既推断不出自动灯光设置,也推断不出环境照度。因此批准这条映射前,必须在停车、点火 ON 状态下逐条记录,并核对日志 payload 第四个字节的最高位是否与下表一致;不一致就不发布该映射,而是回到 DBC 与车辆年款、配置的核对上。

    条件 期望 0x541 bit 31 期望 night_mode
    灯光 OFF 0 false
    近光 ON 1 true
    AUTO 模式 与实际近光点亮状态相同 与实际近光点亮状态相同

    采集与回放必须走两条不同的路:设备以 SocketCAN can0 接入后只做接收,用带时间戳的日志记录 0x541;捕获内容绝不向物理 can0 回发,开发环境只在 vcan0 上回放。每次切换条件时单独记录时刻与观察到的灯光状态。回归 fixture 中不得放入车辆识别信息、位置、时间等敏感原始日志,只保留已验证的 0x541 帧与必要的最小状态帧。

    三条边界与当前证据状态

    把散落在 NOTICE 与源说明里的约束收拢成一张表,才能看清这份解码器能承担什么。

    边界 具体要求 落地动作
    只读 文件与解码器均为接收专用,上游的车辆控制与发送代码不引入 工程上只允许 candump 侧记录,禁止任何向实车总线的写入
    未验证不提升 在车辆年款、市场、配置上用实车日志确认 frame 与 signal 之前,不把新信号加入 canonical state 新增信号先进 raw_signals,验证通过后再提级
    权利与适配 派生自 opendbc 的 MIT 授权内容,须保留原始通知;须核对车型年款与市场适配性 分发时保留上游 MIT 声明,逐车型做适配核对

    证据状态同样要如实标注:源说明指出实车信号验证仍在进行中,且该 DBC 的 BU_ 列表缺少若干被其报文与信号引用的节点名,严格解析器可能需要先做清理;文件本身按字节原样保留。子集规模为 9 条报文、28 个信号,固定版本的上游修订与 MIT 归属均记录在 NOTICE 中。

    常见问题

    这份解码器能当作完整 DBC 解析器使用吗?

    不能。它是已批准 schema 的窄口径只读实现,只解码 9 条报文、28 个信号;未知帧按正常流程忽略,已知报文的 payload 长度不一致时返回错误。要做全量解析应使用通用 DBC 解析器,而不是这份子集。

    raw diagnostics 里的枚举值可以直接当车门或安全带状态用吗?

    不可以。素材明确要求不得自行解释 DBC 枚举的含义,也不得把这些值用于车门、安全带、空调的 canonical 状态或车辆控制。它们在 VehicleState.raw_signals 中以 MESSAGE.SIGNAL 为键只供 HMI Diagnostics 只读查看,只有空调温度给出了 raw × 0.5 + 14 的换算。

    0x541 的近光位要怎么在实车上确认?

    在停车、点火 ON 状态下,对灯光 OFF、近光 ON、AUTO 三种条件分别记录时刻与观察到的灯光状态,并核对日志 payload 第四个字节的最高位是否与判据表一致:OFF 为 0、ON 为 1、AUTO 与实际点亮状态相同。不一致则不得发布该映射,需重新核对 DBC 与车辆年款、配置。

    采集到的车辆日志可以回放到车上总线吗?

    不可以。素材规定捕获内容不向物理 can0 回发,开发环境只在 vcan0 上回放。回放前需加载 vcan 模块并创建 vcan0 接口,再用日志回放工具把 can0 映射到 vcan0。回归 fixture 中也不得保留车辆识别信息、位置与时间等敏感原始日志。

    这份 DBC 子集的权利与适配边界是什么?

    它是 opendbc 中 Hyundai CAN DBC 的只读子集,沿用上游 MIT 授权,分发时必须保留原始通知与归属声明;同时源说明要求在使用前核对车型年款、市场与配置适配性。此外该文件的 BU_ 列表缺少若干被引用的节点名,严格解析器可能需要先清理,实车验证也仍在进行中。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/oas-ai-palisade-read-only/decoder-guide.md(9 条报文解码清单、raw diagnostics 目录与 0x541 验证判据)
    • 项目 01 —— public-can-dbc-datasets/additional/oas-ai-palisade-read-only/NOTICE.md(只读子集的上游修订、MIT 归属声明与只读使用限制)
    • 项目 01 —— public-can-dbc-datasets/additional/oas-ai-palisade-read-only/SOURCE.md(9 报文/28 信号的版本固定、BU_ 缺失提示与年款适配提醒)

    免责声明

    本文所述解码器与 DBC 子集为社区逆向工程与公开派生产物,不代表车辆制造商的官方数据。素材中的使用限制已如实转述:该子集为接收专用,不引入上游车辆控制与发送代码;在按车辆年款、市场、配置用实车日志确认 frame 与 signal 之前,不得将新信号纳入 canonical 状态;分发时须保留上游 MIT 授权通知。文中所有 ID、位定义与换算均来自上述公开文件,未经独立实车验证,不同年款与市场可能存在差异,涉及安全相关用途时仅作参考。

    更新记录

    • 2026-10-14 初稿

  • Alfa Giulia 的 177 行 PID:header 错了会触发防盗

    结论:这份针对 Alfa Romeo Giulia 2.2 JTDm(MY2016)柴油版的 PID 表共 177 行、覆盖 8 个 header,含金量很高但带着一条硬性安全告警:如果车上装的那块直连 CAN 适配板启用了防盗功能,绝对不要用 header 为 DAC7F1 的轮胎 PID,它会触发防盗锁止并让车熄火。使用顺序必须是先看 header,再看公式。

    八个 header 的分工

    命令之前要先发 header,它的作用是选定要对话的 ECU。177 行按 header 的分布极不均衡,这个分布本身就是本表性质的说明。

    Header 行数 性质 代表 PID
    DA10F1 129 厂商自定义,涵盖发动机、DPF、喷油、启动停止 221003 冷却液温度 (((A*256)+B)*0.02)-40 摄氏度
    DABAF1 15 直连 CAN 适配板专用地址 2200000000FC 发动机转速 ((B*256+C) 与 65534 按位与) 右移 2
    DB33F1 11 标准 OBD-II 通道 010C 转速 ((256*A)+B)/4、010D 车速 A km/h
    DAC7F1 8 轮胎压力与温度,带安全告警 2240B1 至 2240B4 四轮压力 ((A*256)+B)/1000 bar
    DA40F1 7 IBS 蓄电池传感器与钥匙位置 221005 同帧多字段,含 SoC(B 字节)与温度(G 减 40)
    DA18F1 5 变速箱相关 221018 扭矩 ((A*256+B)-500) Nm
    DA2AF1 1 转向角 22083C ((SIGNED(A)*256)+B)/16 度
    DA60F1 1 仪表亮度 220104 A

    这张表立刻能读出两件事。第一,真正属于标准 OBD-II 的只有 DB33F1 那 11 行,其余 166 行全是厂商或改装板自定义通道——也就是说,指望一个通用 OBD App 自动识别是不现实的,必须手工导入自定义 PID。

    第二,DA10F1 独占 129 行,而这个 header 有一个非常具体的行为特征:它的响应需要较长时间。作者给出的应对办法很聪明——同一个仪表页里如果混放 DA10F1 的传感器和其他传感器,前者会因为来不及应答而完全不显示。正确做法是把所有 DA10F1 的传感器集中放在同一个页面、与其他页面隔离,因为 App 只轮询当前仪表页上的传感器。

    十五条「?」行与重复 PID

    作者给数据加的自证标注是这张表最值得尊敬的地方:长描述以 ? 开头的行,表示他在自己的车上无法验证。177 行里有 15 行属于这一类,其中包括 MY20 才有的几个电池 PID(如 2240B2 电池 SoH、222B0E 电池电流)与几条 Hall 值(例如 223888 时间格式、2202000001F7 变速箱温度)。把这些行直接搬进自己的仪表配置,等于替作者承担未验证的风险。

    现象 实例 该怎么处理
    长描述以 ? 开头 ? 前缀共 15 行,作者自述无法在车上验证 仅在确认车型年款后作为候选,不进告警逻辑
    同一 PID 重复出现且单位不同 012F 燃油液位同时给出百分比 A*100/255 与升 A*53/255 二选一,不要同时上仪表否则读数冲突
    同一 PID 重复出现且公式不同 里程 22380E 一处为三字节除以 10,另一处为「总里程减去该值」 按车辆年款选定一条,交叉验证后再用
    同一 PID 原样重复 DPF 再生次数 2218A4 出现两次,公式相同 导入前去重,避免同名重复传感器
    仅在特定年款可用 MY20 系列电池 PID 标注 (MY20) MY2016 柴油车不适用,勿据此判断电池状态

    还有一个平台差异必须写在前面:这些参数全部在一台 2.2 柴油 Giulia 上测试。作者明确说,其中一部分在 2.0 与 2.9 汽油版上不适用也不工作;反过来,论坛上流传的汽油版 PID 在他车上也不工作、未经他测试。跨动力总成使用这份表之前,请先假设它不成立。

    两条硬约束:安全与轮询预算

    第一条是安全。为了绕过 ELM327 拿不到的数据,作者使用了一块廉价的 CAN 适配板,并为它定义了专用 header DABAF1;长描述里标注为直连板专属的 15 行就是走这条通道的。而紧跟着有一条全大写的告警:如果车上的这块板启用了防盗功能,不要使用 header 为 DAC7F1 的轮胎 PID,它会触发防盗并让车熄火。这一条属于操作红线,不是免责声明。

    第二条是轮询预算。每个传感器都有一个 priority 字段:1 表示每个仪表刷新循环都请求一次,1/50 表示每 50 个循环才请求一次。作者的建议是按信号的变化速度分配——涡轮压力变化快、给高优先级,冷却液温度变化慢、给低优先级,把这个页面挤出来给快变量。这个字段是传感器自身的属性,在所有使用它的仪表页生效,因此改一处会影响到其他页面。

    常见问题

    为什么我的 App 用这份表什么都读不出来?

    先看 header 混放问题。177 行里 129 行属于 DA10F1,而这个 header 的响应较慢:同一仪表页若混放 DA10F1 与其他 header 的传感器,DA10F1 会来不及应答而不显示。作者的建议是把所有 DA10F1 传感器集中在同一页并与其他页隔离。

    轮胎 PID 到底能不能用?

    看车上那块直连 CAN 板是否启用了防盗:如果启用,绝对不要用 header 为 DAC7F1 的 8 行轮胎 PID,它会触发防盗锁止并使车辆熄火。没有装该板或未启用防盗时才可尝试,且同样建议你先确认车辆年款。

    标着 ? 的 PID 是什么意思?

    作者自述:长描述以 ? 开头的 PID 是他无法在自己车上验证的条目,共 15 行,包含 MY20 系列的几个电池参数与若干未确认的 Hall 值。这类条目只能作为候选,不应写入告警或长期趋势判断。

    2.0 汽油版能不能直接套用这份表?

    不能直接套用。全部参数是在一台 2.2 JTDm 柴油 MY2016 上测试的,作者明确说明其中一部分在 2.0 与 2.9 汽油版上不适用也不工作。跨动力总成使用前应先假设不成立,逐条实车验证。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/danardi78-alfa-giulia-pids/README.md(适用车型 Giulia 2.2 JTDm MY2016、文件清单与八列字段含义、? 前缀表示无法验证、DA10F1 响应较慢的仪表页排布建议、priority 字段取值为 1 与 1/50 的含义、直连 CAN 板专用 header DABAF1 及其目的、DAC7F1 轮胎 PID 触发防盗锁止的安全告警、仅柴油版测试的适用边界)
    • 项目 01 —— public-can-dbc-datasets/additional/danardi78-alfa-giulia-pids/custompids.csv(177 行 PID 的 header、命令 ID、公式、单位与量程;用于核对 DA10F1 129 行、DABAF1 15 行、DB33F1 11 行、DAC7F1 8 行等的分布与 15 行 ? 标注)

    免责声明

    本文所述 PID、header 与换算公式均来自公开开源资料分享,属第三方逆向工程产物,未经独立实车验证,也不代表车辆制造商的官方数据。文中前述的安全告警(启用了防盗功能时禁用 DAC7F1 轮胎 PID)为原作者明确声明,实际操作前必须确认车辆配置。不同年款、市场与动力总成存在差异,涉及行车安全的判断请以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • canlab-zn6:四层只读约束与 9 个受限诊断查询

    结论:canlab-zn6 的「只读」不是一个 UI 设计选择,而是四层各自独立的关:固件跑控制器 listen-only 并持续拉住收发器的 silent 引脚、wire protocol 里根本没有发帧命令、Host 只在 decode-only 门面后面用 can_decode、 contributing scope 直接把写诊断与 ECU 仿真列为不接受。它的价值恰恰在于这套自限——看懂这四层,就知道为什么它是稳妥的上车第一步,也知道它当前拿不到什么。

    四层只读约束分别拦住了什么

    大多数「只读工具」只在应用层藏了一个按钮。这份 Rust 工具链把约束下沉到了四个不同层次,每一层的失效模式都不一样,因此值得逐层拆开看。

    层次 机制 拦住的动作 残余风险
    固件 控制器 listen-only 模式,并持续把收发器硬件 silent 输入保持在有效状态 ESP32-S3 / ESP32-C6 板卡自发帧 依赖电气连接正确,官方要求接车前用电表验证 silent 引脚
    协议 主机 CLI、Web 应用、SocketCAN 适配器、wire protocol、MCP server 均无任意发送命令 来自上位机的单帧注入 与本工具同栈协作时才成立;换成其他 CAN stack 自行接入则不受此约束
    解码 Host 通过 decode-only 门面调用 can_decode,其编码方法不暴露给采集、诊断、固件、Web 服务与 MCP 构造并编码报文的能力 无;但若有人 fork 后改动门面,需重新审计
    社区 贡献指南明确不接受增加在车控制、刷写、写诊断或 ECU 仿真的改动 通过 PR 把写能力带进来 fork 版本不受此约束

    这四层里真正悬而未决的是第二层的物理版本落差:受保护的状态机与 Wokwi 诊断仿真器都已就位,但实体板卡的诊断发送仍处于关闭状态,要等板级的 ARM 与 fail-silent 电气测试完成。也就是说,当前 S3 与 C6 的实体发布包是采集专用(capture-only),诊断只能通过 Linux 上的 receive-only SocketCAN 之外的路径得到 仿真,不能指望在真车上读 Mode 21。

    值得把这句警告当回事:作者在文档里明确写了这不是车规产品,而是实验性研究设备。把它接上一台日常代步车之前,先读完安全边界那一章并用电表验证 silent 引脚,是唯一负责任的顺序。

    为什么自动变速箱的 DBC 是空的

    这份工具的 ZN6 DBC 定义被拆成 common / manual / automatic 三层 profile,每层显式声明它叠加哪些 DBC 文件、以及是否加载 candidate 条目。空文件是有意保留的一种状态。

    层/文件 内容 是否加载 证据状态
    zn6_gen1_common.dbc 已发布信号,基于 MIT 授权的 FT86 Gen1 map 与 RaceChrono 映射 三个 profile 共同底层 published,含测试向量
    zn6_gen1_mt.dbc 手动挡专有部分 由 MT profile 叠加加载 published
    zn6_gen1_at.dbc 自动挡专有部分 由 AT profile 叠加加载 目前为空,刻意不出表
    zn6_gen1_at_inventory.toml 仅登记存在性与标称速率,不断言任何位布局 独立引用,来源为 Toyobaru CAN ID map 6 个 ID:328/329/330 均 100 Hz、872 为 20 Hz、977 为 8 Hz、1218 为 1 Hz
    evidence.toml 13 条逐信号证据,含来源 URL、车型变体、观测量与测试向量 始终随定义引用 12 条 published + 1 条 candidate

    AT DBC 空着不是因为没人做,而是源于一条明确写进文档的原则:看起来合理却是编造出来的字段,比明确标注「未知」更糟。因此 include_candidates 默认 false,candidate 级别的条目(例如 ID 865 那个未经验证的三位挡位字段)不会被加载,除非显式要求。

    这套结构的直接推论是:evidence.toml 里的每一行都带车型变体限定,比如车门与尾箱的标志位分配只在右舵车上验证过、安全带只有驾驶员侧测过而乘客侧行为未知。把 published 当成「全变体通用」而不加甄别地套用,左舵车的车门信号就会错位。

    九个诊断查询的参数与限速

    诊断目录是整套工具里最容易被低估的部分:它把每个请求的预算写死了,包括超时与轮询上限,而不是靠调用方自觉。

    查询 service / 参数 解码方式 timeout / max_hz 置信度
    车速 1 / PID 13 linear_u8,offset 2,factor 1.0 250 ms / 2.0 Hz published
    发动机转速 1 / PID 12 linear_u16_be,factor 0.25 250 ms / 2.0 Hz published
    冷却液温度 1 / PID 5 linear_u8,bias −40 250 ms published
    节气门位置 1 / PID 17 linear_u8,factor 0.3921568627 250 ms / 2.0 Hz published
    控制模块电压 1 / PID 66 linear_u16_be,factor 0.001 250 ms / 1.0 Hz published
    VIN 9 / PID 2 ascii,offset 3,length 17 750 ms / 0.2 Hz published
    存储故障码 3 / 无参数 trouble_codes,offset 1 750 ms / 0.2 Hz published
    机油温度 33(Mode 21)/ PID 1 linear_u8,bias −40 400 ms / 1.0 Hz candidate,opt_in
    变速箱油温裸值 33(Mode 21)/ PID 130 raw,offset 2 400 ms / 1.0 Hz candidate,opt_in

    请求 ID 统一为 2016、响应为 2024,即 0x7E0 / 0x7E8 这一对标准 OBD 地址。真正的判别价值在中间两列:四个 PID 用 250 ms 超时、400 ms 给 Mode 21,而 VIN 与故障码给到 750 ms 并把轮询压到 0.2 Hz。这个分配隐含一个判断—— identification 类请求慢且贵,只能偶尔问;物理量类请求便宜,但也不能无上限。自己做轮询调度时,这份表是最省事的初始值。

    另外两个 Toyota Mode 21 查询都带 opt_in = true 且标记 candidate。它们的 expected_prefix 是十进制的 [97, 1] 与 [97, 130],正响应前缀落在 Mode 21 的规则内;但把 raw 类型的 ATT 温度直接做成仪表读数是不合适的,它连缩放系数都没有。

    常见问题

    这套工具能在实车上发送 CAN 帧吗?

    不能。固件跑控制器 listen-only 模式并保持收发器 silent 引脚有效,主机 CLI、Web 应用、SocketCAN 适配器、wire protocol 与 MCP server 都没有任意发送命令,且 Host 只用 can_decode 的解码面。当前 S3/C6 实体发布包是采集专用,实体诊断发送要等板级 ARM 与 fail-silent 电气测试。

    为什么自动挡的 DBC 文件是空的?

    这是一个刻意的选择。作者认为看起来合理却是编造的字段比明确标注未知更危险,因此 zn6_gen1_at_inventory.toml 只登记自动挡报文的存在性与标称速率(328/329/330 为 100 Hz、872 为 20 Hz、977 为 8 Hz、1218 为 1 Hz),不断言位布局;真正的信号定义要等到有可辩护的定义与测试向量再进 DBC。

    candidate 级别的信号默认会参与解码吗?

    不会。三个 profile 的 include_candidates 都是 false,Candidate 条目需要显式请求才加载。目前 evidence.toml 的 13 条里只有 1 条是 candidate——ID 865 那个未经验证的三位挡位字段,其余 12 条为 published。

    贡献一份新的信号定义需要提供什么?

    来源、车型变体、观测记录与测试向量,四项缺一不可。并且面向在车控制、刷写、写诊断或 ECU 仿真的改动明确不在接收范围内——这条贡献禁令与前述四层只读约束是一致的。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/dimitri-canlab-zn6/README.md(read-first 定位、ESP32-S3/C6 与 TJA1051T/3 收发器、listen-only 与 silent 引脚、无任意发送命令、capture-only 实体发布、诊断 TX 待测试、capture 的四种输入源、Rust 1.96 与 2024 edition、MIT OR Apache-2.0)
    • 项目 01 —— public-can-dbc-datasets/additional/dimitri-canlab-zn6/docs/definitions.md(common/manual/automatic 三层 profile、candidate 不默认加载、AT 清单仅登记存在与速率、AT DBC 保持为空的原则、Host 只用 decode-only 门面)
    • 项目 01 —— public-can-dbc-datasets/additional/dimitri-canlab-zn6/definitions/zn6_gen1_at_inventory.toml、definitions/evidence.toml、definitions/diagnostics.toml(6 个 AT ID 与标称速率、13 条证据的置信分布、9 个诊断查询的 service、参数、解码器、timeout_ms 与 max_hz)

    免责声明

    本文所述工具行为、定义分层与诊断参数均来自公开开源仓库的说明与配置文件,属社区研究产物,未经实车独立验证,也不代表车辆制造商的官方数据。该项目自我定位为实验性研究设备而非车规产品。涉及在用车接入的操作请以官方资料与实际测量为准。

    更新记录

    • 2026-10-14 初稿

  • LiveWire S2 Del Mar:60 报文副总线与 9 个 UDS 模块

    结论:2024 LiveWire S2 Del Mar 的公开 CAN 资料给出了两件东西,用途完全不同:副总线的广播 .dbc(60 条报文、111 个信号)和一份跨 9 个诊断模块的 UDS DID 目录。转速、车速这类实时量直接从广播拿;电池单体电压、ABS 轮速、绝缘电阻这类不广播的数据,必须走 BCM 网关用 0x22 读——把它们混为一谈是初学者最常见的错。

    总线事实:先记这四条

    这份社区资料把前置条件写得非常干净,接入之前四条必须对齐。

    维度 取值 错配后果
    速率与类型 500 kbit/s,classical CAN,11 位标准 ID 按 CAN FD 或扩展帧配置时完全解不出帧
    字节序 big-endian,Motorola 起始位记作 @0 多字节信号反向拼接,数值量级明显偏大
    保护机制 约 36 个 ID 带 E2E 保护(AUTOSAR Profile-1 风格) 自定义解析时未跳过校验字节,信号整体错位
    拓扑 副总线上只有 BCM、IM、TCU、DLC;电机、BMS、ABS 在主总线 在副总线上找电机转速,永远找不到

    第三条 E2E 保护值得单独说:D7 是 6 位 alive counter,D8 是覆盖 D1 到 D7 的 CRC-8 / SAE-J1850(多项式 0x1D,初值与异或输出均为 0xFF)。它的价值不在于安全,而在于充当解码预言机——位对齐一旦算错,CRC 立即失败,不需要等到物理量不合理才发现问题。这是逆向过程中极少数可以机器判定对错的抓手。

    九个模块各管什么

    副总线的广播成员是 BCM(网关)、IM(仪表)、TCU、DLC 四个,其余全部在主总线上,通过 BCM 网关用 ISO-TP 上的 0x22 ReadDataByIdentifier 读取。下面这张表是挑选 UDS 模块时的直接依据。

    模块 请求与响应 ID 角色 能拿到的关键数据
    BCM 0x7E2 → 0x7EA 车身控制器,副总线网关 纵向加速度 ADC 快照、内部 mV 电源轨、时间同步、骑行模式配置表、防盗与钥匙块
    VSC 0x7E4 → 0x7EC 车辆监控控制器,监管 ABS 与 IMU 实时电机转速与驱动状态、扭矩记录、油门位置、电池包端电压、当前骑行模式序号及逐模式标定三元组
    RESS 0x7E6 → 0x7EE 高压电池管理,信息价值最高的模块 逐单体电压阵列、逐单体温度阵列、包电流双传感 A/B、SoC/SoE/SoH、累计安时
    EHCU 0x7E7 → 0x7EF Bosch ABS 液压单元 双路逐轮轮速、前制动压力、制动激活标志、12 V 辅助电源轨、ABS 事件计数器
    OBC / HV 0x7D3 → 0x7DB 车载充电机,交流充电时才活跃 充电器与枪状态、充电电流与功率、充电机散热片温度、母线电压
    TCU 0x7D4 → 0x7DC 远程通信,含 eSIM 与 GPS,隐私敏感 GPS 位置、信号强度、归属网络、固件版本

    表里没有列的还有 LHCM(0x7E3,识别信息与硬件件号,同时携带 VIN)、RHCM(0x7E5,制动激活标志与内部参考电源轨)、以及一个 ID-only ECU(0x7F0 → 0x7F8,只有供应商件号、没有任何实时数据块,属于硬上限)。

    需要注意的是,9 个已映射响应者是下限而不是总数。资料明确提到存在一个未映射的响应方(响应 0x6E6 / 请求 0x6DE)被被动观察到,另有目标地址 0x7F2;并且 IMU 本身没有 UDS 地址,它由 VSC 监管。这意味着你在总线上看到的地址比这张表多,是预期行为。

    读懂它的置信度标注

    这份资料最值得模仿的不是结论,而是它把不确定性强塞进数据结构里:每条信号的行内注释都带四类信息——置信度、标定依据、已撤回的假设与尚未解决的疑问。[CONFIRMED](两种以上方式交叉验证)、[STRONG](一次干净抓包且物理一致)、[TENTATIVE](数据不足)。已撤回的假设会原样保留而不是删除,避免后来者重复尝试同一条死路。

    这套标注的实际用法:写解析代码时,只对 [CONFIRMED] 与 [STRONG] 做断言和告警,[TENTATIVE] 一律只记录不做判断。把三级标注 flatten 成一个 schema,等于把作者的证据强度信息彻底丢弃。

    它的取证方法也相当严格:单人单次动作隔离抓包 + 差分分析;一次相关只构成假设,必须有第二次「专门设计来推翻它」的抓包失败才算成立;未被动作覆盖的字节标记为未解,而不是当作常量写死。这套流程解释了为什么 111 个信号里真正能用作线上判据的比例并不高——大量位置至今没有足够证据。

    三条使用边界

    第一,0x27 SecurityAccess 门禁:BCM、VSC、TCU 上有 16 个 DID 被上锁,种子为 128 位随机数,其内容是按相邻关系推测的,不是读出来的。任何把推测值当成实测值的下游都会出错。第二,隐私:车主 PII 在这份公开副本中被抽掉,VIN、模块序列号、GPS 坐标与 SIM 标识都变成占位符,但结构完整,复现时必须自己填自己的值。第三,版本状态:整体标注为 v0.1 进行中,TENTATIVE 条目可能错误或不完整;并且它只描述如何读取与理解总线,不含任何执行动作的方法。

    常见问题

    电机转速和电池单体电压能不能直接从副总线广播里读?

    不能。副总线上的广播成员只有 BCM、IM、TCU、DLC 四个,电机/逆变器、电池、ABS、充电机、远程通信都在主总线上,需要通过 BCM 网关用 ISO-TP 上的 0x22 ReadDataByIdentifier 主动读取。能直接广播读到的只有广播成员的那些信号。

    E2E 的 CRC 除了校验还有什么用?

    它是逆向时的位对齐预言机。约 36 个 ID 采用 AUTOSAR Profile-1 风格保护,D7 为 6 位 alive counter,D8 为覆盖 D1 到 D7 的 CRC-8 / SAE-J1850,多项式 0x1D、初值与异或输出 0xFF。只要起始位或长度算错,CRC 立即失败,可以在物理量失真之前发现错位。

    已映射的 9 个模块是不是这辆车的全部 ECU?

    不是,作者明确写这是一个下限。被动观察到的还有一个未映射响应方(响应 0x6E6、请求 0x6DE)以及目标地址 0x7F2;IMU 没有自己的 UDS 地址,由 VSC 监管。也就是说实际地址数会多于这 9 个。

    为什么有些 DID 的内容看起来是「猜」出来的?

    因为 BCM、VSC、TCU 上有 16 个 DID 被 0x27 SecurityAccess 上锁,种子是 128 位随机数,作者无法读取其值,只能按相邻关系推测其含义。使用这类字段时必须标注为推测值,不能参与任何判定逻辑。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/inklit-livewire-s2-delmar/README.md(500 kbit/s、11 位 ID、Motorola @0、约 36 个 ID 的 E2E 与 CRC-8 参数、60 报文 111 信号、9 个 UDS 模块、CONFIRMED/STRONG/TENTATIVE 标注体系、取证方法论、CC BY 4.0 授权)
    • 项目 01 —— public-can-dbc-datasets/additional/inklit-livewire-s2-delmar/MODULES.md(九个诊断模块的请求与响应 ID、角色与关键数据;9 为下限的说明、0x27 上锁 16 个 DID、IMU 无 UDS 地址、PII 抽除说明)

    免责声明

    本文所述总线参数、模块地址与信号数量均来自社区公开整理的说明文件,属第三方逆向工程产物,未独立实车验证,也不代表 LiveWire 或任何制造商的官方数据。不同年份、市场与固件版本的 ECU 可能存在差异。涉及 0x27 安全访问的字段为作者推测值。任何改动请以实际测量与官方资料为准,操作在用车存在风险。

    更新记录

    • 2026-10-14 初稿

  • G55/W463 的 CAN-B 故障隔离:三步 A/B/A 对比定位连接器

    结论:这辆 W463 G-Class 的间歇性故障最终定位在后车门控制模块连接器的进水腐蚀上,但真正有复用价值的是诊断方法:先描述物理层特征,再用受控的 A/B/A 对比隔离分支,最后才去拆连接器。关键判据是容错 CAN-B 失去了两根导线之间的互补关系并反复进入类单线故障模式——普通的帧计数监测能描述症状,却解释不了成因。

    三条总线的速率差异决定了故障表现

    这台 2011 年款 G55 AMG(W463)公开了两条 curated DBC:g55-canc.dbc 对应标称 500 kbit/s 的 CAN-C,g55-canb.dbc 对应标称 83.333 kbit/s 的容错 CAN-B。出现故障的是后者。

    总线 标称速率 类型 故障中的表现
    CAN-C 500 kbit/s 高速 CAN 故障期间继续正常工作
    CAN-B 83.333 kbit/s 容错 CAN 有效帧完全中断

    CAN-B 仍能工作而 CAN-C 不受影响,这一对比本身就是一个强信息:它把故障范围从「整车电气问题」收敛到「车身网络分支」,同时排除了供电与接地这类公共成因。

    容错 CAN 为什么不能按高速 CAN 的思路查

    容错 CAN 收发器允许在单根导线故障时继续通信,代价是它的正常状态依赖两根导线之间的互补关系。这次故障中,这个互补关系丢失了,总线反复进入类单线的失效模式,而不是简单地被钳位到某个电平。理解这一点才能解释为什么故障是间歇的、为什么模块会时好时坏。

    三步 A/B/A 隔离法

    整个排查用了三次受控对比,每次只改变一个变量,并且用相同的无损采集方式记录。核心原则是示波器与 CAN 记录仪被视为同一个实验:模拟测量描述线路状态,数字采集确定有效帧是否真的在丢失。

    步骤 配置 观测结果 推论
    A 基线 完整分支全部接上 记录到持续异常 建立物理层特征基线
    B 断开 断开一个可疑分支,重复同样的无损采集 观测窗口内持续异常消失 异常与该分支相关
    A 复现 重新接回该分支 异常复现 排除巧合,确认因果
    细分 分支保持连接,但在模块连接器处分开 异常再次消失 定位到模块/连接器边界,而非主干

    最后一步是整套方法的关键。断开分支能证明「问题在这一支」,但只有「分支保持连接、仅分开模块连接器」才能把故障定位到模块与连接器的交界处,而不是主干线束。少了这一步,结论会停留在「换这个模块」。

    受控对比必须锁定的变量

    对比要成立,三次采集必须保持一致:探针位置、终端电阻、比特率与采集设置都要相同。任何一项变化都会让「异常消失」失去意义——它可能只是采集条件变了。这也是为什么作者强调示波器与记录仪要当成同一个实验来设计。

    可迁移的故障特征

    在多次重复采集中,反复出现的特征有五条。它们构成了判断「这是物理层间歇故障而非协议层问题」的依据。

    特征 表现 为什么重要
    互补性丢失 不是永久性电平钳位 区分容错 CAN 失效与普通短路
    双低与双高状态 占据事件的相当比例 说明两根线被共同驱动
    共模电压偏移 每根线仍会回到正常静止区 说明不是完全断开
    总线级帧中断 CAN-B 有效帧完全中断,CAN-C 继续工作 把范围收敛到车身网络分支
    复现间隔不规则 安静三五分钟不代表系统干净 否定「短时间观察无异常即通过」

    其中「复现间隔不规则」这一条对实际操作影响最大。间歇性故障最容易被误判为已修复,就是因为观察窗口太短。作者明确指出,安静的三五分钟不能作为系统干净的依据。至于具体阈值,则属于仪器配置范畴,不应当作通用限值照搬。

    证据分级与公开边界

    这份案例研究自带一套四级证据标签,并在文末声明了各自的适用范围。

    等级 定义 本案例中的对应内容
    Measured 在采集或物理测量中直接观测到 一台 W463 上重复的无损采集与物理层观测
    Derived 由实测观测推算得出 分支定位,以及互补性丢失与间歇性容错收发器路径之间的关联
    Borrowed 取自已识别的外部来源 来自已识别维修文档的平台拓扑与连接器引脚描述
    Hypothetical 可检验的解释,非既定事实 —

    案例最后明确列出了不主张的范围:不适用于每一台 W463、每一种 CAN-B 收发器、或每一种间歇性车身网络故障。原始采集、车辆身份、维修记录与当前修复状态都不在公开内容之列;公开的 DBC 仅用于被动分析与台架解码,不是发送报文的指南。平台拓扑参照的是 Model 463 车内 CAN 图 PE00.19-U-2300GC(通过 Operation CHARM 镜像查阅),该维修资料本身未在此重新分发。

    常见问题

    为什么帧计数监测查不出这类故障?

    因为帧计数只能描述症状,不能解释成因。这次故障的本质是容错 CAN-B 物理层丢失了两根导线之间的互补关系并反复进入类单线故障模式,必须测量模拟线路状态才能看到;数字侧的帧计数只会显示「有效帧变少了」。

    断开分支后异常消失,为什么还要再接回去?

    为了排除巧合。只做断开一次,无法区分「异常与该分支相关」和「异常恰好没在这段时间出现」。重新接回后异常复现,因果才成立;再进一步保持分支连接、只在模块连接器处分开,才能把故障定位到模块与连接器边界而非主干线束。

    安静三五分钟没出现异常,能算修好了吗?

    不能。案例中明确记录复现间隔是不规则的,因此安静的三五分钟不构成系统干净的证据。判断是否修复需要更长的观察窗口,或按同样的受控对比重新采集。

    这套方法能用到其他车型上吗?

    方法可迁移,结论不能照搬。案例声明不主张适用于每一台 W463、每一种 CAN-B 收发器或每一种间歇性车身网络故障。可复用的是「先测物理层、再做受控分支对比、最后才拆连接器」这个顺序,以及把示波器与 CAN 记录仪视为同一个实验的做法。

    公开的 DBC 能用来修车吗?

    不能作为发送依据。公开文件明确说明它们仅用于被动分析、记录与台架解码,不是向车辆发送或注入报文的操作指引。文中也提醒,DBC 条目不等于出厂规格,须连同注释与配套案例一起读,并区分实测、推导、借用与未验证的事实。

    延伸阅读

    参考资料

    • 项目 08 —— public-can-dbc-datasets/additional/cipher982-g55-w463/can/case-studies/can-b-fault-isolation.md(故障机理、三步隔离法、故障特征清单与证据分级)
    • 项目 08 —— public-can-dbc-datasets/additional/cipher982-g55-w463/can/README.md(CAN-C 500 kbit/s 与 CAN-B 83.333 kbit/s 容错 CAN 的 DBC 与证据标签定义)
    • 项目 08 —— public-can-dbc-datasets/additional/cipher982-g55-w463/can/PROVENANCE.md(三类证据来源、维修文档参照与解释规则)
    • 项目 08 —— public-can-dbc-datasets/additional/cipher982-g55-w463/README.md(公开工作区的范围声明与内容边界)

    免责声明

    本文转述的是公开仓库中的一份诊断案例,其中的观测结果来自单一车辆,作者明确不主张其适用于所有同平台车辆或所有同类故障。文中提到的物理层阈值属于具体仪器配置,不应作为通用限值。公开的 DBC 仅供被动分析与台架解码,不构成向车辆发送或注入报文的操作指引。实际维修请以专业检测与官方维修资料为准。

    更新记录

    • 2026-10-14 初稿

  • tinygrad 优化器为何不能只存权重:zero_grad 置 None

    结论:tinygrad 的优化器是训练期参数更新器,不是车端规划器或控制器;把它接进车端推理链没有意义。有两个事实最容易被忽略。第一,zero_grad() 是把每个参数的 .grad 设为 None,不是对梯度缓冲填零,下一轮 backward() 才重新生成梯度。第二,step() 的执行包含参数、动量/矩和偏置幂三组状态的同批 realize,只序列化权重而遗漏优化器状态,不能无损续训。此外 FUSE_OPTIM 与 TRAINING 默认都是 0,它们是 tinygrad 的上下文/构建配置,不是车辆安全授权。以下为固定快照的静态分析,未运行完整优化器矩阵或实车测试,本地没有模型实体。

    公共生命周期

    Optimizer.__init__() 拒绝负学习率,只把 is_param 张量作为可更新参数并按对象去重,非参数进入 buffers;无可更新参数会断言。device 默认取第一个参数的设备,OPTIM_DTYPE 默认 float32,学习率 dtype 至少为 float32。_new_optim_param() 为动量/矩状态创建零张量:普通模式逐参数创建,设备为 tuple 时使用 zeros_like,融合模式只创建一条展平状态,长度由 pos_params 决定。

    调度与执行上,schedule_step() 要求 TRAINING=1:融合模式拼接参数与梯度、执行一次 _step() 再按原形状拆回,普通模式逐参数更新;它调用 assign() 排定状态和参数写入,返回额外状态、参数和 buffer 的实现列表,梯度缺失则通过 unwrap() 报错。step() 把 schedule_step() 返回的张量交给 Tensor.realize();_apply_update() 将更新量搬到参数设备并从 detach() 后的参数中减去。由于 schedule_step() 已有 assign 副作用,不应视为只读计划 API。

    入口 实现及外部契约
    Optimizer.__init__() 拒绝负学习率;仅 is_param 张量可更新并按对象去重;OPTIM_DTYPE 默认 float32。
    _new_optim_param() 创建零状态张量;融合模式只建一条展平状态,长度由 pos_params 决定。
    zero_grad() 把 .grad 设为 None,不是填零;下轮反传重新生成。
    schedule_step() 要求 TRAINING=1;融合拼接后一次 _step() 再拆回;有 assign 副作用。
    step() / _apply_update() 前者把张量交给 realize();后者在参数设备上从 detach() 参数减去更新量。
    OptimizerGroup 汇集多优化器的参数/buffer/排定更新;无跨组参数去重,也未调用基类构造器。

    优化器族与状态布局

    优化器族大致分为 LARS 家族与 LAMB 家族。SGD() 是 LARS(tcoef=0, pre_wd=True) 的包装;LARS._step() 可按参数/梯度范数计算 trust ratio,支持两种动量与 Nesterov,权重衰减和学习率在 classic 分支的作用次序不同。Muon() 复用 LARS 更新主干,先对梯度应用 Newton-Schulz,再做学习率及后置权重衰减,并明确拒绝 fused=True。

    优化器 实现要点 边界
    SGD() LARS(tcoef=0, pre_wd=True) 包装 复用 LARS 主干与权重衰减次序。
    LARS._step() 按参数/梯度范数算 trust ratio;两种动量与 Nesterov 权重衰减与学习率次序在 classic 分支不同。
    Muon() 先 Newton-Schulz 再学习率与后置权重衰减 拒绝 fused=True;形状与数值稳定性不能由接口名保证。
    Adam() 包装 LAMB(adam=True),trust ratio 固定为 1 不加权重衰减。
    AdamW() 同 Adam 包装 默认衰减为 0.01。
    LAMB._step() 更新幂状态与一、二阶矩,做偏置修正与 eps 分母稳定,可选算 trust ratio 梯度与状态设备不同则先搬至状态设备;更新量 shard_like(t) 在参数设备应用。

    状态布局的关键是 OPTIM_DTYPE 默认 float32、FUSE_OPTIM 默认关闭;融合时会把参数和梯度展平拼接,切换融合配置或设备布局不能直接复用旧状态形状。学习率 dtype 至少为 float32。

    状态机与调用顺序

    典型训练调用为:loss.backward() 前先 zero_grad(),随后 loss.backward()、step()。step() 的执行包含参数、动量/矩和偏置幂的实现;仅序列化权重而遗漏优化器状态,不能无损续训。FUSE_OPTIM 默认 0、TRAINING 默认 0,这两个开关是 tinygrad 的上下文/构建配置,不是车辆安全授权。OptimizerGroup 的交叉参数重叠、状态文件版本兼容、多设备搬运以及异常发生在部分 assign() 之后的恢复语义,均应列入训练插件测试矩阵。

    开关 / 状态 默认值 含义
    OPTIM_DTYPE float32 优化器状态张量的默认 dtype。
    FUSE_OPTIM 0(关闭) 融合时参数与梯度展平拼接为单条状态。
    TRAINING 0(关闭) schedule_step() 要求其为 1;是构建/上下文配置,非安全授权。
    优化器状态集合 — 参数 + 动量/矩 + 偏置幂,step() 时同批 realize。

    ONNX 与车端关系

    OnnxRunner._init_from_graph() 通过图中训练域节点设置 is_training,OnnxRunner.__call__() 在该标志下设置 Context(TRAINING=...)。ONNX 的 Adam() 借用 TinyAdam,但把外部 V/H 及学习率装入临时优化器,并依据步数初始化偏置幂——这属于 ONNX 训练域功能,不表明 openpilot 车端执行在线学习。

    车端一侧,openpilot 的 compile_model() 在构建时把 ONNX 编译为捕获的模型工件;运行期 ModelState.run() 调用 run_warp/run_model 并解析输出。当前本地源码未见该路径实例化 tinygrad.nn.optim 或调用 step();由于缺少模型实体,只能确认可见源码的调用边界,不能断言模型图内部的全部节点。

    硬件与插件重构建议

    把优化器、梯度、训练域 ONNX 和权重写入归入离线训练/构建插件;车端推理插件只接受已验证、版本绑定、只读的模型工件,不要给训练插件 CAN 发送、panda 安全模式或控制消息发布权限。模型工件契约应记录 ONNX/opset、tinygrad 版本、dtype、输入输出 shape、目标设备、权重哈希和参考数值;优化器状态另行版本化,fused、参数顺序和设备分片都需一致。硬件替换只接管相机帧、时间戳、缓冲拷贝/映射和推理执行端,保持 modelV2 等输出 schema、延迟预算和健康信号稳定,不可通过更换优化器绕开感知、规划、车型协议与 panda 固件安全门。

    常见问题

    zero_grad() 是把梯度清零吗?

    不是。它把每个参数的 .grad 设为 None,而不是对已有梯度缓冲填零;下一轮 backward() 会重新生成梯度。

    只保存权重就能无损续训吗?

    不能。step() 的执行包含参数、动量/矩和偏置幂三组状态的同批 realize;只序列化权重而遗漏优化器状态,无法无损续训,优化器状态需要另行版本化保存。

    tinygrad 优化器与 openpilot 车端推理是什么关系?

    没有直接关系。优化器是训练期参数更新器;openpilot 在构建时把 ONNX 编译为捕获的模型工件,运行期 ModelState.run() 只运行该工件,当前可见源码未见车端实例化 tinygrad.nn.optim 或调用 step()。

    FUSE_OPTIM 或 TRAINING 打开会影响车辆安全吗?

    不应这样理解。这两个开关默认都是 0,是 tinygrad 的上下文/构建配置,不是车辆安全授权;训练数值测试也不等于整车安全验证。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TINYGRAD_OPTIMIZER_TRAINING_STATE_INFERENCE_BOUNDARY_DEEP_DIVE.md(取用了优化器族与状态表、zero_grad 置 None、三组状态同批 realize、ONNX 训练域与车端推理边界)。

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

    更新记录

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