结论:这辆 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),需要主动发请求才会应答。这里全部是只听模式,从不发帧,因此它们不可能出现在抓包里。
延伸阅读
- 公共 CAN/DBC 数据集怎么选:四类来源与三道筛查 —— 把「有没有抓包」和「抓包能不能用」分开判断
- OBD-II PID 入门:车速 0x0D 为什么是整数 —— K 线上的取数口径与这里形成对照
- 车电接入:为什么 ESP32 的 TWAI 不等于 OBD 接口 —— 50 kbit/s 这类非标准波特率对硬件的要求
- Ram ProMaster 2022:三总线与 19 个 UDS 模块 —— 同为房车类车身网络的另一份档案
- 车型 CAN 数据库
参考资料
- 项目 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 初稿
发表回复