标签: DBC

  • 大众 MLB/PQ 构帧器:返回类型不匹配与固件白名单

    结论:大众 MLB 与 PQ 共用同一个 CarController 和一套「统一 CCS 插件接口」,但两者的纵向实现并不等价。静态源码里有两处硬边界:PQ 纵向助手返回帧列表(与 can_sends.extend() 一致),而 MLB 助手返回 CANPacker.make_can_msg() 的 (address, bytes, bus) 单帧三元组,extend() 会把三个成员分别加入列表——这是源码可见的返回类型不匹配;同时 MLB 固件安全配置只允许 HCA_01、LDW_02、两总线的 LS_01,没有 ACC_05/ACC_02。统一接口不能掩盖这些差异。

    共享控制器与两套构帧器

    基线是 openpilot 固定快照 c8fb906,覆盖 mlbcan.py 与 pqcan.py 的全部实现函数,并追踪共享车型控制器、接口配置、DBC packer 与 panda safety。仅做静态分析:未发 CAN、未改 ECU、未跑实车。共享控制器 CarController.update() 先限扭矩、做重复值缓解,再以非零扭矩作为 HCA 使能值。

    功能 MLB 实现 PQ 实现
    转向 create_steering_control() 输出 HCA_01,状态 7/3 与 lkas_enabled 对应,扭矩拆绝对值和符号。 create_steering_control() 输出 HCA_1,仅在 enabled 且扭矩非零时状态 7,否则 3。
    LDW / HUD create_lka_hud_control() 直接委托 MQB 构帧。 构造 LDW_Status,可复制五个原车警示字段,再覆盖车道线/告警/转向提示。
    巡航按钮 复制 LS_01 四个配置字段,原车 COUNTER+1 mod 16,覆写 cancel/resume。 对 GRA_Neu 作同类处理。
    纵向状态 acc_control_value() 与 acc_hud_status_value() 均固定返回零。 acc_control_value() 按活动/主开关返回 1/2/0;acc_hud_status_value() 另将故障置 6。

    按钮帧由 pcmCruise、原车 COUNTER 变化与 cancel/resume 请求共同决定发送,总线由 CAN.ext 选择;LDW/HUD 按平台 LDW_STEP 发送,输入来自 CarState 的原车缓存。纵向状态分支只在 openpilotLongitudinalControl 为真时走自控纵向及其 HUD 分支。

    需要说明的是,「什么时候发」和「发什么内容」由不同东西决定:LDW/HUD 依平台的 LDW_STEP 节拍发送,纵向 HUD 依 ACC_HUD_STEP 把单个返回值 append 到发送列表,而内容取自 CarState 缓存的原车配置与计数。节拍常量与原车缓存只保证本机按序产出候选帧,并不等于车辆已接受该帧。

    纵向路径与返回类型不匹配

    加速度与纵向 HUD 构帧是差异最集中的地方。MLB 的 create_acc_accel_control() 用空字段构造单个 ACC_05 三元组,create_acc_hud_control() 用空字段构造 ACC_02;PQ 的 create_acc_accel_control() 构造 ACC_System 并返回单元素列表(关闭时加速度字段用 3.01),create_acc_hud_control() 构造 ACC_GRA_Anzeige,包含状态、目标车速和距离。

    控制器统一调用 can_sends.extend(...),但两种助手的返回类型并不一致:PQ 返回 list[frame],与 extend() 语义一致;MLB 返回 CANPacker.make_can_msg() 的 (address, bytes, bus) 单帧三元组,extend() 会把三个成员分别加入 can_sends,不再是帧列表。这是源码可见的返回类型不匹配,MLB 自控纵向不能据此视为可工作。

    这类差异不会在简单调用里立刻显形:PQ 返回单元素列表,可直接被 extend() 展开为帧;MLB 返回的是 make_can_msg() 的三元组,被 extend() 展开后会把地址、字节和总线各自当成一个元素,破坏原本的帧列表结构。只有真正进入发送链路,这种结构错位才会以异常或错误帧的形式暴露。

    校验与固件白名单

    MLB 的 volkswagen_mlb_checksum() 对十个指定地址使用不同 XOR 初值,其他地址回退 MQB/MEB CRC;PQ 文件没有独立校验函数,由其 DBC/packer 负责已声明的计数和校验字段。CANPacker.pack() 按 DBC signal 类型选回调并填入校验,但它不替代 panda RX/TX 安全检查。

    换句话说,校验只回答「这段字节是否按 DBC 约定自洽」,不回答「这条帧是否应该被允许发出」。后者由固件白名单与安全模式独立裁定,两者不能互相代替。

    可达性边界 源码可见事实
    接口能力 接口对 PQ/MLB 都可在 alpha_long 置位时设置 openpilotLongitudinalControl,但两者纵向实现不等价。
    返回类型 MLB 纵向助手返回单帧三元组,被 extend() 拆成三个成员;PQ 返回帧列表。
    固件白名单 MLB 仅允许 HCA_01、LDW_02 与两总线的 LS_01,无 ACC_05/ACC_02。
    构建门 MLB/PQ 两个 safety 模式都仅在 ALLOW_DEBUG 下注册,发布构建不能因识别到平台便假定可启用。
    数据质量 按钮门的当前实现只比较 COUNTER 是否变化,未同时校验该帧接收时间或原车数据质量。

    PQ 的 ACC_System 虽然存在明确的活动/非活动加速度编码、固件也有单独的纵向配置与加速度限幅,但这仅说明源码中存在对应路径,不等于车辆已验证。按钮帧引用 CarState 缓存的原车配置及计数,接线/插件层需保留 bus、时间戳与报文健康,不能只传一个 cancel 布尔量。

    新硬件的验收要点

    新硬件的验收应分别列出 MLB/PQ 车型身份、网络位置、线束、DBC、固件 safety 配置、计数/校验、失联数据新鲜度和拒帧行为。尤其不能让「统一 CCS 插件接口」掩盖 MLB 的占位纵向实现或返回类型差异。

    验收项 MLB 关注点 PQ 关注点
    纵向可发性 返回类型须先修正,且固件白名单不含 ACC 帧。 返回列表与 extend 一致,但仍需实车验证。
    安全模式 仅在 ALLOW_DEBUG 注册。 仅在 ALLOW_DEBUG 注册。
    计数/校验 十地址 XOR 初值 + MQB/MEB 回退。 由 DBC/packer 声明字段负责。
    数据新鲜度 按钮门不校验接收时间。 按钮门不校验接收时间。

    本机隔离探针未完成:系统 Python 缺 numpy,工作区 Python 缺 capnp;本篇没有运行 DBC 字节回放、目标固件或实车试验。归档源码未编辑。

    常见问题

    为什么大众 MLB 的自控纵向「看起来有实现」却不能用?

    因为源码里 MLB 的纵向助手返回的是 CANPacker.make_can_msg() 的 (address, bytes, bus) 单帧三元组,而控制器用 can_sends.extend() 追加,会把三个成员拆开,不再是帧列表;同时 MLB 固件安全配置只允许 HCA_01、LDW_02 与两总线的 LS_01,没有 ACC_05/ACC_02。返回类型不匹配与白名单缺口是两处独立证据。

    PQ 和 MLB 的转向构帧有什么差别?

    MLB 的 create_steering_control() 输出 HCA_01,状态 7/3 与 lkas_enabled 对应,扭矩拆成绝对值和符号;PQ 输出 HCA_1,仅在 enabled 且扭矩非零时状态取 7,否则取 3。两者都经共享控制器先限扭矩、做重复值缓解。

    两个平台都设了 openpilotLongitudinalControl 就等价吗?

    不等价。接口对 PQ/MLB 都可在 alpha_long 置位时设置 openpilotLongitudinalControl,但两者的纵向实现(返回类型、固件白名单、状态编码)都不同;且 MLB/PQ 两个 safety 模式都仅在 ALLOW_DEBUG 下注册,发布构建不能因识别到平台就假定可启用。

    按钮帧只比较 COUNTER 变化够吗?

    源码中该门只比较 COUNTER 是否变化,没有在本地控制器同时校验该帧的接收时间或原车数据质量。接线/插件层因此需保留 bus、时间戳与报文健康,不能只传一个 cancel 布尔量。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/VW_MLB_PQ_CAN_BUILDER_CONTRACT_DEEP_DIVE.md(取用了 MLB/PQ 构帧函数矩阵、纵向返回类型不匹配、MLB 校验与固件白名单、验收要点)。
    • 项目 02 —— openpilot-research/VOLKSWAGEN_MLB_PANDA_SAFETY_DEEP_DIVE.md(仅用于确认 MLB safety 语境的接续关系,未取用其未公开内容)。

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

    更新记录

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

  • Toyota 雷达适配:A/B 槽聚合触发与健康发布的边界

    结论:Toyota 雷达接口不是「每帧到达就出结果」,而是跨轮聚合 A 槽、以最后一个 B 槽作为发布触发的观测链:只有 B 槽到达时才调用 _update() 并清空集合,否则返回 None。这带来三个可复核边界:触发长期缺失时不会生成新 RadarData 或错误事件;can_valid 只在 _update() 时被读取;无解析器时基类每五次调用输出空点,且外层 valid 为真并不代表实体雷达正常。ECU 禁用是独立的通信控制动作,与雷达观测插件必须分权。

    可用性由 DBC 决定,与 ECU 通信控制是两件事

    radarUnavailable 由 DBC 配置决定:TSS2 配置默认含雷达 DBC,而 RADAR_ACC 平台配置会移除雷达总线 DBC。源文档明确,radarUnavailable 表示当前适配没有可解析的雷达 DBC,不等于物理雷达不存在。该路径实例化的 RadarInterface 没有解析器,落入基类定期输出空点的路径。

    与可用性并列、但属另一层的是 ECU 通信控制:RADAR_ACC 且打开实验纵向时设置 DISABLE_RADAR,初始化时向 ECU 发通信控制,控制器周期发送 tester present。源文档强调这是车辆通信与纵向配置动作,不是 RadarInterface 的运行时健康开关,不能在没有车型、线束和安全审查时照搬。

    A/B 槽聚合与发布触发

    _create_radar_can_parser() 按车型选地址组与总线:TSS2 用 A 0x180..0x18f、B 0x190..0x19f;传统用 A 0x210..0x21f、B 0x220..0x22f。A/B 注册 20 Hz,STATUS_MSG 注册 10 Hz,总线为 1。

    解析组 地址范围 名义频率 总线
    TSS2 A 槽 0x180..0x18f 20 Hz 1
    TSS2 B 槽 0x190..0x19f 20 Hz 1
    传统 A 槽 0x210..0x21f 20 Hz 1
    传统 B 槽 0x220..0x22f 20 Hz 1
    STATUS_MSG 源文档未给出具体地址 10 Hz 1

    RadarInterface.update() 把解析器本轮成功解析地址并入跨轮集合;仅当最后一个 B 槽出现时才调用 _update() 并清空集合,否则返回 None。_update() 查询 can_valid 与状态/预故障,只处理累计集合里的 A 槽,按 VALID、LONG_DIST、NEW_TRACK、valid_cnt、对应 B 槽 SCORE 新建、延续或删除点。

    两处细节值得单独指出:B 的 SCORE 从 vl 当前缓存读,源文档指出它未要求 B 与 A 同周期;VALID 为真时直接满足发布条件,SCORE>50 仅为替代路径。A 槽中 NEW_TRACK 或距离越界会先清 valid_cnt,有效近距测量再加一;NEW_TRACK 新建 trackId,否则保留槽位现有点。

    健康与发布的三个缺口

    缺口 静态表现 结论边界
    B 触发长期缺失 无 B 触发时不发布新消息,也不生成错误事件;历史地址仍在集合里 适配层不会主动汇报该故障
    健康门只在 _update() 读取 can_valid 与 STATUS_MSG 只在生成结果时变成 RadarData.errors 触发丢失时错误来不及成为事件
    无解析器的空点路径 基类每五次调用产生空 RadarData;card 仅非 None 时发布 外层 valid 为 errors 全假,不代表实体雷达正常

    CANParser.update() 只返回本轮成功解析地址集,vl 保留各信号最近值;can_valid 检查注册消息缺失/超时和计数器,并有失效计数门。源文档明确 can_valid 只在 _update() 时被读取。无解析器时,RadarInterfaceBase.update() 每五次调用产生空 RadarData,card 仅在返回非 None 时发布 radarTracks,外层 valid 为 errors 全假。因此空点且外层 valid=True 不是「实体雷达正常」的证明,订阅者还要同时看 CarParams 与数据年龄。

    几何约定与插件重构验收

    几何输出为 dRel=LONG_DIST、yRel=-LAT_DIST、vRel=REL_SPEED。插件移植要锁定坐标约定、单位、测量时刻和传感器安装位姿,不能只复制数值字段。上游 radard 以 modelV2 为轮询源,读取最近的 radarTracks 和车况后发布 radarState;源文档提醒,上游不发布时融合循环仍按模型帧推进,不能把输出频率理解成雷达采样频率。

    插件输出应包含 sensor_id、车型/DBC 版本、采样时戳、扫描周期 ID、A/B 配对依据、点年龄、健康原因码和观测状态,并区分「不适用、被禁用、未收到、CAN 不健康、状态故障、正常空点」。接入 radard 前,用只读回放验证 16 组 A/B 顺序错乱、B 槽丢帧、状态报文丢失、NEW_TRACK 复用、距离 255 门槛、VALID 与低 SCORE 组合以及总线超时。

    源文档还给出触发缺失时的要求:应由独立 deadline 产生超时健康事件,而不是等待某个 B 地址恢复后才汇报;历史槽位与 SCORE 必须有最大年龄。这与「以最后一个 B 槽作为发布触发」的机制互补,提醒订阅者不能只依赖适配层的自行汇报。

    最后是分权:ECU 禁用/恢复与雷达观测插件分权,属受信任车辆网关能力,不要把诊断写操作授权给普通传感器插件。本篇未运行测试;上述风险是源码条件推论,不是已复现的实车缺陷。

    常见问题

    radarUnavailable 是不是代表车上没有雷达?

    不代表。源文档明确 radarUnavailable 由 DBC 配置决定:RADAR_ACC 平台配置移除了雷达总线 DBC,所以该路径没有可解析的 DBC,这不等于物理雷达不存在。

    为什么雷达偶发不出结果却不报故障?

    因为发布触发是最后一个 B 槽。源文档指出,B 触发长期缺失时不会生成新的 RadarData 或错误事件,历史地址仍在集合里;can_valid 也只在 _update() 时被读取,所以触发丢失时适配层不主动发布故障。

    没有雷达解析器时输出空点且 valid 为真,能认定雷达正常吗?

    不能。无解析器时基类每五次调用产生空 RadarData,card 仅在返回非 None 时发布,外层 valid 为 errors 全假。源文档明确这不能作为实体雷达正常的证明。

    DISABLE_RADAR 是雷达观测的健康开关吗?

    不是。源文档指出它是车辆通信与纵向配置动作:RADAR_ACC 且打开实验纵向时设置,并向 ECU 发通信控制、周期发送 tester present,与 RadarInterface 的运行时健康状态是两件事。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TOYOTA_RADAR_ADAPTER_DEEP_DIVE.md(取用了地址组与名义频率表、A/B 槽聚合触发语义、健康发布缺口与插件重构验收清单)。

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

    更新记录

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

  • 2017 Subaru BRZ 的 CAN 帧阅读笔记:端序、位序号与 WIP 配置

    结论:这台 2017 款 Subaru BRZ 的档案记录的不是一张成品信号表,而是一套「怎么读 CAN 帧」的方法笔记:如何区分 Motorola/大端与 Intel/小端、位序号为什么与端序无关、商业仪表工具里的 offset/length/startbit/bitcount 分别指什么,以及用回放脚本在没有实车时验证解码。主素材明确这是进行中(WIP)的工作,配置里的偏移、缩放与标签尚未逐条与保留的抓包核对;因此本文把它当作方法记录,而不是可直接套用的信号定义。下述结论仅限源文档记录的这一台车。

    车辆与适用范围

    源文档是车主对自己车辆的私有研究记录。按项目的来源说明,车辆是作者的 2017 Subaru BRZ,配置、市场与 ECU 变体未完全说明;仓库没有找到许可或再分发授权,因此属于非公开的研究副本。文档给出的抓包是作者自述的一段「普通短途驾驶」,共 23,451 行数据。

    项 内容
    车型 2017 Subaru BRZ(据仓库说明)
    年款 / 市场版本 2017;源文档未给出配置、市场版本与 ECU 变体
    ECU / 改装状态 取自 OBD 口的主 CAN 广播;作者用某款 OBD-USB 适配器按 500k 波特率读取
    采集跨度 保留一段约 23,451 行数据的短途驾驶日志;来源说明另提到其他抓包未一并保留
    适用范围 仅限这一台 2017 BRZ;配置的偏移、缩放与标签尚未与抓包逐条核对

    总线与寻址

    源文档按「8 字节 / 64 位经典 CAN 帧」讲解,没有涉及 CAN FD。示例帧是 0x141: 84 26 95 27 B1 82 A7 00,作者用它演示如何定位第 43 位。文档还提醒:商业仪表工具展示字节顺序的方式与常见约定不同,要单独留意。

    项 内容
    物理层 双绞线 CAN;作者按 500k 波特率读取(据文档自述)
    链路层 / 比特率 500 kbit/s(文档自述)
    寻址方式 源文档以 11 位示例 ID(如 0x141、0x0d3)讲解,未展开 11 位 / 29 位差异
    端序 Motorola = 大端,Intel = 小端;小端按递增字节地址读,大端按递减字节地址读
    位序号 全帧 64 位序号固定:字节 0 的位 0 是第 0 位,字节 1 的位 0 是第 8 位,依此类推;端序不改变位序号
    CAN FD 源文档未涉及

    已解码信号

    主素材没有给出成品信号表,只给了一个示例帧与若干配置属性。为守住「只抄有证据的条目」这条线,下表登记源文档明确写出的字段与规则,涉及具体缩放/标签的条目一律标注为未逐条核对。

    字段 / 规则 位置 解码 / 说明 证据
    Custom_Engine_Torque 0x141,bitcount=15 自定义输入;示例未给缩放 README 示例配置
    Engine_Engine_Stop 0x141,startbit=15,bitcount=1 1 位,单位 bit README 示例配置
    Wheel_Torque 0x141,offset=2(第 3 个字节) 示例未给长度与缩放 README 示例配置
    转速类目标值 0x141,offset=4,length=6 映射到商业仪表工具的 RPM 输入(targetId 37) README 示例配置
    车速 长度不足 8 字节的帧(示例 0x0d3) 回放补零后读不准,作者说明该信号受补零影响 README「回放」小节
    帧 ID 写法 XML 的 frame id 可为十进制或 0x 前缀十六进制;若外层有 baseId 则最终 ID = baseId + id README「frame id」小节
    值的 name 与 targetId XML 的 value 有 targetId 则写入工具内置输入;否则用 name 新建 ECU-Specific 自定义输入 README「value」小节
    配置规模 WIP 配置 28 个 frame 元素、145 个 value 元素;偏移、缩放、标签未逐条核对 来源说明
    位序号查找法 任意帧 先把每字节转 8 位,再按全帧序号表定位目标位 README「位序号」小节

    读取方法与注意事项

    第一条是端序与位序号要分开理解。源文档反复强调「位的位置不会因为端序改变」:不管大端小端,第 43 位永远是第 5 个字节里从右边数第 4 个位(位 3)。端序只影响你按什么顺序把连续的字节拼成一个多字节数值。混用是这类解码最常见的错误来源。

    第二条是配置属性的含义——startbit 是开始读的位,bitcount 是一次读多少位,offset 是从哪个字节开始(十六进制),length 是连续读几个字节。作者用一款商业 CAN 数据库编辑器做可视化对照来推导这些含义,因为该工具的术语与仪表工具不同。

    第三条是回放验证的坑。源文档说明其回放脚本会把不足 8 字节的帧用零补齐,例如 0x0d3: 00 06 C0 0F 00 00 42 会变成 8 字节。补零等于给多字节字段多加了 16 位,会改变按位计数与端序结果,因此「用回放测车速」这类信号会读不准。作者建议回放时把推送速率设到能产出约 400 帧/秒,以接近实车从 OBD 口 500k 波特率读到的速率。

    证据等级与边界

    属于方法性、可核对的:端序与位序号的对应规则、配置属性含义、帧 ID 与 baseId 的加法规则。属于未验证的:整套 WIP 配置的偏移、缩放与标签——来源说明写明它们「没有被独立地对照保留的抓包检查过」,而且这套配置是从公开的前代 Subaru DBC 改编而来。

    两处不能外推。第一,车辆本身就是单一样本,且配置、市场与 ECU 变体未说明;同型号不同配置不宜套用。第二,回放脚本的补零行为会系统性歪曲短帧信号,任何用回放得到的数值都不能当作这台车的真实解码结果。

    关于「安全访问」类问题:源文档未提供任何绕过方法,且不建议尝试;本文只整理公开文档记录的解码方法,不涉及对车辆安全机制的规避。

    常见问题

    大端和小端只影响读的顺序吗?

    是的。源文档说明位的位置不会因端序改变,改变的只是你按什么顺序把连续字节拼成多字节数值:小端按递增字节地址读,大端按递减字节地址读。

    第 43 位在示例帧里怎么找?

    按源文档的全帧序号表,第 43 位位于第 5 个字节(示例中为 0x82)内从右数第 4 位(位 3),该帧此位为 0。这个方法与端序无关。

    为什么回放测车速不准?

    因为回放脚本把不足 8 字节的帧用零补齐,等于给字段多加 16 位,改变了按位计数与端序结果。源文档明确说车速信号正来自一个不足 8 字节的帧,因此受此影响读不准。

    这份配置里的 ID、缩放能直接拿来用吗?

    不能直接当作已验证结果。来源说明指出这套 WIP 配置的偏移、缩放与标签尚未与保留抓包逐条核对,且改编自公开的前代 Subaru DBC;使用前需自行复核。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/painellope-brz-2017/CAN_Decoding/README.md(主来源:CAN 帧阅读方法、示例帧与配置属性说明)。
    • 项目 01 —— public-can-dbc-datasets/additional/painellope-brz-2017/SOURCE.md(快照范围、配置规模与「未逐条核对」的边界提示)。
    • 项目 01 —— public-can-dbc-datasets/additional/painellope-brz-2017/CAN_Decoding/CAN_Frame_Datalogs/fileIndex.md(保留抓包的用途与回放补零说明)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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

  • 桌面还是手机:CAN 数据记录工具的四个能力层级

    结论:桌面与手机的 CAN 数据记录工具并非同一品类,而是覆盖四个能力层级:CAN 物理 / 帧层、OBD 服务 / PID 层、厂商增强诊断层、维修决策层。桌面端多出 USB、串口与 Windows 平台上的 J2534 入口,手机端则受限于 BLE / Wi-Fi / 经典蓝牙,二者都不天然支持工程级总线分析。理解这条分层,就能解释为什么「支持 CAN」不等于「能诊断所有 ECU」,也能解释重卡诊断为何要单独看待。

    桌面与手机的入口差异

    公开资料显示,桌面诊断软件与手机应用在物理接口上并不对等。桌面端通常覆盖更完整的接口集合,手机端的连接方式则相对收敛:

    维度 桌面端(PC) 移动端(手机)
    操作系统 Windows 10 / 11、macOS 较新版本 iOS 与 Android
    接口形态 USB、串口、经典蓝牙、BLE、Wi-Fi BLE、Wi-Fi、MFi,Android 另有经典蓝牙
    专业总线入口 Windows 另支持 J2534 兼容设备 通常不提供
    典型场景 离线回放、报告与大屏分析 随车实时读数与一次诊断会话

    两个边界需要说清楚:其一,桌面端对 J2534 的支持通常限定在 Windows,不能外推到 macOS;其二,移动端一般不提供 USB 入口。因此「桌面能力更全」只在特定接口与平台组合下成立,跨平台购买时还需逐项核对授权是否分开。

    四个能力层级:帧、服务、增强、决策

    抛开平台,工具的能力可以按抽象层次排成四级:

    层级 数据表示 主要工作
    CAN 物理 / 帧层 总线、ID、时间戳、字节 采集、过滤、解码规则研究
    OBD 服务 / PID 层 服务号、PID、DTC、物理量 通用诊断与实时监测
    厂商增强诊断层 ECU、私有服务、车型包 多模块诊断、适配与保养
    维修决策层 故障解释、报告、配件 / 流程 判断下一步行动

    这个分层解释了为什么单靠通用 OBD2 适配器与故障码表,无法自然长成工程级 CAN 工具或维修厂诊断平台。原始 CAN 层先保留时间戳与原始帧,再用 DBC 或人工分析建立语义;OBD 服务层则围绕服务号与 PID 的请求和物理量换算。两者处于不同抽象层,不能把「支持 CAN」视为「已能诊断所有 ECU」。

    需要区分的是,原始帧类工具的定位是工程级分析与仿真,而不是给车主解释故障码;它们围绕时间戳、帧 ID、总线与载荷组织数据,用 DBC 把帧转成信号只是其中一步。选择时应先明确目标是「复盘一段总线」还是「读一次故障码」,两者对应的工具形态并不一样。

    原始 CAN 与诊断之外:数据可离线复盘

    桌面端的另一项结构差异是离线可复盘。公开资料中的开源 CAN 分析工具支持从多种日志格式(如 CSV、candump、Vector、PCAN)导入导出,围绕时间戳、帧 ID、总线、方向、长度与载荷组织帧,并借助 DBC 将帧 ID / 位段转成可解释信号。这类工具的定位是工程级总线分析与仿真,而不是给车主解释故障码。

    与之相对,一些桌面 OBD 工具的核心仍是「设备随附的诊断软件」定位:故障码、实时 PID、Mode 05 / 06 / 09、日志回放与 CSV。二者的差别在于:前者以原始帧为中心,后者以服务与物理量为中心。

    重卡与商用车是独立品类

    公开资料强调,重卡 / 商用车诊断的协议、故障语义与售后场景与轻型车不同,不应把轻型车排放诊断能力直接推到重卡。重卡类工具通常依赖专用蓝牙接口,面向 DPF 再生、降额等场景,并可能提供双向命令;另有相邻的维修资料类产品,提供故障码、接线图与维修资料,属于诊断之后的知识层,并不等同于直接采集车辆数据。

    对选型者而言,这意味着:如果目标车辆是商用车,通用 OBD App 的能力清单参考价值有限,需要单独核对协议与售后链路。从能力层级看,重卡工具的重心通常落在「厂商增强诊断层」与「维修决策层」,依赖专用接口而非通用适配器,其数据入口与售后链路都与轻型车不同。

    常见问题

    桌面端一定比手机端能力更强吗?

    不能一概而论。桌面端在 USB、串口与 Windows 的 J2534 入口上更完整,适合离线回放与大屏分析;但 J2534 通常限 Windows,且桌面与移动授权常需分别购买。要按具体接口与平台组合核对。

    「支持 CAN」就等于能诊断所有 ECU 吗?

    不等于。原始 CAN 层只保留时间戳与原始帧,需要 DBC 或人工分析才能建立语义;OBD 服务层则只覆盖标准服务与 PID。能否读到某个 ECU,取决于车型协议、总线拓扑与适配器能力。

    重卡诊断能用轻型车 OBD 工具替代吗?

    不建议。重卡的协议、故障语义与售后场景与轻型车不同,公开资料明确不应把轻型车排放诊断能力直接推到重卡,通常需要专用接口与面向 DPF、降额等场景的功能。

    桌面工具的离线回放有什么价值?

    它可以围绕时间戳、帧 ID、总线与载荷复盘一段采集,并用 DBC 把原始帧转成可解释信号,是对比多份日志、验证解码规则的重要手段,也是移动端一次会话不擅长的场景。

    延伸阅读

    参考资料

    • 项目 01 —— car-scanner-research/桌面与原始CAN.md(取用桌面与移动接口差异、四个能力层级、原始 CAN 分析定位与重卡品类边界;未引用任何逆向材料)。
    • 项目 01 —— car-scanner-research/逐产品拆解.md(取用移动端接口集合口径,用于对比桌面端)。

    说明:本文仅取用公开资料对桌面与移动诊断工具的能力分层与接口口径,未安装任何安装包、未连接总线、未进行通信抓包,也未核验各商品宣称的接口兼容与平台支持。文中所有层级、接口与边界均出自源文档,标注为未验证的结论保留其原有边界。

    更新记录

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

  • 日产 Leaf ZE1:0x1DA 与 0x1D4 的 CRC 同多项式、两种算法

    结论:在一份以日产 Leaf ZE1 命名的 CAN 数据库与配套研究文档里,0x1DA 与 0x1D4 两个帧使用同一个 CRC 多项式 0x85(MSB-first),但算法细节不同:0x1DA 校验时把帧 ID 字节 0xDA 拼进输入并输出异或 0xBF;0x1D4 生成校验时不拼帧 ID、也不做输出异或,只对 b0 至 b6 求 CRC。第三帧 0x11A 则根本没有 CRC 字节,b7 是多路复用(mux)载荷。需要强调:源文档自己的来源说明写明这些 CRC 结论属于「上游声明、未在本归档独立核验」,且该车型的精确年款、配置与固件均未确认。

    本文整理自该研究文档与一份 ZE1 命名的 DBC 快照。文中所有结论的边界,都以下文「证据等级与边界」一节为准,不做拔高。

    车辆与适用范围

    素材包含两部分:一份以 Leaf ZE1 命名的 DBC(含 48 条消息、257 条信号行),以及作者围绕 0x1DA、0x1D4 的 CRC 处理所写的研究文档。文档称 CRC 结论曾用 4 帧已知良好的参考帧校验,但那 4 帧来自第三方 VCU 样机与日产逆变器的采集,并非本归档内独立复核。

    项 内容
    车型 日产 Leaf(DBC 以 ZE1 命名,即 Leaf-CAN_ZE1.dbc)
    年款 / 市场版本 源文档未确认;精确年款、配置与固件 applicability 均未建立
    ECU / 改装状态 涉及逆变器状态帧与扭矩请求帧;文档语境为第三方 VCU 搭配日产逆变器的样机
    采集跨度 CRC 结论基于文档所述 4 帧参考帧;未给出完整日志规模
    适用范围 仅限该 DBC 快照与文档所声明的帧;不得外推至其他年款或固件

    总线与寻址

    本文素材没有给出该车的物理层速率或 11 位 / 29 位寻址结论,也没有 CAN FD 的说明。文档讨论的是帧级 CRC 与字段布局,工作对象是 0x1DA、0x1D4、0x11A 三个 8 字节帧。下表只列文档明确涉及的内容,其余标注为源文档未给出。

    项 内容
    物理层速率 源文档未给出
    寻址 源文档未给出 11 位 / 29 位结论
    CAN FD 源文档未提及
    帧长 所涉三个帧均为 8 字节载荷
    校验字段位置 0x1DA 与 0x1D4 的 CRC 在 b7;0x11A 无 CRC,b7 为 mux 载荷

    已解码信号

    下表汇总文档给出的 CRC 参数、帧结构与 DBC 片段中的字段。凡未标注独立复核的,均沿用「上游声明」边界。位宽写法如 23|12 表示起始位 23、宽 12 位;@0- 表示 Motorola 字节序、有符号。

    信号 位置(ID / 位域) 解码 证据
    CRC_1D4(发送校验) 0x1D4 b7(DBC 56|8) poly 0x85、init 0x00、无输出异或、输入为 b0 至 b6(不拼帧 ID) 上游声明,未独立核验
    0x1DA 线上校验 0x1DA b7 poly 0x85、init 0x00、输出异或 0xBF、输入为 [0xDA, b0..b6] 上游声明;按文档所述对 4 帧参考帧吻合
    时钟选择 clock 0x1DA b6 低 2 位 clock = b6 与 0x03 上游声明
    HCM_CLOCK 0x1D4 38|2 2 位计数,对应 b4 滚动(见下) DBC 片段
    MotorAmpTorqueRequest 0x1D4 23|12 @0- 比例 0.25,范围 0 至 1024 Nm DBC 片段
    ChargeStatus 0x1D4 48|8 MODEMASK 取值 DBC 片段
    StatusOfHighVoltagePowerSupply 0x1D4 34|1 1 位状态 DBC 片段
    Relay_Plus_Output_Status 0x1D4 46|1 1 位状态 DBC 片段
    JoystickGearPosition 0x11A 4|4 4 位 DBC 片段
    CarOnOffStatus 0x11A 13|3 3 位 DBC 片段
    Multiplexor M(mux) 0x11A 48|8(b6) 选择器,逐帧 0、1、2、3 循环 DBC 片段与文档
    HeartbeatVCM 0x11A 24|8(b3) 取值在 85 与 170 之间(文档记 b1 应为 0x40) DBC 片段
    ECOselected 0x11A 12|1 1 位 DBC 片段
    0x11A 的 mux 载荷 0x11A b7 mux0 至 3 对应 0x6B、0xEE、0xE4、0x61 文档表

    读取方法与注意事项

    接收侧校验。对 0x1DA,文档的做法是:用 [0xDA, b0..b6] 求 poly 0x85、init 0x00、xorOut 0xBF 的结果,与收到的 b7 比较,不等即判为坏帧;文档还给出一种运行时策略——连续坏帧达到 10 次则关闭 CAN 回退。

    发送侧构造(文档语境)。0x1D4 每 10 毫秒发送一次:b0、b1 为静态 0x6E,b2、b3 为 0x00,b4 是滚动计数器,b5 为静态充电状态 0x44,b6 为静态 0x01,b7 填入按上文规则算出的 CRC。文档强调 b4 的计数器必须按 0x87 → 0xC7 → 0x07 → 0x47 循环,且 0x11A 的 b1 必须是 0x40,否则逆变器会持续拒帧。

    第三帧无 CRC。0x11A 是每 10 毫秒的心跳:b0 至 b5 为静态字节,b6 逐帧循环 0、1、2、3 作 mux 选择器,b7 随 mux 取对应值,没有校验字节。文档把 b7 明确称为「启动 / mux 载荷数据」,并指出它「不是 CRC 字节」。

    操作提示。以上帧属于车辆逆变器通信;任何在真实总线上发送或改写这些帧的操作都会影响实车,风险自负。本文只转述源文档的算法描述,不构成操作指引;源文档未提供安全访问绕过方法,也不建议尝试。

    证据等级与边界

    第一层边界来自来源说明本身:该归档明确写着,DBC 与其中若干信号「解析器兼容性与信号准确性需要独立复核」,且文中标注了诸如 SG_: 这样的畸形语法与形如 4294967256 这类不合理的无符号物理下限;CRC 结论是上游声明,未在本归档独立验证;精确车型年款、配置与固件 applicability 未确认。许可方面,检查的快照中也没有发现通用仓库许可证,仅作为私有离线研究副本保留。

    第二层边界来自文档自己的措辞:它把结论表述为「已验证并工作」,但依据的 4 帧参考帧来自第三方样机,不是本归档的实测;文档另记录了三类被否定的旧做法(例如错误的 0x1D29 多项式),不应被当作可用方案。

    因此,本文给出的 CRC 参数、计数器循环与字段布局,只能视为「这份 ZE1 命名 DBC 与这份文档所声明的范围」,不能外推到其他 Leaf 年款、其他固件或其他逆变器组合。需要真值时应以实车采集与独立复核为准。

    常见问题

    0x1DA 和 0x1D4 的 CRC 到底差在哪?

    差在两处:0x1DA 把帧 ID 字节 0xDA 拼进输入、并对结果异或 0xBF;0x1D4 不拼帧 ID、也不做输出异或,只对 b0 至 b6 求 CRC。两者共用同一个 MSB-first 多项式 0x85。

    0x1D4 的 b4 计数器为什么必须按 0x87 → 0xC7 → 0x07 → 0x47 循环?

    这是源文档记录的要求:计数器不按该顺序滚动时,逆变器会拒帧并给出状态「100 000」。文档把「计数器未按 10 毫秒循环」列为导致拒帧的可能原因之一。

    逆变器状态「100 000」和「000 000」分别是什么意思?

    按文档,状态「100 000」表示帧被拒绝,「000 000」表示帧被接受。文档还列出可能导致拒帧的原因,包括 0x1D4 的 CRC 错误、0x1D4 计数器未循环、0x11A 未发送或 mux 未循环,以及 0x11A 的 b1 不是 0x40。

    0x11A 有没有 CRC 字段?

    没有。文档明确说明 0x11A 不含 CRC,其 b7 是随 mux 选择器变化的启动 / 载荷数据,而非常规校验字节。

    这些结论能直接套到别的 Leaf 上吗?

    不能。来源说明写明 CRC 结论为上游声明、未在本归档独立核验,且精确年款、配置与固件 applicability 未确认。任何外推都需要以实车采集和独立复核为前提。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/kristianehr-leaf-ze1/docs/CRC_ALGORITHM_DEFINITIVE.md(本项目车型档案主来源,取用了 0x1DA / 0x1D4 / 0x11A 的 CRC 参数与帧结构)。
    • 项目 01 —— public-can-dbc-datasets/additional/kristianehr-leaf-ze1/docs/NISSAN_LEAF_1DA_CRC.md(取用了 0x1DA 的校验参数与 clock 定义)。
    • 项目 01 —— public-can-dbc-datasets/additional/kristianehr-leaf-ze1/SOURCE.md(取用了适用范围与「未独立核验」等边界说明)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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

  • 两套 Filter 与 PID:同名类为何不能直接替换 import

    结论:openpilot 里同时存在两套控制原语:opendbc 车型侧与 openpilot 主机侧的同名 FirstOrderFilter 与 PIDController。它们在一阶低通更新公式上一致,但在参数顺序、前馈倍率、限幅与积分回退上并不等价,因此同名类不等于可替换 API,也不能仅靠替换 import 完成迁移。

    两套原语并存:车型侧与主机侧

    opendbc 侧的实现位于 opendbc/car/common/filter_simple.py 与 opendbc/car/common/pid.py,服务车型控制器;openpilot 主仓库的同名实现在 common/filter_simple.py 与 common/pid.py,服务 controlsd、风扇控制等主机侧调用方。两者都用源码 diff 与三项原 PID 数值探针核对过,但未运行完整车型控制、CAN 或实车。

    把两套类当成一个接口,最直接的后果是第三位置参数含义错位、前馈被多乘或少乘一个倍率、限幅修改后的内部积分状态分叉。这些差异在静态等式一致时看不出来,只有在动态响应里才暴露。

    函数级对照:滤波与 PID 的差异

    行为 opendbc 实现 openpilot 实现 差异后果
    一阶低通 update() 用 alpha=dt/(rc+dt);update_dt() 可重算系数。 同一更新式,但只有 update_alpha(rc),dt 是公开字段。 当前相同参数下公式一致;插件若依赖 update_dt() 不能直接换成主仓库类。两者都不会自动读取真实采样间隔。
    第二种滤波 HighPassFilter.update() 返回快、慢两个低通输出之差,构造时要求 rc2 > rc1。 BounceFilter.update() 是带速度状态的 UI 动画原语。 高通类严格说是带通;与弹性滤波不是同类用途。
    PID 构造 参数顺序 k_p,k_i,k_f,k_d,...;标量用 numbers.Number 包装。 参数顺序 k_p,k_i,k_d,...;标量用 (int,float) 判断。 第三个位置参数在两套实现中含义不同,必须显式命名与适配。
    PID 前馈 update() 计算 f = feedforward * k_f,另有 override。 update() 直接 f = feedforward,没有 override。 相同 feedforward 值不保证相同输出;更新方法第四位置参数也分别是 override 与 feedforward。
    PID 积分 积分可按 0.3/rate 向零回退,普通更新按 1/rate 积分并按扣除 P/D/前馈后的输出裕度限幅。 对候选总输出越界方向抑制积分增长,同时用输出 neg_limit/pos_limit 直接夹积分项;允许 set_limits() 动态改限。 正下限及运行时收紧限幅会产生不同的内部状态,输出暂时相同不代表后续响应相同。
    PID 附加状态 error_integral 返回 i/k_i,reset() 清空分量。 reset() 清空分量,但无该属性。 当插值 k_i=0 时读取此属性会除零;属性值也随速度相关 k_i 改变。

    真实调用链:谁在用哪一套

    Toyota 控制器为纵向 PID 传 k_f=1、速度相关 k_i 和加速度上下限,每三帧更新加速度滤波器,并使用 HighPassFilter 的坡度动态分量。这里的采样频率是车型控制代码给出的名义频率,不是滤波器自动测得。Nissan 控制器按车速重设转角低通 rc,非横向激活时直接覆写 angle_filter.x,再经过车型角度限制;滤波参数与状态重置策略由车型层负责。

    body 控制器有车速和差速两个 PID,差速积分冻结判断读取 error_integral;当 CC.enabled 为假时输出目标转矩为零,但 PID 内部状态未在该分支调用 reset(),该状态延续的动态影响需要回放或台架核对。主机侧方面,横向 PID 与风扇控制导入的是 openpilot.common.pid,把车型 PID 的经验直接搬到这些调用方会改变前馈和积分语义。

    限幅探针揭示的积分状态差异

    三项原 PID 探针可用固定命令复现。取 k_i=0.004、rate=2、误差 5、前馈 50、输出范围 [30,100],主仓库 PID 把名义积分增量 0.01 夹为 i=30,输出 80;车型 PID 按前馈剩余裕度保留 i=0.01,输出 50.01。改成对称范围 [-100,100] 后两者此步均输出 50.01。这与风扇目标中的 30% 点火下限直接相关,但不等于车辆控制器也使用正下限。

    另一探针先在宽范围积累 i=20,再把输出上限从 100 收到 10 且误差置零:两者当前输出都被夹为 10,但主仓库 PID 保留 i=20,车型 PID 把 i 夹为 10。主仓库的横向与纵向控制器都会变更 PID 限幅,但探针只验证通用原语的构造案例,没有证明实际车型的参数变化会触发同一数值状态。插件迁移必须比较积分状态和后续恢复轨迹,不能只比较饱和帧输出。

    探针设置 主仓库 PID 车型 PID
    范围 [30,100]、误差 5、前馈 50 积分夹为 i=30,输出 80 保留 i=0.01,输出 50.01
    范围 [-100,100]、同参数 输出 50.01 输出 50.01
    先积累 i=20,上限收到 10,误差置零 输出 10,保留 i=20 输出 10,把 i 夹为 10

    面向插件化的约束

    对同一个采样周期,两套实现对「限幅是只夹输出还是也夹积分」的回答并不相同,这正是插件必须显式声明契约、而不能继承默认行为的原因。对滤波/PID 插件应显式记录实现来源、参数单位、采样周期、速度插值表、限幅、前馈倍率、接管与重置策略,不要以类名作为兼容性判断。适配层应使用关键字参数构造和更新,并用离线阶跃、饱和、接管、掉帧、失活重启用例比较输出与内部状态——静态等式一致不能证明车型在新硬件时序下等价。

    最后需要重申边界:这两套 Python 控制原语都只生成候选控制量;车型构帧、card 活动门和 panda MCU safety 仍是独立边界,不能由 PID 插件自行绕过。

    常见问题

    为什么两个同名的 FirstOrderFilter 不能直接互相替换?

    因为在当前相同参数下更新公式虽然一致,但 opendbc 版有 update_dt() 可重算系数、dt 是公开字段;主仓库版只有 update_alpha(rc)。依赖 update_dt() 的插件换成主仓库类会直接失败,两者也都不自动读取真实采样间隔。

    PIDController 的第三个位置参数为什么危险?

    opendbc 侧参数顺序是 k_p,k_i,k_f,k_d,...,主仓库侧是 k_p,k_i,k_d,...。第三个位置参数在两套实现中分别代表 k_f 和 k_d,位置传参迁移会静默改变含义,必须改用关键字参数并显式适配。

    相同错误与前馈下,两套 PID 的输出会一样吗?

    不保证。opendbc 侧计算 f = feedforward * k_f 并有 override,主仓库侧直接 f = feedforward。限幅探针也显示:范围 [30,100] 时两者输出分别为 80 与 50.01,仅当范围对称后才都输出 50.01。

    输出暂时相同是否代表两套 PID 等价?

    不代表。先积累 i=20 再把上限收到 10 时,两者当前输出都被夹为 10,但主仓库 PID 保留 i=20、车型 PID 把 i 夹为 10,后续恢复轨迹不同。迁移必须比较积分状态而非单帧输出。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/OPENDBC_FILTER_PID_CROSS_REPO_CONTRACT_DEEP_DIVE.md(取用了两套滤波/PID 的函数级对照、真实调用链与限幅探针数值)。
    • 项目 02 —— openpilot-research/COMMON_RUNTIME_PRIMITIVES_DEEP_DIVE.md(仅用于确认主仓库运行时算法原语的边界)。

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

    更新记录

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

  • Honda 平台表:38 个条目里的 DBC、flags 与线束提示

    结论:Honda 的平台表把三类东西放在不同层:软件平台定义(固定快照里 38 个 Honda/Acura 条目)、DBC 与 flags、以及 HondaCarDocs 的线束提示。三者不能互相替代——同 DBC 不等于同线束,CAN FD DBC 不等于 CAN FD 平台,文档线束提示也不等于接线验证。任何车型插件都必须冻结精确平台 ID、DBC 哈希、静态与动态 flags、物理总线图与线束版本,不能只凭「Bosch/Nidec」或 DBC 文件名授予控制权。

    38 个平台条目:计数的是代码平台

    在固定快照 c8fb906 的 CAR 枚举里,Honda/Acura 有 38 个平台条目:26 个 Bosch 配置,其中 7 个为 HondaBoschCANFDPlatformConfig,另有 12 个 Nidec 配置。源文档强调,这里计数的是代码平台,不是市场车型、年款或可装车数量。

    HondaBoschCANFDPlatformConfig 继承 Bosch,构造时加 BOSCH_CANFD,并默认 PT DBC 为 honda_common_canfd_generated;HondaNidecPlatformConfig 加 NIDEC,各平台通过 radar_dbc_dict() 同时声明 PT DBC 与 acura_ilx_2016_nidec 雷达 DBC。

    软件类别 代表平台 DBC / flags 含义
    Bosch 有雷达 HONDA_ACCORD、HONDA_CRV_5G PT DBC 依车型而异;BOSCH_ALT_BRAKE 可为静态标志,也可由指纹条件补上。
    Bosch 无雷达 HONDA_CIVIC_2022、HONDA_HRV_3G、ACURA_ADX BOSCH_RADARLESS 使车端总线、按钮目标和纵向权限走不同分支;不能只按 DBC 名判断。
    Bosch CAN FD HONDA_ACCORD_11G、HONDA_CRV_6G、ACURA_MDX_4G_MMR BOSCH_CANFD 标志和默认 PT DBC 一起定义平台;本快照参数层不开放 CAN FD 实验纵向。
    Bosch 非 CAN FD 但复用 CAN FD DBC ACURA_MDX_4G PT DBC 仍是 honda_common_canfd_generated,源码注释明确「not CANFD car but shares same dbc」;安全模式和总线拓扑不能由 DBC 文件名反推。
    Nidec HONDA_CIVIC、HONDA_CRV、HONDA_ODYSSEY PT/雷达双 DBC;NIDEC_ALT_SCM_MESSAGES、NIDEC_ALT_PCM_ACCEL 和全车门字段标志决定输入/输出分支。

    CarSpecs 与参数复用不等于实车测定

    CarSpecs 给质量、轴距、转向比、质心前轴比、最低转向/使能速度和轮胎刚度因子;通用 get_params() 将其写入 CarParams,并加默认载荷后计算惯量/轮胎刚度。平台间存在显式参数复用:旧 HONDA_HRV 复用 HONDA_HRV_3G.specs,旧 HONDA_PILOT 复用四代 Pilot 的 specs。源文档提醒,这是代码共享,不是目标车辆物理参数已经测定的证据。

    CarInterface._get_params() 把静态平台资料与指纹、固件、用户选择合成为实际 CarParams 和 safetyParam;CarState.get_can_parsers() 按 PT/相机/车身路由建立解析器,RadarInterface 在 Nidec 平台另用 Bus.radar DBC 建立雷达解析器,CanBus/CarControllerParams 再把车型能力映射为物理总线与转向扭矩上限。源文档据此把「平台表」定位为软件声明,而不是可直接施工的线束图。

    静态 flags、指纹动态标志与总线映射

    平台表中的 BOSCH、NIDEC、BOSCH_RADARLESS、BOSCH_CANFD、BOSCH_ALT_RADAR 是构造时的静态能力。CarInterface._get_params() 还从指纹动态设置 BOSCH_EXT_HUD、HYBRID、条件性的 BOSCH_ALT_BRAKE 以及特定五代 CR-V 的 HAS_BSM;固件 EPS 版本含逗号则置 dashcamOnly。BOSCH_TJA_CONTROL 在控制器有分支,但本快照平台定义中未见设置,不能把 dormant 代码当作已开通能力。

    CanBus 把 Bosch 有雷达平台的 PT 映射到 offset+1、radar 到 offset,纵向模式影响 LKAS 发送总线;radarless/CAN FD/Nidec 使用另一组映射。CarState 的相机解析器使用同一 PT DBC,却绑定 CanBus.camera 物理输入;BSM parser 使用 Bus.body DBC 但读取 CanBus.radar,需要 panda 转发 B-CAN。源文档据此总结:DBC 语法共用、逻辑 Bus 名称和真实总线接线是三个不同层次。DBC 语法共用不代表物理总线相同。

    这些 flags 的取值分支会一路传到构帧与限幅:例如 BOSCH_RADARLESS 影响车端总线与按钮目标,纵向模式影响 LKAS 发送总线。因此只按 DBC 名或品牌判断输入/输出分支,会同时误判总线映射与限幅参数。

    线束提示只是文档族

    HondaCarDocs.init_make() 按 flags 选择文档线束族:Bosch CAN FD 为 bosch_c,radarless 为 bosch_b,其他 Bosch 为 bosch_a,Nidec 为 nidec。CarDocs 的年款、最低转向速度、套件和 AEB/FCW 脚注用于支持表呈现,不构成引脚、供电、继电器或 EMC 的工程验收。部分平台 car_docs=[] 只是不在支持表展示,不代表平台代码不存在。

    把这些层叠起来看:平台表决定「软件认为这是什么车」,flags 决定「走哪条分支」,DBC 决定「怎么解析字段」,线束文档决定「支持表怎么展示」,而物理接线与 ECU 固件证据是独立的一层。四者只有在插件描述符里逐项冻结,才谈得上可复现。

    插件描述符必须冻结什么

    必须冻结的字段 为什么不能省
    精确平台 ID、DBC 哈希 同 DBC 不等于同线束;ACURA_MDX_4G 复用 CAN FD DBC 却是非 CAN FD 平台。
    静态 flags(BOSCH / NIDEC / BOSCH_RADARLESS / BOSCH_CANFD) BOSCH_RADARLESS 会使总线、按钮目标和纵向权限走不同分支。
    动态 flags(EXT_HUD / HYBRID / ALT_BRAKE / HAS_BSM / dashcamOnly) 由指纹与固件条件设置,不能只按 DBC 名或品牌判断。
    物理总线图、线束版本 逻辑 Bus 名称与真实接线是不同层次,DBC 名不能反推拓扑。
    ECU 固件证据、panda safety 模式 决定控制权授予与失联回退,不能只靠「Bosch/Nidec」授予控制权。

    源文档给出的顺序是:先建立目标车型的只读 CAN/电源/接线记录,再验证 PT、camera、radar、body 路由、原车 ADAS 保留和失联回退;没有原厂资料和台架证据,不把文档线束类型当作可照抄的接线表。此处仅完成静态结构分析,未验证 38 个平台的实际车辆参数或装配兼容性。

    常见问题

    38 个 Honda 平台条目等于 38 款可装车型吗?

    不等于。源文档明确,这 38 个是代码里的平台条目(26 个 Bosch 配置含 7 个 CAN FD 配置,加 12 个 Nidec 配置),计数的是代码平台,不是市场车型、年款或可装车数量。

    用了同一个 CAN FD DBC,是否说明这平台就是 CAN FD 车?

    不是。ACURA_MDX_4G 的 PT DBC 仍是 honda_common_canfd_generated,但源码注释明确它「not CANFD car but shares same dbc」,说明安全模式和总线拓扑不能由 DBC 文件名反推。

    文档线束提示(bosch_a/b/c、nidec)可以直接照抄接线吗?

    不可以。这些是 HondaCarDocs.init_make() 按 flags 选择的文档线束族,用于支持表呈现,源文档明确它们不构成引脚、供电、继电器或 EMC 的工程验收。接线仍需原厂资料与台架证据。

    BOSCH_TJA_CONTROL 出现分支,是否代表该能力已开通?

    不代表。源文档指出它在控制器里有分支,但本快照的平台定义中未见设置,属于 dormant 代码,不能当作已开通能力。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HONDA_PLATFORM_DBC_HARNESS_MATRIX_DEEP_DIVE.md(取用了平台类别计数、静态与动态 flags 分层、总线映射与线束文档族选择逻辑)。

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

    更新记录

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

  • Strym 工具链:Toyota RAV4 / CHR 与 Honda Pilot 的 CAN 记录与可视化

    结论:Strym 不是一个「某台车的信号表」,而是一套围绕 comma.ai Panda 与 Giraffe 连接器的 CAN 数据记录、分析与可视化工具链,文档把它的适用车型举例为 Toyota RAV4、Toyota CHR 与 Honda Pilot。它能做的是:把 OBD 口采到的原始帧按 DBC 解码成时间序列,再用 isoviz/isolog 实时绘图或离线分析。关键边界在于——真正的信号定义来自外部 DBC 文件(示例引用 newToyotacode.dbc),源文档并未附上逐信号解码表,因此不能据此列出任何一台车的具体信号清单。

    本文整理自该工具项目的说明文档。它记录的是「数据怎么进、怎么出」的管线,而不是某个车型的字段含义;下文严格区分「文档已说明的工具能力」与「文档未给出的车型信号定义」。

    车辆与适用范围

    源文档以举例方式列出可用车型,并说明其数据形态是「Panda 与 Giraffe 采到的时间序列」。文档给出的示例 CSV 文件名包含一个 RAV4 的 VIN,但它没有标注年款、市场版本或具体配置,也没有针对任一车型给出字段验证结论。

    项 内容
    车型 文档举例:Toyota RAV4、Toyota CHR、Honda Pilot(表述为「supported modern vehicles such as …」)
    年款 / 市场版本 源文档未确认;示例 CSV 文件名含 VIN 2T3MWRFVXLW056972,未标注年款
    ECU / 改装状态 由 comma.ai Panda USB-CAN 板配合 Giraffe 连接器接入车辆 OBD 口采集
    采集跨度 源文档未给出;示例为一段 CSV 记录
    适用范围 工具能力层面;不含任一车型的逐信号定义与验证

    总线与寻址

    文档只说明数据经 Panda 与 Giraffe 从 OBD 口进入主机,并由 DBC 文件解析消息。它没有给出物理层速率、11 位 / 29 位寻址、CAN FD 是否存在等链路层细节,也没有描述 OBD 口后是否存在网关。这些都属于源文档未覆盖项,本文不补。

    项 内容
    物理层 经 comma.ai Panda + Giraffe 从 OBD 口采集(源文档未给速率)
    链路层 源文档未给出
    寻址 源文档未给出 11 位 / 29 位结论
    CAN FD 源文档未提及
    消息定义来源 外部 DBC 文件(示例引用 newToyotacode.dbc)

    已解码信号

    源文档是工具项目,没有提供逐信号的解码表;信号含义由外部 DBC 决定,而该 DBC 内容未收录在文档中。下表只列出文档中可确认的可解码对象与数据通路,每条的证据都指向工具说明本身,而非某台车的实测。

    条目 位置(来源 / 入口) 解码 / 处理 证据
    消息类型 SPEED DBC newToyotacode.dbc;示例属性号 1 作为 isoviz / isolog 的「消息类型 + 属性号」入参 源文档示例代码
    车速时间序列 strymread(file).speed() 返回速度数据框(DataFrame) 源文档「快速开始」
    时间列 Time CSV 第 1 列(Unix 秒) 解析为 Pandas DataFrame 的时间列 源文档格式表
    数值列 Message CSV 第 2 列 解析为 DataFrame 的数值列 源文档格式表
    车型 Toyota RAV4 支持车型举例 由 Panda + Giraffe 接入 OBD 采集 源文档
    车型 Toyota CHR 支持车型举例 同上 源文档
    车型 Honda Pilot 支持车型举例 同上 源文档
    DBC 解码 cantools 库 按 DBC 消息定义解码原始帧 源文档依赖与示例
    实时可视化 isoviz() 边采集边绘制目标信号,同时写 CSV 源文档示例
    离线分析读入 strymread(csvfile, dbcfile) 读 CSV + DBC 后调用分析方法 源文档快速开始

    读取方法与注意事项

    输入形态。文档规定工具期望的时间序列是两列 DataFrame:Time(Unix 时间戳)与 Message(数值)。文档特别说明,虽然默认来源是 Panda,但任何符合该两列格式的时间序列都可以使用这套方法,因此它的处理对象是「规整后的时间序列」,不是原始帧本身。

    实时与离线两条路径。实时路径用 isoviz():它按「消息类型 + 属性号」指定要看的信号,一边把 CAN 消息写进 CSV,一边绘制该信号;按 Ctrl-C 触发 SIGINT 处理,结束记录并把图分别保存为 pdf 与 pickle。离线路径先用 strymread() 读入 CSV 与 DBC,再调用具体分析方法(例如 speed())。

    工程条件。文档列出的软件要求是 Ubuntu 20.04 或更高、Python 3.12 与 UV 包管理器;硬件要求是 comma.ai Panda 板、Giraffe 连接器与一台带 CAN 总线的现代车辆。若在树莓派上安装,文档建议从源码装 Python 3.7,并预先准备 NumPy 的预编译二进制,否则构建会比较麻烦。以上都是源文档给出的操作条件,本文未在实车上复核。

    证据等级与边界

    源文档是工具说明,没有为任何字段标注置信度或证据等级,也没有给出原始 CAN 抓包或实车验证结论。它唯一带有「实测」色彩的内容是一段示例 CSV 与一段示例 DBC 的引用,二者都只是用法演示。

    因此边界很明确:第一,本文列出的信号条目只是「文档说明的处理对象与数据通路」,不代表任何车型的真实字段含义;第二,真正的信号定义在外部 DBC 里,而该 DBC 未被收录,无法据本文列出某一台车的信号清单;第三,支持车型是文档的举例表述,其年款、配置与协议细节均未确认。任何字段级结论都不能从工具文档外推到具体车辆。

    常见问题

    Strym 支持哪些车型?

    源文档以举例方式写出 Toyota RAV4、Toyota CHR 与 Honda Pilot,并说明适用对象是「带 CAN 总线的现代车辆」。它没有给出完整的支持清单,也没有标注每款车的年款与配置,本文不据此扩展。

    它能直接给出某台车的信号清单吗?

    不能。该项目的信号含义来自外部 DBC 文件,示例引用的是 newToyotacode.dbc,而源文档没有收录该文件内容,也没有逐信号解码表。它提供的是「用 DBC 解码」的工具能力,不是现成的车型字段表。

    它的数据格式是什么?

    文档规定核心输入是两列 Pandas DataFrame:Time(Unix 时间戳)与 Message(数值)。符合该格式的时间序列都能使用这套方法,不一定非得来自 Panda。

    这套工具能替代某台车的协议分析吗?

    不能。它负责记录、解码与可视化,协议与字段含义由外部 DBC 决定。要判断某台车的字段,仍需以该车自己的 DBC 与实测为准,本文不把工具文档当作车型结论。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/jmscslgroup-strym/README.md(本项目车型档案唯一来源;取用了支持车型举例、两列数据格式与 isoviz/strymread 工作流)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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

  • Cabana 消息详情:标签同步、编辑提交与告警的三条边界

    结论:Cabana 的「消息详情」页是 DBC 分析/编辑界面,不提供车端 CAN 发送或安全模式授权。它把消息选择、位图/信号视图、历史页与告警收敛在一个 DetailWidget 里,通过 MessageId(source/address)建立标签并刷新状态。需要记住三条边界:告警不是协议证明(不查校验、计数器与新鲜度);编辑只是内存 DBC 变更(磁盘保存是另一条链);关闭最后一个标签后详情与序列化的活动消息 ID 可能仍指向旧项。

    入口与状态传播

    主窗口把消息列表选中事件接到 CenterWidget::setMessage(),CenterWidget 在首次选择时惰性创建详情页。DetailWidget::setMessage() 用 source/address 组成的 MessageId 查找或新建标签,选择标签后同步 SignalView、BinaryView 和 LogsWidget,最后刷新标题/告警。TabBar::setCurrentIndex() 会同步发 currentChanged,回调再次进入 setMessage() 时由相同 ID 的早退门避免重复更新。

    函数 主要职责 边界
    findOrAddTab() 以 MessageId::toString() 作为标签文本和查找键,新增时填充消息名 tooltip。 tooltip 只在新增时设置;DBC 改名后 refresh() 更新标题,不同步重写旧标签 tooltip。
    restoreTabs() 从会话字符串恢复当前 DBC 中存在的消息标签,再尝试恢复活动项。 fromString() 解析失败退回 0:0;只查 DBC 存在性,坏字符串可能别名到 0:0;未知 DBC 消息不会恢复。
    refresh() 重算标题、删除按钮可用性和告警。 它不是所有子组件状态的唯一刷新器;信号视图/历史页各自响应 DBC、撤销事件。
    updateState() 若到达集合包含当前 ID,更新当前显示的位图或历史页,再重算告警。 未显示页不在该调用中增量更新;切换到 Logs 页时另调用 onShown()。

    标签关闭请求由 DetailWidget 构造连接直接交给 TabBar::removeTab()。若关闭最后一个标签,TabBar 发 currentChanged(-1);详情页的回调忽略负索引,也未清空 msg_id_ 或销毁 CenterWidget。因此最后一个标签关闭后,详情内容及序列化的活动消息 ID 可能仍指向旧项——这是源码可见的状态边界,实际 GUI 表现待验证。初始 msg_id_ 为默认 0:0,setMessage() 的相同 ID 早退也使首次选择真实 0:0 时不能建立标签。

    告警不是协议证明

    updateWarnings() 仅在当前 ID 能找到 DBC 消息时检查三类问题:没有收到该 source/address 的帧、DBC 长度与最新帧长度不同、以及 BinaryView::getOverlappingSignals() 返回的位重叠。没有 DBC 消息时不生成这些告警。重叠集合受当前可见复用分支和 Normal 类型筛选影响,不能当作完整 DBC 冲突审计。

    告警会检查 告警不会检查
    该 source/address 是否收到帧 checksum 是否正确
    DBC 长度与最新帧长度是否一致 计数器连续性与回绕
    当前可见信号是否位重叠 时间戳新鲜度
    —— 车辆拓扑与 panda safety

    热力图菜单有 Hidden、Live 和当前范围/All 三态;timeRangeChanged 回调在共享范围变化时更新文字,并让无范围的直播流进入 live 统计模式。需注意 Hidden 是布局可见性,不等于清掉已有统计。热力图菜单也只影响显示与统计模式,不会修改原始 CAN 帧。

    消息编辑与撤销栈

    editMsg() 以已有 DBC 长度或最新帧长度初始化非阻塞 EditMessageDialog。弹窗的 Name、Size、Node、Comment 字段分别使用字符过滤/数值输入/多行文本;只有用户改动 Size 输入时才把数值夹到 1..CAN_MAX_DATA_BYTES。若初始长度为 0 而用户不碰该输入,静态代码未再次夹取;是否可经完整 GUI 流程提交 0 需验证。

    validateName() 大小写不敏感地禁止保留名 untitled,非空且修改过时检查当前 source 的 DBC 重名;空字符串没有被此函数判为无效,nameValidator 只过滤新输入字符、允许删空字段。result() 会 trim 名称/节点/注释,DetailWidget::draw() 在弹窗返回「已接受」后才压入 EditMsgCommand。

    更细的一条边界:EditMsgCommand::undo() 以 old_name.empty() 判断原消息是否不存在。若先把已有消息改为空名称,再编辑一次,后一条命令的撤销会按「新建消息」路径移除消息。删除按钮另走 RemoveMsgCommand。DBCFile::updateMsg() 直接保存传入名称和长度到内存对象,没有补充非空名称验证。这些都是内存 DBC 编辑,磁盘保存是另一条链。

    插件与硬件边界

    若把详情页拆成插件,消息选择、可见统计、DBC 编辑和安全诊断应分成不同能力。编辑插件输出版本化 DBC 草稿和校验结果,不能把「当前无告警」解释为硬件接线或发送规则安全——告警本就不检查 checksum、计数器、新鲜度与 panda safety。插件宿主应统一校验名称非空、CAN 长度范围、source/address 身份和提交事务,再让只读分析视图消费已确认的版本;硬件采集插件保留原始帧及时间戳,不依赖 UI 当前选项。

    常见问题

    Cabana 详情页没有告警,是否说明报文没问题?

    不是。updateWarnings() 只检查「是否收到帧」「DBC 长度与最新帧长度是否一致」「当前可见信号是否位重叠」三类;它不检验 checksum、计数器、时间戳新鲜度、车辆拓扑或 panda safety,且重叠集合受可见复用分支和 Normal 类型筛选影响,不能当作完整 DBC 冲突审计。

    为什么关闭最后一个标签后详情还可能指向旧消息?

    因为关闭请求直达 TabBar::removeTab(),关闭最后一个标签时 TabBar 发 currentChanged(-1),而详情页回调忽略负索引,也未清空 msg_id_ 或销毁 CenterWidget。于是详情内容及序列化的活动消息 ID 可能仍指向旧项。这是源码可见边界,实际 GUI 表现待验证。

    编辑消息时把名称删空会发生什么?

    validateName() 不把空字符串判为无效,nameValidator 也允许删空字段,DBCFile::updateMsg() 更不会补非空校验。更麻烦的是 EditMsgCommand::undo() 以 old_name.empty() 判断原消息不存在:先改空名再编辑一次,后一条命令的撤销会按「新建消息」路径移除消息。

    Cabana 能直接向车上发 CAN 或授权安全模式吗?

    不能。这页是 DBC 分析/编辑界面,源码明确它不提供车端 CAN 发送或安全模式授权。它做的是内存 DBC 编辑与显示统计,磁盘保存是另一条链;插件化时也应把编辑、统计与安全诊断拆成不同能力。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CABANA_DETAIL_MESSAGE_TABS_EDIT_WARNING_STATE_DEEP_DIVE.md(取用了详情页入口与标签同步、告警三类检查、编辑弹窗与 EditMsgCommand 撤销语义、插件与硬件边界)。
    • 项目 02 —— openpilot-research/CABANA_DBC_UNDO_MULTIFILE_DIRTY_CONTRACT_DEEP_DIVE.md(仅用于确认命令栈专题的接续关系,未取用其未公开内容)。

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

    更新记录

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

  • BMW E46 PT-CAN 数据档案:MS42/MS43 的 500 kbps 报文表

    结论:BMW E46 的 PT-CAN 是一张没有加密、没有网关隔离的广播总线:500 kbps、11 位 ID、DLC=8,DME、DSC、IKE、转向角传感器和(选装)变速箱都在这张网上直接广播,被动监听即可解码转速、水温、车速、四轮速、转向角、制动压力、挡位、里程与油量等信号。它的典型难点不在「能不能抓」,而在同一份 ID 在 MS42(M52TU)与 MS43(M54)上字节位置不同:源文档明确标注 MS42 的字节位置是从 MS43 文档抄来的、仍需真实抓包验证。下述结论仅限源文档记录的这一套 DME+DSC+IKE 组合。

    车辆与适用范围

    源文档是一份开源车载仪表项目为 E46 机箱写的 CAN 参考,信号表的权威来源是公开的 MS4X 维基,项目自述其解码表「逐字节镜像维基表格」。项目本身处于早期(README 自述 Early),只覆盖 E46 的 PT-CAN 一张网。

    项 内容
    车型 BMW E46,覆盖 M52TU(MS42)与 M54(MS43)两种 DME
    年款 / 市场版本 源文档未给出具体年款与市场版本,只按 DME 区分 M52TU / M54
    ECU / 改装状态 DME(MS42/MS43)、MK60 / DSC III ABS、IKE 仪表、转向角传感器、可选 EGS 自动变速箱;使用 SLCAN 适配器被动监听,不改线束
    采集跨度 源文档未给出日志数或帧数;DBC 侧记载 MS42 文件 11 条报文、102 条信号,MS43 文件 11 条报文、108 条信号
    适用范围 仅限文档所记录的 MS42/MS43 + DSC + IKE 组合;具体年款、动力总成、ABS 版本与配置须自行核对

    总线与寻址

    源文档明示 PT-CAN 运行在 500 kbps、11 位 ID、DLC=8。多字节整数都注明端序;位位置写成 B<字节>.<位>,位 0 是 LSB;百分比缩放的规范系数是 BMW 的 0.390625 = 100/256。有一点很关键:项目只做广播帧的监听与解码,明确不做诊断协议(无 DS2、无 UDS、无 EDIABAS)。

    项 内容
    物理层 双绞线 CAN,PT-CAN(动力总成 CAN)
    链路层 / 比特率 500 kbps(源文档明示)
    寻址方式 11 位 ID,DLC=8
    CAN FD 否;源文档按经典 CAN(8 字节载荷)处理
    端序约定 绝大多数报文为 Intel/小端;LWS1(0x1F5,转向角)为 Motorola/大端
    不做的事 不含诊断协议;电池电压不在 PT-CAN 广播,只能走 DS2 诊断

    已解码信号

    下表从源文档的信号表中抽取代表性条目。MS43 独有或与 MS42 位置不同的信号单独标注;源文档标注「未验证」「无外部旁证」的条目按原样保留。

    信号 位置(ID / 字节 / 位) 解码 证据
    转速 N_ENG 0x316 DME1,B2-B3 小端 × 0.15625 rpm MS4X 维基 0x316 页
    指示扭矩 TQI_CAN 0x316,B4 × 0.390625 % MS4X 维基 0x316 页
    冷却液温度 TEMP_ENG 0x329 DME2,B1 × 0.75 − 48 → °C MS4X 维基 0x329 页
    油门踏板 TPS_CAN 0x329,B5 × 0.390625 % MS4X 维基 0x329 页
    车速 VSS 0x153 ASC1,13 位(B1.3-7 + B2) ((MSB×256+LSB − 0x160) × 0.0625) km/h 维基公式;源文档指出维基公式与「最小值 0x160」不自洽,解码器补了偏移并钳位到 ≥0
    四轮车速 0x1F0 ASC2,B0-B7 小端(每 2 字节一轮) (raw − 44) / 15.875 km/h MS4X 维基 0x1F0 页;raw 最小 0x002C=44 对应 0
    转向盘转角 ANG_PSTE 0x1F5 LWS1,B0-B1 大端 有符号 × 0.04394° 维基页;源文档指出维基把量表四舍五入,真实系数约 0.04395,解码器仍用维基系数
    转角角速度 VEL_ANG_PSTE 0x1F5,B2-B3 大端 有符号 × 0.04394 °/s MS4X 维基 0x1F5 页
    制动压力 P_BRAKE 0x1F8 ASC4,B2 × 1 bar(0–255) MS4X 维基 0x1F8 页
    纵/横向加速度 AX_REF / AY_REF 0x1F3 ASC3,10 位有符号原始值 维基未给缩放;界面按 10 位有符号原始值呈现 MS4X 维基 0x1F3 页(无缩放公式)
    挡位 GEAR_INFO 0x43F EGS1,B0.0-2 3 位枚举(0=N,1-6 前进挡,7=R) MS4X 维基 0x43F 页
    里程 KM_CTR_CAN 0x613 ICL2,B0-B1 小端 × 10 → km MS4X 维基 0x613 页
    油量 FTL_CAN 0x613,B2.0-6 7 位原始值(仪表映射到刻度) MS4X 维基 0x613 页
    机油温度 TOIL_CAN 0x545 DME4,B4(MS43 独有) raw − 48 → °C MS4X 维基 0x545 页;MS42 不广播此字节
    油面高度(MSS54HP) 0x545,B6(MS43 独有) (raw − 158) / 10 → L MS4X 维基 0x545 页

    读取方法与注意事项

    第一是端序陷阱:源文档特别提醒 Intel/小端的 start bit 是 LSB,而 Motorola/大端的 start bit 是 MSB,两者在同一个字节里所指的位完全不同。E46 上除 LWS1 转向角外都是 Intel,LWS1 是唯一的 Motorola 报文,混用会解出错误数值。

    第二是 MS42 / MS43 的字节错位:源文档用一整节列出 DME 发送帧里两种 DME 把信号放在不同位置的条目。例如 TQI_MAF_CAN 在 MS43 位于 DME1 的 B7、在 MS42 位于 B6;机油温度、换挡提示、机油压力低、胎压、机油液位等一串信号在 MS42 上根本不广播。源文档据此为 MS42 做了一个去掉 MS43 专有信号、并重定义 TQI_MAF_CAN 与 ERR_AMT_CAN 字节位置的配置。

    第三是维基公式的已知瑕疵。车速那条,维基同时给出「公式 = ((MSB×256)+LSB) × 0.0625」和「最小值 0x160(0 km/h)」,两者只有在先减偏移再缩放时才自洽;解码器选择减去 0x160 并钳位到 ≥0,让静止车读 0。转向角那条,维基把量表取整到 5 位有效数字(0.04394),真实系数约为 0.04395,量程末端会有约 0.2° 偏差。

    第四是取值语义:源文档规定解码器只在「该帧本就不携带该信号」时(多路复用场景)返回空值,绝不把「数值未知」编码成空值——那是仪表该处理的事,不是解码器的职责。

    证据等级与边界

    可视为外部旁证的是各条 ID 对应的维基页面,项目自述解码表与维基表格逐字节一致;这属于公开文档级证据,不等同于本项目自己的实车复现。属于明确不确定性的是 MS42:维基 MS42 页自己写了「数值抄自 MS43 页,需验证」,因此 MS42 专有的字节位置在真实抓包确认前应视为暂定。

    不能外推的地方有两处。第一,可选装件会改变报文存在性:EGS 自动变速箱、SMG 变速箱、MSS54 相关信号只在对应配置上出现,源文档明确把 0x338 DME3、0x43D AMT1、0x445 TXU1、0x610 ICL1 列为「未解码」,理由分别是赛道化选装、缺真实 SMG 抓包、维基页为空、以及对 VIN 远程帧只做被动监听。

    第二,本文所有条目都绑定 E46 PT-CAN 这一张网与文档所列 ECU 组合,不能套到其他宝马机箱或其他总线(例如诊断总线)。电池电压这条尤其要记住:源文档写明它不在 PT-CAN 由任何已记录的 DME 广播,只能通过 DS2 诊断获得,所以「在广播流里找电池电压」在这台车上找不到。

    常见问题

    E46 的 OBD 口能直接抓到这些信号吗?

    源文档记录的是 PT-CAN 广播帧的监听与解码,总线为 500 kbps、11 位 ID。它明确不做诊断协议(无 DS2、无 UDS、无 EDIABAS),也不保证从标准诊断适配器(如只透传 ISO-TP 请求/响应的电缆)能拿到广播帧;实际取数用 SLCAN 适配器被动监听。

    为什么同一条 ID 在 MS42 和 MS43 上解码结果不同?

    因为源文档列出了一串信号在两种 DME 上位于不同字节:例如 TQI_MAF_CAN 在 MS43 是 DME1 的 B7、在 MS42 是 B6,机油温度等信号 MS42 直接不广播。项目为此为 MS42 单独定义了一套配置。

    车速解码为什么要先减 0x160?

    因为源文档指出维基的公式与「最小值 0x160 对应 0 km/h」不自洽,只有在缩放前减去该偏移才能让静止车读 0;解码器据此补了偏移并钳位到 ≥0。

    电池电压能从总线上读到吗?

    不能。源文档写明电池电压不在 PT-CAN 由已记录的 DME 广播,只能通过 DS2 诊断获得。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/emdzej-dashx-e46/docs/bmw-e46-can-ids.md(主来源:E46 PT-CAN ID 与逐字节信号表)。
    • 项目 01 —— public-can-dbc-datasets/additional/emdzej-dashx-e46/docs/vehicle-overlays.md(DBC + overlay 表达方式与端序/复用说明)。
    • 项目 01 —— public-can-dbc-datasets/additional/emdzej-dashx-e46/SOURCE.md(快照范围、DBC 信号条数与适用范围提示)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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