标签: 日志诊断

  • SAM 电动车 CAN 笔记:37 报文、161 信号与一处字节序纠错

    结论:SAM 电动车这份 CAN 资料的价值不在规模,而在它把每一条解码修正的证据来源都写清楚了:37 条 BO_ 报文、161 条 SG_ 信号之外,另有十余处改动被分成「固件确认」「车主确认」「推断未确认」三档。最关键的一条是 0x292 的 FECU 故障寄存器——字节 6 与字节 7 相对标准 Intel 24 位读法是互换的,按错的字节序会把 0x200000 读成 0x00020。二次使用前应先按证据等级过滤,而不是把 DBC 整体当标准答案。

    规模与总线前提:37 条报文是怎么构成的

    这份 DBC 面向单一项目车辆,覆盖电池、电机、充电器、ECU、仪表与故障六类流量,不属于任何厂商车系的通用定义。报文总数 37 条里有一条是 Vector 的零长度独立信号容器,并非实际发送的帧,因此真实可收发的报文比计数少一条。总线速率是 125 kbit/s,但这个数字并不来自 DBC 自身:BS_: 行没有记录波特率,125 kbit/s 是车辆的已知实际设置,在回放工具里作为默认值写入。

    轨迹侧的限制同样需要提前确认。配套解析只支持 PCAN-View 的 .trc v2.0,早期 v1.1 格式(列布局不同、没有 DT 标记)会被解析成零行,表现为「未找到任何 CAN 数据帧」的提示而不是报错崩溃。SAMPlay 记录仪的 .TXT 导出也被接受,且两种格式共用一个加载按钮,靠嗅探首个非空行的内容来分派,而不是看扩展名——后者只有 .txt 后缀,从扩展名上无法与其他文本区分。

    时间轴上的取数模型也值得先明确:加载时每个信号在整段轨迹上完整绘制一次,光标滑块移动的是一条竖线,仪表、指示灯与状态标签按零阶保持重新取值,也就是取「最后一次实际收到的值」而不是插值。播放由 50 毫秒周期的定时器驱动,且每一拍都按起始时刻加实际经过的时间设置光标,而不是按固定步长累加,因此播放速度与墙上时钟一致,不受回调抖动影响。单步前进或后退跳到的是相邻帧自身的时间戳,所以「一步」始终等于一条报文。

    项目 数值 说明
    BO_ 条目 37 含 1 条零长度独立信号容器,非真实发送帧
    SG_ 条目 161 覆盖电池、电机、充电器、ECU、仪表、故障
    总线速率 125 kbit/s DBC 的 BS_: 行未记录,取车辆已知设置
    轨迹格式 .trc v2.0、SAMPlay .TXT v1.1 解析为零行
    电芯数 36 0x406 循环 37 个槽位,第 37 槽恒为 0 mV
    采样模型 零阶保持 刷取取「最后一次实际收到的值」,不做插值

    三档证据:哪些改动能直接用

    这份笔记最值得借鉴的做法,是把修正按可信度分档,而不是统一标注为「已修正」。固件确认档的依据是车辆仪表节点的 STM32 CAN 接收处理代码——代码不会撒谎,因此符号位、报文内容、字节序这类结构性错误一旦被代码确认,就可以当定论用。车主确认档来自车辆使用者的直接说明。推断档则只有数据回归作为支撑,必须保留原始计数并列显示,不能当作标定过的物理单位。

    证据等级 涉及报文与信号 改动内容 判据
    固件确认 BO_912 Motor_speed 无符号改为有符号 此前出现的约 65535 rpm 假尖峰由此而来
    固件确认 BO_658(0x292)FECU_error 字节 6 与字节 7 互换 接收处理以 16 位指针取 RxData[6] 起两字节,再或上 RxData[5] 左移 16 位
    固件确认 BO_448 VoltageLV 系数由 1 改为 0.00625 V/count 固件阈值判断中直接出现该常数,改后读数约 13.8 V
    固件确认 BO_960(0x3C0)Ustopcharge 解码前先屏蔽字节 1 高 2 位 该 ID 在固件中被复用为 3 条子报文,高 2 位是复用标志而非数据
    固件确认 BO_400(0x190) 占位信号名整体替换为 5 个 Skai 状态与故障位 固件对应分支定义了启用、功率模块错误、硬件错误、驱动错误、驱动告警
    车主确认 BO_657 trip_km 系数由 1 改为 0.1 原始值按 100 m 步长存储,车主于 2026-08-05 确认
    推断未确认 BO_401/BO_656 扭矩 系数由 1 改为 0.005 Nm/count 原始值在全部 5 段行驶日志中都精确截幅在正负 10000
    推断未确认 BO_657 加速与刹车踏板 有符号改为无符号 各日志中 28% 至 45% 的帧原始值落在 32768 以上

    为什么 0x292 必须脱离 DBC 单独处理

    故障寄存器不是一个连续字段,而是由三段拼接而成:字节 6 是最低字节,字节 7 是中间字节,字节 5 是最高字节。这种字节置换无法用一条连续的 SG_ 表达,因此该信号在 DBC 里干脆不再有信号行,改由解码函数直接从原始字节重建。后果很直观:按旧的连续小端 24 位读法,5 段行驶日志里每一帧都被解成 0x00020,而按固件正确的字节序是 0x200000——两者相差四个数量级,故障码从「20」变成「200000」。这类错误不会表现为异常,只会表现为一个看起来合理的错误码,所以必须拿固件代码交叉验证,而不是靠肉眼比对数值范围。

    推断档:系数是怎么回归出来的

    推断档的共性是找不到固件依据,只能拿总线上的另一个独立测量做参照。直流母线电压拿 BMS 的包电压做基准,比例稳定在 0.0997 附近,等于精确的一分之一;充电器高压沿用同一族系数,实测比例 0.06211 与十六分之一相差约 0.6%,落在两个独立电压传感器加零阶保持重采样的合理误差内。12 V 侧电流没有独立参照,改用功率平衡交叉验证:13.8 V 乘以换算后的电流,得到的 DCDC 输出功率中位约 160 W、区间 95 至 215 W,与车辆已知典型负载吻合。

    信号 报文 采用系数 回归证据
    skai_DC_Link_Voltage BO_656(0x290) 0.1 V/count 与包电压比值 0.0997 正负 0.002,样本约 36k
    VoltageHV BO_448(0x1C0) 0.0625 V/count 比值 0.06211 正负 0.00001,与包电压同族系数
    DCDC_12V_Current BO_704(0x2C0) 0.0625 A/count 功率平衡后中位约 160 W,区间 95 至 215 W
    DCDC_HV_Current BO_704 0.0625 A/count(仅对称推定) 约 36k 样本恒为 0,无任何非零验证点
    Inside_temp/Outside_temp BO_960(0x3C0) 0.1 摄氏度/count 系数为 1 时读数 280 至 377 度,物理上不可能
    扭矩(指令与实际) BO_401/BO_656 0.005 Nm/count 正负 10000 count 对应实车约正负 50 Nm

    仍以原始计数呈现、未做标定的有四类:DCDC_HV_Current、Voltage_12V、加速与刹车踏板开度、Ustopcharge。它们在界面上带原始计数标记,避免被误读成百分比或电压。

    电芯内阻:两个不能跳过的测量约束

    逐芯内阻估算用的是同一电芯相邻两次读数的电压差与电流差,按过原点拟合,这样未知的开路电压及其 SOC 漂移在差分中抵消。方法上有两条约束是数据逼出来的:电流必须取 10 Hz 的 0x386 而不是 1 Hz 的 0x186;电压相对电流存在约 1 秒滞后,忽略滞后会把相关系数从约 0.96 拉低到约 0.74,并使内阻低估约 25%。

    环节 取值 影响
    电流源 0x386,10 Hz 0x186 仅 1 Hz,只作兜底
    电压滞后 0.95 至 1.0 秒 滞后取 0 时相关性约 0.96 降到约 0.74,内阻低估约 25%
    电芯刷新周期 3.7 秒 0x406 循环 37 槽位,第 37 槽恒为 0 mV
    结果(新 BMS 车) 约 0.67 至 0.70 毫欧/芯 R 方约 0.95,95% 置信区间约正负 0.02 毫欧
    结果(原厂 BMS 车) 约 1.8 至 2.5 毫欧/芯 一致离群:10 号 +28%、19 号 +17%、28 号 +13%、12 号 +10%
    已知干扰 BMS 启动垃圾帧 需按 1500 至 5000 mV、电流绝对值 1000 A 以内的合理窗过滤

    两辆车的差异说明这套方法对 BMS 版本敏感:新 BMS 上报的 0x306 内阻最小、平均、最大三个值都是 1.0,看起来是占位值;原厂 BMS 则给出真实数值。跨车比较前必须先确认 BMS 变体。还有两点需要在使用结果时说明清楚:一是这里得到的是约 1 至 4 秒尺度上的有效内阻,包含快速极化,并不等于电芯规格书上的 1 kHz 交流内阻,而且它随温度变化——新 BMS 轨迹里的电芯温度读数平坦在 21 至 22 摄氏度,因此未做温度修正,只在汇总里显示范围;二是充电轨迹是恒流工况,几乎没有可用的电流变化,此时界面直接说明无法计算,而不是给出一个看起来合理的数字。另外,电芯离群判定默认阈值设在相对中位数高出 15% 及以上,在当前约 4% 的离散度下,逐芯排序在不同行程之间只能部分复现,因此单次结果不足以判定某颗电芯劣化。

    验证步骤与三个真实陷阱

    没有交互式显示环境可用,全部功能通过 matlab -batch 无头验证:以带路径参数的构造函数打开轨迹,再直接驱动返回结构体上的回调来模拟拖动、切页与按钮点击,把仪表、指示灯与状态值逐个对照手工解码的原始字节。回放通道则在 MathWorks 虚拟 CAN 回环上验证——一个通道发、另一个通道收——单步、突发顺序、解除武装、断连四类动作都逐帧比对,只有 USB 驱动层未经实测。另外还有一条一次性检查:确认界面配置里引用的每个信号键都真实存在于 DBC,改任一侧都应重跑。

    陷阱 现象 定位与处理
    SAMPlay 文本解析 FECU_error 在 389 帧里恒为 0 sscanf 的 %2x 只跳空白不跳逗号,遇到首个逗号即停,字节 1 至 7 全为初始 0;改用正则取两位十六进制记号后修复。影响面是所有位于字节 0 之外的信号
    效率中位值大于 1 另一辆车未修正时中位效率 1.20 按「电池功率 = k 乘机械功率 + 损耗」逐格拟合,k 约 0.946(本车)与约 0.71(另一辆),两车控制器参数化不同
    「2 秒后 CAN 上无 BMS」故障位 位 0x00200 在 3 份日志中从未置位 已核对原始字节,非解析缺陷:这些日志里 0x186、0x206、0x286、0x306、0x386、0x406、0x486、0x506、0x705 全部缺失,仅有的 0x205 发送方是仪表 ECU,只能说明仪表在命令接触器,不能证明 BMS 在线
    回放安全 接入实车总线后习惯性点播放 连接本身不发送任何帧,需单独勾选「播放或步进时发送」才武装;BLE 断连、CAN 超时、按键三者任一都会释放

    常见问题

    这份 DBC 能直接用于我的车辆解析吗?

    不能。37 条 BO_ 报文与 161 条 SG_ 信号面向单一项目车辆,覆盖电池、电机、充电器、ECU、仪表与故障流量,既不是厂商车系的通用定义,也不构成车型年款适配承诺。可作为同类电动车报文组织方式与修正记录方法的参考。

    为什么故障码会显示成完全不同的数值?

    因为 0x292 的 FECU_error 不是连续小端 24 位字段,而是字节 6 作最低字节、字节 7 作中间字节、字节 5 作最高字节。按标准字节序读会把 0x200000 读成 0x00020,两者相差四个数量级,因此必须按固件接收处理的方式从原始字节重建。

    推断档的系数能不能当标定值用?

    不能。扭矩的 0.005 Nm/count 只依据原始值在 5 段日志中一致截幅于正负 10000 与实车约 50 Nm 上限推出,没有独立传感器做回归;直流母线电压与充电器高压则有比值证据支撑,可信度更高。界面上保留原始计数并列显示,就是为了让使用者自行判断。

    估算电芯内阻时最容易犯的错误是什么?

    忽略电压相对电流约 1 秒的滞后,以及用 1 Hz 的 0x186 替代 10 Hz 的 0x386 作为电流源。前者会让相关系数从约 0.96 降到约 0.74 并使内阻低估约 25%,后者在零阶保持下会显著放大散点。

    回放轨迹到真实总线上有什么前置条件?

    需要车辆网络工具箱与适配器驱动,且连接并不等于发送:必须单独勾选武装开关才会真正发帧。未检测到适配器时会退回到虚拟通道。官方说明也提示,除驱动层外其余代码路径已在虚拟回环上验证,正式依赖前建议再做一次真实硬件冒烟测试。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/janniksteiner-sam-can/PROJECT_NOTES.md(架构、解码修正的证据分级、推断系数回归依据与已知限制)
    • 项目 01 —— public-can-dbc-datasets/additional/janniksteiner-sam-can/README.md(应用定位、支持的轨迹格式与项目文件构成)
    • 项目 01 —— public-can-dbc-datasets/additional/janniksteiner-sam-can/SOURCE.md(37 条 BO_ 与 161 条 SG_ 的规模、两种 BMS 变体差异与许可证状态说明)

    免责声明

    本文所述的 DBC 与解码修正均为社区逆向工程与研究笔记产物,不代表车辆制造商的官方数据。文中数字均来自上述公开仓库的说明文件,未经独立实车验证;不同车型年款、ECU 固件与 BMS 版本可能存在显著差异,跨车套用字节偏移与系数前必须重新回归。该仓库为 GPL-3.0 许可,二次分发前请自行确认其对底层 DBC 与早期贡献的适用性。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-15 初稿

  • 思域 FL5 的 25 个 DID 与 109 个信号:只读 UDS 请求流程

    结论:这份参考给出 25 个 UDS 服务 22 的 DID 与 109 个解码信号,来自同一辆 Civic Type R FL5(K20C1 2.0T,6 速手动)在 2026-09-08 至 2026-09-16 的八轮只读采集。第一件事不是数信号,而是承认它的形态:这辆车 OBD 口后面有网关,被动监听 24.8 分钟、59 万帧里 16 个 CAN ID 全是 18DAxxxx 诊断帧,广播帧数为 0。所以主键是「模块地址 + DID + 字节 / 位偏移」,不是 CAN ID。

    形态决定主键:为什么它不是一份 DBC

    钥匙 ON、怠速、行驶全程广播帧数都是 0,文档里每一个数字都是「主动问、ECU 答」得到的正应答字节。同一条物理通道上靠 29 位 ID 里的目标地址区分四个已解码 ECU(ECM、VSA、EPS、组合仪表)。

    因此 DBC 不适合作为主格式:DBC 描述周期广播帧的布局,而这里没有广播帧可描述。要机器读,应该用 data/signals.json 或 data/signals.csv,或者 OBDb signalsets/v3 兼容的 obdb-signalset.json——后者只收录「已确认(研究级)」与「中」两档,低置信与候选留在文档里不进 signalset。

    层 事实
    物理 OBD-II 口:6 脚 CAN-H、14 脚 CAN-L、4 脚 GND、16 脚 12 V 常电。6 / 14 按插进车的公头方向数,数反会得到 0 帧
    链路 经典 CAN 2.0B,500 kbit/s,单帧不超过 8 字节,无 CAN FD(12 份日志约 190 万帧,FD 帧计数 0)。只证明网关外的 OBD 侧
    寻址 29 位 ISO 15765 物理寻址:请求 18DA 加目标地址加 F1,应答 18DAF1 加目标地址;诊断仪地址固定 F1。点名全车用功能广播 18DB33F1。11 位 7E0 / 7E8 上 Mode 01 无任何应答
    传输 ISO 15765-2:请求单帧 03 22 DID高 DID低,用 0x55 填满 8 字节;应答 54 到 63 字节数据区 = 首帧 FF 加 8 到 9 个连续帧 CF。流控 30 00 00(填充 0xAA)必须在几毫秒内回,晚了 ECU 直接放弃
    应用 UDS 服务 22 ReadDataByIdentifier。本车默认会话免握手(不需要 10 03,更不需要 27);3E 保活实测不需要(600 s 连续采样零错误)。此条只在本车验证过
    偏移口径 offset 指去掉应答头 62 加 DID 两字节之后的数据偏移;[a:b] 不含 b;value = raw 乘 scale 加 add。默认大端,明确标注小端的除外

    时序与预算:单 ECU 串行上限约 33 Hz

    下表是主机侧观测值,不是 ECU 的硬限制。采集用的适配器无硬件时间戳,所以「VSA 80 ms」应读作首帧到末帧的整段,而不是帧间隔,也不能据此断定是 STmin 硬限制。

    模块 / DID 请求到完整 p50 p95 相邻 CF 间隔
    ECM 10:2610 24.9 / 27.4 ms 30.7 / 30.6 ms 约 2 ms
    ECM 10:2611 29.3 ms 33.5 ms 约 2 ms
    ECM 10:268F 27.3 ms 29.8 ms 约 2 ms
    VSA 28:4001/4004/4007/4073 约 100.1 ms 约 100.6 ms 约 10 ms
    EPS 30:48BD/48C0/48C4 约 67.9 ms 约 69 ms 约 8 ms

    串行模型是成立的:2051 轮逐轮恒等式「周期 = 各请求到完整之和 + 各间隙之和」残差为 0,轮间开销 1.48 ms/轮,由此得到单 ECU 串行上限约 1 秒除以 30.5 ms,即约 33 Hz(仅 ECM)。跨模块并发在协议上允许(不同地址各挂一个未完成请求),本车从未验证;按模块求和再取最大的理想周期只比串行快约 28%,不是 100 ms。作为对照,原厂调校工具自己轮询 22 2610 与 22 2611 各 18.13 Hz。

    NRC 含义 本车观察
    0x31 requestOutOfRange(该 DID 不存在) 扫描时的主力否定应答,全归档最常见
    0x11 serviceNotSupported 服务 21(五个模块各 256 个 LID)与服务 24 全部返回它,本车不支持,不必再试
    0x22 conditionsNotCorrect 有处理逻辑;扫描中零星出现
    0x78 requestCorrectlyReceived-ResponsePending 有处理(最多 5 乘 timeout);全归档统计里不计入否定应答
    0x80 厂商自定义 / 未列入标准表 第二轮扫 ECM 时出现过一次

    两条吃了亏才写下的采集纪律

    其一,一笔请求超时会触发对该地址的隔离,所以采样组的第一笔要放最稳的 DID。其二,行驶中不要把扫描和核心采样混在一条串行链上——边行驶边扫描的一份日志里核心采样最长断流 7.891 s,而纯核心采样的另一份最长只有 1.244 s。

    模块清单与扫描覆盖

    零件号由 22 F181 或 22 F110 读出,是同款车共有的零件号,不是单车序列号;数据集不含 VIN、ECU 序列号或任何单车唯一标识。功能广播点名时,公开信号集里有而本车无应答的地址有 0x2A、0x0E、0x11、0x1D、0x01、0x15、0x16。

    地址 身份 零件号 扫描覆盖 有正应答 DID 收录 状态
    0x10 ECM/PCM 发动机控制 37805-66V-H020 0000–FFFF 全段 77 个(26xx 52 + 30xx 5 + DC59–FFFF 段 20) 10 个 扫描中 6F2B / 6F8A / DC58 三个单点无应答触发隔离,未复读
    0x28 VSA 车身稳定(ABS/ESC) 57114-T60-AA20 0000–A082 31 个(4000–400A、4030–403B、4070–4073、0EEC、4100、48AF、48F5) 5 个 A083–FFFF 未扫
    0x30 EPS 电动转向助力 39990-T60-J030 0000–7EEB 20 个(全在 48xx) 5 个 重扫 0000–2A30 无新增;2A31–FFFF 待续
    0x3A ADS 自适应减振 39390-31M-A040 0000–FFFF 全段 16 个(48AF、48F5、52xx、5280/5281、E600、E602、F1xx) 0 个 10 个数据类里 9 个逐字节恒定,唯一在动的 520A 未解码
    0x53 SRS 气囊 77959-T60-H830 未扫 — 0 个 故意不碰
    0x60 组合仪表 78108-T60-H020 7000–70FF 18 个(有数据的 6 个) 5 个 其余段未扫
    0x70 未知(33137 系,疑似大灯控制) 33137T24Y112M1 未扫 — 0 个 功能广播点名有应答
    0xB0 多用途摄像头(Honda Sensing) 36161-T60-H050 0000–00C5 0 0 个 对服务 22 基本不配合,两次都在 00xx 某个 DID 超时被隔离;已放弃
    0xB3 Honda Sensing 子单元 36162-T51-H010 未扫 — 0 个 功能广播点名有应答
    0xB7 驻车雷达控制单元 39670T60E010M1 未扫 — 0 个 功能广播点名有应答
    0xED 车身控制 / 网关之一(38898 系) 38898-T60-H010 未扫 — 0 个 与 EF 成对
    0xEF 网关 / 车身控制之一(38897 系) 38897-T60-H010 未扫 — 0 个 功能广播时它最先应答,疑似网关

    信号总表摘录

    四个模块、25 个 DID、109 个信号,置信度分布为已确认(研究级)52、中 39、低 / 候选 18。文档不升不降任何一条的置信度。下面摘录 ECM 两个主包与几个跨模块的对照项;「实测范围」是全部采集里出现过的最小 / 最大值与样本数,只说明观察到过什么,不定义量程也不定义报警阈值。

    模块 : DID 信号 偏移 解码 单位 实测范围(样本数) 置信
    10:2610 ENGINE_RPM 6(2 B 大端) raw 乘 0.25 rpm 0 到 5300(53,908) 已确认(研究级)
    10:2610 MAP_ABS 9(2 B 大端) raw 乘 0.0078125 kPa 22.27 到 282.35(53,908) 已确认(研究级)
    10:2610 ECT 11(1 B) raw 减 40 摄氏度 33 到 90(53,908) 已确认(研究级)
    10:2610 VEHICLE_SPEED 18(1 B) raw km/h 0 到 137(53,908) 已确认(研究级)
    10:2610 BATTERY_V 22(1 B) raw 乘 0.1 V 6 到 14.6(53,908) 已确认(研究级)
    10:2610 OIL_PRESSURE 44(2 B 大端) raw kPa 0 到 626(53,908) 已确认(研究级)
    10:2611 LAMBDA 8(2 B 大端) raw 乘 3.0517578e-05 lambda 0.765 到 2.000(42,797) 已确认(研究级)
    10:2611 SHORT_TRIM 10(1 B) raw 乘 0.78125 减 100 % -10.16 到 17.97(42,797) 已确认(研究级)
    10:2611 GEAR 19(1 B) raw gear 0 到 6(42,797) 已确认(研究级)
    10:2611 PEDAL 20(2 B 大端) raw 乘 0.005 % 0 到 60.83(42,797) 已确认(研究级)
    10:2611 BOOST_PRESSURE 24(2 B 大端) raw 乘 0.013157894736842105 kPa 绝压 94.51 到 215.08(42,797) 已确认(研究级)
    10:2611 WASTEGATE 28(2 B 大端,有符号) raw 乘 0.001 mm(未证实) -0.176 到 8.412(42,797) 已确认(研究级)
    10:268F OIL_TEMP_MODEL 15(1 B) raw 减 40 摄氏度 32 到 82(20,684) 已确认(研究级)
    28:4005 BRAKE_PRESSURE 7(2 B 大端) raw 减 25 bar 0 到 106(4,840) 已确认(研究级)
    28:4007 REVERSE_GEAR 11(bit 1,掩码 0x2) raw 与 0x2 bool 0 到 2(3,515) 已确认(研究级)
    28:4007 CLUTCH_PEDAL 24(1 B) raw % 0 到 100(3,515) 已确认(研究级)
    30:48BD STEER_ANGLE 11(2 B 大端,有符号) raw 乘 0.1 deg -405.6 到 408.1(5,164) 中
    30:48C4 WHEEL_SPEED_FL 23(2 B 大端) raw 乘 0.01 km/h 0 到 115.23(3,193) 已确认(研究级)
    60:7022 ODOMETER_CLUSTER 6(3 B 大端) raw km 不公开(1,394) 已确认(研究级)
    60:7028 AMBIENT_TEMP 17(1 B) raw 摄氏度 26 到 34(2,059) 已确认(研究级)
    60:7029 FUEL_LEVEL 12(2 B 大端) raw 乘 0.01 L 16.46 到 43.88(1,393) 已确认(研究级)

    三条容易读错的约定

    同名信号出现在两个 DID 时(VEHICLE_SPEED_COPY、STEER_ANGLE_COPY),是两个不同位置的独立字段,解码与置信度各按所在 DID 读。标 kPa_abs 的是绝压;要增压表压请用同一应答包内的 MAP_ABS 减 BARO(同包相减,不要减 101.325 这类常数)。带掩码的位字段在「实测范围」列显示的是掩码后的原始值,例如掩码 0x8 的置位状态显示为 8,而不是 1。

    证据边界:未映射的变化字节与不可辨识项

    已映射的 25 个 DID 里还有 206 个未映射的变化字节、1,183 个位,其中在单次采集内部就会变的有 189 字节、1,124 位——由位级覆盖分析从全部采集算出。其中 4 个字节里「未映射位」与「已映射位」同处一室,按整字节做候选筛选的工具看不到它们。

    类型 位置 现状 缺什么
    未映射 10:2610 [8] 与 [27] 完全相同,燃压低字节镜像 未命名,仅登记
    未映射 10:2610 [16] 日志内变化,只有低 4 位(掩码 0x0F),5 种取值,与水温 r 约 -0.77 位域身份未定
    未映射 10:2610 [51] 0 到 240 阶梯,步长约 7.5 未扫,性质未知
    未映射 10:2611 [53:55] / [55:57] ECM 内部短时负向修正量,值域 -53 到 0;位宽不可辨识(该值域内 s8 与 s16 小端恒等);两者是单向包含关系 需要一个绝对值大于 127 的样本才能分开
    未映射 10:268F [12] / [13] / [14] 带 255 端点码;[12] 与转速的关系在差分与配对两道检验下都存活(r = 0.21 / 0.256),是 50 个候选里唯一存活的 不命名
    未映射 3A:520A 整包 64 B ADS 唯一在动的数据包,未解码;多字节字段候选 3 个(10:12、36:38、38:40,均大端) 整包未扫
    不可辨识 偏航率刻度 0.0045(运动学模型)对 0.003924(后轮差速几何法) 需外部偏航率参考(不低于 10 Hz、误差小于 3%)或实测轮距与滚动周长
    不可辨识 2610[39] 的门限 单一压力门限与 Schmitt 迟滞均已推翻;转速 / 档位分层使留出集 log-loss 改善 17 到 18% 未过 20% 门槛,判不出,不是无效应
    不可辨识 AIRC_MAX / AIRC_PROT / AIRC_COMP 三个字节 [49] / [57] / [58] 全程相等 三个名字在现有数据里无法区分
    不可辨识 扫描期链路停摆的机制 现象强相关;单 ECU 忙已排除,网关限流 / ISO-TP 异常 / 工具实现 / 静默超时四种都证据不足 缺适配器状态遥测,分不开「工具阻塞」与「总线 / 网关静默」

    常见问题

    这份参考能直接转成 DBC 吗?

    不能,也不该转。DBC 描述的是周期广播帧的布局,而这辆车的 OBD 侧广播帧数为 0,全部数据来自服务 22 的问答。主键是模块地址加 DID 加字节 / 位偏移,同一条物理通道上靠 29 位 ID 里的目标地址区分四个已解码 ECU。要机器读请用 signals.json、signals.csv 或 OBDb 兼容的 signalset。

    为什么按 7E0 / 7E8 发 Mode 01 收不到任何东西?

    因为这辆车走 29 位物理寻址:请求是 18DA 加目标地址加 F1,应答是 18DAF1 加目标地址,诊断仪地址固定 F1;11 位地址上 Mode 01 全程零应答。另一个常见失误是引脚数反了——6 脚 CAN-H、14 脚 CAN-L 要按插进车的公头方向数,数反了同样得到 0 帧。

    轮询频率能开到多高?

    串行模型下约 33 Hz(仅 ECM),由 1 秒除以单事务约 30.5 ms 得出,轮间开销 1.48 ms/轮,2051 轮恒等式残差为 0。跨模块并发在协议上允许但本车从未验证,理想化估算只比串行快约 28%。作为对照,原厂调校工具自己只跑 18.13 Hz。

    为什么两条里程信号不给出实测范围?

    因为精确里程读数能定位到具体某一辆车,作者主动不公开。样本数照常给出:ECM 的 ODOMETER 是 365 个样本,仪表的 ODOMETER_CLUSTER 是 1,394 个,两条相互核对一致且单位都是 km(公开信号集里写成英里是错的)。

    「已确认(研究级)」是不是等于已经标定过?

    不等于。它只表示这一辆车上证据强——与独立仪表逐点对上、或与调校工具的未取整导出逐列拟合到 r 约等于 1、或有干净的前后对照动作。它不代表已用独立仪器标定,也不代表在原厂车上成立。这辆车装有调校工具,ECU 已解锁,市场版本也未确认。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/caldis-honda-civic-fl5-dids/SOURCE.md(固定修订、许可条款、25 DID / 109 信号的范围与限制)
    • 项目 01 —— public-can-dbc-datasets/additional/caldis-honda-civic-fl5-dids/README.zh-CN.md(分层事实、逐事务耗时、NRC、模块扫描覆盖与信号总表)
    • 项目 01 —— public-can-dbc-datasets/additional/caldis-honda-civic-fl5-dids/README.md(英文版信号总表与已推翻说法)
    • 项目 01 —— public-can-dbc-datasets/additional/caldis-honda-civic-fl5-dids/CHANGELOG.md(数据版本 1.0.0 与 1.0.1 的变更记录)

    免责声明

    本文所述 DID 与信号均为社区只读诊断所得,不代表本田的官方数据,全部来自同一辆装有调校工具(ECU 已解锁)的 Civic Type R FL5,年款与市场版本均未确认。采集与本文档制作全程只使用服务 22 及配套只读服务,不含任何刷写、安全访问或内存读取内容;被明确排除且不得据本文尝试的服务包括 27、23、2E、31、11、14 与 34 到 37。把这里任何一条用在别的车上之前请自行复核。涉及安全相关用途时请仅作参考。

    更新记录

    • 2026-10-15 初稿

  • 用 SavvyCAN 示例数据集复现 CAN 分析流程:三份 DBC 与 12 种格式

    结论:SavvyCAN 自带的 examples/ 目录是最小成本的复现环境:三份示例 DBC 分别是 10 报文 22 信号的动力总线样本、501 报文 548 信号的大规模档案、6 报文 31 信号的部件级总线,量级相差约 80 倍,正好覆盖「小规模逐条核对—大规模过滤检索—部件级拓扑」三类分析动作。采集硬件并非必需,工具能直接读写 12 种日志格式(其中 3 种只读),因此用示例数据就能把整条流程走通。需要注意的是:三份示例都没有独立的年款适配与信号验证记录,且其中一份大规模档案在归档里还有 502 报文 761 信号的后续修订版,两者应按不同来源快照分别留存。

    三份示例 DBC:量级差 80 倍,用途完全不同

    把三份示例放在一张表里看,最直观的不是报文数,而是它们的性质差异:LeafPowertrainBus.dbc 是标注为 Leaf 的动力总线样本,规模小到可以逐条人工核对;ThinkCity.dbc 是 501 条报文的城市车档案,价值在于压测过滤与检索;bms.dbc 只有 6 条报文却命名了 EVTVBMS、GEVCU 与逆变器三类部件,是典型的部件级总线示意。

    示例文件 报文数 信号数 文件性质 证据边界
    LeafPowertrainBus.dbc 10 22 标注 Leaf 的动力总线示例 代数、年款、硬件与信号验证均未记录
    ThinkCity.dbc 501 548 标注 Think City 的大规模档案 并非每条报文都确立了车型年款适用性
    bms.dbc 6 31 部件级示例,含 EVTVBMS、GEVCU、逆变器 通用部件示意,不是某款整车的原厂 DBC
    归档中的 ThinkCity_modified6.dbc 502 761 同名的后续大幅扩充修订版 与上一行是两个来源快照,不可互相覆盖

    第四行值得单独说明:501/548 与 502/761 只差 1 条报文,信号数却差了 213 条。这说明后者的修订主要集中在信号定义而非报文条目扩充。归档时的正确做法是把两者当作独立快照各自留存,而不是用新版本替换旧版本。

    用示例数据复现分析流程:每个文件演示什么

    三份示例对应三种不同的分析动作。小规模样本适合验证「DBC 定义与原始字节是否自洽」,大规模档案适合验证「过滤、分组与检索是否好用」,部件级示例适合验证「多设备报文关系能否被正确建模」。把它们按这个顺序跑一遍,就能在不接硬件的前提下判断工具链是否完整。

    分析动作 推荐示例 可验证的能力 演示不了什么
    逐条核对信号定义 LeafPowertrainBus.dbc 报文清单浏览、信号曲线绘制、缩放与偏移是否自洽 代数与年款适配判定
    大规模检索与过滤 ThinkCity.dbc 按 ID 区间过滤、按报文名搜索、大文件加载与刷新性能 逐条物理值的实测验证
    部件级拓扑建模 bms.dbc 多设备(BMS、整车控制器、逆变器)报文归属与发送关系 整车级网络拓扑与网关行为
    格式往返一致性 任意一份 + 日志导出 导入后再导出,比较 ID 与数据字节是否逐帧一致 时间戳在不同格式间的保真度差异
    来源快照比对 ThinkCity.dbc 与修订版 同名报文在两版之间信号数变化的对比 哪一版更接近原车的判定

    为什么「导入再导出」是最先该跑的一条

    这条流程不需要理解任何信号含义,只需要比对 ID 与 8 个数据字节是否逐帧一致。它能一次性暴露三类问题:格式解析器的边界处理、扩展帧与标准帧的区分、以及多帧时序是否被保留。在这条通过之前,后续任何信号级分析都建立在不可靠的输入上。

    十二种读写格式:九种可写,三种只读

    工具的定位是「加载、保存与采集 CAN 帧」,用于可视化、逆向、调试与抓包。它明确说明采集设备并非必需,因此格式支持清单直接决定了它的离线分析能力边界。清单里前九种可读写,后三种只能读。

    序号 格式 读写能力
    1 BusMaster 日志文件 读 + 写
    2 Microchip 日志文件 读 + 写
    3 CRTD 格式(OVMS 日志格式) 读 + 写
    4 GVRET 原生格式 读 + 写
    5 通用 CSV(ID 加 D0 到 D7) 读 + 写
    6 Vector Trace 文件 读 + 写
    7 IXXAT Minilog 文件 读 + 写
    8 CAN-DO 日志 读 + 写
    9 Vehicle Spy 日志文件 读 + 写
    10 CANDump / Kayak 只读
    11 PCAN Viewer 只读
    12 Wireshark socketcan PCAP 文件 只读

    采集侧则走另一条路:除推荐的 CANDue 板(需运行 GVRET 固件)之外,可以使用任意 Qt SerialBus 驱动,包括 socketcan、Vector、PeakCAN 与 TinyCAN。也就是说,离线分析用文件,实车采集走 SerialBus,两条路径互不影响。

    可复现环境:依赖全部在源码树内

    这份工程不依赖源码树或 Qt 安装器之外的任何东西,绘图库 QCustomPlot 已直接集成进源码,无需单独下载。编译门槛集中在 Qt 版本上:因为依赖 QtSerialBus 及其他新特性,需要 5.14.0 或更高版本。

    环境项 要求 说明
    Qt 版本 5.14.0 或更高 低于此版本缺少 QtSerialBus,无法编译
    屏幕分辨率 至少 1024×768 支持多显示器,4K 显示器可用
    macOS 二进制 至少 macOS 10.15 针对当前二进制构建的要求
    Qt6 支持 QT6WIP 分支,2026-03-11 用 Qt 6.9 测试通过 质量约为 beta,多数功能可用
    绘图依赖 QCustomPlot 已集成 无需外部下载
    Ubuntu 模块缺失 安装 libqt5serialbus5-dev、libqt5serialport5-dev、qtdeclarative5-dev、qttools5-dev 解决「Unknown module(s) in QT」类报错
    调试构建 qmake CONFIG+=debug 后 make 获取更多运行期信息

    标准编译路径是克隆仓库后依次执行 qmake 与 make,再直接运行可执行文件;Linux 上可用 ./install.sh 生成桌面快捷方式。若编译失败,官方给的第一步重试动作是重新 qmake、make clean、make;仍未解决则检查安装 Qt 时是否勾选了 SerialBUS 支持。

    常见问题

    没有采集硬件,能用 SavvyCAN 做分析吗?

    可以。工具明确说明采集设备不是使用前提,它能直接加载并保存 12 种格式,其中 9 种可读可写,CANDump/Kayak、PCAN Viewer 与 Wireshark socketcan PCAP 三种为只读。用 examples/ 下的三份 DBC 配合日志文件即可跑完整条离线流程。

    三份示例里哪份适合作为第一条分析流程?

    建议从 bms.dbc 或 LeafPowertrainBus.dbc 这类小文件起步。LeafPowertrainBus.dbc 有 10 条报文 22 条信号,可以逐条核对缩放与偏移是否自洽;bms.dbc 有 6 条报文 31 条信号,适合看多设备报文归属。501 报文 548 信号的 ThinkCity.dbc 更适合压测检索与过滤性能。

    ThinkCity.dbc 与归档里的另一份 ThinkCity 该怎么取舍?

    不取舍,两份都要留。归档记录显示后者是同名的后续大幅扩充修订版,规模为 502 报文 761 信号,比前者多 1 条报文、多 213 条信号。两者属于不同的来源快照,正确做法是按各自 provenance 分别留存,而不是用新版覆盖旧版。

    bms.dbc 能当整车 DBC 用吗?

    不能。它是部件级示例,6 条报文 31 条信号里命名的是 EVTVBMS、GEVCU 与逆变器等部件,用于展示部件之间的报文关系。它既不是某款命名整车的原厂 DBC,也不包含整车级拓扑与网关行为。

    Qt6 分支现在可以投入日常使用吗?

    需按 beta 质量评估。仓库里的 QT6WIP 分支可配合 Qt6 编译,2026-03-11 用 Qt 6.9 测试通过,官方表述是「多数功能应该可用」,并请求反馈问题。若用于正式分析流程,仍建议以 Qt 5.14.0 或更高的稳定构建为主。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/collin80-savvycan-examples/SOURCE.md(三份示例 DBC 的报文数、信号数、SHA-256 与修订版快照关系)
    • 项目 01 —— public-can-dbc-datasets/additional/collin80-savvycan-examples/README.md(12 种读写格式、Qt 版本与分辨率要求、编译与排错步骤)

    免责声明

    本文所述示例 DBC 均为公开仓库中的演示文件,其代数、年款、硬件适配与信号验证均未在说明文件中确立,不代表车辆制造商的官方数据。文中数字均来自上述公开仓库的说明文件,未经独立实车验证。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

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

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

  • RiceTimer:GNSS UTC 统一时钟,DBC 解析仍在计划里

    结论:RiceTimer 是一台把 GNSS、IMU 与 CAN 三路原始数据同步写进 MicroSD 的记录器,但它今天并不解析信号——data/dbc/ 目录下目前只有一份版权声明,说明数据部分派生自 MIT 授权的 OpenDBC;真正的 DBC-based CAN bus parsing 明确写在 Planned Future Work 里,并且作者特别注明它不会影响日志输出,日志永远是原始 CAN 报文。想拿发动机转速和挡位,得自己离线过一遍 DBC。

    它记录什么:三路数据共用一个 GNSS 时钟

    这个项目最扎实的设计决定是把时间基准统一了:所有消息都按 GNSS 的 UTC 时钟打时间戳,而不是按各自 MCU 的上电时间。这决定了后面能不能把 IMU 的姿态和 CAN 的轮速对齐到同一条时间轴上。

    数据源 器件 时间戳方式 注意点
    GNSS uBlox NEO 系列模块 + 外接有源天线 NMEA 语句本身带 UTC 必须接 SMA 天线,板上没有内置 GNSS 天线
    IMU ST LSM6 系列(LGA14)六轴加速度 + 陀螺 硬件在 data available 时刻打时间戳 SPI 总线占用 IOMux 快速引脚,不可随意改引脚
    CAN 一路 3.3 V 非隔离收发器 固件在报文交付时刻打时间戳 兼容 5 V 网络;支持 11/29 位帧,不支持 FD

    三者的时间戳来源不同——IMU 是硬件级的、CAN 是固件级的——这意味着它们相对于 GNSS 时钟都存在各自的偏移。做融合之前,这个偏移必须测出来并补偿,否则 IMU 与 CAN 的数据在时间上会有系统性的错位。

    日志格式也刻意朴素:每行一个数据点,且每一条有效 NMEA 语句都被逐字写入。这样做的代价是文件体积偏大,收益是后期可以用任何外部工具重放,不必依赖设备自身的解析器。示例日志 sample-log.txt 里 GPS 坐标做了部分脱敏处理,因此那份文件的 NMEA 校验和是对不上的——这不是日志坏了。

    硬件接口契约

    接车之前值得把引脚定义核对一遍,因为它是裸板电源与总线共口的设计,接错就是烧片子。

    接口 引脚 电气取值 注意点
    USB Micro-B 供电 + 串口调试烧录 CP210x 桥接 同时也是 ESP32 的编程控制台
    JST SH 5 针(短边) 1 GND、2 VIN、3 Reserved、4 CAN L、5 CAN H VIN 标称 5 至 14 V CAN 与电源共用同一个座,引脚 3 保留未接
    SMA(短边) 外接有源天线 必需 无内置天线,不接则 GNSS 不工作
    JST SH 4 针(长边) 1 GND、2 VCC、3 SDA、4 SCL VCC 标称 3.3 V 兼容 QWIIC 与 Stemma QT,现用于 SH1107 的 128×64 OLED
    Micro SD 4 位 SD 总线 Micro-SDHC 占用 IOMux 快速引脚;重流量下建议低延迟卡

    SD 卡这条容易被忽略。CAN 数据不多时任何 Micro-SDHC 都能用,但报文流量较大时建议换低延迟卡,以避免潜在丢数。市面上通常标注为 Industrial、Endurance 或「行车记录仪专用」的就是这一类。换句话说,丢帧的根因未必在 CAN 侧,可能在文件系统的写入延迟上——而这个问题在 CAN 侧是查不出异常的。

    DBC 目录现在的实际内容

    往下钻一层看 data/dbc/,会发现这个目录目前只有一份 README,全文只有一个信息:这里的数据部分派生自 OpenDBC(MIT 授权),详见 LICENSE。目录里没有可加载的 .dbc 文件。

    这与 README 里的计划清单完全对应——DBC-based CAN bus parsing 被列在 Planned Future Work,描述是「把 DBC 加载到设备上,从而提取并利用实时信号,例如发动机转速、油液温度、车辆自身的 IMU、轮速等」。而紧接着那句话才是关键:这不会影响日志输出,日志始终包含原始 CAN 报文。

    这条约束很值得照抄。它把设备切成两条正交的路径:实时显示在未来可以走 DBC 解析,落盘数据则永远保持原始。收益是后处理链路不会因为解析定义的改动而失效——原始帧始终可以重新解码;代价是每次会话都会多写一份原始报文,占用存储。对一个以复盘为目标的项目来说,这是一笔划算的买卖。

    计划清单里剩下的四件事

    README 明确列出了待办,读它能判断这台设备的成熟度:一是稳定日志格式与后处理流程(当前日志格式尚未正式规定,这对依赖它做自动分析的人是硬约束);二是传感器融合与标定,需要对 IMU、车辆、GNSS 天线之间的刚性变换做标定;三是预测圈速,当前 OLED 只显示少量实时诊断信息;四是启用 Wi-Fi 与 BLE——选 ESP32 就是为了留这一步。

    还有一条对项目形态影响很大:README 明确写「本项目目前不接受贡献」。这解释了为什么它是一个清晰的单人项目而不是一个社区协议库——想要更多的信号定义,只能等作者更新或自己在离线侧补全。

    作为参照,作者已经用早期原型硬件在 Thunderhill East 赛道的实数据跑通过后处理,转换成空格分隔表并交给 CircuitTools 分析。也就是说「赛道实车采集的数据转成空格分隔表,再交给 CircuitTools 分析」这条主链路是通的,缺的只是设备端的 DBC 解析那一环。

    常见问题

    RiceTimer 现在能直接在设备上解出发动机转速吗?

    不能。DBC-based CAN bus parsing 仍列在 Planned Future Work 中,data/dbc/ 目录目前只有一份说明版权与 OpenDBC 派生关系的 README,没有可加载的 dbc 文件。要得到转速、水温、轮速等信号,需要在拿到原始日志后自行用外部工具按 DBC 解码。

    这台设备支持 CAN FD 吗?

    不支持。它只有一路 3.3 V 非隔离 CAN 收发器通道,兼容 5 V 网络,支持 11 位与 29 位帧,文档明确指出 FD 不支持。接 FD 总线不会得到正确报文。

    为什么建议换低延迟的 SD 卡?

    因为写入延迟会在 CAN 流量大时造成丢数。作者说明 CAN 数据不多时任何 Micro-SDHC 都能用,但报文流量较大时应换低延迟卡,通常是标注 Industrial、Endurance 或行车记录仪专用的型号。这类丢帧在 CAN 侧查不到异常。

    示例日志里的 NMEA 校验和为什么对不上?

    因为示例日志的 GPS 坐标做了部分脱敏处理。README 明确说明这一操作会导致校验和不匹配,这是隐私保护的结果,不代表日志损坏或记录链路有问题。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/hpde-rice-timer/README.md(定位与原名 ShiftWatch、三种数据源与器件型号、GNSS UTC 统一时间戳、CAN 收发器电气与 FD 不支持、两个 JST SH 接口的引脚定义、SD 卡低延迟建议、示例日志脱敏说明、Planned Future Work 六项、暂不接受贡献)
    • 项目 01 —— public-can-dbc-datasets/additional/hpde-rice-timer/data/dbc/README.md(该目录数据部分派生自 MIT 授权的 OpenDBC,详见 LICENSE)

    免责声明

    本文所述硬件规格、引脚定义与项目状态均来自公开开源仓库的说明文件,属社区项目产物,未经实车独立验证,也不代表任何制造商的官方数据。日志格式尚未正式规格化,接口定义可能随硬件版本变化。涉及车载供电与总线接入的操作请以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

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

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

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

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

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

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

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

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

    三步 A/B/A 隔离法

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

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

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

    受控对比必须锁定的变量

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

    可迁移的故障特征

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

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

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

    证据分级与公开边界

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

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

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

    常见问题

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

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

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

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

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

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

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

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

    公开的 DBC 能用来修车吗?

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

    延伸阅读

    参考资料

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

    免责声明

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

    更新记录

    • 2026-10-14 初稿

  • Outlander PHEV 的 CAN 日志:双通道同录与三条总线怎么分工

    结论:这份 Outlander PHEV 日志集的价值不在报文清单,而在「双通道同时录 + 视频打时间戳」这个组合。三个总线分两组录制:主线 C 总线(带诊断口那条)与 EV-CAN 同录,EV-CAN 与 BMU-CAN 同录,因此同一个物理事件能在两条总线上互相对齐。BMU 侧 0x3C2 每 100 ms 报一次电池电流,零点在 0x80000000,是天然的时间基准。

    两条录制配置决定了能回答什么问题

    采集者用的是 GVRET 硬件,日志存成 SavvyCAN 可读的 CSV。通道分配是理解整套日志的钥匙:main-ev 目录里通道 0 是主线 C 总线(即带诊断连接器那条),通道 1 是 EV-CAN;ev-bmu 目录里通道 0 是 EV-CAN,通道 1 是 BMU-CAN。

    目录 通道 0 通道 1 能对齐的事件
    main-ev 主线 C 总线(诊断口所在) EV-CAN 车身动作与驱动系统响应
    ev-bmu EV-CAN BMU-CAN 用电设备动作与电池电流变化

    这个配置带来一个直接好处:EV-CAN 在两组里都出现,因此可以把两组日志通过 EV-CAN 上的共同事件接起来,间接得到「车身动作 → 电池电流」的因果链,而不需要三次同时录制。

    时间戳的来源与精度

    每个日志目录下的说明都给出一串 TS 数值,单位是秒,来自 SavvyCAN 文件时间戳,由人工对照视频读出。作者明确说明这些时间戳「不是 100% 准确」。使用时应当把它当作事件排序依据而非精确时延依据——它能告诉你「压缩机启动发生在按下 AC 之后」,但不能告诉你相差了多少毫秒。

    BMU 侧:0x3C2 是整套日志的锚点

    0x3C2 是这堆日志里最有用的报文,因为它同时满足三个条件:高频、物理量连续、与下游事件强相关。它的前 4 字节是一个大端值,表示电流流向;零点为 0x80000000,大于该值表示电流从电池流出(放电),小于该值表示充电电流。第 5 字节在所有状态下都是 C8,作者推测为状态字节。

    字段 位置 取值含义 备注
    电流值 字节 1–4 大端,零点 0x80000000 大于零点为放电,小于为充电
    状态 字节 5 恒为 C8 含义未确认
    周期 — 100 ms 适合做事件对齐

    作者专门提到该总线上大部分报文已在 SimpBMS 项目的解码文档里有说明,因此 BMU 侧的解读工作主要集中在少数几条上。这也是做车型逆向时的常见省力路径:先找社区已有的解码文档,再补自己的缺口。

    EV 侧:0x388 的逐字节观察

    0x388 被判断为空调压缩机状态报文,是这份日志里逐字节记录最完整的一条。它的价值在于提供了一个「状态机 + 设定值」同帧的范例。

    字节 观察行为 推断
    字节 0 压缩机启动后由 01 变 81,停止后变 82 运行状态位
    字节 1 多为 00,字节 0 由 81 变 82 时变为 02 次级状态位
    字节 2–5 数值干净,疑似设定值 可能是传感器输出或目标值
    字节 6 常见(可能恒为)00 未使用
    字节 7 压缩机关为 02,开为 06 压缩机开关指示

    这条报文的可靠性来自交叉验证:作者记录了 EV 模式开空调时 0x388 的首次非零时刻(TS 247.92380 出现第一条 01 00 00 00 00 00 00 06,TS 251.12 字节 2–3 首次非零),并指出 BMU 通道上 0x3C2 的电流尖峰与视频、与 0x388 的取值变化几乎完全吻合。三条独立证据对齐,才把一个「疑似」变成可用结论。

    负向对照的价值

    日志集里有两个刻意做的对照实验。一个是「enable-ac-no-refrigerant」:在冷媒被抽掉之后开空调,TS 669 之后电池电流没有明显变化,因此判断压缩机根本没启动,空调控制器可能检测到低压而拒绝吸合。另一个是「ac-on-no-refrigerant」,在主线 C 总线上重录了一遍。这类「什么都没发生」的日志在逆向里极有价值——它给出了信号在负条件下的基线。

    类似的还有加热器那两段。作者原本想录 EV 模式下的加热器工作,后来发现这辆车根本没有装电加热器,只有暖风水泵在转。这段日志因此被保留为一个明确的反例:报文可能照发,但末端执行器不存在。

    日志清单与使用场景

    整套日志按录制场景命名,覆盖的模式差异决定了各自适合回答什么问题。EV 目录下有 8 组,主线目录下有 3 组。

    场景 适合用来找什么
    normal-mode-gear-selection 挡位切换对应的报文变化(N/D/R/P 反复切换)
    charge-mode 与 long-charge-mode-novideo 充电模式进入与发动机介入/退出的时序
    driving-ev 低速前进后退、B 挡与 4WD Lock 模式下的驱动报文
    ac-on-ev-mode 压缩机启停与 0x388、0x3C2 电流的对应关系
    enable-ac-no-refrigerant 负向对照:无冷媒时电流与状态报文的基线
    heater-attempt-ev-mode 与 ev-heater-mode-maybe-again 反例:无电加热器时的报文行为
    steering-lock-to-lock 方向盘左右打死过程中的转向角报文
    parking-brake 驻车制动反复拉放的单变量对照

    其中 parking-brake 那组是很好的单变量样本:从 TS 370 到 TS 392,驻车制动每隔约 2 秒拉放一次,连续十次,几乎没有其他干扰动作。要定位一个开关量报文,这种设计比长距离行驶高效得多。

    常见问题

    这两组日志为什么不一次录三条总线?

    因为录制设备只有两个通道,作者改为分两组录制并让 EV-CAN 在两组中都出现,从而用 EV-CAN 上的共同事件把两组日志接起来,间接得到主线 C 总线与 BMU-CAN 之间的对应关系,无需三通道硬件。

    日志里的时间戳能用来算精确时延吗?

    不能。TS 数值是从 SavvyCAN 文件时间戳人工对照视频读出的,作者明确说明不是百分之百准确。它适合确定事件先后顺序与大致间隔,不适合作为毫秒级时延测量的依据。

    0x3C2 的电流值怎么判断是充电还是放电?

    以前 4 字节的大端值与零点 0x80000000 比较:大于零点对应电流从电池流出(放电),小于零点对应充电电流。该报文每 100 ms 发送一次,第 5 字节在所有状态下恒为 C8。

    0x388 能确认是空调压缩机状态吗?

    属于强推断而非定论。作者给出了逐字节观察:字节 0 在压缩机启动后由 01 变 81、停止后变 82,字节 7 在压缩机开时为 06、关时为 02;同时 BMU 侧 0x3C2 的电流尖峰与视频记录、与 0x388 取值变化几乎完全吻合。三条证据对齐,但仍应视为高置信度推断。

    为什么还要录「无冷媒」和「没有电加热器」这种日志?

    因为负向对照能给出信号在条件不满足时的基线。无冷媒时 TS 669 之后电池电流无明显变化,说明压缩机未启动,控制器可能因低压拒绝吸合;加热器那两段则证明这辆车未装电加热器,报文可能照发但末端执行器不存在。这类样本能防止把「报文存在」误读成「功能存在」。

    延伸阅读

    参考资料

    • 项目 05 —— public-can-dbc-datasets/additional/projectgus-outlander-phev-logs/README.md(车辆状态、日志格式 GVRET CSV 与三个目录的分工)
    • 项目 05 —— public-can-dbc-datasets/additional/projectgus-outlander-phev-logs/ev-bmu/README.md(通道分配、0x3C2 电流编码、0x388 逐字节观察与各组日志时间戳)
    • 项目 05 —— public-can-dbc-datasets/additional/projectgus-outlander-phev-logs/main-ev/README.md(主线 C 总线三组日志的场景与时间戳)
    • 项目 05 —— public-can-dbc-datasets/additional/projectgus-outlander-phev-logs/cmu/README.md(CMU EEPROM 的 sigrok 逻辑分析仪采集说明)

    免责声明

    本文所述日志来自一辆 2019 年款 Outlander PHEV 事故车的采集记录,车辆状态与配置的代表性有限。文中报文 ID、字节取值与时间戳均为该公开仓库的观测记录,未经独立复现,其中多处标注为推断而非确认。请勿据此对车辆进行任何发送或控制操作,实际工作请以自有采集与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • Mirage CAN 节点地图:59 个地址里只有 2 个已解码

    结论:这份 Mirage CAN 节点地图索引了 59 个节点地址,但只有 2 个达到「部分解码并理解」的成熟度:101 发动机状态与 425 外部进入状态;另外 36 个仅采集了样本,21 个只是看见过。没有任何节点达到「完整理解」级别,而 450/451/453/454 这一组被单独拆出成文,因为它们行为高度相似且很可能来自同一个物理模块。

    先读状态图例,再读节点列表

    这份材料最有价值的不是某个字节的含义,而是它自带的一套成熟度分级。作者用四个色块把每个节点区分开:红色表示只是看见过、不再跟进;黄色表示已采集样本数据;绿色表示部分数据已解码并理解;蓝色表示对该节点的报告方式有像样的理解(接近完整)。

    成熟度 含义 节点数 可直接用于解码吗
    已看见 仅记录地址,无样本 21 否,只作地址清单
    已采集样本 有原始字节样本,未解释 36 否,可作比对基线
    部分解码 部分字段已确认含义 2 是,但需自行复核
    理解较完整 报告方式已基本掌握 0 不适用

    把这张表放在最前面看,能避免一个常见误用:把地址清单当成已解码的信号表。这份材料里真正被作者标注为已理解的,只有两个节点。

    两个成熟度为「部分解码」的节点

    101 是单字节的发动机状态,只在车辆处于非「钥匙关闭」的任何状态时出现,报告很慢(约 1 pps)。425 是外部进入状态,八字节,其中车门与锁止两字节已给出取值表,但作者注明「尚未完整映射」。

    节点 字节 取值 含义
    101 字节 1 01 按下 FAST 按钮时短暂出现
    101 字节 1 04 钥匙通电,发动机未运转
    101 字节 1 44 发动机正在起动
    101 字节 1 84 发动机运转中
    425 字节 2 00 / 01 / 03 车门:全关 / 右前 / 左前
    425 字节 3 01 / 02 锁止:已锁 / 未锁

    这两个节点的共同特点是:取值离散、可用外部动作直接触发验证、且重复出现稳定。这也是判断一个 CAN 节点能否从「样本」升级为「解码」的实用判据。

    另一类可用动作验证的字段:315 的灯组

    315 的第 5 字节随转向灯与灯光状态变化,虽然节点本身仍标为「已采集样本」,但这一字节的取值规律已经被梳理出来:全关为 00,示宽灯为 09,近光灯为 11;转向灯在此基础上叠加,左转为 02(叠加后示宽灯态 0B、近光灯态 13),右转为 04(叠加后 0D、15),并在闪烁中交替。这种「基础值 + 增量」的编码在日系车型里很常见,值得单独记下来。

    45x 这一组为什么单独拆文件

    450、451、453、454 四个地址被作者放到单独的 45x.md 里,理由写得很直白:它们行为与报告方式非常相似,很可能相关,甚至是同一个物理模块。这种判断在逆向工程里很关键——连续地址段上的相似帧往往不是四个独立 ECU,而是一个模块的多路复用输出。

    字节序 取值特征 说明
    字节 1 恒为 FD 四者共同
    字节 2 区分地址的关键字节 450 为 11;451 为 13 与 31;453 为 14;454 为 10
    字节 3 恒为 3F 四者共同
    字节 4–8 恒为 FF 四者共同

    四个地址共用相同的常量字节,只有字节 2 不同,且报告速率一致(约 2 pps)。这强化了「同一模块、不同子标识」的假设。注意 451 的字节 2 有两个取值(13 与 31),意味着它可能承载了状态切换而非单纯的标识。

    从常量字节能推出什么

    字节 3–8 全为 FF 而非常见的 00 填充,这一细节本身有信息量:它排除了「未使用字节清零」的默认实现,说明该模块的报文模板是带填充的。对只做解码的人来说,这能避免把 FF 误读为「满量程」之类的物理量。

    使用边界:这不是 DBC,也没有许可证

    材料的来源说明写明了几条硬边界,必须在引用前确认。它覆盖的是 2013 年款之后的 Mirage,但具体观测年款、市场与 ECU 变体均未说明;它自称是占位性质的项目,未做过独立的采集复核或逐信号验证;仓库未找到许可证文件。

    边界项 说明 对使用者的含义
    格式 节点观察笔记,非 DBC 不能直接被 cantools 等工具加载
    车型精度 2013 年款之后,年款/市场/ECU 未说明 跨年款复用前需自行复核
    验证程度 仅两个节点部分解码,无独立复核 其余节点只能作为采样基线
    权利状态 未发现仓库许可证 仅可作为私有离线研究副本

    因此这份材料合理的用法是:作为地址清单与假设来源,配合自己的采集做交叉验证;而不是作为可直接投产的信号定义。

    常见问题

    这份节点地图能直接当 DBC 用吗?

    不能。它是 CAN ID 观察记录与部分字节级解码笔记,不是 DBC 文件,也不能被 cantools 之类的工具直接加载。要使用需要自行把已确认的字段整理成 DBC 或解析规则。

    59 个节点里有多少是可以放心用的?

    只有 2 个达到「部分解码并理解」的级别,即 101 发动机状态与 425 外部进入状态;另有 36 个只有样本数据、21 个仅记录地址。没有任何节点达到作者定义的「理解较完整」级别。

    450、451、453、454 是四个不同的 ECU 吗?

    很可能不是。作者把它们单独拆成 45x.md,理由是四者行为与报告方式高度相似,可能相关甚至就是同一个物理模块。四者共用相同的常量字节(字节 1 为 FD、字节 3 为 3F、字节 4–8 为 FF),只有字节 2 不同,报告速率同为约 2 pps。

    315 节点的转向灯取值是怎么编码的?

    采用基础值加增量的方式。第 5 字节全关为 00,示宽灯为 09,近光灯为 11;左转在此基础上加 02、右转加 04,且与灯光状态叠加:左转在示宽灯态为 0B、近光灯态为 13,右转对应 0D 与 15,取值在闪烁中交替。

    这份材料能用于其他年款的 Mirage 吗?

    不能默认可以。来源说明指出它覆盖 2013 年款之后的车型,但具体观测年款、市场与 ECU 变体均未说明,且未做过独立采集复核。跨年款复用前必须用自己的采集逐信号验证。

    延伸阅读

    参考资料

    • 项目 04 —— public-can-dbc-datasets/additional/phauxcamus-mitsubishi-mirage-can/Nodes/README.md(59 个节点索引、四档状态图例与逐节点观察数据)
    • 项目 04 —— public-can-dbc-datasets/additional/phauxcamus-mitsubishi-mirage-can/Nodes/45x.md(450/451/453/454 的共性观察与字节 2 取值)
    • 项目 04 —— public-can-dbc-datasets/additional/phauxcamus-mitsubishi-mirage-can/SOURCE.md(快照提交、采集日期、覆盖年款与权利状态)

    免责声明

    本文引用的节点数据来自公开仓库的逆向观察笔记,作者本人未做逐信号验证,仓库也未声明适用的具体年款、市场与 ECU 变体,且未发现许可证文件。文中所有字节取值均为该仓库的观测记录,不代表车辆制造商的官方定义。请勿将其用于安全相关用途,复用前请以自有采集独立验证。

    更新记录

    • 2026-10-14 初稿

  • Athena 日志转发:4096 字符分帧、100 秒等待与 1 小时重发门槛

    结论:Athena 诊断日志转发是在 swaglog 已落盘之后的独立一段:非 PC 会话线程每约 10 秒扫描 swaglog_root,把候选文件整份读入、组装 RPC、按 4096 字符拆成 WebSocket 帧发送,并等待最多约 100 秒的成功响应。三个数字决定重发行为:4096 字符分帧、100 秒确认等待、3600 秒重发门。要注意两个非持久化事实:写入 user.upload 的发送时间只是「已尝试」标记,不是云端交付证明;确认只依据响应 JSON 的 result.success,不核对文件哈希或服务端对象键。而响应里的 id 会被直接拼进日志根路径,log_handler() 未校验它是否为本进程已发送的普通文件名。

    转发链:从轮转文件到 WebSocket 分帧

    本地来源是 SwaglogRotatingFileHandler 按 INFO 以上轮转落盘的文件,转发线程读的是完整文件而非逐条 logMessage。下表是这一段的关键函数与职责。

    函数 / 组件 实现职责
    SwaglogRotatingFileHandler 把 INFO 以上日志轮转落盘;转发线程读取完整文件,而非逐条 logMessage。
    get_logs_to_send_sorted() 枚举 Paths.swaglog_root(),检查 user.upload,未标记或标记早于一小时的文件进入候选;排序后排除候选集末项。
    xattr_cache.getxattr()/setxattr() 按路径与属性名缓存读取;本模块 setxattr() 先清缓存再写属性。来自其他进程的属性改动不主动失效。
    log_handler() 非 PC 会话线程每约 10 秒重新扫描;每轮弹出一个候选,写发送时间、读全文件、组装 RPC,并消费确认队列。
    send_queue_push()、ws_send() 日志请求采用低优先级,队列按 (priority, seq) 排序;发送线程把消息按 4096 字符拆成 WebSocket 帧。入队不等于已写到网络。
    jsonrpc_handler()、is_response() 带 id 且含 result/error 的消息进入模块级 log_recv_queue;无专门的请求来源或连接代号关联。

    三个数字决定重发行为:4096 / 100 / 3600

    第一是分帧长度:ws_send() 把消息按 4096 字符拆成 WebSocket 帧,发送中失败时该消息已经从队列取走,源码没有针对这条发送失败即时重排,重发只能依赖一小时扫描。第二是确认等待:当前文件对应的响应最多等待约 100 次、每次 1 秒。第三是重发门:get_logs_to_send_sorted() 用当前墙钟秒数减去 xattr 里的发送时间,无标记或超过 3600 秒即视作可重发。

    阈值 / 取值 含义 边界
    4096 字符 ws_send() 的 WebSocket 分帧长度 发送中失败后消息已出队,无即时重排
    约 100 秒 等待当前文件成功响应的最长时间(约 100 次 × 1 秒) 期间收到其他 ID 的响应仍会处理其成功标记
    3600 秒 无标记或标记早于一小时才重新列为候选 失败后不会立刻重试
    2147483647 本地「收到成功响应」的哨兵值 正常当前时间下不会进入候选,但不是对象存储的签名回执

    还有一个排序细节:返回值是 sorted(logs)[:-1],排除的是当次可发送候选中排序最大的文件,而不是先在全部目录项中识别活动文件。如果最新活动文件已有哨兵值而不在候选集,另一个较新候选也会被排除——这是源码排序条件的推导,文件名与实际轮转状态的一致性仍需验证。

    「已尝试发送」标记先写,打开可能失败

    log_handler() 从候选尾部取文件,先写当前 Unix 秒数到 user.upload,后打开并完整读取文件、调用 send_queue_push()。这个顺序带来一个明确后果:文件打开失败、进程中断、发送线程尚未发送、网络中断,都可能留下「已尝试」标记;而没有成功确认时要等一小时条件满足才能重新列为候选。探针也复现了这一点——打开日志失败前已经写入了「尝试发送」时间,且没有排入发送队列。

    此外,int.to_bytes(curr_time, 4, ...) 与 open(...).read() 之间没有事务,单个轮转文件会全部进入内存及 JSON 消息,这条路径没有专属的文件大小上限。对读属性时的 ValueError/TypeError 会被当成「未标记」,其他 OSError 交由外层循环记录。

    确认只信响应 JSON,不校验文件身份

    result.success 为真时,才把响应 id 对应的文件标成哨兵值;错误响应、超时或解析异常不会直接清理原发送时间。确认只基于响应 JSON 字段,不核对文件哈希、服务端对象键或持久化状态。由于每个请求的 id 就是文件名,而消费响应时未核对当前已发送的 ID 集合,这里存在可被误触的风险:响应 id 被直接传给 os.path.join(Paths.swaglog_root(), log_entry) 并用于 setxattr(),is_response() 只检查形状。

    探针显示:在没有当前发送任务时,形如 {"id":"../outside","result":{"success":true}} 的假响应仍会使 log_handler() 尝试对拼出的 /logs/../outside 写成功属性。这只证明函数没有已发送 ID 校验,不证明生产 WebSocket 可收到该响应、外部路径可写或服务器已持久化日志。插件化时正确的做法是把关联 ID 映射为宿主持有的文件句柄或受限路径,而不是用远端字符串拼路径。

    硬件与插件化验收

    源文档给出一条清晰的验收方向:诊断转发只能在非实时服务域运行,限制单文件与单次 RPC 大小,并定义网络费用、电量与存储写入预算,不影响控制周期与录制带宽;以本地生成的不可猜任务 ID 绑定文件身份、内容摘要和连接代号,独立记录 pending/sending/accepted/retry_wait,只有服务端可验证确认才推进终态;日志收集与远端转发分别授予能力,默认脱敏设备标识、位置、令牌和用户数据。测试上应在离线服务模拟器中注入发送前崩溃、分帧中断、延迟/重复/伪造响应、墙钟跳变、xattr 不可用、活动文件轮转及超大文件。

    常见问题

    写入 user.upload 的发送时间能证明日志已上传成功吗?

    不能。它只是「已尝试」标记,源码先写这个时间再打开并读取文件,因此文件打开失败、进程中断或网络中断都可能留下该标记。真正的成功标记是收到 result.success 后写入的 2147483647 哨兵值,而它也只是本地的成功响应标记,不是对象存储的签名回执。

    发送失败后日志会立刻重试吗?

    不会立刻。get_logs_to_send_sorted() 用当前墙钟秒数减 xattr 发送时间,无标记或超过 3600 秒即视作可重发;ws_send() 发送中失败时消息已经从队列取走,源码没有即时重排,重发依赖一小时扫描。

    为什么确认只等 100 秒,而不是一直等?

    源码对当前文件对应的响应最多等待约 100 次、每次 1 秒。期间即使收到其他 id 的响应,仍会处理其成功标记,直到收到当前 id 或等待结束;log_recv_queue 位于模块级,重连时不会清空。

    响应里的 id 被直接当路径用,有风险吗?

    有校验缺口。响应 id 会被直接拼进日志根路径并用于 setxattr(),而 is_response() 只检查形状、log_handler() 未验证这个 ID 是本进程已发送的普通文件名。探针证明函数没有已发送 ID 校验,但不证明生产环境可达或被利用;插件化时应改用受限路径或文件句柄映射。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/ATHENA_DIAGNOSTIC_LOG_FORWARD_ACK_DEEP_DIVE.md(取用了转发函数的组件索引、4096/100/3600 三个阈值、发送标记先写、确认字段与路径拼接风险、插件化验收要求)。

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

    更新记录

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