标签: 数据管线

  • 聆风 ZE0 的 CAN 信号:LBC 六组读数与 64 A 折返电流

    结论:2012 款 Nissan Leaf(ZE0)的只读遥测建立在两件事上:ELM327 走 Car-CAN 的 ISO 15765-4(11 位、500 kbit/s),以及一小撮 UDS 服务 0x21 组读取。这台车的标准 OBD-II PID 完全不实现,模式 01 的请求 0100 只会返回 NO DATA;真正的数据来自电池控制器的六组 UDS 应答。所有信号按「已验证、静态、待定」三档标注,其中最大的坑是第五组的包电流:它是有符号 16 位除以 1024,超出正负 32 A 就会以 64 A 为单位折返。

    传输层:先把 ELM327 的几条硬约束定死

    这套档案把所有适配器的公共事实放在最前面,因为它们决定了后面所有字节能不能拿到。协议用 ATSP6(ISO 15765-4,11 位,500 kbit/s,引脚 6/14 的 Car-CAN);UDS 对电池控制器需要一组固定的头与流控设置,而针对 Car-CAN 的被动嗅探必须用 ATCAF0——如果保留 ATCAF1,大部分原始帧会直接打印成数据错误。

    事实 细节
    协议 ATSP6,ISO 15765-4 CAN,11 位,500 kbit/s,Car-CAN(OBD 引脚 6/14)
    标准 OBD-II PID 这台车不实现,0100 返回 NO DATA
    电池控制器 UDS 请求头 ATSH 79B、应答过滤 ATCRA 7BB、格式 ATCAF1、流控头 ATFCSH 79B、块大小与帧间隔设置,另加 ATFCSM1;由整车控制器在 Car-CAN 与 EV-CAN 之间桥接
    被动嗅探 必须用 ATCAF0,否则绝大多数原始帧打印为数据错误
    缓冲区 未过滤的 ATMA 约 24 帧就溢出,必须配 ATCRA 过滤
    EV-CAN(引脚 12/13) 标准 6/14 接口下不可达,需要改线的 OBD 延长线
    蓝牙连接 每次写入都要带应答标志,否则收不到通知

    同一份档案还维护了一条替代路径:把一台原生 USB-CAN 控制器直接接到诊断口上,由 USB ID 决定走哪条驱动。

    电池控制器:六组 UDS 读数的分工

    电池控制器的六组数据按职责分开轮询,而不是一次全读。分组第一、二、四、五组标记为已验证,第三与第六组仍属待定。偏移量是相对 61 NN 之后载荷的 0 基位置。

    组 长度 内容 状态
    01 电池状态 39 字节 高压电流两个传感器、包电压、12 V、绝缘电阻、HX、SOC、容量 已验证
    02 单体电压 192 字节单体数据,解析填充到 200 字节 96 路无符号 16 位毫伏,末位之后用 0xFFFF 填充 已验证
    03 32 字节 字节 10–11 为最高单体毫伏,12–13 为最低单体毫伏,其余未知 待定
    04 温度 18 字节 四组「无符号 16 位原始值 + 有符号 8 位摄氏度」,字节 12 看起来是均值 已验证
    05 扩展状态 74 字节 最高/最低单体、温度原始值、放电标志、包电流、绝缘、分段差值与组电压 已验证
    06 均衡 25 字节 24 个数据字节即 192 位,每单体对 2 位,非零疑似表示正在泄放 待定

    第一组的字节偏移

    字节 字段 量纲
    0–3 高压电流传感器 1 有符号 32 位除以 1024 安
    6–9 高压电流传感器 2 有符号 32 位除以 1024 安;在一次 13 分钟行驶中测到负 66 安,库仑积分占 SOC 乘容量推算值的 96%
    18–19 包电压 无符号 16 位除以 100 伏,与 96 路单体之和相差在 0.1 伏内
    20–21 12 V 蓄电池 无符号 16 位除以 1024 伏
    22–23 绝缘电阻 千欧
    26–27 HX 无符号 16 位除以 100
    29–31 SOC 无符号 24 位除以 10000,单位百分比
    33–35 容量 无符号 24 位除以 10000 安时,SOH 再除以 66 安时
    4–5、24–25 未知,静态 样本值分别为 0x0287 与 0x00F2

    电流传感器的行为也定了处理规则:三个电流来源在怠速时绕着零点漂正负 0.5 安,第五组的放电标志跟着噪声走;传感器 1 很粗,以约 2 安为台阶。500 瓦负载下,传感器 2 与第五组相差在 0.05 安以内。于是读取器以传感器 2 为基准逐周期取值,每 5 秒学一次相对第五组的偏移,再叠加上本车的零位标定,并在控制器报告放电时把正电流钳零;仪表侧把绝对值小于 0.6 安(功率小于 0.25 千瓦)视为怠速。

    第五组的电流会折返,不是饱和

    这是这套档案里最有决策价值的一条。第五组的包电流是有符号 16 位除以 1024,理论上在正负 32.0 安处就该饱和,但这台车的实际电流远大于此。2026-09-03 的一次 13 分钟行驶给出了答案:171 条记录、92 个新鲜第五组样本,第一组传感器 2 达到负 66.5 安。

    假设 超过正负 32 安样本的中位误差(21 个)
    线性延伸(第五组等于传感器 2) 8.49 安
    在正负 32 安处饱和 8.49 安
    另一套斜率(半量程) 11.88 安
    折返(16 位混叠) 2.50 安

    饱和假设被直接证伪:邻域超过 40 安的样本没有一个停在轨值上,全部 169 个样本里只有 4 个落在 30 至 32 安区间,绝对电流分布直到边界都是平滑的;最大值只到 31.919 安,而且跨越边界时符号会翻转——传感器 2 为负 34.11 安时第五组读到正 31.71 安,负 47.50 安时读到正 15.57 安,负 41.13 安时读到正 23.58 安。轨内标度则是对的:在三行窗口内传感器 2 移动小于 3 安的 25 个样本中,两者中位相差 0.27 安、最差 1.43 安,拟合关系为 g05 约等于 0.956 倍传感器 2 减 0.199,R² 0.995。

    工程后果很具体:读取策略把传感器 2 当作权威值是对的,但如果它每次都在新鲜第五组样本上学习偏移并在样本间传递,那么超过 32 安时学到的差值本身就是折返误差。那次行驶中,融合后的电流相对传感器 2 中位偏离 5.9 安、九十分位 23.6 安、最差 41.1 安,随后「正在放电却读到正电流就钳零」的规则又把大量行驶行直接归零。修正办法是只在第五组读数明确处于轨内、且两次读数接近时才学习偏移。另有一条仍未解决:传感器 1 在负载下读到的是传感器 2 的中位 1.358 倍(十分位 1.294、九十分位 1.429,48 个超过 15 安的样本),而做库仑积分能与 SOC 对上的是传感器 2。

    还有一处信任边界:存入数据库的是控制器报告的原值,不再是策略改写后的值。进程量全部落在 current_adj_a 与 power_adj_kw 上,并用 current_adj_src 注明经过的步骤;任何车型档案的策略都不得改写已报告字段。

    Car-CAN 被动帧与其他 ECU 探测

    车身侧的被动帧必须靠 ATCAF0 加 ATCRA 逐号抓。下面这批已验证的条目构成了挡位、灯光、车门、门锁与胎压的数据源。

    ID 字节 字段 解码 状态
    0x421 b0 挡位 08 停车、10 倒车、18 空挡、20 前进、38 经济 已验证,五种全部
    0x174 b3 粗挡位 AA 停车或空挡、99 倒车、BB 前进或经济 已验证,无法再细分
    0x358 b2 转向灯 80 关、82 左、84 右、86 危险灯 左右已验证,危险灯为社区值
    0x385 b2–5 胎压 psi 等于原始值除以 4,顺序为左前、右前、右后、左后 静态,数值合理;小于 5 psi 视为传感器休眠
    0x5C5 b1–3 里程 无符号 24 位,单位随 0x355 b6 bit5 静态,与仪表一致
    0x5B3 b1 仪表 SOH 原始值右移 1 位,百分比 静态,与电池控制器一致
    0x60D b0 车门 08 驾驶位、10 副驾、20 左后、40 右后、80 尾门 已验证
    0x60D b2 门锁 18 已锁、00 未锁 已验证
    0x60D b0 / b1 灯光 04 示宽、02 近光、08 远光、01 雾灯 已验证
    0x284 b4–5 车速 原始值除以 100 km/h 待定,只见过 0
    0x5A9 b1–2 剩余续航 无符号 16 位右移 4 位后除以 5 km 待定,标度未确认
    0x180 b5 加速踏板 原始值除以 2,百分比 待定
    0x292 b6 制动踏板 / 制动灯 原始值大于 0 即制动 待定

    其他 ECU 的探测结果基本是负的,构成了边界:整车控制器对所有模式 22 与 0x21 请求都返回否定码 0x80,需要原厂会话与安全访问;逆变器与转向模块完全无应答;ABS、车身模块与助力转向会回应第一组读取,尚未解码。EV-CAN 上的 0x1DB、0x1DA、0x55B 等报文虽然有公开社区可视化的布局记载,但在这台车上从未被看到过,档案只把它们作为待核对的假设放在帧仿真器里,而这些编码器刻意不与真实车辆的解码器放在同一处。

    OBD-II 引脚 信号
    6 / 14 Car-CAN 高低,普通 ELM327 用的就是这一对
    13 / 12 EV-CAN 高低,电池、逆变器与充电机所在网络
    11 / 3 AV-CAN 高低,信息娱乐网络,本项目不读
    4 / 5 底盘地 / 信号地
    8 仅在车辆上电时输出 12 V
    16 常电 12 V

    原生 USB-CAN 通道带来的量级变化

    把 ELM327 换成 CANable 2.0 一类的原生 USB-CAN 控制器后,被动项不再需要 ATCRA 加 ATMA 的停留开销,整个循环坍塌为纯 UDS 请求时间。2026-10-03 在这台车上的实测结果是:蓝牙约 2 秒的完整循环压缩到 0.58 秒,第一组读取 0.07 秒、96 路单体读取 0.29 秒;开单体日志后,全新 96 路读取的中位间隔为 351 毫秒(2.77 次每秒,最大 498 毫秒)。

    项目 实测结果
    识别方式 USB ID 16d0:117e 为 slcan 固件,1d50:606f 或 1209:2323 为 candleLight,0483:df11 为烧录模式
    被动窗口 10 秒内 1,692 帧每秒、49 个报文 ID、0 错误帧
    完整循环 0.58 秒,含电池各组、96 路单体、暖通模块三组读取和全部被动 ID
    ISO-TP 间隔 暖通模块 在间隔 0 毫秒时丢连续帧(6 次里只有 3 次完整),5 与 10 毫秒时 6 次全通
    终结电阻 适配器跳线必须断开;车辆诊断口在 6/14 与 13/12 间测得约 60 欧
    接线基准 优先用引脚 5 信号地,而不是承载车身负载电流的引脚 4 底盘地

    EV-CAN 那条通道则以只听模式为唯一打开方式:只听模式下的控制器连应答位都不驱动,总线在电气上与没有这个监听节点时完全相同;「曼哈顿 EV-CAN」配置项会强制只听,且不提供覆盖开关。需要注意的细节是,不同固件对只听的实现强度不同:candleLight 需要重新打开设备并回读模式,slcan 的静默命令无法从主机侧确认,而 SocketCAN 由内核强制且可被校验。

    常见问题

    为什么标准 OBD-II 的 PID 在这台 Leaf 上完全读不到?

    因为这台车压根没有实现这套标准诊断服务:模式 01 的 0100 请求直接返回 NO DATA。所以所有可用的数据都要从自定义的 UDS 服务走,主要是电池控制器的 0x21 组读与暖通模块 的 0x21 组读。这也意味着通用诊断 App 在这台车上基本拿不到东西。

    第五组的包电流为什么超过 32 安就不可信?

    因为它是有符号 16 位除以 1024 的字段,没有更宽的备选字段也没有钳位,真实值超出正负 32 安后就会以 64 安为单位连符号一起折返。13 分钟行驶里,邻域超过 40 安的样本没有一个停在轨值上,且跨越边界时符号必定翻转;四种假设中折返假设的中位误差最小,为 2.50 安。处理方式是把第一组传感器 2 作为权威值,并限制只在轨内学偏移。

    换成原生 USB-CAN 控制器后,提升具体体现在哪里?

    最直观的是被动项的开销消失:原先每个被动项需要一次「设过滤加监听」的停留,约 0.2 至 0.8 秒;原生控制器一直在收全部帧,被动项退化成一次字典查找。结果是完整循环从蓝牙的约 2 秒降到 0.58 秒,单体日志开启时 96 路新读数中位间隔 351 毫秒。另一个区别是它能挂到 EV-CAN 引脚上,而这在 ELM327 方案里做不到。

    想在 EV-CAN 上取数据需要什么条件?

    需要一根改线的 OBD 延长线,把母座的 6 与 14 分别接到插头的 13 与 12,或者直接通过引出全部 16 针的直通分流接头取 13/12 两针。两条总线同样是 500 kbit/s、11 位 ID,适配器终结跳线必须断开。软件侧把总线标识明确配置为 EV-CAN,强制以只听模式打开。

    数据库里存的是不是控制器原值?

    是。策略层不再改写电流与功率字段,保留的就是解码器产出的控制器原值;融合偏移、零位标定与放电钳零这些派生步骤全部落在带后缀的调整字段上,并用专门的字段注明经过哪些步骤。另有测试约束所有车型档案不得改写已报告字段。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/davidkarnowski-leaf-ze0/docs/SIGNALS.md(传输层事实、LBC 六组字节偏移、Car-CAN 被动帧、EV-CAN 引脚表与三档置信度分级)
    • 项目 01 —— public-can-dbc-datasets/additional/davidkarnowski-leaf-ze0/docs/CAN_TRANSPORT.md(原生 USB-CAN 的实现方式、接线与终结要求、只听模式作用域,以及验证与未验证清单)
    • 项目 01 —— public-can-dbc-datasets/additional/davidkarnowski-leaf-ze0/README.md(只读边界、安全说明与两个车辆档案的规模)

    免责声明

    本文所有 CAN ID、字节偏移、缩放系数与置信度标注均来自一台 2012 款 Nissan Leaf 的公开只读遥测档案,属社区逆向工程产物,不代表车辆制造商的官方数据,也未经独立实车复现。不同年款与市场区域的字节偏移可能发生变化,标注为待定的内容不得用于计量、交易或安全判定。文中提及的诊断请求仅用于读取,涉及动手接线时请先确认车辆断电状态与终结电阻,本文不构成任何维修、购买或行驶决策的唯一依据。

    更新记录

    • 2026-10-15 初稿

  • MX-5 ND2 的 CAN 状态表:221 个开放字段与三级证据标签

    结论:这份 Mazda MX-5 ND2 的 HS-CAN 档案,价值不在仪表盘能画几个数,而在于它把总线上的每一个可用位域分成三层:已确认信号、带 _maybe 或 _related 后缀的开放字段,以及已经证伪的负结果。截至 2026-09-30:参考日志里 1,977 个会变化的位中,未被 DBC 信号覆盖的从 1,311 位降到 1,124 位;跨全部日志仍有 221 个字段在半数以上日志里变化却没对上物理含义。同期修掉的最严重问题不是某个信号定义,而是分析工具本身的缺陷:50 份日志重算后,离合器百分比公式被修正为严格的 0.5 倍原始值。

    硬件链路与双份 DBC

    这台车的采集链路很朴素:OBD 口的高速 CAN(引脚 6 为 CAN_H、14 为 CAN_L,4/5 为地,16 为常电 12 V)经一根自制 Cat5e 短接线接到 CANable 2.0 方案的 USB 适配器,再由树莓派按 KeyState 信号自动启停 candump 日志。总线为 500 kbit/s,通道只有 can0;适配器上的 R120 终结跳线必须拨到 OFF,因为它只是挂在总线上的一个分支,不是总线端点。

    项目 取值 这么定的原因
    USB 适配器 CANable 2.0 方案,工作模式 USB ID 1d50:606f,烧录模式 0483:df11 两种固件可从 USB ID 区分;误入烧录模式需断电数秒重插
    总线与速率 高速 CAN 单通道 can0,500 kbit/s ND2 的 OBD 口只暴露这一条高速总线
    终结跳线 R120 OFF 车辆总线两端已终结,第三颗 120 欧电阻会拉偏显性电平
    主用 DBC 持续维护的 MX5ND 高速总线扩展版 所有修正与新增都落在这一份
    上游原始 DBC 保持原样不做改动 来自社区 6th-Gen Mazda 项目,许可为 CC BY 4.0,留作对照基准
    运行环境 树莓派只读根文件系统加内存叠加层 只有挂日志的 U 盘可写,系统改动需要两次重启

    命名约定本身就是这套档案的核心契约:没有后缀的信号表示已在多份日志上定量确认;_maybe 表示释义或刻度尚未锁定,只允许原始值透传;_related 表示它与某个已知量同源但不是同一物理量,不能当成简单镜像。此外还有一批信号被显式标记成「名字看起来正常但实际不可用」,例如社区 DBC 里两个修正过的加速度通道,工程上一律改用车身控制模块的同名加速度通道。

    已锁定的场位:可以直接引用的定义

    下表摘自状态表与开放字段表的确认部分,包含 2026-09-26 实车测试新增的条目。每一条都带具体的判定依据,而不是「看起来合理」。

    信号 位置 公式 / 单位 判定依据
    离合器行程 已有 DBC 通道 百分比等于 0.5 倍原始值 2026-09-29 重算后 R² 达 1.000,旧的 0.4665 倍加 0.56 被判定为分析工具假象
    实际发动机百分比扭矩 0x167 byte4 3.0242 倍原始值减 177.849,残差 4.07 个百分点 与标准 PID 0x62 两次行驶对照,2026-09-29 再校准 +2.6%
    制动压力 已有 DBC 通道 斜率 0.0012413、偏移 +32.7986 与真实 OBD 参考通道 R² 0.986,2026-09-29 再校准 +2.4%
    倒挡 0x445 bit 7 布尔量 倒车时横摆率符号反向,吻合度 99.7%,样本 1,195
    牵引力控制介入 0x211 bit 40 布尔量 5 份日志 6 段弯心出弯工况,同时伴随发动机扭矩下降
    强减速标志 0x211 bit 43 布尔量 6 份日志 16 段,触发区间 0.46 至 0.84 g,中位数 0.60 g;两次实车全力制动已确认
    ABS 介入 0x211 bit 42 布尔量 35 分钟日志中占 0.052%,恰好落在两段真实调制区间;同日返程对照组为 0.000%
    DSC 指示灯 0x415 2|2 3 表示闪烁 介入时的值与自检灯阶段的值不同;旧定义长期读作关闭,已不作为状态依据
    冷机指示 0x4F7 5|2 3 表示冷机 21 次记录全部在冷却液正好 55.0 °C 时切换
    车身模块供电电压 0x43F 19|10 0.016 V 每 LSB R² 0.998,且恒定低于直流变换器通道 0.68 V
    燃油消耗计数器 0x420 byte2 每升 4,834 个计数 两次加满共加油 34.4 升;与空气质量流量及空燃比的速率相关性达 0.99 至 0.999
    行驶距离计数器 0x420 byte1 每步 0.1992 米 对着里程表增量标定,覆盖 5 份日志;早期 0.209 是车速高估带偏的结果
    剩余保养里程 0x3D1 47|14 公里,原始值等于 173764 减里程公里数 34 份日志 R² 达 1.000000,取代了原先取值混乱的同名字段
    转向角速度幅值 0x082 byte5–7 12 位,约 0.5 度每秒每 LSB,符号在 byte7 bit0 28 份日志中对角度求导,相关性达 0.988 至 0.998
    方向盘绝对角度(EPAS) 0x86 0.1 倍原始值减 1600 度 剔除无效帧后 R² 达 0.9997 至 0.99995,残差 0.65 至 0.94 度
    交通标志限速 0x35F 4|7 km/h 取值 30 到 120,与实际行驶速度吻合,0 表示无限速
    平均油耗 0x4F3 byte4–5 大端 0.01 升每百公里,60 秒更新一次 加油复位时刻对齐,后续与计数器、距离推算相差 2.6 毫升

    最后一行值得单独说明:瞬时油耗在高速 CAN 上并没有找到独立的广播通道,实践中可以用燃油消耗计数器除以行驶距离计数器自行计算;这也是这套档案一贯的写法——公开发的是方法,而不是整车轨迹数据。相比之下,机油温度与爆震退角这两个关键量完全不在广播里:机油温度对应厂商私有的模式 22 自定义数据标识 0x1310,公式是首字节乘 256 加次字节、再除 100 减 40,标准 PID 0x5C 在这台车上不被支持,因此每 10 秒主动轮询一次;爆震退角对应同样是厂商私有的标识 0x03EC,公式是有符号 16 位整数除 512,两次独立对照分别为 73% 和 81% 逐位相同。

    仍然开放的字段:221 个未知字段的分类

    开放字段表把「还没有名字」的东西也做了归类。参考日志里 1,977 个会变化的位中,未被 DBC 覆盖的从 1,311 位降到 1,124 位;跨全部日志统计,至少在半数日志中会变化的开放字段共 221 个:32 个测量场(419 位)、42 个状态场、128 个标志位、10 个计数器、9 个校验或噪声。剩余测量位大多集中在前视摄像头报文(0x242 至 0x246)与信息娱乐报文(0x3D2、0x4FE)里。完整报文聚类还给出另一个量级:151 条报文中有 74 条带动态未分配字段,其中 36 条完全未知。

    字段 观察到的行为 假设 定向实验
    0x200 byte4–5(偏移二进制) 载荷域,与踏板、扭矩的相关性达 0.72 至 0.94 驾驶员需求扭矩或指示扭矩 用牵引力控制请求量做牛米锚点
    0x200 byte2–3 多数取负 40 至正 10,能量回收时更低,冷机可到负 49 附件损失扭矩 怠速下逐项开关空调与后窗加热
    0x20A byte2–3 疑似有符号;清理后重扫,燃料切断在 15 份日志中相关性中位数 0.71,目标空燃比 7 份为 0.69 混合气或滑行状态 追加标准 PID 对照
    0x4DA byte0/1/2/4 起步取 50,滑行时下降,加载荷后缓慢上升 催化器温度或氧存储模型 同时记录催化器温度 PID
    0x4FA byte2–3 清理后重扫:扭矩 11 份日志相关性中位数 0.65,节气门 6 份 载荷或暖机自适应 暂缺
    0x21F byte4–5 清理后重扫:进气歧管压力 6 份日志相关性中位数 0.70 歧管压力或载荷量 暂缺
    0x4D4 byte0–5 点火后归零,几分钟后出现;byte0 在高速公路上升,byte1 在堵车时增长 驾驶评分或经济监视器 按时刻拍照记录显示屏
    0x09B bit 2 每 50 到 100 秒出现约 10 秒;怠速期间电流通道下降约 100 个计数 大功率用电设备,疑为散热风扇 听或观察风扇并记录时刻
    0x4DF byte0、byte5 冷启动时紧贴合进气温度,相关性 0.980 与 0.994,但斜率分别为 1.44 和 2.24 K 每计数 变换器或电容温度,也可能是随温限值 红外测温仪对比

    字节扫描工具本身也经历过一次去伪:早期版本把单个初始化帧造成的常值(例如 0x596)和锚点起始段的自检灯突变误判成候选,清理后候选对从 105 降到 58。工具保留了两处已知局限:排名把 R² 四舍五入到两位,导致「胜出的布局」常常只是一段近似区间;缺少分辨率细化能力时,较短且拟合相差不大的子域可能取胜,工具会对这种情况打印更长的备选提示。目前仍列为中等候选的开放字段是 0x20A byte2–3、0x4FA byte2–3、0x200 byte6–7 与 0x21F byte4–5。

    负结果与方法陷阱

    这套档案最值得借鉴的一点是它把「找不到」也写成了结论,并附上对照组强度。下面几条在别的 ND2 项目里很容易被重复劳动。

    结论 证据 后果
    垂直加速度、侧倾角速率与俯仰角速率不在高速 CAN 上 25 组「CAN 加手机」平行记录,正对照组横摆率相关性达 0.95;夜间启动也未见照度调节电机的位置斜坡 只能转向 UDS 会话或中速总线去取
    车上没有可用的日历时钟通道 600 多个未知字段做过计数行为筛查,最佳结果是 0x4FE 上的一个 3.33 Hz 点火计时脉冲,周期在 3 份日志上的变异系数仅 0.000025,但每次点火归零 无实时钟期间的日志只能靠文件名加偏移量纠时,后期加装了 DS3231 时钟模块
    高频轮询会挤掉同总线上的第三方适配器 约 170 次每秒的请求压力下,对方 19% 至 29% 的请求先读到我们的应答,5% 至 18% 完全无应答,应用侧请求率跌到 0 检测到 0x7DF 或 0x7E0 上有外来请求时,把自身轮询限流到 5 轮每秒
    把某个字段当成挡位会出错 原先用 MT_Gear_Actual 取 7 表示倒挡,实际那是点火后约两秒的初始化值,倒车时该字段为 0 挡位判定改用 0x445 bit 7
    分析工具差错会让比例尺系统性偏小 重采样函数在拟合时对稀疏参考做了线性插值,压低了 R² 与斜率 离合器、制动压力、扭矩、电池电压的比例尺全部重新折算

    还有一处工程细节值得注意:胎压是这套档案里唯一不做周期性广播的量,必须靠主动 UDS 请求获取(请求头 0x720,模式 22,应答落在 0x728,DBC 里按 PID 做多路复用),轮询周期刻意设为 120 秒以压低总线占用。轮位映射直到 2026-09-26 才完整收口:轮胎 1 为左前、轮胎 2 为右前,轮胎 3 与 4 分别为左后、右后。

    常见问题

    为什么这份档案里还有 221 个字段没有名字?

    因为作者把「未解释但会变化」的字段也当作成果登记,而不是静默丢掉。统计口径是至少在半数日志里发生变化的开放字段,共 221 个,其中包含 32 个测量场(419 位)、42 个状态场、128 个标志位、10 个计数器和 9 个校验或噪声字段;剩余测量位集中在前视摄像头与信息娱乐报文里。

    总线上的车速为什么比 GPS 高约 2.9%?

    这是这台车本身的特性,不是解码错误:70 份带手机 GPS 的日志做比值得到 0.971,即总线车速偏高约 2.9%。它会影响加速、制动、极速与滑行阻力拟合中的绝对数值,因此状态表里专门留了一条提示:性能模型使用绝对值时要扣掉这个偏差。

    为什么要给部分信号加 _maybe 后缀而不是直接用?

    后缀是这套档案的证据等级标签。_maybe 表示释义或刻度尚未锁定,只能做原始值透传,不能换算成物理量;_related 表示与某个已知量同源但不是同一物理量的镜像。历史上确有反例:某个 15 位扩展场曾被引申为非线性,后来发现是取错位导致的。

    第三方 OBD 适配器为什么会被本项目的轮询挤掉?

    因为快轮询组在 0x7E0 上以约 170 次每秒发请求,与手机端适配器共用同一个报文头,导致对方 19% 至 29% 的请求先读到我们的应答、5% 至 18% 完全得不到应答,应用侧请求率从约 19 次每秒掉到 0。修复办法是检测到外来请求时把自身轮询限流到 5 轮每秒。

    高速 CAN 上有没有可以直接对时的日历时钟通道?

    没有。600 多个未知字段做过专门的计数行为筛查,最佳候选是 0x4FE 上的一个 3.33 Hz 点火计时脉冲,周期在 3 份独立日志上的变异系数只有 0.000025,但它每次点火都会归零;另一个看起来像的量被证明是累计工作时间,熄火即停。因此无实时钟期间的日志只能靠文件名加偏移量纠时,后续加装了 DS3231 时钟模块。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/mainbrain-mx5-nd2-can/docs/status/can-bus.md(硬件链路、双份 DBC 策略、可解码信号分组、开放点与纠时机制)
    • 项目 01 —— public-can-dbc-datasets/additional/mainbrain-mx5-nd2-can/docs/status/can-open-fields.md(已确认信号表、实车测试新增项、开放字段分类与报文聚类数量)
    • 项目 01 —— public-can-dbc-datasets/additional/mainbrain-mx5-nd2-can/README.md(仓库定位、许可证与刻意不公开的内容说明)

    免责声明

    本文所述的 CAN ID、字节位定义、缩放系数与验证状态均来自一台 Mazda MX-5 ND2 车主自行记录的公开档案,属社区逆向工程产物,不代表车辆制造商的官方数据,也未经独立实车复现。不同年款、市场区域与 ECU 软件版本可能存在差异;标注为开放或假设的内容不得用于计量、安全判定或控制用途。本文不构成主动诊断、刷写或执行器操作的授权。

    更新记录

    • 2026-10-15 初稿

  • Ram ProMaster 2022 三总线地图:125 kbit/s 实测与 16 个 UDS 端点

    结论:2022 Ram ProMaster 的被动 CAN 工作必须按三条独立总线规划:C-CAN(OBD 引脚 6/14,500 kbit/s)、B-CAN / CAN IHS(DLC 引脚 3/11,125 kbit/s 实测)和 CAN CH(DLC 引脚 12/13,500 kbit/s 实测)。这批记录里登记的 UDS 端点共 16 个,全部走 29-bit ISO-TP normal-fixed 寻址。真正决定能不能落地的不是「能读多少帧」,而是三条边界:OEM 文档里的 CAN-B 50K 说法不能用来配波特率;B-CAN 上 0x760 的被动里程只被认可为中控模块的本地副本;0x1F4 字节 4 高位已被完整复核驳回,不能当当前挡位。

    三条总线的速率、引脚与证据来源

    三条总线里只有 C-CAN 的 500 kbit/s 同时有 OEM 文档和实测两个来源,另外两条的通信参数都来自本机实测。B-CAN 是最容易被带偏的一条:该车型的 COMMUNICATION / CAN BUS DESCRIPTION 明确把 CAN-C 写成 500K、CAN-B 写成 50K,但这份文档用的是 C-1 到 C-8 与 BH 这套分支命名,与 DLC 图的 CAN C/CH/IHS 命名体系并不对齐。2026-07-20 在引脚 3/11 上的被动捕获给出 125 kbit/s 且零 RX 错误,因此以实测为准,文档里的 50K 只保留为线索。

    总线 速率 引脚 接入方式 证据边界
    C-CAN / HS-CAN 500 kbit/s OBD 6/14 经 SGW 旁通的内部 C-CAN tap,Board A CAN1 角色 动力总成与诊断;法规要求的 OBD-II Mode 01 PID 不经此旁通
    B-CAN / CAN IHS 125 kbit/s(实测) DLC 3/11 标签转接线的 B-CAN 支路,Board A CAN2 角色 舒适与车身;派生配置的 8 个目标里 4 个已应答,另外 4 个仍超时
    CAN CH(第二条高速总线) 500 kbit/s(实测) DLC 12/13 灰色 12/13 适配器,Board B CAN1 角色 被动捕获零收发错误,同时完成 ABS、EPS、HALF、ORC 的身份读取

    永久安装用两块双通道适配器承载三个角色(gs_usb,USB VID:PID 1d50:606f):C-CAN 与 B-CAN 分别落在第一块的 dev_id 0(CAN1)和 1(CAN2),CAN CH 在第二块的 dev_id 0,剩下那一路保持 down。四块板载红色终结跳线全部拆除,因为三个 tap 接入的是车内已经终结的网络;虽然适配器支持 CAN FD,暴露出来的三条总线一律配成 classical CAN、FD off。角色由 USB 序列号加 dev_id 解析,Linux 的 canN 编号只是枚举顺序,不作为身份依据。

    16 个 UDS 端点的总线分布与行为差异

    下表是已登记的端点。它们的 DID 命名空间按 ECU 隔离——雷达的某个 DID 和其他模块的同名 DID 没有任何关系,所以不要把这些行合并成一张「整车 DID 表」。

    键 模块 总线 TX → RX 已记录的行为
    radar_acc Bosch ACC 雷达 C-CAN 18DA2AF1 → 18DAF12A 扫描途中切断点火仍会 ACK;车速只能走内部 DID,OBD PID 不可用
    pcm 动力总成模块(3.6L) C-CAN 18DA10F1 → 18DAF110 固定 DLC 8 零填充是已知可用配方;遗留会话请求已两次验证
    rf_hub RF Hub(TPMS / RKE) C-CAN 18DAC7F1 → 18DAF1C7 点火 OFF 仍应答,属 KL30 常电,是驻车取电压的入口
    tcm ZF 948TE 变速箱 C-CAN 18DA18F1 → 18DAF118 读取 211B–2132、2136–213E 与 026B 均为正向
    shifter 电子换挡器 C-CAN 18DA1FF1 → 18DAF11F 身份已读取,可用于模块级区分
    bcm_ccan 车身控制模块 C-CAN 端点 C-CAN 18DA40F1 → 18DAF140 执行类请求受电源模式门控,钥匙 OFF 常见否定响应
    cluster 组合仪表 IPC C-CAN 18DA60F1 → 18DAF160 已把若干仪表符号逐项绑定到具体 DID
    telematics 远程通信盒模块 C-CAN 18DAC6F1 → 18DAF1C6 唯一一个车上后台诊断客户端从不查询的已注册模块
    ics_bcan 中控面板 B-CAN 18DA85F1 → 18DAF185 里程 DID 2001 以 0.200 Hz 轮询,收到 302/302 正向应答
    uconnect_bcan 主机 / 显示屏 B-CAN 18DA87F1 → 18DAF187 身份与阶段性故障码家族匹配
    climate_bcan 自动空调模块 B-CAN 18DA98F1 → 18DAF198 所选变体未通过校验,其仪表标签不可直接套用
    emcm2_bcan 中控菜单 / 音量按键 B-CAN 18DAD9F1 → 18DAF1D9 设备子类型匹配加上 OEM CAN-IHS 角色
    abs_canch ABS / ESC CAN CH 18DA28F1 → 18DAF128 历史偶发故障码,最后一次测试通过
    eps_canch 电动助力转向 CAN CH 18DA30F1 → 18DAF130 无故障报告
    half_canch 前视摄像头辅助模块 CAN CH 18DA31F1 → 18DAF131 同一身份还可通过网关在 C-CAN 侧被动读到
    orc_canch 乘员约束控制器 CAN CH 18DAC0F1 → 18DAF1C0 无故障报告

    网关路由是这张表的隐藏前提:在 C-CAN 上发给 CAN CH 目标的物理请求会被网关逐帧转发,中位延迟 0.31 ms(51 次交互,落在 0.23–2.78 ms 区间),网关还会在 CAN CH 侧替换成自己的流控帧;ECU 应答被复制回 C-CAN 时目标字节被改写为 F2,所以 C-CAN 上看到的 18DAF2xx 是网关副本,不是第二个诊断仪。另有一条与时统高度相关的事实:车上存在一个后台诊断客户端,会在外部 F1 流量静默 92 秒以上后才启动只读扫描,一旦外部轮询恢复便在 1 至 2 秒内终止;做被动相关性分析时必须把这些窗口单独标出来。

    C-CAN 广播帧:可以直接进遥测的那部分

    下面这张表只收已经 allowlist 进遥测的信号。入选判据是「是否与同类 DID 做过定量化对照」,而不是看上去是否合理——这也是为什么同属 0x100 的扭矩字节还停在候选名单里。

    ID 字节 / 位 解码式 含义 验证强度
    0x2EF 帧存在性 无 点火 ON 门控 已验证;帧计数门控曾失败,存在性门控有效
    0x41A byte0 4.0 + raw × 0.05 V 系统电压 已验证,含驻车唤醒场景
    0x41D byte2 raw × 4 kPa 机油压力 与内部 DID 逐字节镜像,怠速 733/733 样本 R² 0.9998047
    0x2ED byte0 raw − 40 °C 冷却液温度 怠速 R² 接近 1.0,负载工况 1,351/1,356 样本
    0x0FC bytes0–1 BE u16 屏蔽低两位后除以 4 rpm 发动机转速 负载工况 1,351/1,356 样本 R² 0.9999794
    0x0F0 bytes3–4 高 11 位 右移 5 位后减 500 Nm 变速箱请求扭矩上限 236 样本 R² 0.99999986
    0x1F7 打包 17 位:byte0 bit0 加 bytes1–2 raw / 32 rpm 变速箱输出轴转速 低于 16 位边界 R² 0.99999578
    0x1F7 byte3 有符号 i8 0.375 × raw + 57 °C 变速箱油温 多次 RMSE 低于 0.89 °C,另含一次预热反例
    0x1F7 bytes4–5 BE u16 raw / 2 rpm 涡轮 / 输入轴转速 盲区复原 2,014 样本 R² 0.99996705
    0x101 byte0 bit0、byte1 左移 3 位、byte2 高 5 位 raw / 16 km/h 瞬时车速 由变速箱侧证据裁定为 /16,否决早期的 /32 候选
    0x0EE bytes0–1 BE u16 raw / 128 km/h 独立高分辨率车速 169 样本 R² 0.9990360
    0x5A0 byte3 / byte2 mph 与四舍五入后的 km/h ACC 设定车速 两字节在 1 km/h 内一致才允许发布
    0x0E0 byte0 raw mph 限速标志识别值 DLC 4、10 Hz,读数为 0 时不发布

    一处值得单独说明的工程约定:2026-07-19 的离线分析确认 0x101 的 bytes0–2 是瞬时车速,而不是旧笔记里写的里程累加器。两条日志分别给出 539,982 帧(约 99.998 Hz,原始值域 0–2153)和 224,137 帧,帧内 CRC-8/SAE-J1850 在 224,137 帧上全部匹配。0x0EE 独立跟踪同一物理量,回归关系约为 0x0EE_raw ≈ 8.0099 × speed_raw − 11.6,移动样本相关系数 0.9999919。两者 integrals 在另一条记录的差值为 0.007 km,证明的是彼此关系,不是物理量纲。

    被驳回的候选与还没闭环的边界

    这批记录的价值有一半来自「已经否掉什么」。下面四项在别的项目里很容易被当成常识采用,但在这里都有明确的驳回或降级理由。

    候选 结论 决定性证据
    0x1F4 byte4 高位 = 当前挡位 驳回 完整复核扫过 4,187,827 帧得到 42,574 对有效轴速比样本;开发段里 21,725 对取值为 7、6,548 对取值为 8,但两组中位数都是 0.699,而 OEM 八挡为 0.580
    B-CAN 0x760 = 整车里程 仅认可为中控本地副本 对 302 次内部里程应答,冻结公式为「十分之一公里值等于 10 倍被动原始值」;174 次邻近匹配 p95 误差 0 计数,但与仪表仍有约 11.6 mi 差
    0x417 bytes2–3 = 变速箱油温 驳回 两次独立盲驾都不支持该假设,最佳 R² 仅 0.44824 且斜率为负
    0x100 bytes0–1 = 实际扭矩 保留候选 2,014 样本盲区分段里 overrun 段 R² 仅 0.63031,不能当作实际扭矩发布

    里程那条还要多说一句:2026-08-30 的三总线行驶记录里,中控计数器在首个 25 分钟窗口增加 23.0 km(14.291537 mi),独立车速积分为 14.365140 mi,相差 0.512%;跨钥匙周期的第二次比对里,中控增加 116.817 mi、积分增加 116.511358 mi,相差 0.262%。这两组数字证明的是相对刻度,不是绝对基线。

    正面闭环的案例也有一个,就是机油寿命。在驻车、点火 ON、发动机 OFF 的条件下,向 PCM 发出一条固定 DLC 8 零填充请求 03 22 21 85 00 00 00 00,物理地址为 18DA10F1 → 18DAF110,收到正向应答 62 21 85 52,无符号字节换算后为 82%。这次读取没有会话切换、没有 TesterPresent、没有流控帧,C-CAN 发送计数仅从 127233 增加到 127234,三条总线的收发错误计数保持为零。存在 limits 的工程约定是:参数超出 0 到 100 时置为不可用,而不是裁剪成百分比。

    常见问题

    OEM 文档写明 CAN-B 是 50 kbit/s,为什么实际按 125 kbit/s 配置?

    因为那份文档使用的是另一套分支命名(C-1 到 C-8 与 BH),与诊断口的 CAN C/CH/IHS 命名体系并不对齐,其 50K 描述更可能指向另一条内部支路。2026-07-20 的被动捕获在引脚 3/11 上测得 125 kbit/s 且零 RX 错误,2026-07-25 的灰色适配器捕获又独立在引脚 12/13 上确认 500 kbit/s,两者都以实测作为配置依据。

    B-CAN 上的 0x760 里程值能不能替代仪表里程?

    不能。它与中控内部计数器的关系式已经冻结并复制过:十分之一公里值等于 10 倍被动原始值,174 次邻近匹配的 p95 误差为 0 计数、平均绝对误差 0.1724 计数;相对刻度也经两次独立检验,分别为 0.512% 与 0.262%。但它与仪表的绝对读数仍相差约 11.6 mi,因此只能当中控本地副本使用,不能据此摘掉仪表上的星标。

    为什么读取 PCM 机油寿命不需要切换诊断会话?

    2026-10-03 的实车验证显示,在驻车、点火 ON、发动机 OFF 条件下发出一条零填充请求,就拿到了正向应答 62 21 85 52。但这不能推广成「默认会话支持 22 读取」的普遍结论:继承会话没有被正面识别,且同一 PCM 的其他 DID 曾明确要求遗留会话。工程上的处理是沿无会话切换的配方发一次,被拒绝就上报需要会话并就此停止。

    0x1F4 的字节 4 高位能不能直接当成当前挡位?

    不能。2026-09-22 的两秒样本曾把它对上七挡比值 0.699,但完整复核扫过 4,187,827 帧、取到 42,574 对有效样本后,出现了大量比值稳定在 2.842、1.909、1.382 而该位仍为 7 的情形;开发段里取值为 8 的 6,548 对,中位数同样是 0.699 而不是八挡的 0.580。这些都是稳定工况下的反例,不是换挡瞬态。

    C-CAN 上出现 18DAF2xx 帧意味着什么?

    意味着那是网关把 CAN CH 上某个 ECU 的应答复制回 C-CAN 的副本,目标字节从 F1 改写为 F2,并不是第二个诊断仪在发话。转发延迟中位 0.31 ms,51 次交互落在 0.23–2.78 ms 区间,网关还会在 CAN CH 侧替换自己的流控帧。做被动归因时不要把它算成外部工具的流量。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/jakeiwen-promaster-2022/docs/bus-map.md(三条总线的速率、引脚与证据,16 个 UDS 端点,以及三条总线的广播帧表)
    • 项目 01 —— public-can-dbc-datasets/additional/jakeiwen-promaster-2022/findings/2026-07-19_ccan_drive_signal_analysis.md(0x101 与 0x0EE 车速字段的定位与两条日志的样本量)
    • 项目 01 —— public-can-dbc-datasets/additional/jakeiwen-promaster-2022/findings/2026-08-30_three_bus_drive_odometer_validation.md(中控里程与被动 0x760 的冻结公式及跨钥匙周期复制)
    • 项目 01 —— public-can-dbc-datasets/additional/jakeiwen-promaster-2022/findings/2026-09-22_gear_and_oil_life_offline.md(挡位候选驳回的完整复核数据与机油寿命闭环)

    免责声明

    本文所有波特率、引脚、CAN ID、字节位定义与转换系数均来自一台 2022 款 Ram ProMaster 的公开逆向记录,未经独立实车复现;不同年款、市场区域与 ECU 软件版本可能存在差异,标注为「已验证」的结论仅指在该车当时的捕获条件下成立。本文不构成对任何车辆执行主动诊断、刷写或执行器操作的授权,涉及安全相关用途请以实际测量与制造商资料为准。

    更新记录

    • 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 初稿

  • StreetDrone 自动驾驶平台的 CAN 接口:36 报文与 184 信号

    结论:StreetDrone 的 CAN 接口不是一份车型协议库,而是一条把 CAN 帧翻译成 ROS 话题的解码链。它的核心定义 databases/candatafile.dbc 有 36 条 BO_ 报文与 184 条 SG_ 信号,把平台/客户控制、诊断、GPS 与惯导报文混在同一份文件里;上游没有确立任何量产车型的年款适配,也没有给出独立的信号精度证据。真正决定它能不能进产品的是另外三件事:VERSION 字符串含异常字符、文件里挂着一条长度为 0 却带大量信号的 Vector 伪报文、以及仓库快照中找不到许可证文件。

    ROS 话题里的三层数组:报文名、信号值与单位分开传

    这个 ROS 节点订阅 /received_messages,为每条 CAN 报文在 /sd_state_msgs 上发布一个话题。它不做信号侧的强类型封装,而是把解码结果摊平成三层并行数组:报文层给出 message_names 与 message_ids,信号层在每个 message_data 元素里再给出 signal_names、signal_data 与 signal_units。这种结构的好处是新增报文不需要改消息定义,代价是取用方必须自己做索引对齐。

    层级 字段 类型 取值含义
    帧头 std_msgs/Header header uint32 seq / time stamp / string frame_id 标准 ROS 头,用于对齐时间戳
    报文层 string[] message_names 字符串数组 与 DBC 报文名一一对应
    报文层 uint16[] message_ids 无符号 16 位数组 CAN 报文 ID,索引查找的入口
    报文层 sd_state_msgs/Message_Data[] message_data 结构体数组 与 message_ids 同下标对齐
    信号层 string[] signal_names 字符串数组 单条报文内的信号名
    信号层 float64[] signal_data 双精度数组 已按 DBC 缩放解出的物理值
    信号层 string[] signal_units 字符串数组 信号单位,与信号值同下标

    看清楚这张表才能理解后面所有取数操作:没有 message_data.wheel_speed 这种字段,只有「先按 ID 找到下标,再用下标取三个数组」这一条路。单位随值一起下发,说明物理量换算已经在节点内完成,下游只负责消费。

    36 条报文与 184 条信号:控制、诊断与定位混编

    36 条报文除以 184 条信号,平均每条约 5 条信号,说明这份 DBC 里既有少量高位复用的控制/状态帧,也有信号数很少的稀疏帧。上游 README 明确写到,每个报文的细节都在 candatafile.dbc 里,并给出了大小端帧布局的外部说明链接,但并未逐条给出标定方法。因此把它当作「平台内部的解码契约」是合适的,当作「某款量产车的信号定义」则超出证据范围。

    内容类别 是否在文件内 证据边界
    StreetDrone / 客户控制报文 包含 未给出执行机构分辨率与时延证据
    诊断类报文 包含 未给出会话类型与服务号清单
    GPS 报文 包含 未给出坐标系、更新率与精度
    惯性传感器报文 包含 未给出零偏、量程与标定方法
    量产车型 / 年款适配 不包含 README 未确立任何 make / model / year
    信号精度的独立证据 不包含 README 中没有独立精度验证记录
    常用报文速查表 另有文件 指向 message_index.pdf,不在 DBC 内

    从报文 ID 到信号值:索引查找模型

    上游给出的示例是取轮速报文:在 message_ids 里线性查找等于 280 的下标,命中后用同一个下标去取 message_names 与 message_data,再遍历该元素的三个数组打印信号名、值与单位。这里的 280 是 README 中的十进制写法,换算成十六进制为 0x118。整个模式没有任何 ID 到结构体的映射表,ID 就是唯一的键。

    复现解码链:从 rosbag 回放到话题抽样

    上游给出的运行方式是双终端:一端回放 can-interface/bags 下的 2020-02-28-12-19-00.bag(加 -l 循环播放),另一端构建并启动解码节点。这样无需实车即可验证 DBC 与节点的对齐情况。

    步骤 操作 产物 / 判据
    1 构建 cd can-interface/catkin_ws 后 catkin_make 工作空间编译通过
    2 加载环境 source devel/setup.bash 自定义消息可被 rosmsg 找到
    3 核对结构 rosmsg show sd_state_msgs/SD_State_Msgs 打印出三层数组定义
    4 回放数据 cd can-interface/bags 后 rosbag play -l 2020-02-28-12-19-00.bag 模拟 /received_messages 输入
    5 启动节点 roslaunch sd_state_msgs sd_state_msgs.launch 开始发布 /sd_state_msgs
    6 抽样验证 rostopic echo -n1 /sd_state_msgs 单帧三层数组,检查单位串是否为空
    7 取指定报文 遍历 message_ids 命中 280,取 message_data[i] 轮速信号名、值与单位

    接入自写节点时还要补两处依赖声明:CMakeLists.txt 的 find_package(catkin REQUIRED COMPONENTS ...) 里加入 sd_state_msgs,package.xml 里同时写 build_depend 与 exec_depend。上游对 kinetic 版本的说明还给了排错路径:删掉 devel 与 build,rosdep install --from-paths src --ignore-src 后重新 catkin_make。

    入库前必须核对的三处风险

    这份 DBC 的问题不在信号数量,而在文件本身的规范性。它同时触发了「严格解析器可能直接拒绝」与「分发权利不可判定」两类风险,且两者都不能靠读信号定义发现。

    风险点 具体表现 处理动作
    VERSION 异常字符 版本串含非常规字符,部分解析器在文件头即报错 先用宽松解析器预检,必要时手工规整版本行
    零长度伪报文 存在一条长度为 0 的 Vector 独立信号伪报文,却挂载大量信号 单独剥离该条定义,再校验其余报文的长度一致性
    无许可证文件 检查的仓库快照里没有许可证文件,再分发权利不可判定 仅作离线研究副本,对外分发前先确认权利
    车型适配缺位 未确立量产车型与原厂 CAN 装配关系 只作平台内解码参照,不写进车型协议档案

    常见问题

    这份 DBC 能直接解析量产车型的 CAN 吗?

    不能。上游明确没有确立任何量产 make / model / year,也没有原厂 CAN 装配关系。36 条报文混合了 StreetDrone 与客户的控制报文、诊断报文、GPS 与惯导报文,属于平台自有的解码契约,只能作为同类平台的参照。

    为什么 36 条报文里会混进 GPS 和惯导报文?

    因为这份 DBC 描述的是整套平台状态话题,而不是某个 ECU 的收发矩阵。自动驾驶平台把定位与惯量传感器也挂到同一条解码链上,是常见的工程做法;代价是这份文件不能按「车型总线」口径去切分或复用。

    严格解析器报错时第一步该查什么?

    先查 VERSION 行与零长度伪报文这两处。文件里有一个含非常规字符的版本串,部分解析器在读文件头阶段就会失败;另有一条长度为 0 却挂载大量信号的 Vector 独立信号伪报文,会让长度校验失败。建议先剥离该条定义,再校验其余报文。

    在 ROS 里取轮速报文 280 的信号值要几步?

    核心是两步:先在 message_ids 里线性查找等于 280 的下标(280 是十进制写法,换算为 0x118),再用同一下标取 message_data 元素的 signal_names、signal_data 与 signal_units。前提是已完成 catkin_make、source devel/setup.bash,并在 CMakeLists.txt 与 package.xml 中声明 sd_state_msgs 依赖。

    这份素材可以随产品分发吗?

    不建议在未确认权利前分发。检查的仓库快照里没有找到任何许可证文件,再分发权利不可判定。当前定位是私有离线研究副本,若确需分发,应先向上游确认授权,并保留原始文件用于离线分析。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/hngu97-streetdrone-can/SOURCE.md(36 条 BO_ 与 184 条 SG_、VERSION 异常、零长度伪报文与许可证状态)
    • 项目 01 —— public-can-dbc-datasets/additional/hngu97-streetdrone-can/UPSTREAM_README.md(ROS 消息结构、轮速报文 280 示例、kinetic 运行与排错步骤)

    免责声明

    本文所述 DBC 与解码链均来自公开仓库的说明文件,未经过独立实车验证,不代表车辆制造商或平台方的官方数据。不同车型年款、市场与固件版本可能存在差异。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • 五菱 Air EV 的 CAN 信号:115 个 ID 与一处未解的字节冲突

    结论:五菱 Air EV 的这份 CAN 嗅探研究给出了一条可复现的低成本采集链路和 115 个唯一 ID 的规模,但三个主参数的字节映射存在未解决的冲突:README 把油门踏板记在 0x17D 的字节 4 与 6,而随车保留的处理脚本把 0x155 标为踏板,并额外加了一个 0x1E5 电池电压。仓库里既没有 DBC、也没有原始 CSV 抓包和字节级换算公式,因此这些 ID 只能当作候选,不能当作已验证的解码表。

    一条 500 kbit/s 的被动采集链路是怎么搭起来的

    整套采集是被动的:ESP32 DevKitC 加 MCP2515 收发模块,走 OBD-II 接口的 6 脚(CAN High)与 14 脚(CAN Low),固件以监听模式运行,不做任何主动请求。晶振配置为 8 MHz,CAN 速率设为 500 kbit/s。一次约 49 分钟的道路测试记录下 115 个唯一 CAN ID。

    抓下来的原始记录以分号分隔的 CSV 落地,Python 侧用 pandas 读取后,把 Data_Raw 这一列按空格拆成 Byte_1 到 Byte_8 八列,再按 CAN ID 做映射标注,最后导出成一份包含全部 115 个 ID 的 Excel。未被映射表命中的 ID 统一标记为「尚未识别」。

    环节 配置项 取值 说明
    控制器 MCU / 收发器 ESP32 DevKitC + MCP2515(8 MHz) 固件使用 mcp_can 库,监听模式
    物理接入 OBD-II 引脚 6 脚 CAN High,14 脚 CAN Low 直接挂诊断口,无需破线
    总线速率 标称波特率 500 kbit/s 由固件按 8 MHz 晶振配置
    采样规模 时长与 ID 数 约 49 分钟,115 个唯一 ID 覆盖多种行驶工况
    落盘格式 CSV 分隔符 分号 读取时需显式指定分隔符
    后处理 字节拆分 Data_Raw 拆为 Byte_1–Byte_8 缺失字节填占位符
    产出 汇总文件 REKAP_TOTAL_PERCOBAAN_DUA.xlsx 115 个 ID 全部进入汇总表

    三处冲突:油门到底在哪个 ID

    把 README 的结论和保留下来的处理脚本并排看,会发现两套标注并不一致。差异集中在踏板这一项上,另外脚本多出一个电池电压条目,而 README 完全没有字节级换算公式。

    参数 README 给出的 ID 与字节 处理脚本给出的 ID 一致性判断
    加速踏板位置 0x17D,字节 4 与字节 6 0x155 冲突,未解决
    电机转速 0x16E,字节 5 与字节 7 0x16E ID 一致,无换算公式
    电池电流 0x16C,字节 5 与字节 6 0x16C ID 一致,无换算公式
    电池电压 未提及 0x1E5 脚本独有,无字节定义

    需要强调的是:脚本里的映射只是一张 ID 到标签的字典,用于给每一行打标注,并不包含缩放因子、偏移量或字节序。也就是说,即便 ID 确定,也无法据此直接得到工程量。README 里那句「字节 4 与 6」是目前唯一出现的字节位置线索,且同样没有给换算规则。

    为什么这个冲突不能靠猜来解决

    两个 ID 指向同一类信号,在实际解码里是两种完全不同的后果:如果 0x155 才是踏板而 0x17D 是别的量,那么按 README 取数会得到一条看似有变化、实则无关的物理量曲线,而且因为它随工况变化,很容易被误判为「已经解出来了」。判定办法只有一条——同时抓两个 ID 的原始数据,踩踏板做阶跃,看哪个字节随踏板线性跟随。

    证据边界:仓库里缺什么

    这份材料的定位是学术研究记录,不是可用的解码资产。判断它能用到什么程度,关键看缺失项,而不是看已给出项。

    缺失项 对使用的影响 建议处理
    DBC 文件 无法用标准工具直接解码全部 115 个 ID 自建候选 DBC 时把每个 ID 标为待验证
    原始 CSV 抓包 无法复算、无法复核字节位置 自行重采,保留原始文件
    字节级换算公式 ID 确定也得不到工程量 用独立参考源做标定后再写公式
    具体配置年款 无法判断结果能否迁移到同车型其他批次 记录自己的车辆配置,不跨车套用
    独立标定记录 三个主参数的精度无从评估 用仪表读数或 OBD 标准 PID 交叉验证
    明确的再分发许可 README 只要求署名,未给出许可证 仅作私有离线研究副本,商用前先确认权利

    仓库里的两份 DOCX 论文和若干绘图图像并未随本次归档保留,因此论文中是否给出了更完整的推导,无从判断。这也是为什么「未解决」比「错误」更准确:现有材料不足以判定谁对。

    常见问题

    这份研究能给五菱 Air EV 出一份可用的 DBC 吗?

    不能。仓库里没有 DBC,也没有原始 CSV 抓包与字节级换算公式,只有 115 个 ID 的规模和三个(实为四个)候选标签。它能提供的是采集方法和候选 ID 清单,DBC 必须由使用者自行采集、标定后重建。

    油门踏板到底应该用 0x17D 还是 0x155?

    目前无法确定。README 把踏板记在 0x17D 的字节 4 与 6,保留下来的处理脚本却把 0x155 标为踏板,两者冲突且未解决。可靠的判定方式是同时抓这两个 ID,踩踏板做阶跃,看哪个字节随踏板线性跟随。

    500 kbit/s 是这个车型 OBD 口的确定波特率吗?

    它是这次采集所用的配置值,不是车型的官方标称值。固件按 8 MHz 晶振把 CAN 速率设为 500 kbit/s 并成功收到 115 个 ID,说明该配置在本车可用;其他批次或年款是否一致未做验证。

    为什么脚本映射出四个 ID 却没有任何换算公式?

    因为脚本里的映射只是一张 ID 到标签的字典,作用是在汇总表里给每行打标注并统计 ID 分布,本身不做物理量解码。字节位置线索只在 README 中出现过一次,同样没有给出缩放因子与偏移量。

    这份材料可以再分发或放进产品吗?

    不建议直接使用。检查的仓库快照中没有找到任何许可证文件或 SPDX 声明,README 只提出署名要求,而署名要求并不构成明确的再分发许可。目前应按私有离线研究副本处理,商用前须确认权利。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/ilham511-wuling-air-ev/README.md(500 kbit/s 被动采集、115 个 ID、三个主参数的候选 ID 与字节)
    • 项目 01 —— public-can-dbc-datasets/additional/ilham511-wuling-air-ev/Processing_Data.py(ID 到标签的映射字典、字节拆分逻辑与汇总导出流程)
    • 项目 01 —— public-can-dbc-datasets/additional/ilham511-wuling-air-ev/SOURCE.md(快照版本、许可证状态与证据边界说明)

    免责声明

    本文所述 CAN ID、字节位置与采集参数均来自公开仓库的说明文件与脚本,未经独立实车验证,且 README 与脚本之间存在未解决的标注冲突。车辆配置年款、市场与 ECU 版本可能导致差异。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • TrevCAN 树莓派节点:347 条报文定义与 460 种载荷布局

    结论:TrevCAN 的树莓派节点是一个纯发送端:它只读 SocketCAN 原始帧,按 100 ms、最多 500 帧一批推给服务端的 /ingest/can,本身不做任何 DBC 解码,也从不向物理总线发帧。它的仿真模式覆盖 7 份 DBC 里的全部 347 条报文定义、共 460 种载荷布局,但生成的是合成覆盖数据,不是物理自洽的车辆模型。

    车端与服务端的职责切分

    这个项目的设计取舍非常明确:车上那台树莓派上不需要 TREVCAN-Explorer 检出目录里的任何文件。DBC 解码、InfluxDB 写入与前端全部跑在车下的服务器上。车端只剩一个瘦发送器,负责把 SocketCAN 上收到的原始帧打包送走。

    这样切分带来两个直接后果。好处是车端依赖少、故障面小,换 DBC 不必动车上的部署;代价是车端无法做本地语义判断——它不知道自己送走的是电池单压还是转向角,任何与信号含义相关的逻辑都必须放到服务端。

    环节 运行位置 职责 是否接触总线
    SocketCAN 帧读取 树莓派 从 can0 读取原始帧 只收不发
    批处理与 HTTP 上报 树莓派 按 100 ms、最多 500 帧一批推到 /ingest/can 否
    DBC 解码 车下服务器 用已启用且已排序的 DBC 解码收到的帧 否
    InfluxDB 与前端 车下服务器 落库与展示 否

    启动前必须先做的一件事

    按车辆实际波特率配置并拉起 can0,再启动发送器。这一步在文档里被放在显眼位置,而且与另一条约束互为补充:发送器只接收 CAN 流量,不会向物理总线发送。也就是说它天然是只听(listen-only)的,不可能因为软件缺陷把帧打进整车网络——前提是 can0 本身配置正确。

    如果车上用的不是原生 SocketCAN 设备,可用 --interface 与 --channel 选择其它 python-can 接口,无需改动代码。

    仿真模式:347 条报文与 460 种布局

    带 --simulate-rate 启动时,程序不会打开任何 CAN 接口,也绝不在物理总线上发送。它用 cantools 把 trevcan_pi/dbcs/ 里 7 份当前 DBC 的定义编码成原始帧,全部 347 条报文定义都会被覆盖,包括每一个多路复用分支——完整一轮是 460 种载荷布局。

    460 对 347 这个差值本身就是信息:平均每 3 条报文里就有 1 条带多路复用,多出来的 113 种布局全部来自复用分支。如果解码端只按报文 ID 取第一个布局,这部分帧一定会被解错。

    参数 取值 含义
    覆盖报文定义 347 7 份 DBC 的全部报文定义
    每轮载荷布局 460 含每个多路复用分支
    速率语义 总帧率 --simulate-rate 是每秒总帧数,不是每条报文的帧率
    20 Hz 一轮耗时 约 23 秒 460 帧除以 20
    460 Hz 一轮耗时 约 1 秒 不含处理开销

    数值在后续轮次中会变化,但始终落在每个信号在 DBC 内可表示的上下限之内;枚举值则在已定义的值之间轮转。命令与配置类报文只作为网络遥测计入,不代表车辆真的执行了这些命令。

    交付语义:批处理、队列与不保证送达

    这一段是整个项目里最需要在使用前读明白的部分。它把「尽力而为」写得很具体,而不是含糊带过。

    机制 取值 溢出或失败时的行为
    批处理窗口 100 ms 窗口内最多打包 500 帧
    队列容量 10,000 帧 溢出时丢弃最旧的帧,并在日志中计数
    HTTP 批次失败 不重放 失败或不确定的批次不做重放
    中断恢复 自动恢复传输 但中断期间无磁盘积压,也无送达保证
    停止方式 SIGTERM 与 Ctrl-C 未发送的帧可能被丢弃

    把这些数字连起来看,缓冲深度是可以估出来的:批处理窗口为 100 ms、每批上限 500 帧,对应每秒 5000 帧的处理上限;队列 10,000 帧约等于 2 秒的缓冲。总线负载一旦持续超过这个量级,丢的就是最老的数据,而不是最新的——对事后分析来说这个取舍需要提前想清楚。

    安全侧也有明确要求:共享令牌和数据必须用私有网络、VPN 或 HTTPS 保护;INFLUXDB_TOKEN 只留在服务端,不上车。

    开机自启与仿真切换

    车上部署的关键不是写服务文件,而是把秘密与路径分开管理:服务文件里只留用户名与检出路径,令牌与服务端地址放进单独的 env 文件并收紧权限。这样既避免了令牌进版本库,也让同一份服务定义能在不同车上复用。

    步骤 操作 要点
    改服务文件 编辑 systemd/trevcan-pi.service,填入自己的用户名与检出路径 服务文件本身不含令牌
    写环境变量 在 /etc/trevcan-pi.env 写入 CAN_INGEST_TOKEN 与 TREVCAN_SERVER 地址形如 http 加主机名再加 8000 端口
    收紧权限 sudo chmod 600 /etc/trevcan-pi.env 仅 root 可读
    安装并启动 复制到 /etc/systemd/system/,执行 daemon-reload 与 systemctl enable --now trevcan-pi --now 同时完成开机自启与立即启动
    看日志 sudo journalctl -u trevcan-pi -f 队列溢出计数也在这里
    切换仿真 把 ExecStart 里的 --channel can0 换成 --simulate-rate 20 迁移时须先停掉并禁用旧的全栈服务

    两点值得单独记:CAN 接口的配置由系统单独管理,不由这个服务负责;测试用 python -m unittest discover -s tests 即可跑完,测试用 cantools 对每条报文与每个复用布局做编解码往返、校验信号覆盖与取值边界,并 mock HTTP 上报,不需要硬件、InfluxDB 或网络监听。

    归档取舍:保留 2 份、剔除 5 份

    归档最终只保留了两份字节互异的 BMS 与 DAQ 固件 DBC 快照,合计 277 行 BO_ 报文定义与 1,373 行 SG_ 信号定义。其余五份各有各的去向,理由不完全相同。

    文件 处置 理由
    trevcan_pi/dbcs/BMS-Firmware-RTOS-Complete.dbc 保留 与已有归档无重复;Git blob 94d4168a43352804615ae7659ac04475d09c43a8
    trevcan_pi/dbcs/DAQ-Firmware.dbc 保留 与已有归档无重复;Git blob 45ee363c49bdeeea30d0824bc0243529e118bf99
    Baby_MOBO.dbc 剔除 CRLF 归一化后与同批次已有归档中的文件完全相同
    VCU.dbc 剔除 CRLF 归一化后与同批次已有归档中的文件完全相同
    can9-database-01.09.dbc 剔除 CRLF 归一化后与同批次已有归档中的文件完全相同
    Inverter.dbc、hvc.dbc 剔除 已与同批次已有归档中的文件字节完全相同
    examples/simulation.dbc 说明为合成示例 仅用于演示的最小样例

    需要注意:这里被剔除的文件只是不重复归档,不代表它们在工程上被弃用。仿真模式仍然按 7 份当前 DBC 覆盖 347 条报文定义。

    两个 ID 撞车:0xC0

    文档点出了一个原始 CAN 的结构性限制:逆变器的 Command_Message 与 VCU 的 VCU_INV_Command 共用标准 ID 0xC0。两份定义都会被仿真并打印告警,但服务端解码时用的是第一个匹配的已启用 DBC,无法按 ID 区分这两条定义。后果很直接:同一 ID 的帧解出来是命令还是状态,取决于你在服务端启用了哪份 DBC、以及它的排序。测试某个仪表盘前,必须确认启用并排好的是目标那份。

    常见问题

    这个树莓派节点会往车上发 CAN 帧吗?

    不会。文档写明发送器只接收 CAN 流量,不向物理总线发送;仿真模式下更是完全不打开 CAN 接口、绝不在物理总线上发送。启动前还需按车辆实际波特率配置并拉起 can0,配置正确时它对整车网络是只听的。

    仿真生成的 460 种布局能当真实车辆数据用吗?

    不能。它覆盖 7 份 DBC 中全部 347 条报文定义、含每个多路复用分支共 460 种布局,但数值只是在 DBC 可表示范围内变化、枚举按定义值轮转,属合成覆盖数据而非物理自洽的车辆模型。命令与配置类报文也仅作为网络遥测计入。

    为什么 0xC0 这个 ID 会解出两种含义?

    因为原始 CAN 不携带 DBC 身份。逆变器的 Command_Message 与 VCU 的 VCU_INV_Command 共用标准 ID 0xC0,服务端按第一个匹配的已启用 DBC 解码,无法靠 ID 区分。因此要在服务端启用并排好你实际要测的那份 DBC,否则解出来的是另一条定义。

    断网期间采集到的报文会补传吗?

    不会。批处理最多 500 帧、窗口 100 ms;队列容量 10,000 帧,溢出时丢弃最旧帧并在日志计数;失败或不确定的 HTTP 批次不重放。传输会自动恢复,但中断期间没有磁盘积压,也不保证送达;SIGTERM 或 Ctrl-C 停止时未发送的帧同样可能被丢弃。

    归档只留了两份 DBC,其余五份为什么没有?

    原因分两类:Baby_MOBO.dbc、VCU.dbc 与 can9-database-01.09.dbc 在 CRLF 归一化后与同批次已有归档中的文件完全相同;Inverter.dbc 与 hvc.dbc 则已字节完全相同;examples/simulation.dbc 是合成示例。剔除只是避免重复归档,不代表这些文件在工程上被弃用。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/elidresnin-trevcan-pi/UPSTREAM_README.md(纯发送端定位与车端不依赖 Explorer 检出文件、venv 与 requirements 安装流程、CAN_INGEST_TOKEN 与 –server 参数、can0 需按车辆波特率预配置、只收不发、–interface 与 –channel、–simulate-rate 的总帧率语义、347 条报文定义与 460 种布局、0xC0 ID 冲突与服务端按首个匹配 DBC 解码、–dbc 顶层目录扫描规则、systemd 开机自启与 /etc/trevcan-pi.env、500 帧 / 100 ms 批处理与 10,000 帧队列及不保证送达、unittest 测试范围)
    • 项目 01 —— public-can-dbc-datasets/additional/elidresnin-trevcan-pi/SOURCE.md(保留两份 BMS 与 DAQ 固件 DBC 快照、277 行 BO_ 与 1,373 行 SG_ 合计、快照修订 684065b99b79ae2748fe061ccd70aaabf2f94337 与采集日期 2026-10-04、三份文件因 CRLF 归一化重复被剔除、两份已字节相同、examples/simulation.dbc 为合成示例、两份文件各自的 Git blob 与 SHA-256、未找到仓库许可证与原始组件归属故仅作私有离线研究副本)

    免责声明

    本文所述批处理参数、队列容量、报文与布局数量、命令与路径均来自公开开源仓库的说明文档,属第三方工程产物,未经独立实车核验。归档方已声明仿真数据为合成覆盖数据而非物理自洽的车辆模型,硬件适配与信号正确性未经检查,且该快照未找到仓库许可证与原始组件归属,仅应作为私有离线研究副本保留。涉及安全相关用途时请以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • 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 初稿

  • CANedge 到 InfluxDB:两种解码口径与 200 信号上限

    结论:把 CANedge 的 MF4 日志变成 Grafana 面板,第一步不是装软件,而是先定「何时解码」:数据量很大且只偶尔看图,用按需求解码、不需要数据库的 Grafana Backend;看板加载速度是硬指标、愿意为没人看的信号也付存储成本,才用预先解码写库的 InfluxDB Writer。而第二条路有一个不可协商的硬限制——免费版 InfluxDB Cloud 单设备最多 200 个 unique signals。

    两种口径:先按此表选型

    这个项目自己就把取舍讲清楚了,两者不是新旧关系,而是「解码时机」的不同。

    维度 CANedge Grafana Backend CANedge InfluxDB Writer
    解码时机 终端用户需要时才处理 提前批量处理,可周期触发或随文件上传触发
    是否需要数据库 不需要 需要,解码结果写入 InfluxDB
    适合场景 数据量庞大,只处理要看的那部分 看板加载速度是关键指标
    主要代价 每次打开面板都要算一遍 没人看的信号也被解码、也被存储
    本文分析对象 否 是,本仓库属于后者(已标为 LEGACY)

    值得注意的是仓库顶部已经把这条路标为 LEGACY 且不再支持,官方推荐转向 Grafana-Athena 集成。把它当作教学材料而不是生产依赖更合适:它的配置方式很清晰地暴露了一个 DBC 解码入库管线的全部接缝——把它的结构读明白之后迁移到新方案,比直接啃新方案要快。

    Writer 路径的六项能力与两个硬约束

    这条写在 features 列表里的能力清单,实际决定了能从 MF4 里榨出什么。

    能力/约束 取值 影响
    日志来源 本地磁盘或 S3 服务器上的 MF4 S3 场景把 devices 写成 your_bucket/device_id 形式,并设 s3 = True
    时间区间 可写死固定区间,也可用动态周期 固定区间需配合 days_offset = None 才能按真实日期写入
    解码 DBC 解码,可选择性抽取指定信号 这是规避信号上限的主要手段
    重采样 可选,重采样到指定频率 显著降低时序点数,但要留意 aliasing 掩盖峰值
    多帧协议 tp_type 支持 j1939、uds、nmea 需要配套 DBC,示例为 dbc_files/nissan_uds.dbc
    InfluxDB Cloud 免费版 单设备最多 200 个 unique signals 超限必须加信号过滤列表或缩减 DBC

    200 这个数字是这套方案最容易踩的暗桩。它的单位是 unique signals per device,而不是总行数——一份看上去很「全面」的整车 DBC 动辄上百个信号,多设备同时入库就会比分片更快地顶到上限。可行做法只有两个:在 inputs.py 里加过滤后的信号列表,或者把 DBC 本身裁剪到 200 个信号以内。补一句:仓库里的删除函数是 delete_influx(name),传入的是 measurement 名即设备 ID,例如 influx.delete_influx("958D2219"),测试污染要用它清理。

    目录契约:数据怎么放比怎么跑更重要

    绝大多数「跑不通」的问题出在这一步,而不是 Python 依赖。本地磁盘模式下,LOG/ 目录必须按 CANedge SD 卡的原样结构组织:[device_id]/[session]/[split].MF4。作者的建议是直接用自己 SD 卡上的 LOG/ 替换示例的 LOG 文件夹,而不是自己拼层级。

    DBC 文件统一放 dbc_files 文件夹,然后在 inputs.py 里更新 devices 与 dbc_paths。这三个复数形式不是装饰:设备、DBC 与时间偏移是一一对应的配置三元组,漏改任何一项都会导致「跑起来了但图上是空的」。

    标准流程建议严格按顺序走:先装 Python 3.9.13(Windows 记得勾 Add to PATH)、用 install.bat 建虚拟环境装依赖;然后用示例数据跑 main.bat,在 Grafana Cloud 里装 TrackMap 插件、导入 dashboard-template-sample-data.json,把时间范围设为最近 7 天——示例数据的落点是写完那一刻,不看最近 7 天就是空图。确认看到有效曲线之后,再替换成自己的数据并导入 dashboard-template-simple.json。

    走向生产之前要做的两件事

    第一件是自动化。路径验证完成后,仓库里的 deploy-aws-lambda/ 目录给了以 AWS Lambda 自动运行的 README,配合 S3 侧的上传触发,可以把「插卡倒入服务器」这步彻底去掉。

    第二件是成本。作者在文档里明确提醒:如果用的是付费的 InfluxDB Cloud 加付费 S3 服务器,建议在测试早期就盯着用量,避免意外的成本增长。实践上更好的做法是把写入点数与读取请求量各自做成独立指标长期观察,而不是等账单。

    常见问题

    该选 Grafana Backend 还是 InfluxDB Writer?

    看解码时机需求:数据量大且只有偶尔看图时选 Backend,它按终端用户需要时才处理且不需要数据库,代价是每次开面板都要算;看板加载速度是硬指标时选 Writer,它提前处理并写库,代价是没人看的信号也会被解码存储。

    免费版 InfluxDB Cloud 的信号数超限怎么办?

    作者给出的两个办法是:在 inputs.py 里加过滤后的信号列表,或者把 DBC 文件裁剪到 200 个信号以内。限制的单位是单设备 200 个 unique signals,不是总行数,所以多设备入库会更快触顶。

    为什么管道跑通了但 Grafana 面板是空的?

    先看时间范围,再看 days_offset。示例数据的落点是写入那一刻,需要把时间浏览器设到最近 7 天;换成自己的数据时必须把 days_offset 设为 None,否则数据会按偏移后的日期写入,落在当前查询窗口之外。

    能不能处理 UDS 或 J1939 这类多帧数据?

    可以。把 tp_type 设为 uds、j1939 或 nmea,并配上对应的 DBC 文件。仓库里给的验证方式是加 dbc_files/nissan_uds.dbc 并把 tp_type 设为 uds,用示例数据即可试通。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/css-canedge-influxdb/README.md(LEGACY 状态与 Grafana-Athena 推荐、Backend 与 Writer 的 pros and cons 对照、六项 features、Python 3.9.13 与 Windows/Linux 安装步骤、本地与 S3 两种数据加载路径、LOG/ 的 [device_id]/[session]/[split].MF4 目录契约、200 unique signals 上限、tp_type 的 j1939/uds/nmea 取值、dashboard-template-sample-data.json 与最近 7 天查看窗口、delete_influx(name)、AWS Lambda 自动化与云成本提醒)

    免责声明

    本文所述能力、参数与配置方式均来自公开开源仓库的说明文件。该仓库已被上游标记为 LEGACY 且不再支持,文中 200 unique signals 等限制为 InfluxDB Cloud 免费版在其文档成文时的声明值,实际额度与价格可能已调整。涉及付费云服务与生产部署请以官方最新文档与实际用量为准。

    更新记录

    • 2026-10-14 初稿

  • ECT2026 的 DBC:16 报文 61 信号同时喂固件与 Flutter

    结论:ECT2026 的 network.dbc 不是挂在某个 App 旁边的说明文件,而是同时被 C 固件工具链与 Dart 解码目录消费的唯一真源:16 条报文、61 个信号。真正的使用门槛不在信号规模,而在两处容易被忽略的事实——归档方在该修订版里没有找到仓库许可证,且这个 DBC 描述的是一台生态马拉松原型车的自建网络,不是任何量产车型的原厂定义。

    一份 DBC,两条生成链

    这套舱内 telemetry 面板值得借鉴的地方在于:它没有让「协议定义」和「解析代码」各自演化。上游文件被固定为 dbc/network.dbc,一致性靠代码生成保证,而不是靠人工同步。

    下游产物 生成方式 消费方 漂移风险
    C 语言解码源码 python -m cantools generate_c_source --database-name network dbc/network.dbc ECT2026 车载固件 低,由 cantools 统一语义
    Dart 解码目录 tools/generate_can_dart.py 生成并提交入库 Flutter App 解码路径 低,产物入版本库可 diff
    信号与状态映射 lib/models/telemetry/can_bindings.dart 手工维护 App 状态树与 MQTT 指标名 中,改名需同步 broker 侧订阅
    Spool 与 CSV 镜像 由信号解码结果透传 本地会话 CSV 与 Grafana 面板 中,字段名变更会污染历史数据

    这张表里真正需要人盯的只有第三、四行。前两行是「改 DBC 后跑一遍生成器」,机器保证一致;后两行是「改命名之后谁还依赖旧名」,只能靠约定。实际踩坑通常出现在 MQTT 指标名上——telemetry/eco_archers/events 这个导出主题一旦被外部订阅方写死,重命名就不再是本地重构,而是跨端协议变更。

    DBC 的 61 个信号按功能分成踏板与辅助控制、电池包与辅助电源、车辆运动与时间、GPS、电机状态五类。这个切面恰好说明车辆本身的形态:它不是一条完整的动力总成总线,而是「驾驶员输入 + 能耗 + 位置 + 电机」的最小闭环,服务于节能赛事的能耗管理,而不是故障诊断或刷写。

    数据流背后的三层持久性

    这份文档的工程价值更在于它把「车里可能断电」「蜂窝链路可能断」「服务端可能挂」三件事分别做了兜底,且每一个参数都写死在文档里,可以直接对照自己的项目做取舍。

    层级 取值 触发时机 失效后的表现
    可读 CSV 镜像 缓冲写、每秒 flush;默认保留 7 天 MQTT 服务启动时按保留天数裁剪 突发断电最多丢约 1 秒的行,不清空整份数据
    MQTT 本地 spool 磁盘最多保留 50,000 个待发批次 broker 不可达时堆积,恢复后重放 触及上限丢弃最旧批次,驾驶舱显示 SPOOL 并标记 DROP-OLDEST
    服务端 CSV 落盘 csv_streamer.py,1 秒 fsync、不设保留策略 随写入持续进行 服务端异常时该侧数据缺失,但车端 spool 仍完整

    三层的关键在于「各自独立」。车端 SQLite/WAL 的 edge_spool.db 面向链路可靠性,CSV 镜像面向现场可读性,服务端 1 秒 fsync 面向服务端掉电。把它们合成一层,任何一次裁剪或掉电都会同时击穿三个目标。

    还有一个容易被忽略的行为:镜像的 7 天裁剪发生在 MQTT 服务启动时。如果设备长期离线不启动服务,策略并不会执行,磁盘占用会一直往上涨。做车机部署时,容量估算要按「上线时才裁剪」而不是「按自然时间滚动」来做。

    怎么把这套后端跑起来

    文档给了两条等价路径。一条是单容器:仓库里的 ops/backend/Dockerfile 把 TimescaleDB、Mosquitto、Telegraf 与 Grafana 打包进同一个镜像,运行在 tini 之下作为 PID 1,并把 Telegraf 固定在 1.31.x、Grafana 固定在 13.1.1,同时对数据库、broker 与 CSV 服务分别做健康检查;对外暴露 1883、5432、3000、8080 四个端口。另一条是 ops/local-stack/docker-compose.yml 的多容器栈。

    两条路径的选择依据其实很朴素:单容器适合快速验证,代价是任一组件重启会牵动整容器;多容器适合长期运行,代价是编排与网络配置要自己维护。若只需要观测而不需要长期留存,还可以只跑 ops/backend/Dockerfile.csv-streamer 给出的最小镜像。

    建议的顺序是先用样本数据跑通整条链路——docker build 之后直接 docker run,再用 db/scripts/verify_setup.sql 核对 hypertable 与索引,确认 telemetry_raw、sessions、laps 三张表的行数符合预期——最后才替换成自己的 DBC 与会话数据。跳过这一步,后面所有的曲线都可能是空跑。

    什么时候不该直接搬用它

    有三个边界必须提前划清。第一是权利状态:归档说明在检视的修订版中没有找到仓库许可证,因此该 DBC 与派生文件被作为私有离线研究副本保留,任何再分发或对外数据服务之前都需要先确认权利。第二是适用范围:它是项目专用车辆网络定义,把 ID 与信号布局套到量产车上属于无意义迁移。第三是证据强度:这一归档没有独立核验部署情况、信号缩放系数与物理量测量,61 个信号的单位与 factor 不应被当作可直接用于能耗结算的标定值。

    常见问题

    ECT2026 的 DBC 能用于量产车的 CAN 解析吗?

    不能。它描述的是生态马拉松项目车自建的网络,覆盖踏板与辅助控制、电池包与辅助电源、车辆运动与时间、GPS、电机状态五类,共 16 条报文 61 个信号,属于项目专用定义。任何 ID 与信号布局都不能跨车型迁移。

    本地 CSV 镜像的 7 天保留是怎么执行的?

    由 MQTT 服务启动时按配置的保留天数裁剪,默认值是 7 天。这意味着裁剪时机与服务的启动强绑定:设备长时间离线且不启动服务时,镜像文件不会被清理,磁盘占用会持续增长,部署前需要按最坏情况估算容量。

    MQTT 断连一段时间后数据会全部重放吗?

    不一定。本地 spool 最多在磁盘保留 50,000 个待发批次,broker 恢复后自动重放;一旦触及这个上限,最旧的批次会被丢弃,同时驾驶舱显示 SPOOL 并标记 DROP-OLDEST,此时需要人工判断数据缺口的位置。

    这份 DBC 现在适合公开再分发吗?

    不适合。归档说明指出在检视的修订版中没有找到仓库许可证,虽然 Git blob 与 SHA-256 都已固定可用于比对,但权利状态未明。在确认权利之前,它只适合作为私有离线研究副本,不应集成进对外产品或公开发布的数据集。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/lugilugi-ect2026/SOURCE.md(仓库与固定修订版、DBC blob 与 SHA-256、16 报文 61 信号、五类信号切面、未找到许可证与未独立核验的声明)
    • 项目 01 —— public-can-dbc-datasets/additional/lugilugi-ect2026/README.md(App 栈与 MQTT 导出主题、spool 与 50,000 批上限、7 天保留与每秒 flush、单容器后端与四个端口、generate_can_dart.py 与 can_bindings.dart、cantools generate_c_source 调用方式)

    免责声明

    本文引用的报文数量、信号数量与工具链调用方式均来自公开开源仓库的说明文件,属社区项目产物,未经独立实车核验,也不代表任何车辆制造商的官方定义。文中参数(如 50,000 批上限、7 天保留、每秒 flush、Telegraf 1.31.x 与 Grafana 13.1.1)为上游文档声明值,不同版本部署可能存在差异。涉及行车安全与能耗结算的判断请以实车测量为准。

    更新记录

    • 2026-10-14 初稿