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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注