标签: 车辆状态

  • Indian Springfield 2017 的解码计划:八个未解字节与请求墙

    结论:这份解码计划的证据底是 42,365 帧,来自 2026-08-15 一个下午的四份抓包;总线上 30 个 PGN 分四组——已发货 20、传输层 4、已理解但故意不发 1、真未知 5。收尾状态可以一句话说完:剩下「八个单字节、四个毫无变化的 PGN,以及一切需要主动请求的东西」。它没有 DBC,靠的是硬件只听模式加受控动作。

    总账:42,365 帧是变化数,不是发送数

    这是读这份计划前必须先接受的一条限制。固件只在某个 ID 的载荷与上一帧不同时才发布该帧(updateTable() 仅靠 memcmp 置脏标志),抓包工具记录的正是这条流。所以文件里每个「帧数」都是变化次数,不是发送次数。由此有三件事不成立,而每一件看起来都很像成立:

    • 推导不出报文速率。PGN 65381 在四次骑行里的 299 条记录说明它变了 299 次,不说明它慢。
    • 分不开周期性与事件驱动。两者在去重之后长得一模一样。
    • 滞后与相关是在不规则采样上算的。油门那条结论扛住了这一点——它有标准和手册双重印证——但这样找到的临界相关比看上去要弱。

    不过「跨 N 条记录恒定」仍然是有效陈述:它在每一次看到该报文变化时都确实保持了那个值,只是它不是关于 N 次发送的陈述。

    谁在总线上:地址声明比流量猜测可靠

    每个模块都在 PGN 60928 上用 64 位 J1939 NAME 宣布自己是谁:制造商、功能、实例。这些声明自 2026-08-15 起就在抓包里了,但模块地图此前有整整三周是靠流量猜的。这张表可信,是因为有两条自己会互相检验:SA 0 声明 Engine 并发送发动机数据;SA 23 声明仪表并发送里程、小计和外温。两次「说是什么」与「做什么」的独立吻合,才让功能 30 对 SA 39 也说得通。

    SA 声明次数 制造商代码 功能 原本靠流量猜的
    0 4 51 Engine(发动机) ECU ✓
    11 4 7 不在标准表里 ABS
    23 4 340 Instrument cluster(组合仪表) 组合仪表 ✓
    39 4 449 Management computer(管理计算机) 「VCU、车身」✓ 大意对
    136 8 146 Instrument cluster(组合仪表) 「声明了地址然后一句话不说」

    SA 136 是这一节的发现:它声明了与仪表相同的功能(19)却来自另一个制造商(146 对仪表的 340),而且从没发过别的任何东西。它还比谁都声明得勤——四次骑行共八次,其它模块每份抓包只有一次。一个通电、比谁都执着地自报家门、然后毫无贡献的模块不是坏模块,它更像是「显示类已装设备,而这辆车不给它活干」。

    另外 SA 11 的 NAME 解码不出来,这件事本身就值一行:它给出功能 194、车辆系统 78、行业组 7。行业组是三位,7 未使用;车辆系统 78 超出任何组的范围。反转字节序得到功能 255「not available」,这至少是一种合法的「拒绝声明」——但只反转这一个模块而不反转另外四个,是假设不是发现。记为异常,不解释掉。

    仍开放的字节:八个单字节与四个沉默 PGN

    下表汇自「仍开放」一节与逐字节清单,零基字节编号与代码一致。「标准怎么说」一列引用标准,标准从 1 计数,少数较老的注释也照此。

    PGN SA byte 取值 / 范围 已知 怎么验
    65382 0 0 255 个取值 / 4,721 帧 一个活的发动机量,负荷、点火提前角与喷油脉宽是候选。这是总线上最忙的未查字节,也是探针最后还在追的东西 开着巡航定速跑:稳住油门、改变负荷,看它跟负荷还是跟手
    65265 0 3,bit 0-1 取 0 和 1 开放线索,2026-10-04。位于标准给 SPN 595「巡航激活」的位置,且在八月抓包里会动 在下一次用到巡航的骑行里,把该值与派生出的 hold 一起记下来比对
    61444 0 7 75 / 103 到 185 标准里发动机需求扭矩(SPN 2432,偏移 -125)的位置。只读来打印,未发布 它应当跟随油门,并在油门松开瞬间转负
    65215 11 4 88 / 39 到 220 与车速 r = +0.84,偏移 125 = 零。若刻度成立,这是来自 ABS 模块的后轮速度——第三个速度源 一段已知急加速的抓包
    65265 11 0 2 / 63 到 127 标准:驻车制动 / 双速轴 / 巡航暂停,均为两位字段 一次受控测试
    61441 11 5 3 / 204 到 220 0xCC、0xCD、0xDC——与巡航开关字节相同的三个值,可能是镜像 停车按十分钟 SET 与 RESUME
    65265 0 6 2 / 240 到 241 241 出现在除六条之外的全部记录;240 出现过两次各约一秒 未知,价值低
    61445 39 4 2 / 32 到 83 标准 ETC2 字节 5 的变速箱字段;Indian 在此 PGN 已偏离标准(档位以 ASCII 编码放在字节 6) 未知,价值低
    65387 0 全部 无变化 钥匙开时发一次,作为对 SA 23 请求的应答 一段能触发新东西的抓包
    65388 23 全部 无变化 每次钥匙开时发一次,不是周期性的 同上
    65393 23, 39 全部 无变化(8 + 4 条记录) — 同上
    56832 23 全部 无变化(5 条记录) — 同上

    PGN 65382 的其余部分已经交出来了:字节 1 是转速除以 256(被撤回的「油门」),字节 3-4 是仪表显示的续航里程——十六位小端,字节 3 低、字节 4 高,一计数一公里,无缩放无偏移。所以 Indian 对发动机怎么运转还留着什么私货,最可能就在字节 0 里。

    一个仍未关掉的问题:油门字节有两个静止值

    PGN 65266 SA 0 字节 7(SPN 51)的解码本身已经定了,刻度也对:raw 250 恰好是 100.0%,正是 J1939 单字节百分比量程的顶端,全油门正落在上面。没定的是没人碰油门时阀片停在哪。车库里九小时、发动机从未启动的那一轮自己就证明了传感器只要点火开着就会报数——这个字节有 153 次变化。

    状态 中位数 最大
    发动机停转,低于 50 rpm 5.6% 到 6.0% —
    怠速,50 到 1200 rpm 4.4% 到 4.8% 12%
    骑行,高于 1200 rpm 9.6% 到 11.2% 100%

    停放着的机器读约 6%,这是阀片坐在怠速限位上而不是完全关闭,符合预期。不符合预期的是:同一天里这个字节还去过 0 到 1.6%,共二十六次,与正常的 4.0 到 4.8% 读数整天交替出现——不是另一次会话,两个簇在交替。发动机停着没人骑,阀片应该待在一个地方。候选有三个,都没测:回位弹簧没每次都把阀片留在同一位置;点火周期内传感器或 ECU 在首次真实读数前短暂报零;或者有别的东西在熄火状态下推动阀片,比如怠速执行器自检。

    已识别但故意不发货,以及确认不在总线上

    下面这些是已经看懂的,列出来是为了不让别人把它们当成未知,或者第二次去解它。

    PGN SA byte 它是什么 为什么不给字段
    65254 39 1, 2 时钟:分、时 快 12 小时 07 分,且不是显示屏上的那个钟
    65381 39 1,bit 2 / 4 / 6 转向灯开关左 / 右 / 取消,拨杆动作期间的一次短脉冲 灯本身已经从 65089 发货
    65386 39 1,bit 0 远光激活,全程保持 headlight 已经从 65381 取
    65276 23 1 组合仪表自己的油量读数 为了确定性选了 SA 0
    65265 39 6, 7 报文计数与校验和(SPN 524079) 不是信号
    65382 0 1 转速除以 256 —— 被撤回的油门 rpm 的粗副本
    65390 39 0 唤醒时的 DF 对 FF,同秒还有另外 8 个 PGN 在动 唤醒突发;总线活过来这件事本身说得更好

    「不在总线上」这一节有严格的入会条件:该信号必须在采集时被实际操作过且仍未出现。仅仅是「在它本来就不可能出现的数据里没出现」的,只能算未测。

    在总线上 不在总线上
    车速、档位、转速、胎压 喇叭
    灯、转向、危险警告 边箱锁
    刹车、巡航、加热手把 风挡电机(此地未装)
    倾斜、油量、温度 Trip 2

    规律是:这条总线承载的是别家模块需要的状态,不是便利性的作动。喇叭和边箱锁是「开关进模块、模块驱动自己的输出」,整条链路都在一个模块内部,没什么需要告诉别人。这也是为什么维修手册里 FMI 9「Abnormal Update Rate」格外有用。

    怎么读标准:FMI 9 是制造商的自白

    DTC 表列出部件、失效模式(FMI)与代码。多数失效模式描述的是一根线:开路、短路到 B+、电压过低。但 FMI 9 不一样。一个模块只有在「它期待某个信号周期性从别处来」的前提下,才可能抱怨这个信号来得太慢——在这台机器上,那个「别处」就是 CAN。所以每一条 FMI 9 都是制造商承认某信号被广播。整本手册里一共只有七条。

    SPN 信号 代码 本地状态
    84 Vehicle Speed Signal P160A 已发货 —— PGN 65265
    523 Gear Sensor Signal P1914 已发货 —— PGN 61445,也是档位突变日志的对象
    5582 Static Roll Angle P1062 已发货 —— 几乎可以确定就是 PGN 2304 的倾斜字节
    520300 Tire Pressure Sensor (Front) C1085 已发货 —— PGN 65268
    520302 Tire Pressure Sensor (Rear) C1090 已发货 —— PGN 65268
    520329 Operator Switch Status(pOSS1) P1063 部分回答:PGN 65381 的字节 1 位 2/4/6 是转向灯与取消,字节 2 位 0 是危险警告;2026-09-18 又发现字节 0 的两颗手把按键。但报文是专有的,无法证明它就是标准参数
    520330 Immobilizer P106A 已找到并发货 —— PGN 65386 SA 39 字节 0 位 6-7

    其中 SPN 5582 是一次安静的确认:倾斜值是先从 PGN 2304 里经验性地找到、并因看起来像而命名为「tilt」的,手册叫它 Static Roll Angle,来源是 SPN 520296「Accelerometer」。信号是真的,按设计就在总线上,我们读它这件事有了一个名字。

    手册还有两条限制要记住。其一,手册覆盖七款车而不是这一辆,DTC 表是七款的并集——SPN 520294/520295「Windshield Motor Driver / Switch」是 Chieftain 与 Roadmaster 的部件,Springfield 的挡风玻璃是快拆手动的;先确认车上有没有这个件再去追它的信号。其二,诊断码只能证明模块「知道」某个信号,不能证明它把它广播出来:前刹车开关是 SPN 520322、后刹车是 520323,两个都真实且有名有姓,但它们是一根根线进模块的,模块之后有没有把它们放上总线,是手册不回答的另一个问题。巡航那一节正是栽在这条上——出现在表里不等于出现在线上。

    常见问题

    这份计划有没有 DBC?

    没有。上游没有 DBC,这份材料是一份站得住的优先级清单加方法记录。它给出的是 PGN、源地址、字节位置、实测取值范围、已试过的动作和被推翻的假设,要落成 DBC 还得自己把每条信号按同样的受控动作再走一遍。

    42,365 帧能不能换算成报文速率?

    不能。固件只在某个 ID 的载荷与上一帧不同时才发布该帧,抓包记录的正是这条变化流。所以「帧数」是变化次数而不是发送次数:推导不出报文速率,也分不开周期性与事件驱动。唯一仍然成立的是「跨 N 条记录恒定」,但它不是关于 N 次发送的陈述。

    巡航「已接合」状态到底能不能读到?

    从 SA 39 读不到。2026-09-05 路试中巡航确实在保持(94、87、73 km/h,手离开手把),其 CCVS 字节 4 恒为 0xF7,位 0-1 始终是 11;设定车速字节在所有 SA 39 帧里恒为 0xFF。于是固件派生了一个 `cruise`:一次 SET 或 RESUME 按下配合摇臂开且不低于 35 km/h 进入待定,三秒内车速稳定在 3 km/h 以内确认;刹车、离合、摇臂关闭或偏离目标超过 6 km/h 持续两秒退出。取值写 HOLDING 而不是 ON,用一个词把「这是推断」标在数据里。

    SA 136 是什么?

    它声明了与组合仪表相同的功能(19)但不同的制造商代码(146,仪表是 340),而且在 42,365 帧里除了自己的地址声明之外什么都没发过;声明次数还是别人的两倍(四次骑行共 8 次)。最可能是边箱锁模块——Polaris 车身侧锁模块的描述正是「点火时声明地址,之后只在锁 / 解锁时发送」。前半句完全吻合,后半句被测量推翻:三次运行、每个字节都可见、执行器确实在动作,SA 136 一个字节都没发。要直接点名,需要查一下制造商代码 146。

    「不在总线上」和「未测」怎么区分?

    进入「不在总线上」需要该信号在采集时被实际操作过且仍未出现;仅仅在「它本来就不可能出现的数据里没出现」的,只能算未测。巡航条目就踩过这个坑:字节一直读 not available,但那四次骑行里巡航从来没接合过,所以那一节原本没有资格收录;直到 2026-09-05 的路试才把它坐实为 SA 39 一家的确认缺失,而不是整条总线的结论。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/prinsessen-indian-springfield-2017/docs/DECODE-PLAN.md(30 个 PGN 分组、地址声明、仍开放清单与优先级表)
    • 项目 01 —— public-can-dbc-datasets/additional/prinsessen-indian-springfield-2017/docs/UNEXPLORED-BYTES.md(逐字节实测取值范围、油门双静止值与请求墙说明)
    • 项目 01 —— public-can-dbc-datasets/additional/prinsessen-indian-springfield-2017/README.md(车辆与适用车型范围、不在总线上的确认清单与胎压换算方法)
    • 项目 01 —— public-can-dbc-datasets/additional/prinsessen-indian-springfield-2017/SOURCE.md(固定修订、许可条款与范围限制)

    免责声明

    本文所述信号定义均为社区逆向工程产物,不代表车辆制造商的官方数据,且全部来自一辆 2017 Indian Springfield。手册覆盖的 Chief、Dark Horse、Vintage、Chieftain、Roadmaster、Elite 六款未直接测量,跨车型推断的依据是车主对车系的了解而非实测;Scout 与 FTR 是另一台发动机,不在范围内。固件运行在硬件只听模式,不发送任何帧,因此 DM2、DM4 等按需应答的数据不可能出现在现有抓包里。文中代码路径与候选字段不构成厂商规范。涉及安全相关用途时请仅作参考。

    更新记录

    • 2026-10-15 初稿

  • 菲亚特 Ducato X250 车身 CAN:五组抓包能还原出什么

    结论:这辆 Ducato X250 的 OBD 6/14 脚上只有 50 kbit/s 的车身 CAN,500 kbit/s 一帧都没有。五份抓包(30 s 到约 20 min)覆盖钥匙静态、车门与灯光、开关动作、冷启动加油到 7 km 实路行驶,总线共 18 个 ID、约 90 帧/s,19 条已验证解码逐条与仪表、GPS 或 OBD PID 对过。增压压力、发动机负荷、进气温度与故障码不在这条总线上——它们在 7 脚的 K 线上。

    五组抓包样本矩阵:每档各解决一类问题

    抓包是用仓库里的 tools/slcan_capture.py 通过 TCP 23 口连上只听模式的适配器录的,脚本先发 C(关通道)、S2(50 kbit/s)、L(只听),随后每帧写成一行「启动后秒数 + slcan 帧」,例如 t28180080803800000000 即 ID 0x281、DLC 8、后接八个字节。时间戳是相对的,不含位置信息。

    决定一份抓包能还原什么的是工况,不是文件体积。常发帧识别、开关类位分离、随动模拟量拟合、随负荷变化的量验证,各需要不同的一档;下面这张表按「不可替代性」分档,缺任何一档都会让相应的一类信号失去证据。

    抓包文件 时长 工况 能取到的数据 为什么这一档不可替代
    01_ignition-on_engine-off 30 s 钥匙拧到 MAR,全程不碰任何开关 静态基线与常发帧清单:0x281 20 Hz、0x286/0x2A0 10 Hz、0x180/0x380/0x39A 4 Hz、0x3C3/0x3E0 2 Hz、0x603/0x643/0x683 1 Hz;冷车水温与静态电瓶电压 唯一能界定「哪些帧与操作无关」的一档。没有它,后面的字节差分会把常发噪声当成动作响应
    02_doors_and_lights 300 s 车门逐个开合,示宽灯、近光、远光、闪光各一遍 0x380 的手刹 / 车门 / 倒挡 / 电瓶电压 / 低油量告警 / 油量;0x180 的示宽灯、近光、远光、后雾灯 灯光位只在原地逐个打才能分离;行驶中它们与负荷、电压混在一起
    03_misc-actions 420 s 转向灯、刹车踏板、驻车制动及其它开关 0x180 的刹车踏板(b0 bit 7)与左右转向(0x40 / 0x20 随闪光翻转)、0x380 b0 bit 5 手刹、0x39A b2 bit 0 安全带 转向灯是「闪烁」而非电平,只有这一段能看到周期性的位翻转,从而与常亮的示宽灯区分
    04_engine-idle_revs 180 s 冷启动,怠速约 800 rpm,再分别点油门到约 2000 与约 3000 rpm 0x281 的转速 / 水温 / 发动机在转判据;怠速与加油时的电压变化 转速刻度与「发动机在转」的判据只能靠怠速加两段加油对照,静态档给不出斜率
    05_short-drive 约 20 min 7 km 城镇 / 乡村路段,最高约 80 km/h 0x286/0x2A0 车速与 GPS 对照;0x603 里程与续航;0x643 小计(平均油耗、平均车速、时间、距离);0x683 仪表时钟 唯一带车速变化的一档。里程增量(7 km 行程后 +7 km)也只有这一段能验

    18 个 ID 的已验证解码

    参考表的口径:总线为车身 CAN(B-CAN),OBD 6/14 脚,50 kbit/s,11 位 ID,18 个 ID,约 90 帧/s。b0 是第一个数据字节,位号 7(最高位)到 0。每条解码都在车上对照过仪表、GPS 或重复动作,没有一条是仅凭取值范围猜的。

    ID 速率 DLC 信号 解码 怎么验的
    0x180 4 Hz 6 刹车踏板 b0 bit 7 踩踏板
    0x180 示宽灯 b1 bit 5 灯光开关
    0x180 近光灯 b1 bit 3 灯光开关
    0x180 远光 / 闪光 b1 bit 4 拨杆
    0x180 左 / 右转向灯 b2 = 0x40 / 0x20,随闪光翻转 拨杆
    0x180 后雾灯 b1 bit 1(仅在开灯时有效) 开关三次
    0x281 20 Hz 8 发动机在转 用「转速大于 300」,不要用 b1 bit 7 三次钥匙测试(仅钥匙开、启动、运转后熄火)
    0x281 冷却液温度 b3 减 40,摄氏度 水温表指针居中时读 81
    0x281 发动机转速 (b6 乘 256 加 b5)除以 8 怠速约 830,点油门 1800 / 3000
    0x286 10 Hz 8 车速 (b2 乘 256 加 b3)除以 16,km/h 四段匀速与 GPS 差在 1% 以内
    0x2A0 10 Hz 4 车速(副本) (b0 乘 256 加 b1)除以 16,km/h 同 0x286
    0x380 4 Hz 8 驻车制动 b0 bit 5 手刹杆
    0x380 车门 b1 等于 0x0C 表示任一门开,0x00 全关 驾驶室门、生活舱门、储物舱共用一个回路
    0x380 倒挡 b2 bit 2 进出倒挡三次
    0x380 启动电瓶电压 b3 乘 0.16,V(0x53 = 13.3 V) 20 min 行驶中对照启动电瓶库仑计
    0x380 低油量告警 b4 bit 1(0x02 进入储备区,否则 0x00) 仪表储备灯亮时读 0x02
    0x380 油量 b5,升 满箱 90 L 读 91;本车 23 对应 26%
    0x39A 4 Hz 8 驾驶员安全带 b2 bit 0,1 = 未系 插拔三次
    0x3C3 2 Hz 8 驾驶员安全带(副本) b4 bit 1,同 0x39A 同上
    0x603 1 Hz 8 总里程 20 位:b1 低半字节 + b2 + b3,km 61744 = 仪表;7 km 行程后 +7
    0x603 续航里程 11 位:b4 的 bit 2-0 + b5,km 272 = 仪表
    0x603 瞬时油耗(待行驶中验证) b0 + b1 高半字节,BCD,L/100km(11 6x = 11.6) 怠速时与仪表小计一致;随负荷变化
    0x643 1 Hz 8 小计平均油耗 b0 + b1 高半字节,BCD,L/100km 11.7 = 仪表
    0x643 小计平均车速 b2,km/h(截断) 48 = 仪表;等于距离除以时间
    0x643 小计时间 b3 小时、b4 分钟,BCD(47 37 = 47 小时 37 分) 仪表;分钟从 19 跳到 20
    0x643 小计距离 20 位:b5 + b6 + b7 高半字节,乘 0.1 km 2329.2 = 仪表;复位后 0.0
    0x3E0 2 Hz 4 启动电瓶电压(副本) b1 乘 0.16 V,同 0x380 b3 同上
    0x683 1 Hz 6 仪表时钟 b0 小时、b1 分钟,BCD(16 50 = 16:50) 仪表时钟(本车慢约 3 分钟)

    三处「看起来能用、实际会错」的位

    0x281 b1 bit 7 只在点火开且发动机停时为 1,熄火最后一帧它读「在转」,行驶结束后 ECU 还会继续发约 20 s;三种钥匙测试下都以「转速大于 300」为准。0x603 b4 bit 3 是发动机运转标志位,做续航时必须排除,否则会得出 2265 km 这种数。车门位 0x380 b1 只回答「有门开着」,房车改装上驾驶室门、生活舱门与储物舱在同一回路,无法区分。

    灯光的另一个工程约束:同一 ID 上挂三条位条目在 0x281 上能工作,但测试里 0x180 上分离的位条目发布并不可靠,所以灯光、刹车踏板与转向灯在适配器里被合成一个 24 位的 raw_180 值发布,拆位留到 Home Assistant。过滤器文件的字段口径也要注意:CANID 用十进制(641 = 0x281),StartBit 从 b0 的最高位算起(b0 的 bit 7 是 0,b1 的 bit 7 是 8),Expression 里 V 是取出的值、B5 是字节 5。

    证据边界:不在这条总线上,以及会骗人的那一帧

    这套数据最有价值的部分之一,是它明确标注了「测不到」的地方。下面每一条都是试过之后的结论,不是「没看到就等于没有」。

    边界类型 具体情形 处理动作
    总线静默 钥匙拔出后总线完全静默,停车状态读不到任何东西 实时传感器设 expire_after: 15,让它变成 unavailable 而不是冻在旧值
    假帧 钥匙关闭前的最后一帧是假的:油量 0、手刹释放、发动机仍显示运转 不要把任何自动化建在 key-off 边沿上
    错误判据 0x281 b1 bit 7 不是「发动机在转」 一律用转速大于 300 判定
    位污染 0x603 b4 bit 3 是运转标志,参与续航解码 做 11 位续航时排除该位
    回路合并 车门状态只有一个共用回路 只能读「有门开着」,不能定位到具体门
    协议外 增压压力、发动机负荷、进气温度、故障码走 7 脚 K 线(ISO 14230-4 KWP2000) 只带 CAN 的适配器看不到;电瓶电压改看 0x380 b3(车身电脑视角)
    波特率 6/14 脚只有 50 kbit/s 的车身 CAN,500 kbit/s 无帧 50K 是唯一可用档位,模式设 Silent(只听)

    常见问题

    这五份抓包能直接当 DBC 用吗?

    不能。它们是 slcan 文本行(相对秒 + 帧),仓库里没有 DBC。仓库提供的是 tools/decode.py,只解已知信号、只输出变化行,并带 --test 自检。要把抓包变成 DBC,得自己按 ID 做字节差分,再逐条与仪表、GPS 或 OBD PID 对照。

    为什么钥匙拔掉之后一个数据都读不到?

    因为这条车身 CAN 在钥匙离开 MAR 之后完全静默,停车时没有任何帧可听。这不是适配器的问题,是车辆本身的行为,所以任何「停车监控」类的需求在这条总线上都不成立,传感器只能走过期置为 unavailable 的路子。

    车速有两个来源,该用哪一个?

    0x286 的 b2/b3 与 0x2A0 的 b0/b1 都是车速,都除以 16 得 km/h,区别只在 DLC(8 与 4)与所属报文。两者结果一致,与 GPS 对照的偏差在 1% 以内,任选其一即可,但不要在同一条链路上混用而忽略 DLC 差异。

    油量 0x380 b5 为什么不能当百分比用?

    因为满箱 90 L 的箱子上它读到 91,如果是百分比应该接近 100。它的单位接近升,但零点还没在这辆车上最终确认(本车 23 对应 26%,OBD 燃油 PID 给 24%),且钥匙关闭帧里读 0。作为趋势量可用,作为绝对值要谨慎。

    能不能靠这些抓包拿到增压压力或故障码?

    不能。这两类数据在发动机 ECU 的 K 线上(OBD 7 脚,ISO 14230-4 KWP2000),需要主动发请求才会应答。这里全部是只听模式,从不发帧,因此它们不可能出现在抓包里。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/danderik88-fiat-ducato-x250/captures/README.md(五份抓包的时长、工况与录制格式)
    • 项目 01 —— public-can-dbc-datasets/additional/danderik88-fiat-ducato-x250/docs/can-id-reference.md(18 个 ID、速率、DLC、逐信号解码与验证方式)
    • 项目 01 —— public-can-dbc-datasets/additional/danderik88-fiat-ducato-x250/README.md(车辆配置、OBD 引脚分配、Gotchas 与免责声明)
    • 项目 01 —— public-can-dbc-datasets/additional/danderik88-fiat-ducato-x250/tools/slcan_capture.py(S2 = 50 kbit/s、L = 只听的初始化序列)

    免责声明

    本文所述解码均为社区逆向工程产物,不代表车辆制造商的官方数据,且全部来自一辆 2009 年 Ducato X250(2.3 MultiJet,Elnagh Prince 55L 房车改装,意大利市场)。同平台另有 Citroën Jumper 与 Peugeot Boxer(2006–2014),但只有文中这辆 Ducato 实测过。不同车型年款、市场与 ECU 版本可能存在差异。文中所有操作均为只听,不向总线发送任何帧;连接任何设备到车辆 OBD 口的风险由操作者自行承担。涉及安全相关用途时请仅作参考。

    更新记录

    • 2026-10-15 初稿

  • Daybreak 与 HighNoon:105 条报文与 493 条信号

    结论:太阳能车队两代车的 CAN 定义是 7 份按功能域切开的 DBC,而非一份整车文件。Daybreak 三份合计 23 条报文、58 条信号,HighNoon 四份合计 82 条报文、435 条信号。决定复用方式的不是总量,而是信号密度:bps.dbc 平均每条报文只挂 1.47 条信号,LightingCAN.dbc 则高达 9.50 条。

    七份 DBC 的目录划分与规模落差

    归档保留了 7 份字节互不相同的 DBC,分别位于上游 can/dbc/Daybreak/ 与 can/dbc/HighNoon/ 两个目录,并随附上游 docs/README.md 与顶层 MIT LICENSE。目录结构本身就是第一条信息:两代车并不共用一份网络定义,而是各自维护一整套,跨代之间没有版本继承关系。

    按单文件看落差极大。最小的两份是 Daybreak/contactor.dbc 与 HighNoon/SteeringCAN.dbc,各只有 2 条报文;最大的 HighNoon/CarCAN.dbc 有 55 条报文、305 条信号,单份就占全部 105 条报文的一半以上、全部 493 条信号的 61.9%。把它当「车队 CAN 数据集」整体引用会严重失真——重心几乎全在一份文件上。

    文件 所属代次 报文数 信号数 平均信号 / 报文
    can/dbc/Daybreak/bps.dbc Daybreak 15 22 1.47
    can/dbc/Daybreak/contactor.dbc Daybreak 2 11 5.50
    can/dbc/Daybreak/controls.dbc Daybreak 6 25 4.17
    can/dbc/HighNoon/CarCAN.dbc HighNoon 55 305 5.55
    can/dbc/HighNoon/BPSCAN.dbc HighNoon 19 63 3.32
    can/dbc/HighNoon/LightingCAN.dbc HighNoon 6 57 9.50
    can/dbc/HighNoon/SteeringCAN.dbc HighNoon 2 10 5.00

    两代车的量级对比

    Daybreak 侧三份共 23 条报文、58 条信号;HighNoon 侧四份共 82 条报文、435 条信号。后者的报文数约为前者的 3.6 倍,信号数约为 7.5 倍。也就是说,代次更替不只是加了几个节点,而是单位报文承载的信号量整体翻了一倍(2.52 对 5.30)。

    代次 保留文件数 报文合计 信号合计 功能域
    Daybreak 3 23 58 电池包(BPS)、接触器、整车控制
    HighNoon 4 82 435 整车(CarCAN)、电池(BPSCAN)、灯光、转向
    合计 7 105 493 七份文件,互不覆盖

    信号密度决定了每份 DBC 的读法

    把信号数除以报文数得到的密度,是这几份文件里最实用的判据。它不需要打开文件就能告诉你:这份定义主要承载的是离散状态位,还是打包好的连续测量量。密度小于 2 的文件基本是「一条报文一个状态」,读数容易但信息量有限;密度大于 9 的文件则几乎必然是位域打包,必须逐位拆解才能用。

    密度档位 平均信号 / 报文 落在档位内的文件 典型形态 使用建议
    稀疏(小于 2) 1.47 Daybreak/bps.dbc 单条报文只承载少量状态或告警位 逐位确认含义,不宜当作连续测量量使用
    中等(3 到 6) 3.32、4.17、5.00、5.50、5.55 HighNoon/BPSCAN.dbc、Daybreak/controls.dbc、HighNoon/SteeringCAN.dbc、Daybreak/contactor.dbc、HighNoon/CarCAN.dbc 状态位与测量量混合 先抓运行日志看取值分布,再定缩放与偏移
    密集(大于 9) 9.50 HighNoon/LightingCAN.dbc 位域打包大量开关与枚举 按位拆解,逐位与实车现象对齐

    需要强调一点:密度是归档方统计出的 BO_ 与 SG_ 行数之比,它反映的是文件的组织方式,不代表信号已被验证。归档说明写得很清楚——具体车辆修订版本、组件固件与信号准确性均未独立核验。

    另一个不该忽略的落差在命名口径上。Daybreak 这一侧按功能块命名(bps、contactor、controls),HighNoon 这一侧则按物理总线命名(CarCAN、BPSCAN、LightingCAN、SteeringCAN)。两套口径不同,意味着不能靠文件名去推断两代车信号的对应关系,跨代比对只能落到逐信号的位与缩放上。

    被拦下的文件与权利边界

    这一批有五类文件没有进入归档,理由分两种,处理方式也不同。区分清楚很重要,因为「内容为空」与「权利未定」在工程上的后果完全不一样:前者是这份定义本来就没有可用信息,后者是信息可能存在但你不能用。

    对象 拦下理由 性质 处理动作
    HighNoon/DAqCAN.dbc 不含任何报文与信号定义 内容为空 不收录
    Prohelion 组件文件 待原始厂商数据权利审查 权利未定 暂缓收录
    TPEE MPPT 组件文件 待原始厂商数据权利审查 权利未定 暂缓收录
    Elcon 组件文件 待原始厂商数据权利审查 权利未定 暂缓收录
    电机控制器组件文件 待原始厂商数据权利审查 权利未定 暂缓收录

    DAqCAN.dbc 值得单独记录:DAQ 这样的名字很容易让人以为它提供采集通道的完整定义,实际上归档时它是空文件。此外,保留下来的 CarCAN.dbc 是一份聚合文件,其中引用了 BPS、MPPT 等组件的信号,但这些信号的原始来源并未独立确认。

    校验值与去重:怎样确认拿到的是同一份文件

    归档为每份 DBC 都登记了 Git blob 与 SHA-256,快照修订为 04d2b3286f50d8f4119d4dc11b7344140f449d15。两者用途不同:Git blob 用于在仓库内部定位文件版本,SHA-256 用于在仓库之外确认取到的字节是否一致。做二次分发或长期存档时,两个值都该留。

    文件 Git blob SHA-256
    Daybreak/bps.dbc 27f2385d0e3d316841e73db2b51631cdaad25637 b84a9d61eb276587241b39d24792c6cb1c42eb3b6efaad3ecfa727d279f8de32
    Daybreak/contactor.dbc 11745daca9b071eb933a59471ed630c79510eaaf 58ae79db80cb0d20e3dd2b585231b72f8a50bf41800fdd0d0f1878200a134672
    Daybreak/controls.dbc db2678346ce5e7ded982f56ecfa1971a87cfc96d 5484aa7a1ea8e4cc3925d5e714d5f37e58bd022569a4a106fa28ce5d1e06ae32
    HighNoon/CarCAN.dbc 175bc244c2a250857d393a27db6ffff1451bb906 fdd068b71ee3c891e3733d716c8ab4b8df211187892f5ed351dfe4f6f3076fbd
    HighNoon/BPSCAN.dbc 56048d7c6f5d0fad5c655be31f00af5516717a6c 4619aa34e75767a5e05557fa092fa883f90045147cb5e875ac352e7d9f95031f
    HighNoon/LightingCAN.dbc bcae89e10bf3dcb9088465061ceaf4eefcfac5de 0a5fdefcc7f4bdbe5fa23161b544561704600e0fb7675360551381872d2d06ed
    HighNoon/SteeringCAN.dbc a6548a3e1ea4e5ba12af333e622be03efd10f121 64e87d4458729fed6b3d9e8866e26d95d20d113be29dc117fe57fd449e37f6cb

    比对时还有一个容易踩的坑:同一份逻辑文件在不同检出环境下可能呈现 CRLF 与 LF 两种字节序,直接按字节比较会把同一份文件误判成两份。归档方在其他项目上正是用 CRLF 归一化来识别重复的,这里同样应当先归一化再比对。

    工程侧:MCU 端口移植的四项硬要求

    随附的上游文档描述的是一套面向定制 STM32 PCB 的共享嵌入式代码仓库,本身不是 CAN 数据集。它与 DBC 的关联在于:DBC 里定义的报文最终由这些 MCU 上的固件收发,端口是否移植完整,直接决定某条报文在车上有没有对应的实现。

    顶层 Makefile 编译 STM32 所需的全部文件,但设计上不打算被直接调用,而应由另一个 Makefile 调用。测试统一放在 test/ 目录,测试文件以 _test.c 结尾,命令形如 make TEST=blinky PROJECT_TARGET=stm32g473xx,烧录用 make flash。CI 侧提供 makeAllTests.py,支持 -v、-t TESTS、-p PORTS、-h 四个选项,默认对所有端口编译全部测试。

    一个合法端口必须齐备的四样东西

    要素 命名规则 文档给出的示例
    端口目录 位于 stm/stm32f4xx、stm/stm32l4xx 或 stm/stm32g4xx 之下,用 MCU 通用名命名 stm32f413、stm32l431
    链接脚本 形如 STM32*_FLASH.ld,须包含处理器全名 STM32F413RHTx_FLASH.ld、STM32L431CBTx_FLASH.ld
    启动汇编 startup_stm32 加四位系列与线别再加 xx,含向量表与基础异常处理(最关键的是 Reset_Handler) startup_stm32f413xx.s
    配置文件 以精确 MCU 名命名,空格分隔列出要禁用的 BSP 模块 stm32l432kcu.cfg,内容 BSP_DISABLE=CAN UART ADC

    文档特别提醒:请核对 MCU 上实际存在的外设再填写 BSP_DISABLE,否则会遇到奇怪的构建错误。对 CAN 相关项目来说这条尤其重要——如果误把 CAN 写进禁用列表,DBC 里定义得再完整,固件侧也发不出对应的帧。

    常见问题

    两代车的 DBC 能不能按文件名直接对齐?

    不能。Daybreak 侧的命名偏功能块(bps、contactor、controls),HighNoon 侧则偏物理总线(CarCAN、BPSCAN、LightingCAN、SteeringCAN),两套命名口径不同。加上 CarCAN.dbc 是聚合文件,其中引用的 BPS、MPPT 等组件信号的原始来源并未独立确认,跨代只能逐信号比对位与缩放,不能靠文件名推导。

    为什么 bps.dbc 有 15 条报文却只有 22 条信号?

    它平均每条报文只挂 1.47 条信号,是 7 份文件里最稀疏的一份。这种密度通常意味着报文主要承载状态或告警位,而不是连续测量量。在拿它做趋势分析之前,需要逐条确认每个位的含义与有效性,不能假定它自带完整的物理量定义。

    DAqCAN.dbc 与四类组件文件为什么没被收录?

    理由不同。DAqCAN.dbc 在归档时不含任何报文与信号定义,属于内容为空,因此不收录;Prohelion、TPEE MPPT、Elcon 与电机控制器这四类组件文件,则是因为原始厂商数据权利尚待审查而暂缓收录,属于权利未定。前者没有可用信息,后者可能有但你不能用。

    CarCAN.dbc 有 305 条信号,能当作整车信号全集吗?

    不能。归档说明指出它是聚合文件,引用了 BPS、MPPT 等组件的信号,而这些信号的原始来源未经独立确认;同时归档方明确写明,车辆修订版本、组件固件与信号准确性均未核验。它适合作为总线结构的索引,不能直接当作已验证的信号全集使用。

    这些 DBC 对应的 MCU 端口移植要准备什么?

    四样东西:一是在 stm/stm32f4xx、stm/stm32l4xx 或 stm/stm32g4xx 下以 MCU 通用名建立端口目录;二是形如 STM32*_FLASH.ld 且含处理器全名的链接脚本;三是 startup_stm32 加四位系列名加 xx 的启动汇编,内含向量表与 Reset_Handler;四是以精确 MCU 名命名的配置文件,用空格分隔列出要禁用的 BSP 模块,例如 BSP_DISABLE=CAN UART ADC。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/lhr-solar-daybreak-highnoon/SOURCE.md(7 份字节互异 DBC 的目录归属、逐文件报文与信号数、各文件 Git blob 与 SHA-256、DAqCAN.dbc 为空故不收录、四类组件文件待厂商数据权利审查、CarCAN.dbc 为聚合文件且信号来源未独立确认、车辆修订与固件未核验的说明)
    • 项目 01 —— public-can-dbc-datasets/additional/lhr-solar-daybreak-highnoon/README.md(顶层 Makefile 需由另一 Makefile 调用、test 目录与 _test.c 命名约定、make TEST 与 PROJECT_TARGET 用法、make flash、makeAllTests.py 的四个 CI 选项、STM32 端口四要素与 BSP_DISABLE 配置格式)

    免责声明

    本文所述报文数、信号数与文件路径均来自公开开源仓库的说明文档,属第三方逆向工程与团队自建产物,未经独立实车核验,也不代表车辆制造商的官方数据。不同车辆修订版本、组件固件与市场可能存在差异。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • Polaris Sportsman 的 J1939 档案:12 个 PGN 与 250 kbit/s

    结论:Polaris Sportsman 的这份 J1939 档案给出 250 kbit/s、29 位扩展 ID 下 12 个 PGN 的信号定义,但它们是 2022 款 Sportsman 570 的逆向结果,工具面向的 2026 车型仍需逐条复验。另外两处硬边界必须知道:仓库未找到许可证文件,且工具导出的 DBC 因多信号 PGN 会重复占用同一 CAN ID,未被作为可用文件归档。

    12 个 PGN 分成两类看

    整车走 J1939,位速率 250 kbps、29 位扩展 ID,硬件链路是 Polaris 8 针诊断口 → GT150 → DB9 → PCAN DSUB9,配合 PEAK PCAN-USB FD 适配器与 PCAN-Basic 驱动,Python 侧通过 python-can 的 pcan 接口调用 PCANBasic.dll。配置集中在 polaris_can/config.py:interface 为 pcan,channel 为 PCAN_USBBUS1,bitrate 为 250000。

    把 12 个 PGN 按性质分类,比按地址排序有用得多:一类是状态枚举(取值是离散码),另一类是数值量(需要字节序与缩放)。

    PGN(含字节位) 信号 取值 / 换算 类别
    0x18F00500[5] 挡位 0x4C = L、0x48 = H、0x4E = N、0x52 = R、0x50 = P 状态枚举
    0x18FEF100[0] 手刹 0x37 = 已拉起、0x33 = 已释放 状态枚举
    0x18FEF1FE[3] 脚刹 0xDF = 踩下、0xCF = 释放 状态枚举
    0x18FF6CFE[1] 安全带 0xF7 = 系好、0xF3 = 解开 状态枚举
    0x18F006FE[1] 差速锁 0xDF = 锁止、0xCF = 打开 状态枚举
    0x18FDDFFE[0] 四驱模式 0xFC = 2WD、0xFD = 4WD 状态枚举
    0x18FEF200[6] 油门 0x19–0xFA 映射为 0–100% 数值量,有量程端点
    0x18FEF100[1:2] 车速 小端 uint16 除以 500,单位 mph 数值量,需字节序
    0x18FF6600[0:1] 发动机转速 小端 uint16 数值量,未给缩放
    0x18FEEEFE[0] 冷却液温度 字节值 + 40°C 偏移 数值量,偏移方向需复核
    0x18FEFC00[1] 油量 0–254 数值量,未给百分比换算
    0x18FEE500[0:1] 发动机小时计 小端 uint16 数值量,未给缩放

    这张表里最值得注意的是最后一列。状态枚举只要码值对得上就能用;而转速、油量、小时计三项只给了类型与字节序,没有给缩放因子,水温那条甚至写成「字节 + 40°C 偏移」——与常见的减 40 相反。这四项在使用前都必须用已知工况反推。

    为什么导出的 DBC 不能直接用

    工具提供了 polaris-can export-dbc,输出 cantools 兼容格式,可导入 SavvyCAN、Cabana、Vector CANoe。但归档说明明确指出:导出函数会为多信号 PGN 创建多个使用同一 CAN ID 的独立报文,因此该输出未被作为经过验证的 DBC 归档。

    这个坑在 J1939 场景下很典型。像 0x18FEF100 这样同时承载手刹与车速的 PGN,在 DBC 里应当是同一条报文下的两个信号;若导出成两条同 ID 报文,解析器要么只认第一条,要么按 ID 覆盖,结果就是丢信号。因此正确做法是把这份 PGN 表当作原始事实,手工建 DBC,而不是依赖自动导出。

    五种模式:哪一种是真正的逆向工具

    工具提供 decode、log、discover、replay、export-dbc 五种模式,其中 discover 才是为新车型定位信号的核心手段。

    模式 命令 输出 适用场景
    实时解码 polaris-can decode 终端仪表盘,10 Hz 刷新,含帧率、总线负载百分比、唯一 ID 计数 已知信号的日常查看,可加 --output 落盘
    记录 polaris-can log session1.log 微秒级时间戳的 candump 兼容日志 外场采集,后续导入 SavvyCAN 等工具
    发现 polaris-can discover --warmup 30 基线冻结后报告新 ID 与变化字节 新车型定位信号:怠速 30 秒建基线,再逐个操作开关
    回放 polaris-can replay session1.log --dashboard 离线回放,可实时或加速 无硬件时开发与验证
    导出 polaris-can export-dbc polaris.dbc DBC 文件 仅作起点,多信号 PGN 需手工修正

    discover 的工作流是:预热阶段(默认 30 秒,可用 --warmup 60 延长)记录基线流量并冻结,随后进入发现阶段,监控新出现的 ID 与发生变化的字节。文档给出的示例输出显示基线里冻结了 47 个 CAN ID,随后报告新 ID 与具体字节的旧值到新值变化。操作要领是预热期间车辆真正处于怠速,之后一次只动一个开关或踏板。

    解码时匹配成功的信号会打印 [KNOWN],不匹配的被标为需要重新映射的候选——2026 与 2022 的差异正是靠这个标记暴露出来的。

    常见问题

    2022 年的 PGN 表能直接用在 2026 车型上吗?

    不能直接套用。README 明确说明工具面向 2026 款 Sportsman 570 EPS,但内置的 12 个 PGN 定义来自 2022 款 Sportsman 570,属于基线事实,需要在目标车型上逐条验证。若发现不匹配,应用 discover 模式重建:先怠速建立基线,再逐个操作开关与踏板,观察新 ID 与字节变化。

    为什么导出的 DBC 没有被当作可用文件归档?

    因为导出函数对多信号 PGN 会生成多条使用同一 CAN ID 的独立报文。例如 0x18FEF100 同时承载手刹与车速,正确形态应是同一条报文下的两个信号;拆成两条同 ID 报文会导致解析器丢信号。因此该输出未通过验证,未被归档,建议以 PGN 表为事实源手工建库。

    车速 PGN 0x18FEF100 的换算单位是什么?

    原文给出的是小端 uint16 除以 500,单位为 mph。也就是说原始值需要先按小端字节序拼成 16 位无符号整数,再除以 500 得到英里每小时;若系统内部使用 km/h 或 m/s,还要再做一级单位换算。

    没有 PCAN 硬件能不能用这套工具?

    可以。replay 模式支持离线回放日志文件,可实时或加速播放并接入仪表盘,仓库也提供了 tests/sample.log,在硬件未连接时用示例数据即可跑通,单元测试同样不依赖硬件。真正需要硬件的只有 decode、log 与 discover 三种模式。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/marvbull-polaris-sportsman-can/README.md(250 kbit/s J1939 配置、12 个 PGN 的信号定义、五种工具模式与故障排查)
    • 项目 01 —— public-can-dbc-datasets/additional/marvbull-polaris-sportsman-can/SOURCE.md(快照提交、证据边界、PGN_MAP 的 12 个标识符与导出 DBC 的缺陷说明、权利状态)

    免责声明

    本文所述 PGN、码值与换算均来自公开开源仓库的自述与工具代码,属社区逆向工程产物,不代表车辆制造商的官方数据。信号定义来自 2022 款车型,未经独立抓包验证,不同年款、市场与 ECU 版本可能存在差异。仓库未找到许可证文件,相关材料应作为私有离线研究副本,在权利确认前不得再分发或集成。

    更新记录

    • 2026-10-14 初稿

  • VW Grand California 生活舱 CAN:7 个 ID 与 9 条命令

    结论:VW Grand California 的生活舱 CAN 独立于 Crafter 底盘总线,社区已给出 500 kbit/s 的接入方式、7 个 29 位 ID 的功能归属、13 条解码样例与 9 条 cansend 命令。但同一个 ID 在不同字节位上混装多种含义,电流与电压两条规则作者未给出可复核的推导,因此这份表格适合做功能定位与命令复现,不适合直接当计量依据。

    接入:从树莓派到技术柜里的 9、10 针

    项目面向 Grand California 600 与 680 的生活舱总线,作者在免责声明中明确这是教育与研究用途,相关知识产权归属大众集团。硬件用 Raspberry Pi 3B+(作者说明 Pi Zero 2 W 更小巧省电)加一块 Waveshare RS485 CAN HAT(CAN 控制器 MCP2515、485 收发器 SP3485)。软件侧是 can-utils 加 Node-RED 的 node-red-contrib-socketcan 节点,用来搭可在浏览器里打开的仪表盘。

    总线初始化命令为 sudo ip link set can0 up type can bitrate 500000 loopback off listen-only off,监听用 cansniffer can0 -c -t0,发送用 cansend can0 18EF70C9#00.40.00.00.00.00.00.00。物理接入点选在 RGB 灯带控制器(零件号 7C4 947 022)的 9、10 针,该控制器位于 230V 配电盘正下方的技术柜内——这是全篇最省事的一条经验,避免了拆仪表台找线。

    作者还为 SavvyCAN 提供了一份 DBC:VW_GrandCalifornia_J608.dbc。

    七个 ID 的功能归属:一帧多用是常态

    生活舱总线的 ID 全部是 29 位扩展格式(形如 18EFxxxx),且普遍存在「一帧承载多个功能」的情况。下表按作者给出的归属整理,是判断该盯哪一帧的第一层筛选。

    ID 承载功能 备注
    18EF70C9 显示屏开关、卫星(SAT)开关、氛围灯 / 外部灯 / 内部灯 也是文档示例里的发送目标
    18EF70EB 卫星开关、灯光开关、电池剩余时间、车门锁状态 状态回读帧
    18EF71C9 露营模式、WiFi、音响(含蓝牙)、踏板自动 / 伸出 / 收回 命令侧主要目标
    18EF71EB 接收大量内容;露营模式、WiFi、音响状态 作者标注「接收很多东西」
    18EF72EB 灯光开关、230V 输入、电池电流、电压、电量百分比 电池参数集中在这一帧
    18EF73EB 230V 输入、清水与灰水水位 水位需第 1 字节为 01 才有效
    18EF60C9 设置灯带颜色 Y / R / G / B 四组预设值

    注意 18EF72EB 同时承担电流、电压、电量三项:这三项的字节位置完全不同,且分属不同的码制,因此不存在「一个 ID 对应一组语义」的捷径。

    解码规则与位序陷阱

    下表按作者原文的 0 起字节编号整理(原文写作 0ºB、1ºB……)。每行都附了原文给出的样例帧,这是判断规则可信度的关键——有样例的规则可以复现,无样例的只能照抄。

    内容 ID 样例帧 解码规则 可复现性
    清水 / 灰水水位 18EF73EB 00 00 00 00 0B 00 04 00 0 起编号第 4 字节为清水百分比(样例 0x0B)、第 6 字节为灰水(样例 0x04);首字节为 01 时清水箱为空 高,样例与规则自洽
    电池电流(充电) 18EF72EB 50 00 00 8A 59 00 07 02 0 起编号第 6 字节为电流正值(样例 0x07);首字节大于 0 表示充电中 中,未给单位换算
    电池电流(放电) 18EF72EB 00 00 00 0A 4C 00 FF 01 0 起编号第 6 字节为负值,FF = −1、FE = −2、FD = −3,按 MOD 2 递推 中,负值为自定义编码
    电池剩余时间 18EF70EB 01 00 3A 26 00 00 00 00 原文称第 2 字节为小时、第 3 字节为分钟,样例对应 0x3A 与 0x26 中,需确认编号起止
    电池电压 18EF72EB 00 00 00 0A 50 00 FF 01 原文称取第 5 字节(样例值 0x50)转十进制,十位加 5 得整数部分、个位为小数部分 低,推导未说明,需实测复核
    电池电量百分比 18EF72EB 00 00 00 0A 50 00 FF 01 原文称取第 4 字节(样例值 0x0A)为百分比除以 10,经 10*10 换算为 100 低,表述与样例存在张力,需复核
    显示屏开 / 关 18EF70C9 00 40 … / 00 00 … 由 I/O 按键触发,0 起编号第 1 字节为 40 时开、00 时关 高
    车门锁 18EF70EB 00 04 3A 26 00 00 00 00 0 起编号第 1 字节为 04,随后回到 00 中,仅给出单帧切片
    灯带颜色 18EF60C9 1E.00.64.2F… / 08.00.64.25… / 7D.00.64.23… / F5.00.64.2F… 四组预设分别对应 Y、R、G、B,0 起编号第 0 与第 3 字节变化 高,可整帧复现

    两条被标为「低」的规则是最该警惕的:电流与电压共用同一帧,且放电电流用 FF / FE / FD 表示负值、按 MOD 2 递推,与电压那条「十位加 5」的规则都不是标准编码。在没有万用表对照之前,这两项只能看趋势。

    字节编号在同一份表里并不统一

    这是使用这份表格时最容易踩的坑。同一份文档里,水位行说「4ºB = 0B」,只有按 0 起编号才能在样例帧 00 00 00 00 0B 00 04 00 里对上;电流行说「6ºB 是安培值」,也只有 0 起编号才能在 50 00 00 8A 59 00 07 02 里取到 0x07。但电压行与电量百分比行的样例值(0x50 与 0x0A)在同一帧里分别位于从 1 起数的第 5 与第 4 个字节。也就是说原文的编号起止在不同行之间不一致。正确做法是先拿样例帧反推真实位置,再写解析规则,不要直接信任编号本身。

    九条可复现的命令

    作者把可发送的命令单独列了一张表,全部基于 cansend,这也是整份资料里最容易验证的部分。

    功能 命令载荷 目标 ID 效果
    露营模式 00.40.00.00.00.00.00.00 18EF71C9 开 / 关切换
    WiFi 00.01.00.00.00.00.00.00 18EF71C9 开 / 关切换
    踏板自动 04.00.00.00.00.00.00.00 18EF71C9 开 / 关切换
    踏板伸出 10.00.00.00.00.00.00.00 18EF71C9 单次动作
    踏板收回 40.00.00.00.00.00.00.00 18EF71C9 单次动作
    氛围灯 04.40.00.00.00.00.00.00 18EF70C9 开 / 关切换
    外部灯 00.41.00.00.00.00.00.00 18EF70C9 开 / 关切换
    内部灯 01.40.00.00.00.00.00.00 18EF70C9 开 / 关切换
    卫星(SAT) 00.50.00.00.00.00.00.00 18EF70C9 开 / 关切换

    这张表的结构是「同一 ID 内按位区分功能」:18EF71C9 上,0 起编号第 1 字节的 40 / 01 / 04 / 10 / 40 分别对应露营模式、WiFi、踏板自动、踏板伸出、踏板收回;18EF70C9 上则靠 0 起编号第 0 与第 1 两个字节的组合区分氛围灯(04 40)、外部灯(00 41)、内部灯(01 40)与卫星(00 50)。做自动化控制时,正确做法是先读回当前状态再发送,而不是按记忆中的固定序列盲发。

    常见问题

    生活舱 CAN 和 Crafter 底盘 CAN 是同一条总线吗?

    不是。项目说明明确指出生活舱 CAN 独立(或几乎独立)于 Crafter 底盘总线,本文涉及的 ID、命令与解码只适用于生活舱部分。想取发动机、变速箱或底盘数据,需要另外接入底盘侧总线,不能指望从这些 ID 里读出来。

    从哪里接入生活舱总线最省事?

    作者最终选择接在 RGB 灯带控制器(零件号 7C4 947 022)的 9、10 针上,该控制器位于 230V 配电盘正下方的技术柜内。原厂接线图上也标注了多个可接入「便利 CAN」的点位,但灯带控制器这个点被作者评价为最方便。

    电池电流的负值为什么用 FF、FE、FD 表示?

    这是作者从样例帧中归纳出的自定义编码:放电时第 6 字节按 FF = −1、FE = −2、FD = −3 的方式递推,原文写作 MOD 2,与充电时的正值编码不是同一套。由于文档未给出单位与系数推导,实际使用前应与钳形表对照测量。

    清水和灰水水位在同一帧里怎么区分?

    靠字节位置区分:样例帧 00 00 00 00 0B 00 04 00 中,0 起编号第 4 字节(0x0B)是清水百分比、第 6 字节(0x04)是灰水百分比。另外原文注明当首字节为 01 时清水箱判定为空,因此解析时必须先看首字节再取水位值。需注意的是原文编号起止在不同行之间并不统一,实际接入应先按样例帧反推。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/mk4001-vw-grand-california/README.md(硬件与软件清单、500 kbit/s 初始化命令、接入点位与免责声明)
    • 项目 01 —— public-can-dbc-datasets/additional/mk4001-vw-grand-california/Table.md(7 个 ID 的功能归属、13 条解码样例与 9 条 cansend 命令)

    免责声明

    本文内容整理自公开开源项目,原作者已声明该项目仅用于教育与研究,相关 CAN 代码与协议的知识产权归属大众集团,不构成对任何专有系统的授权访问或修改许可。文中字节位置与换算规则均为原作者基于自车观察给出的结果,未经独立验证,不同年款与配置可能存在差异。

    更新记录

    • 2026-10-14 初稿

  • Mazda MX-5 NC 的 CAN 笔记:10 个 ID 与 RaceChrono 公式

    结论:Mazda MX-5 NC(2005–2015)的公开 CAN 笔记给出 10 个可用 ID 与配套 RaceChrono 公式,但覆盖面有硬边界:0x081 转向角、0x085 制动压力、0x090 疑似加速度三帧只在带 DSC 模块的车型(2007 年起)上出现;10 秒怠速直方图里 20 个 ID 中只有 8 个达到 100 Hz,真正被写清解码的不超过 10 个。

    先分清哪些 ID 依赖 DSC

    这份笔记面向第三代 MX-5(NC / NCFL / NCFLFL,即 MK3 / MK3.5 / MK3.75),按 CAN ID 组织,每个 ID 给出字节含义表与 RaceChrono 通道公式。使用时第一个判断不是「这个 ID 有没有」,而是「这辆车有没有 DSC 模块」——转向角、制动压力、疑似加速度三组信号都只在配备 DSC 的车型上存在,也就是 2007 年之后的车。

    CAN ID 更新频率 关键字节 公式 前提条件
    0x201 100 Hz 0–1 转速、4–5 车速、6 油门、7 恒为 FF 转速 = bytestouint(raw,0,2)/4.00;车速 = ((bytestoint(raw,4,2)/100.00)−100)/3.6;油门 = bytestoint(raw,6,1)*2.00 全系
    0x4B0 100 Hz 0–1 左前、2–3 右前、4–5 左后、6–7 右后 四角统一为 ((bytestoint(raw,N,2)/100.00)−100)/3.6 全系,独立承担轮速
    0x200 100 Hz 6 制动开关、7 节气门位置 制动 = data[6] 与 0x1 按位与;节气门 = bytestouint(raw,7,1)*100/255.00 全系;byte7 与 0x215 byte6 同源
    0x215 100 Hz 6 节气门位置,其余多为故障码 bytestouint(raw,6,1)*100/255.00 全系;byte0 与 byte2 互为镜像,byte1 与 byte3 互为镜像
    0x231 40 Hz 1 的 bit3 空挡、bit1 离合 空挡 = data[1] 与 0x4 按位与;离合 = data[1] 与 0x2 按位与 手动挡其余字节恒为 FF 或 00
    0x240 10 Hz 0 计算负载、1 水温、4 进气温度 水温 = bytestouint(raw,1,1)−40;进气 = bytestouint(raw,4,1)−40 全系;水温与 0x420 byte0 同源
    0x081 100 Hz 2–3 有符号转向角,仅 4 字节 bytesToInt(raw,2,2),正值向右 需 DSC 模块,2007 年起
    0x085 100 Hz 0–1 制动压力、2 的 bit0 制动开关 压力 = (3.4518689053*bytesToInt(raw,0,2)−327.27)/1000.00,单位 kPa 需 DSC;系数源自 miata.net 用户数据,作者标注可能错误

    表里最该被记住的不是公式本身,而是最后一列。0x081 与 0x085 一旦用在 2006 年及以前的车上,会表现为「完全没有该 ID」,而不是数值为零。

    车速公式里那两个魔数的来历

    0x201 与 0x4B0 共用同一套换算:原始值以 km/h 为单位但带 100 km/h 偏移,因此先除以 100 再减 100;末尾的 3.6 是把 km/h 换成 m/s,因为 RaceChrono 内部以 m/s 计算,之后由软件按用户选择的单位再乘回去。自己写解析器时若直接用 m/s 之外的单位,就要把这一级换算还原。

    油门通道的公式是 bytestoint(raw,6,1)*2.00,对应 0.5% 的步进;0x201 的 byte2 与 byte3 虽跟随转速变化,但笔记标注它疑似带 0x7fff 偏移且与发动机扭矩无关,属未完成解析项。

    10 秒直方图:用帧数反推频率

    笔记给了一张停车场怠速状态下 10 秒窗口的报文计数直方图,共 20 个 ID。把帧数除以 10 就得到各 ID 的实际频率,这张表可以直接用来判断采样与总线负载。

    10 秒帧数 换算频率 CAN ID(十六进制) 说明
    1000 100 Hz 081、085、090、200、201、211、215、4B0 含全部已解析的动态信号
    500 50 Hz 204、21A 未解析
    400 40 Hz 231、4EC 231 为变速箱,与笔记标注的 40 Hz 一致
    100 10 Hz 212、240、39A、420、430、4FF 240 为发动机数据,与标注的 10 Hz 一致
    10 1 Hz 4F0、4F1 低频,疑似诊断或状态帧

    这张表的实用价值在于排错:如果某台设备上 0x4B0 的计数明显低于 1000,先查总线负载与过滤规则,再去怀疑解码;如果它和 0x240 一起掉,问题多半在总线本身而非解析。

    恒值与未完成项要单独标记

    笔记里有几类条目不能当信号使用。0x211 全为 0xFF;0x212 恒为 80 00 20 40 00 00 00 00,原文用问号表示这只是观察结果;0x090 整帧标记为 WIP,只推测与加速度有关;0x430 的 byte2–7 恒为 0x00,且笔记提示 0x430 在 ND 上才是油量。把它们写进解析配置只会产生恒零通道。

    0x240 这一帧同样混着可用与不可用项:byte0 的计算负载公式为 100*(bytestouint(raw,0,1)/255.00,笔记说明作者在 RaceChrono 的通道列表里找不到对应的 Engine Load 通道;byte3 的节气门位置被标注为绝对开度(计入怠速开度),但作者实测怠速值偏高(约 11%),提示缩放仍需调整;byte2 疑似点火提前角,未给出公式。

    常见问题

    这份 MX-5 NC 的 CAN 笔记覆盖哪些年款?

    笔记标题写明覆盖 2005–2015 的第三代车型(NC / NCFL / NCFLFL,即 MK3 / MK3.5 / MK3.75)。但转向角 0x081、制动压力 0x085 与疑似加速度 0x090 只在带 DSC 模块的车型上存在,也就是 2007 年起;更早的车上这些 ID 根本不会出现。

    0x085 的制动压力公式能直接用吗?

    需谨慎。笔记给出的公式是 (3.4518689053*bytesToInt(raw,0,2)−327.27)/1000.00,单位为 kPa,但作者明确标注系数来自 miata.net 用户数据、可能有误。作为替代,笔记还给出百分比算法 min(0.2*(bytesToInt(raw,0,2)−102),100),未踩时基准值为 102,松开后短暂降到 99 会出现轻微负值。

    车速公式里为什么既要减 100 又要除以 3.6?

    两级换算是两件事。原始车速以 km/h 为单位但带 100 km/h 偏移,所以先除以 100 再减 100 还原真实 km/h;再除以 3.6 是换成 m/s,因为 RaceChrono 内部以 m/s 计算,最终按用户选择的单位自动乘回。自建解析器时若不需要 m/s,应去掉这一级。

    离合与空挡信号在自动挡车上也能用吗?

    不能直接套用。0x231 的 byte1 中 bit3 表示空挡、bit1 表示离合,笔记说明该字节的解读基于手动挡车:手动挡时 byte0、byte2、byte3 恒为 FF,byte4–byte7 恒为 00。笔记也提到该帧对自动挡车可能有更多有意义的数据,但并未给出自动挡下的解析。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/hatsunearu-mx5-nc-notes/mazda_mx5_nc.md(10 个 CAN ID 的字节表、RaceChrono 公式、更新频率与 10 秒怠速直方图)

    免责声明

    本文内容整理自公开开源笔记,其中公式、系数与频率均为原作者基于自车观察给出的结果,部分条目被作者本人标注为未完成或可能错误,未经独立实车验证。不同年款、市场、ECU 版本与是否带 DSC 模块均会导致差异,请以实际抓包与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • Ranger EV 改装的 CarState 规格:五个真信号与一个 vEgo 陷阱

    结论:把一辆 1986 年 Ranger 的 EV 改装车接进 openpilot,真正能被真实信号填满的 carState 字段只有五个:vEgo、gas、brakePressed、gearShifter、standstill。四轮转速是从 vEgo 复制的假值,全部转向字段为零且必须保持为零,巡航状态留到第三阶段。而在进入纵向控制之前,还有一个前置阻塞项:vEgo 目前只在 3 挡正确。

    两条总线与 openpilot 侧的编号映射

    改装车上有两条独立的 250 kbit/s 总线:can0 挂 Thunderstruck MCU/BMS 与 TSM2500 充电机(ZEVCCS 相关 0x351/0x355/0x356),can1 是 X1 / Hyper9 电机的 CANopen TPDO 总线(0x181/0x182/0x183/0x184)。在移植中它们被重新编号为 Hyper9 = bus 0、MCU = bus 1。

    总线 速率 设备 openpilot bus
    can0 250 kbit/s Thunderstruck MCU/BMS + TSM2500 充电机 1
    can1 250 kbit/s X1 / Hyper9 电机控制器 0

    安装时有一处容易踩的坑:Panda 默认是 500 kbit/s,而这辆车的总线是 250 kbit/s,必须先把 Panda 的总线速率改到 250 k,再确认设备能看到 0x181 与 0x183。

    哪些字段有真实来源,哪些是占位

    规格表按「必须有」的驱动信号逐行列出来源与变换。判断标准是:能否在 ranger-dashboard/dbc/*.dbc 里找到对应的报文与信号。

    carState 字段 来源报文 信号与变换 状态
    vEgo / vEgoRaw HYPER9_STATUS 0x181 VEHICLE_SPEED(km/h,有符号 ×0.1),abs(kmh)/3.6 转 m/s 有,但挡位相关
    gas HYPER9_MOTOR 0x183 THROTTLE_REQUEST(-100..100 %),clip(req,0,100)/100 有
    brakePressed HYPER9_STATUS 0x181 SYSTEM_FLAGS bit5(FLAG_PEDAL_BRAKE) 有
    gearShifter HYPER9_MOTOR 0x183 有符号 MOTOR_RPM:大于 20 为 D,小于 -20 为 R,否则 N;FLAG_PARK_BRAKE 置 P 有
    standstill 派生 vEgoRaw 小于 0.1 有
    wheelSpeeds.fl/fr/rl/rr 无 四轮全部等于 vEgoRaw 伪造,无轮速传感器
    steeringAngleDeg / RateDeg / Torque / Pressed 无 全部为 0.0 或 False 缺传感器与 EPS
    cruiseState.available / enabled 无 暂为 False 第三阶段
    doorOpen / seatbeltUnlatched 无 固定 False 无传感器

    转向全为零在只读阶段是可以接受的:openpilot 会照常构建完整的 carState 并完成指纹匹配,只是无法转向。规格里把转向角传感器列为第 1.5 阶段要补的第一件东西——没有它,后续的横向控制就没有反馈闭环。

    vEgo 的挡位依赖问题

    这是进入纵向控制前必须先修掉的问题。X1 报出的 VEHICLE_SPEED 本身是「电机转速 × 一个固定内部速比」,而这辆车用的是 M5OD-R1 五速手动变速箱,速比每换一挡就变一次,因此单一常数的车速只在一个挡位上正确。2026 年 8 月的实测结果是:3 挡基本正确,4 挡读数约为实际值的一半。只读阶段可以容忍,纵向控制绝对不能——错误的 vEgo 会直接破坏速度控制。

    规格给出的修法不是去反推挡位,而是在变速箱下游测速,两个候选来源是 comma 设备自带的 GPS(无需新增布线,巡航准确但低速迟滞)与 M5OD 输出端的尾轴 VSS(真实车速,低速表现好)。拿到挡位无关的真实车速后,显示与日志用的前进挡位反而可以由「电机转速 / 真实车速」对照已知速比四舍五入得到——openpilot 本身只需要 vEgo 与 D/R/N。

    EV 专属信号为什么不进 carState

    openpilot 的 carState 结构里没有电池包电压、SoC、电机温度这类字段。规格明确把这些信号留在仪表盘与 MQTT 上做带外暴露,移植层不处理。

    信号 来源 展示位置
    BATTERY_SOC 0x181 或 MCU 0x355 仪表盘 SoC 表
    DC_BUS_VOLTAGE / CURRENT、power_kw HYPER9_POWER 0x182 仪表盘功率表
    MOTOR_TEMP、INVERTER_TEMP HYPER9_MOTOR 0x183 仪表盘温度
    电池包 V/I/温度、单体极值 MCU 0x356 等 仪表盘与 Home Assistant
    故障等级与系统标志 0x181 或 MCU 0x35a 仪表盘诊断

    如果后续要让这些信号进入 openpilot 内部(比如在 comma 界面显示 SoC,或用电池包故障来门禁接管),规格建议的做法是在 fork 的 carState 上加几个自定义字段,或者走一条旁路的 cereal 消息,而不是硬塞进标准结构。

    分阶段现实阶梯与安全前提

    这份规格把整件事拆成了三级阶梯,并标出了明确的硬件阻塞点。第一阶段只读是这套脚手架当前所处的状态。

    阶段 目标 状态 阻塞项
    1 只读 openpilot 通过 CAN 看到车辆 本脚手架 无
    2 纵向 通过 Hyper9 扭矩/动能回收控速 未开始 safety_ranger.h、vEgo 挡位无关化
    3 横向 转向 被阻塞 车辆无 EPS,需加装转向执行器

    第二阶段的执行路径已经写明:纵向加速度映射到发给 X1 的 MOTOR_TORQUE 命令 RPDO;制动权先由负扭矩/动能回收承担,且需要先实测动能回收的减速度上限。但在这些生效前,必须有 panda 固件里的 safety_ranger.h 钩子逐报文施加限制。第一阶段跑的是 SILENT 安全模式——记录一切,不发送任何东西。

    常见问题

    为什么四个轮速信号要伪造?

    因为这辆车没有独立轮速传感器,规格只能把四个 wheelSpeeds 全部赋成 vEgoRaw。对只读阶段和纵向控制够用,但横向调校最终需要真实的逐轮转速,属于后续要补的硬件。

    vEgo 在 4 挡读数约为实际值的一半,怎么处理?

    不要在总线里反推挡位,改在变速箱下游测速。两个方案:直接用 comma 设备自带的 GPS(无需新增布线,巡航速度准确,低速迟滞),或在 M5OD 输出端装尾轴 VSS(真实车速,低速表现好)。把 carstate.py 的 vEgo 来源从 VEHICLE_SPEED 换成其中之一。

    没有 EPS 是不是就完全做不了 openpilot?

    不是。openpilot 的两件事是转向和控速,这辆车缺的是转向执行器,因此横向被阻塞,但纵向控制不依赖 EPS,仍可在加装转向执行器之前实现。完全脱手的完整功能才需要 EPS 改装这个独立硬件项目。

    EV 的 SoC 和电池温度为什么不在 carState 里?

    因为 openpilot 的 carState 结构本身不建模电池包电压、SoC 与电机温度字段。这些信号保留在仪表盘与 MQTT 上带外暴露。若确实需要在 openpilot 内部使用,规格建议加自定义字段或走旁路 cereal 消息。

    接上 Panda 后第一件事要改什么?

    改总线速率。Panda 默认 500 kbit/s,而这辆改装车的两条总线都是 250 kbit/s,必须先把 Panda 设为 250 k,再确认设备能看到 0x181 与 0x183,之后才进入版本对齐与指纹匹配步骤。

    延伸阅读

    参考资料

    • 项目 03 —— public-can-dbc-datasets/additional/wstewarttennes-ranger-ev-conversion/openpilot-port/spec/ranger_carstate_spec.md(总线映射、逐字段来源与变换、vEgo 挡位依赖与带外 EV 信号清单)
    • 项目 03 —— public-can-dbc-datasets/additional/wstewarttennes-ranger-ev-conversion/openpilot-port/README.md(三阶段阶梯、文件职责、版本对齐步骤与安装流程)
    • 项目 03 —— public-can-dbc-datasets/additional/wstewarttennes-ranger-ev-conversion/README.md(仓库布局与仪表盘仓库关系)

    免责声明

    本文内容来自公开仓库中的自定义移植规格,描述的对象是一辆特定年份的改装车辆,其总线构成、控制器型号与变速箱配置均不具备通用性。文中数字(报文 ID、缩放系数、挡位相关性)来自该仓库的实测记录,未经独立复现。任何涉及车辆控制的改动都应以实际测量与合规验证为准,本文不构成改装或上路操作指引。

    更新记录

    • 2026-10-14 初稿

  • 模型回放:瞬时 50 ms、平均 30/18 ms 与 PC 放宽门控

    结论:模型回放的时序门是样本统计而非端到端保证——modelV2 瞬时上限 50 ms、平均上限 30 ms(检测到大模型时 50 ms),driverStateV2 瞬时上限 50 ms、平均上限 18 ms,二者都丢弃第一条执行时间再取 max/mean。PC 模式会额外忽略位置、车道线、道路边缘、前车轨迹等字段并使用 0.3 的比较容差,CI 下比较结果 failed 被强制清零,因此 CI 的零退出码不能作为严格数值门的通过证据。以下为固定源码归档的静态阅读与纯消息替身探针,未下载路段、未加载模型、未执行模型。

    关键组件与函数

    该入口把道路模型和座舱模型分成两条独立回放链,各自裁取消息、插入标定与设备状态,再顺序调用回放。下表按组件列出职责与约束。

    函数 / 组件 实际职责 约束与风险
    get_log_fn() 由路段、CHESTNUT 和参考名构造 .zst 文件名 CHESTNUT 来自命令行 --chestnut;文件名不是模型权重/设备身份的完整证明
    plot() / generate_report() 把指标序列画成 PNG,并从 modelV2 取 8 项、driverStateV2 取 7 项,共生成 15 张曲线 proposed == master 是 Python 序列精确相等,仅决定报告分组,不参与主判定
    comment_replay_report() 有关联 PR 时上传图片及本次完整日志,并发布评论 其中上传/评论异常在主程序的比较 try 内会转为 failed=True,不能把报告服务失败直接解读为模型输出不等价
    trim_logs() 按相机状态计数选窗口,再把指定辅助主题的全部原始消息追加 默认 START_FRAME=0 时从首条消息开始纳入;追加可能重复和打乱时间顺序
    model_replay() 道路模型和座舱模型分别裁取消息、插入标定/设备状态,顺序调用回放;随后检查大模型状态及执行耗时 两条回放链分别启动进程,不是单个端到端闭环;缺首条标定或设备状态会 StopIteration
    get_frames() 从固定缓存反序列化,或取三路 .hevc,预热 [START_FRAME, END_FRAME) 的 NV12 帧后把 FrameReader pickle 落盘 缓存名只有路段/段号/窗口,未包含权重、软件提交、解码器和视频内容哈希;pickle.load() 要求缓存文件可信
    __main__ 读取路段、回放、按模式比较或更新参考,写 model_diff.txt,用 failed 退出 更新、CI、PC 三个模式改变判定语义,不能只看退出码

    帧窗口与消息来源

    道路模型以 narrowRoadCameraState 与 wideRoadCameraState 双主题计数,座舱模型以 cabinCameraState 计数,并分别追加 EncodeIdx、车辆/CAN 等辅助主题,默认窗口 START_FRAME=0、END_FRAME=60。SEND_EXTRA_INPUTS=0 时会删除裁取日志里的 extrinsicsCalibration,但随后从原路段无条件取首条标定和 deviceState,将其时间改为原日志首条时间并插入两条回放链的索引 1。故原日志为空或缺主题不是可降级场景。

    用不依赖 Cap’n Proto 的假消息执行原版 trim_logs():输入时间序列为 carState@1, other@2, narrow@3, wide@4,窗口 end_frame=1,并要求全量追加 carState。start_frame=0 的返回顺序是 carState@1, other@2, narrow@3, wide@4, carState@1;start_frame=1 返回 narrow@3, wide@4, carState@1。这说明辅助主题既可能重复又可能后置成非时间顺序;下游 migrate() 排序消息,不自动去重。另需注意双摄像头窗口的结束条件是所有相机状态计数恰等于 END_FRAME,其中一路缺帧时可能继续遍历到日志尾部。

    时序门与数值门

    时序门给出各主题的瞬时与平均上限,并统一丢弃第一条执行时间。

    主题 瞬时上限 平均上限
    modelV2 50 ms 30 ms(检测到大模型时 50 ms)
    driverStateV2 50 ms 18 ms
    统计方式 丢弃第一条执行时间 再用 np.max/np.mean;未覆盖首帧冷启动、端到端帧年龄或最坏调度延迟

    chestnut 由道路模型消息中是否有任一 big 标志推断;只有命令行指定 --chestnut 才断言道路模型每条均为大模型且至少有一条大模型输出。最后的 assert timings_ok or PC 使 PC 时序超限只打印,不阻断退出。

    非更新模式下,主程序按道路模型每帧预期 3 条输出、座舱每帧 1 条输出,从远端参考日志按位置切片,忽略 logMonoTime 和部分掉帧/执行时间字段,再调用比较。PC 又额外忽略位置、车道线、道路边缘、前车轨迹等字段,并使用 0.3 的比较容差;这不是「全部模型输出相等」。设置 CI 环境变量时,比较结果 failed 被强制清零,差异仍会写入报告。因此 CI 的零退出码不能作为严格数值门的通过证据。更新模式(--update 或 GIT_BRANCH=master)跳过参考比较,非 PC 才保存并上传新参考;上传异常只打印,不改变 failed,所以零退出码也不证明远端参考已更新。

    自制硬件与插件化验收

    应把道路模型、座舱模型和相机采集分别建成有版本的插件契约:输入包括 NV12 格式/尺寸、相机时钟和帧 ID、标定、设备状态、权重哈希;输出包括 schema 版本、每帧关联键、模型能力和执行时长。缓存以视频/权重/解码器/软件版本的内容哈希为身份,且不加载不可信 pickle。

    参考比较须声明忽略字段与阈值,单独记录实际覆盖帧数、缺帧、重复输入、首次启动耗时、持续时延与输出完整性。PC、CI 和目标车机分别给出独立通过位;实板热态/冷态、负载、供电降额、相机断流和故障回退仍需台架及封闭场地验证。本文没有验证这些动态指标。

    常见问题

    50 ms 与 30 ms 是端到端时延保证吗?

    不是。它们是样本统计:modelV2 瞬时上限 50 ms、平均上限 30 ms,两者都丢弃第一条执行时间再取 max/mean,未覆盖首帧冷启动、端到端帧年龄或最坏调度延迟。

    CI 通过就代表数值回归通过吗?

    不代表。设置 CI 环境变量时比较结果 failed 被强制清零,差异仍会写入报告;报告上传/评论异常则仍可能进入 except 导致失败。零退出码不能作为严格数值门的通过证据。

    PC 模式比较的是全部模型输出吗?

    不是。PC 额外忽略位置、车道线、道路边缘、前车轨迹等字段,并使用 0.3 的比较容差;这并不等于「全部模型输出相等」。

    输入日志的时间顺序有保证吗?

    没有。trim_logs() 把指定辅助主题的全部原始消息追加,可能重复且后置成非时间顺序;下游 migrate() 只排序,不自动去重。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PROCESS_REPLAY_MODEL_FRAMES_TIMING_REFERENCE_DEEP_DIVE.md(模型回放的帧输入、时序门、数值门与报告语义)
    • 项目 02 —— openpilot-research/ARCHITECTURE_GUIDE_CN.md(系统总架构中文导览,作为本文的补充阅读)

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

    更新记录

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

  • VW PQ35 ABS 仿真器档案:Bremse 帧与 0x7E9 车速

    结论:这份档案记录的是 VW PQ35 平台上一组用于「替代原厂 ABS 模块后恢复仪表」的 CAN 报文:一台开源设备(ESP32 + MCP2515)从 OBD 地址 0x7E9 读取 09G 变速箱的车速(PID 0x0D),再向动力总成 CAN(500 kbit/s)发送 Bremse_* 系列帧,让车速表与里程表继续工作。源文档反复强调:它只恢复仪表信号,不恢复 ABS 制动与稳定控制,且没有真实 ABS 帧时制动系统可能报警或表现异常。下述说明仅限源文档记录的那一台车(2009 Jetta 2.5 / 09G / PQ35)。

    车辆与适用范围

    源文档是一个开源仿真器项目的说明,属于单台车实测 + 标定。它同时列出若干「大概率兼容」的车型,但明确标注这些只是推测,只有 Jetta 2.5 2009 一台被标为已测试。校准默认值是按这一台车标定的。

    项 内容
    车型 VW Jetta 2.5 2009(PQ35 平台)
    年款 / 市场版本 2009;源文档未给出市场版本
    ECU / 改装状态 09G 自动变速箱 TCU、MK60EC1 ABS 模块、PQ35 仪表;ESP32 Dev Module + MCP2515 接入动力总成 CAN
    采集跨度 源文档未给出日志数或帧数;标定基于实车速度与里程比对
    适用范围 仅 Jetta 2.5 2009(09G)被标为已测试;Rabbit/Golf 2006-2009、Audi A3 2006-2009、Passat 2006-2010、Skoda Octavia 2006-2008 均标为「大概率(Likely)」,需 09G TCU + MK60EC1 + 500 kbit/s 动力总成 CAN

    总线与寻址

    源文档明示接入的是动力总成 CAN(Powertrain CAN,500 kbit/s),不是舒适 CAN;MCP2515 需 8 MHz 晶振、移除 120 Ω 终端跳线(除非它是总线末端节点)。OBD 响应地址为 0x7E9,源文档未展开 11 位 / 29 位差异,但所列 ID(0x1A0、0x440 等)均为 11 位经典 CAN 口径。

    项 内容
    物理层 双绞屏蔽线 CAN;动力总成 CAN 高/低;两端各 120 Ω 终端
    链路层 / 比特率 500 kbit/s(源文档明示 VW 动力总成标准)
    寻址方式 11 位 ID(0x1A0、0x4A0、0x5A0、0x3A0、0x7E9、0x440、0x540 等)
    CAN FD 否;源文档按经典 CAN 8 字节处理
    方向 设备向总线发送 Bremse_* 帧;从 0x7E9 / 0x440 / 0x540 接收车速与挡位

    已解码信号

    下表登记源文档明确给出的发送帧与接收帧及其周期与用途。涉及具体信号位域的部分,源文档只给到「16 位原始值在 bits 17-31」「每位 10 位」这类粒度,未逐位展开。

    帧 / 信号 位置(ID / 位域) 解码 / 用途 证据
    车速(主来源) 0x7E9(TCU,09G),PID 0x0D OBD 响应读车速;驱动仪表与里程计算的输入 源文档「RX」表
    挡位 Getriebe_1(wahl) 0x440(TCU) 挡位选择,用于运动状态判定 源文档「RX」表
    接合挡 Getriebe_2 0x540(TCU) 接合挡与档位显示,用于驻车判定 源文档「RX」表
    Bremse_1 轮速 0x1A0,16 位原始值在 bits 17-31 车速表主来源;周期 10 ms 源文档「TX」表
    Bremse_3 轮速(冗余) 0x4A0,同 Bremse_1 部分仪表需要此冗余帧;周期 10 ms 源文档「TX」表
    Bremse_2 中轮转数与里程脉冲 0x5A0,16 位中轮转数 + 时间计数器 + 6 位里程脉冲 驱动里程计数;周期 20 ms;空闲用原始值 127(中性) 源文档「TX」表与 v2.0 安全说明
    Bremse_10 单轮脉冲 0x3A0,VL/VR/HL/HR 各 10 位 供 ABS 模块使用的单轮脉冲;周期 20 ms 源文档「TX」表
    Bremse_5 静止位 0x4A8,10 ms ESP 帧——静止位,中性加速度避免误报 源文档「TX」表
    Bremse_8 纵/横向加速度 0x1AC,20 ms 中性值 127 / 361 / 512,用于抑制错误的 ABS/ESP 报警 源文档「TX」表与 v2.0 说明
    Bremse_6 加速度帧 0x1A8,3 字节,10 ms 中性加速度 源文档「TX」表
    Bremse_11 校验帧 0x5B7,20 ms 中性校验值 源文档「TX」表
    Bremse_4 Haldex 帧(可选) 0x2A0,3 字节,20 ms 仅当开关打开时发送 源文档「TX」表

    读取方法与注意事项

    第一,源文档把「恢复什么、不恢复什么」写得很直白:恢复的是车速表指针、里程表公里数与车辆运动状态;不恢复的是 ABS 制动与稳定控制、实时加速度遥测、OBD2 诊断码,以及(没有 Getriebe 报文时的)挡位显示。因此这台设备不是「修复 ABS」,而是让仪表在没有原厂 ABS 模块时继续工作。

    第二,校准有三个默认值且都针对这一台车:车速表偏置修正系数 1.084、里程脉冲/km 系数 0.2581、车轮周长 1.985 m。源文档给出求值方法,例如车速系数可用「GPS 速度 / 表显速度」反推,里程系数用「OBD 脉冲 vs 实际公里」按比例调整,车轮周长按轮胎规格或实测滚动法获得。调整步长建议 ±0.01。

    第三,安全上与「误报」相关的是中性值:源文档在 v2.0 把 Bremse_2 的静止值设为 127、Bremse_8 的加速度中性值设为 127/361/512,目的就是避免触发错误的 ABS/ESP 警告灯;若灯亮,源文档建议先核对这三个中性值。

    第四,可诊断性:串口 115200 输出循环耗时、滤波前后车速、脉冲计数、TX 失败计数、堆/栈占用等指标;源文档提示循环耗时过大(>12 ms)说明时序有问题,TX 失败非零说明缓冲溢出或总线冲突,栈低于阈值会提示栈溢出风险。

    证据等级与边界

    可视为实车证据的是「已测试」一台车与三组默认标定值,以及 0x1A0/0x4A0/0x5A0 等帧 ID 与周期。属于推测的是「大概率兼容车型」列表,源文档用 Likely 标注,并要求用「OBD 响应地址、DBC 帧 ID 匹配、上路前用监测工具验证」三步自行确认。

    不能外推的地方有三处。第一,兼容性依赖三个前提同时成立:09G TCU、MK60EC1 ABS、500 kbit/s 动力总成 CAN;缺一即不适用。第二,标定值是单台车的,车速修正与里程系数在不同轮胎、不同表头下会不同。第三,源文档明确声明设备不恢复 ABS/ESP,制动行为可能异常,任何「照抄即上路」都超出证据范围。

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

    常见问题

    装上这个仿真器后 ABS 会恢复吗?

    不会。源文档明确写它只恢复仪表信号(车速表、里程表、运动状态),不恢复 ABS 制动与稳定控制;没有真实 ABS 帧时制动系统可能报警或行为异常。

    车速是怎么来的?

    设备从 0x7E9(09G TCU 的 OBD 响应,PID 0x0D)读取车速,再据此驱动 Bremse_1(0x1A0)/Bremse_3(0x4A0)的轮速与 Bremse_2(0x5A0)的里程脉冲。

    车速表读数偏高/偏低怎么办?

    调整车速表偏置修正系数(默认 1.084)。源文档给的方法是以恒定 GPS 速度行驶,比较表显值,用「GPS 速度 / 表显速度」推系数,再以 ±0.01 步长微调。

    为什么里程表不动?

    源文档把里程不增归因于里程脉冲/km 系数(默认 0.2581)不对,建议先核实车轮周长,再用实际距离与 OBD 脉冲比对后按比例调整。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/pandolfipedro-pq35-abs/README.md(主来源:车辆、总线、TX/RX 帧表、标定与安全说明)。

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

    更新记录

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

  • Ford Fiesta MK5 影音 CAN 档案:125 kbps 车身信号

    结论:这份档案的对象是2006 年以后的 Ford Fiesta MK5(改款,配 2-DIN 与 6000CD 收音机)的影音 CAN(MS-CAN)。它的数据特征是「低速、易接入、以车身与影音信号为主」:总线速率 125 kbps,从收音机 QuadLock 连接器的 CANH/CANL 即可旁听,不需要 120 欧姆终端电阻;可见内容包括仪表显示屏文字(经 ISO-TP)、电台名、音量、CD 状态、时间、车门/车灯/挡位/踏板等。源文档只覆盖这一台改款 Fiesta 与这台 6000CD 收音机,且其定义文件不完整,部分报文只做到部分解码;下述结论不外推到其他年款或配置。

    车辆与适用范围

    源文档是一份逆向记录:作者以「截取收音机送给仪表 LCD 的文字」为起点,顺带解出若干其他报文。以下范围仅对应这一台车与这台收音机。

    项 内容
    车型 Ford Fiesta MK5,2006 年以后(MK5 改款),配 2-DIN 与 6000CD 收音机
    年款 / 市场版本 源文档以「>2006 改款」界定;其他年款与市场版本未确认
    ECU / 改装状态 无改装;在收音机 QuadLock 连接器的 CANH/CANL 上旁接一块 CAN 扩展板
    采集跨度 源文档未提供日志时长或帧数
    适用范围 仅这一台改款 Fiesta 与 6000CD 收音机的影音 CAN;定义文件不完整,部分报文仅部分解码

    总线与寻址

    源文档对物理层描述简练而具体:接入选定在收音机端,且对终端条件有明确说明。

    项 内容
    物理层 / 比特率 MS-CAN,125 kbps
    寻址 / 帧格式 源文档列出的 ID 均为 11 位标准帧(0x080–0x4F3)
    接入点 收音机 QuadLock 连接器的 CANH/CANL
    终端 不需要 120 欧姆终端电阻(源文档明示)
    CAN FD 源文档未提供(该年代与总线类型指向经典 CAN)
    传输层 仪表文字使用 ISO-TP(ISO 15765-2)分帧,含单帧与首帧/连续帧/流控

    已解码信号

    下表抽取源文档中最有代表性的条目(车身状态、影音与仪表文字)。位置列保留原文 ID 与字节序号,解码列保留其换算公式;证据列注明源文档对该条目的完成度或限定。

    信号 位置(ID / 字节) 解码 证据
    日期时间 0x080 / 字节 1–6 年(两位)、月、日、时、分、秒;1 秒分辨率 车载 RTC 独立于收音机时钟
    电台名 0x1E9 / 8 字节 ASCII 文本 完全解码
    转速 / 车速 / 油门 0x201 / 字节 1–2、5–6、7–8 km_h = speed/100;gas_percent = gas*100/50944 部分字段未标注
    转向灯箭头 0x265 / 字节 1 位掩码:左 0x20、右 0x40 位掩码按需相加
    前灯 / 手刹 0x286 / 字节 1 位掩码:手刹 0x10、示宽/近光 0x80、远光 0x40 示宽与近光共用同一 LED,无法区分
    音量 0x2D8 / 字节 2 volume = volume_raw/8 完全解码
    CD 状态 0x2DA / 字节 2、4 模式 0x2A/0x4A/0x6A;驱动器 0x23/0x21/0x22/0x26 枚举表完整
    CD 曲目 / 时间 0x2DB / 字节 3–4、6 曲目时间 track_seconds = track_time - 64;另含 track_id 非播放时为全 FF
    方向盘模式/选台键 0x2D5 / 字节 2 位掩码:模式 0x10、上选 0x40、下选 0x80 音量键不经此报文
    制动状态(一) 0x360 / 字节 6 未踩 0x60、轻搭 0x68、踩下 0x78 同一状态在两报文重复
    冷却液 + 制动(二) 0x420 / 字节 1、6 coolant_degrees = coolant - 40;制动 0x00/0x10/0x30 两处制动编码不同
    蓄电池电压 0x428 / 字节 2 battery_v = battery/10(十分之一伏) 完全解码
    车门 / 倒挡 / 锁止 0x433 / 字节 1、4、6 门位掩码:左前 0x80、右前 0x40、后备箱 0x08;倒挡 0x02;锁 0x10/解锁 0x20 位掩码完整
    仪表文字 0x4C0 / 短格式与扩展格式 短格式:0x07 0x34 + ASCII;扩展格式:首帧 0x10 + 长度 + 0x34,连续帧 0x21 + ASCII 为 ISO-TP;配套 0x4C8 流控
    车辆识别(VIN 低位) 0x4F3 / 8 字节 周期性广播的车辆 ID(VIN 低位,含生产日期信息) 完全解码

    读取方法与注意事项

    接入非常直接:源文档说明只需把 CAN 接口接到收音机 QuadLock 连接器的 CANH/CANL,无需 120 欧姆终端电阻。这一条与多数两节点试验台的接线习惯相反,源文档把它作为结论明确写出,实际接线时不要凭经验加终端。

    仪表文字那一类是ISO-TP(ISO 15765-2)传输:短格式只在收音机切到 AUX 模式时可见,形如「首字节标志 + 0x34 + ASCII」;扩展格式由一条含文本长度的首帧加若干条提供后续文本的连续帧组成。响应里随后出现的 ACK 报文提供流控:字节 1 为 0x30 表示「可继续发送」,字节 2 为 0x00 表示不做分帧流控,字节 3 为 0x00 表示按发送方最快速度。解析这类文本时,必须按 ISO-TP 的帧类型分别处理,而不能把每条 ID 当独立报文读。

    还有一处容易混淆:制动状态在两条报文里重复出现但编码不同——0x360 用 0x60/0x68/0x78,0x420 用 0x00/0x10/0x30。同一物理状态的多套编码要求解析方选定一个来源并保持口径一致。此外,音量键并不经方向盘那条报文(0x2D5 只覆盖模式与选台键),源文档对此如实标注为「似乎如此」。

    证据等级与边界

    本文属于单台实车逆向观测:ID、字节与换算公式均可在源文档逐条核对,属于直接证据;但源文档没有给出采集时长、帧数或时序,也未声明校验与计数器机制(除 ISO-TP 流控外)。随文档提供的定义文件(某类商业诊断工具的 .kcd 格式)源文档明确标注为不完整,因此不能把它当作完整协议描述。

    两点不能外推。第一,全部内容来自一台「2006 年以后改款」的 Fiesta 与一台 6000CD 收音机;换成其他年款、其他市场版本或其他音响配置,ID 与编码都可能不同。第二,多个报文只做到部分解码,源文档用「unspecified」标记了未解析字节;把这些字节当作已知信号会超出源文档证据。关于安全访问,源文档未提供任何绕过方法,且不建议尝试;本文只整理只读旁听所能看到的内容。

    常见问题

    接入这条总线要不要加 120 欧姆终端?

    不需要。源文档明确:把 CAN 接口接到收音机 QuadLock 的 CANH/CANL 即可,不用终端电阻。这与多数两节点试验台的接线习惯相反,接线时按源文档结论来,不要凭经验加终端。

    仪表屏的文字是怎么传的?

    走 ISO-TP(ISO 15765-2)。短格式如同「0x07 0x34 + ASCII」,只在 AUX 模式可见;扩展格式由带文本长度的首帧(0x10 + 长度 + 0x34)加若干连续帧(0x21 + ASCII)组成,随后有 0x4C8 流控 ACK。解析时必须按 ISO-TP 帧类型分别处理。

    为什么同一制动状态有两种编码?

    因为在源文档里它被两条报文重复承载:0x360 用 0x60/0x68/0x78,0x420 用 0x00/0x10/0x30。这是源文档记录的实际状态,解析时应选定一个来源并保持口径一致,不要混用两套编码。

    随文档提供的定义文件能当完整协议看吗?

    不能。源文档明确标注该文件(某类商业诊断工具的 .kcd 格式)不完整,多处字节以「unspecified」标记为未解析。把它当作完整协议描述会引入未经证实的字段。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/roncapat-ford-fiesta-mk5/README.md(本项目车型档案主来源:总线速率、接入方式、全部已解码 ID 与换算公式)。

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

    更新记录

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