GB/T 27930直流充电兼容性测试:29位CAN ID与时序定位
简介面向新能源汽车充电技术研发、检测认证与充电设施运维人员的实测分析文献聚焦车桩充电兼容性这一制约电动汽车普及的关键难题。内容源自中国汽车技术研究中心试验团队在京津冀、上海、深圳等地40余个直流充电站的实地测试覆盖10余种主流车型从现场充电环境、充电接口、绝缘检测处理以及通信时序、故障模拟等方面系统梳理问题成因并围绕新国标GB/T 27930等要求给出改进思路。资源为1个PDF文件压缩包约2.98MB版式规整、篇幅精炼便于快速通读与检索引用。目前已有131人学习下载适合汽车技术从业者与高校师生作为参考文献用于理解车桩充电匹配流程、排查兼容性故障或支撑相关课题研究与技术报告写作。1. 兼容性测试不是能不能充上电而是这套 27930 时序有没有被走完同一辆电动汽车上个月在 A 品牌 120 kW 直流充电桩上充得好好的换到 B 品牌 60 kW 桩插枪 30 秒后弹出充电失败请重新插枪再换到 C 品牌又能充只是充到 SOC 78% 就断了。售后拆桩、进店查车两边都报无故障码。这类挑桩现象九成不是硬件坏了而是 GB/T 27930 定义的那套车桩对话——握手、辨识、参数配置、预充、充电、结束——在超时窗口、字段边界、状态机推进顺序上出现了偏差。直流充电桩兼容性测试要做的就是把偶发变成可复现、可定位、可回归在 CC1/CC2 与 CAN_H/CAN_L 上取点抓全 250 kbps、29 位扩展帧的全部报文按国标逐条比对时序和字段最后固化成一张能长期跑的用例表。适合充电桩测试、整车 BMS/VCU 标定、第三方检测和售后诊断岗。2. GB/T 27930 直流充电通信链路报文、时序与 29 位 CAN ID 的推算2.1 从充电接口到应用层一次直流充电要跨过哪几层直流充电从来不是插上就有电。GB/T 20234.3 规定的那把 9 芯枪里CC1 由车端确认枪是否插到位CC2 由桩端确认并作为车辆允许充电的握手信号PE 负责等电位与漏电保护。控制导引部分由 GB/T 18487.1 定义插枪后桩侧先输出 12 V检测到电阻分压变化后切到 6 V 并叠加 1 kHz PWM占空比对应桩能提供的最大电流。这一层没谈成后面的 CAN 通信根本不会开始所以台架上永远先看 CC1/CC2 电压再看报文。CAN 层是 250 kbps、29 位扩展帧、双绞屏蔽全程只有车和桩两个节点没有第三方。应用层才是 GB/T 27930 的主场报文结构沿用 SAE J19393 位优先级 18 位 PGN 8 位源地址PDU1 格式里 PDU Specific 位放目标地址PDU2 里放组扩展。充电机源地址惯用 0x56BMS 源地址惯用 0xF4因此只看仲裁 ID 就能分清报文方向。把这四层拆开后面所有兼容性讨论才有坐标混在一起看只会得到时好时坏这种没法交付的结论。层级依据标准关键参数出问题时先看什么物理接口GB/T 20234.1 / 20234.39 芯定义、端子温升、锁止枪座端子有无退针、灼烧控制导引GB/T 18487.1 附录 A/BCC1/CC2 电压、1 kHz PWM 占空比万用表量 CC1 对 PE 电阻数据链路CAN 2.0B250 kbps、29 位扩展帧、120 Ω 终端总线负载、错误帧计数应用层GB/T 27930PGN/SPN、超时窗口、多帧传输抓包看报文序列是否完整2.2 六个阶段与关键报文握手、辨识、参数配置、预充、充电、结束整段充电流程是有明确先后顺序的。握手阶段双方互报协议版本和最高允许充电电压辨识阶段充电机把辨识结果和自己的编号发给 BMSBMS 把电池类型、额定容量、VIN 等一长串信息回给充电机参数配置阶段双方交换电压电流上限并各自给出准备就绪预充阶段桩侧做绝缘检测、闭合接触器并预充到接近电池电压充电阶段按毫秒级周期刷新需求与实测结束阶段正常收尾或由任一方发中止报文。顺序错一步桩和车就会各自认为对方没按规矩来。阶段切换靠的是双向就绪信号而不是单方面超时。比如 CRO充电机输出准备就绪和 BROBMS 充电准备就绪必须都置位桩才允许闭合 K1/K2BMS 侧如果没有收到 CRO 就去等 BCL 的回复就会一直卡在预充前。常见做法是把每个阶段的进入条件、退出条件和超时时间列成一张状态表抓到日志后直接对照比逐帧猜要快得多。报文缩写方向典型周期关键内容充电机握手CHM桩→车250 ms通信协议版本号BMS 握手BHM车→桩250 ms最高允许充电电压充电机辨识CRM桩→车250 ms辨识结果、充电机编号BMS 辨识BRM车→桩250 ms电池类型、额定容量、VIN多帧充电机最大输出能力CML桩→车250 ms最高输出电压、最小/最大输出电流动力蓄电池充电参数BCP车→桩500 ms最高允许电压、最大允许电流充电机输出准备就绪CRO桩→车250 ms就绪状态BMS 充电准备就绪BRO车→桩250 ms就绪状态电池充电需求BCL车→桩50 ms需求电压、需求电流、充电模式电池充电总状态BCS车→桩250 ms测量电压、测量电流、SOC充电机充电状态CCS桩→车50 ms输出电压、输出电流、累计时间动力蓄电池状态信息BSM车→桩250 ms单体最高电压、最高温度BMS 中止充电BST车→桩250 ms中止原因位域充电机中止充电CST桩→车250 ms中止原因位域BMS 错误报文BEM车→桩250 ms错误类型充电机错误报文CEM桩→车250 ms错误类型其中 BRM 数据域较长必须走 J1939 的传输协议先发 TP.CM 声明长度和包数再用一串 TP.DT 分片传输最后靠 TP.CM 的结束帧收尾。兼容性事故里有相当一部分出在这里——BMS 发的分片间隔太长、充电机的流控帧CTS允许包数给得太小、或者某一方在收完最后一包后没清状态下一轮再进辨识就撞车。2.3 用 J1939 规则推算 29 位 CAN ID抓包工具只给一个十六进制 ID人眼很难判断这是谁发给谁的。按 J1939 编码规则自己算一遍比对着文档翻表快得多。def j1939_id(priority: int, pgn: int, sa: int, da: int | None None) - int: 按 SAE J1939 规则拼 29 位扩展帧 IDGB/T 27930 沿用同一套编码。 priority: 报文优先级3 bit越小越优先 pgn: 参数组编号 sa: 源地址 da: 目标地址仅 PDU1 格式PF 240有效 pf (pgn 8) 0xFF # PDU Format决定是点对点还是广播 ps pgn 0xFF # PDU Specific if pf 240: # PDU1PS 位让位给目标地址 ps da if da is not None else 0xFF return (priority 26) | (pf 16) | (ps 8) | sa # 充电机 - BMS 的握手报文源地址 0x56目标地址 0xF4 print(hex(j1939_id(6, 0x2600, 0x56, 0xF4))) # 0x1826f456 - CHM # BMS - 充电机的握手报文方向反过来 print(hex(j1939_id(6, 0x2700, 0xF4, 0x56))) # 0x182756f4 - BHM # 充电机辨识报文 print(hex(j1939_id(6, 0x2800, 0x56, 0xF4))) # 0x1828f456 - CRM逻辑很直白优先级占用 ID 的高 3 位接着是 PF然后是 PS 或目标地址最低 8 位永远是源地址。参数怎么改如果某个报文在总线上总是抢不过别的帧就调小 priority如果换成 PDU2 格式的广播报文pf 会大于等于 240此时 ps 必须按组扩展填值不能填目标地址。抓包时用candump can0,1826F456:1FFFFFFF这种掩码过滤就是拿这套编码做匹配的。2.4 同一份国标为什么还会充不上几类高频偏差国标给的是报文格式和流程框架很多细节留了实现空间偏差就从这些地方冒出来。第一类是超时窗口有的桩把等待 BHM 的时间实现成 5 s有的实现成 3 s冬天 BMS 上电慢一点就直接被判超时。第二类是数值分辨率与偏移电压电流通常按固定分辨率步进温度类信号常带负偏移双方四舍五入方式不同就会出现桩认为 500 V、车认为 499.8 V的临界抖动。第三类是状态机的多做一步桩在收到 BRO 之前先发了 CRO规范上不算错但有些 BMS 会因为顺序不符直接进错误态。字段/环节国标约束常见误实现现场表现握手超时收到对方握手报文后停止发送用固定 3 s 定时器冷车首次插枪必失败多帧流控按 TP.CM 协商包数与间隔固定 20 ms 发完所有分片BRM 丢包、辨识失败电压分辨率按定义步长上报直接截断不四舍五入临界值来回跳反复中止就绪顺序CRO/BRO 双向置位后合闸单侧就绪即合闸预充电流冲击、绝缘报错中止原因位域按位定义上报整字节填 1日志里看不出真实原因3. 搭一套可复现的直流兼容性测试台架接线、抓包与最小验证3.1 台架硬件清单与取点位置台架不必一步到位但取点位置必须一次规划好否则复现时还得重新拆车。最低配置是一台能录 250 kbps 的 CAN 分析仪一路差分探头接 CAN_H/CAN_L 看波形质量一路高压差分探头看直流母线电压一只万用表盯 CC1/CC2 对 PE 的电压和电阻。要验证桩侧逻辑就配一台 BMS 模拟器要验证车侧逻辑就配一台桩模拟器两者都能按脚本回放和注入报文。设备用途接线要点备注CAN 分析仪记录车桩报文并接在 CAN_H/CAN_L共地终端电阻只在两端各 120 Ω差分探头 示波器看总线电平与波特率探头地接 PE用于判断反射、干扰高压差分探头看母线电压与预充过程量程覆盖最高输出电压注意安全距离可编程直流负载模拟电池包吸收电流按电池电压区间设定便于构造边界工况BMS 模拟器 / 桩模拟器单侧闭环验证与真实设备同接口支持报文回放与故障注入绝缘电阻测试仪复核绝缘检测判据断高压后测量仅在断电状态下使用3.2 用 candleLight can-utils 抓一段完整的握手过程Linux 下最省事的组合是 candleLight 类适配器加 can-utils插上就能用不需要装厂商驱动栈。# 1. 拉起 CAN 通道GB/T 27930 固定 250 kbps sudo ip link set can0 down 2/dev/null sudo ip link set can0 type can bitrate 250000 restart-ms 100 sudo ip link set can0 up # 2. 确认状态与波特率重点看 state 是不是 ERROR-ACTIVE ip -details -statistics link show can0 # 3. 全量落盘文件带时间戳方便和车辆日志对齐 candump -L can0 charge_$(date %Y%m%d_%H%M%S).log # 4. 只想盯握手阶段时用掩码过滤:1FFFFFFF 表示 ID 全位匹配 candump can0,1826F456:1FFFFFFF can0,182756F4:1FFFFFFFbitrate必须和车桩一致写成 500000 会直接收不到任何报文restart-ms 100让控制器在总线错误后自动恢复长时间测试时很关键否则一次干扰就断掉整段录制。-L是 log 格式保留纳秒级时间戳后面做时序分析时不用再猜时间对齐。第 4 条命令里的掩码:1FFFFFFF是匹配位数写:1FFFFFF8就变成前 29 位相同的模糊匹配适合一次过滤多个同类报文。如果ip -details里错误计数一直涨先别怀疑协议去看终端电阻和线束走向。3.3 用 python-can 和 cantools 解码并画充电曲线抓下来的原始日志只能看 ID真正定位问题要靠 DBC 解码。DBC 需要按 GB/T 27930 的 PGN 和信号定义自己整理一份整理一次可以长期复用。import can import cantools import matplotlib.pyplot as plt DB cantools.database.load_file(gbt27930.dbc) bus can.Bus(interfacesocketcan, channelcan0, bitrate250000) t0 None t_need, i_need [], [] # BCL车端需求 t_real, i_real, u_real [], [], [] # CCS桩端实测 for msg in bus: if msg.is_error_frame: continue try: decoded DB.decode_message(msg.arbitration_id, msg.data) except (KeyError, cantools.database.DecodeError): continue # 多帧不完整或不在 DBC 内跳过避免脏数据 if t0 is None: t0 msg.timestamp rel msg.timestamp - t0 if Bcl in decoded: t_need.append(rel) i_need.append(decoded[Bcl][CurrentDemand]) elif Ccs in decoded: t_real.append(rel) i_real.append(decoded[Ccs][OutputCurrent]) u_real.append(decoded[Ccs][OutputVoltage]) fig, ax1 plt.subplots() ax1.plot(t_need, i_need, labelBMS 需求电流) ax1.plot(t_real, i_real, label桩输出电流) ax1.set_xlabel(相对时间 / s) ax1.set_ylabel(电流 / A) ax2 ax1.twinx() ax2.plot(t_real, u_real, colorgray, linestyle--, label输出电压) ax2.set_ylabel(电压 / V) plt.show()interfacesocketcan表示走内核 CAN 栈用 PCAN 或 Vector 硬件时换成对应接口名即可bitrate参数在 socketcan 下由ip link决定这里只是文档性声明。解码失败的分支一定要留否则多帧拼装不完整时整段脚本会抛异常退出测试现场最怕这个。需求曲线和实测曲线叠在一起看就能判断问题在车端还是桩端需求上去而实测不动是桩侧限流或接触器问题实测跟着需求走但曲线呈阶梯状抖动多半是调节环路参数或者需求电流本身在跳。3.4 桩模拟器与 BMS 模拟器的闭环回灌抓到一段能充的日志之后把它回灌给被测设备是复现效率最高的做法。常见流程是先把日志转成报文序列按原始时间戳的相对间隔重放给桩模拟器被测桩会像面对真车一样走完整流程反过来把一段有缺陷的序列注入给 BMS 模拟器就能验证车端在异常输入下会不会误进错误态。回灌时把时间戳压缩到 0.1 倍可以在几分钟内跑完一次完整充电回归批量跑用例时才现实。提示回灌的报文序列要先剔除 TP.DT 分片的绝对时间偏差否则重放时多帧会错位得到的是假故障。4. 直流充电兼容性典型故障的定位路径从握手失败到充电中断4.1 先分层物理层、链路层、应用层各看什么排查最忌讳一上来就翻报文。正确的顺序是从下往上先确认 CC1/CC2 电压对不对再看总线上有没有帧最后才看应用层流程。CC1 电压不对说明枪没插到底或锁止没到位总线上一帧都没有看终端电阻和线束有帧但对不上才进入协议层。这套顺序能把三分之二的玄学问题挡在第一层。现象最可能的层级验证手段典型结论插枪后完全无反应控制导引 / 物理量 CC1、CC2 对 PE 电压枪未锁止或 CC 回路虚接有 CC 电压但无 CAN 帧数据链路ip -details看错误计数、量终端电阻波特率错或线束短路只有单侧握手报文应用层抓包看 ID 方向一侧超时窗口设得太短辨识阶段反复重来应用层 / 多帧过滤 TP.CM、TP.DT 检查分片流控参数不匹配充电中途停应用层 / 热管理看 BST、CST 与 BSM温度或单体电压触及边界4.2 绝缘检测与预充阶段的失败点绝缘检测是直流充电里最容易被低估的一环。桩在合闸前会对直流母线施加检测电压测正负母线对 PE 的绝缘电阻低于阈值就直接报绝缘故障。现场常见的误报来源有三种车端加了非标准的 Y 电容导致测量回路充放电时间变长雨天枪口或线束受潮以及桩的检测时序和车端的泄放时序撞在一起。判断方法很简单用绝缘电阻测试仪在断电状态下实测一遍如果实测值远高于判据却仍然报错问题就在时序而不在绝缘本身。预充阶段的问题则集中在电压差上。桩侧要让母线电压爬到接近电池端电压才闭合主接触器压差过大会产生冲击电流BMS 会认为异常并中止。有些车在低温下电池内阻升高预充时间会明显变长如果桩的预充超时窗口是固定值冬天就会出现夏天能充、冬天不行。这类问题改不了车只能在用例里单列低温工况提前和桩厂对齐超时参数。4.3 中止报文与错误码怎么读中止时报文才是答案但只打印一个十六进制值等于没看。BST 和 CST 的中止原因一般放在数据域的首字节按位表示不同原因把它拆成可读文本日志才算能用。# 中止原因位于 BST / CST 数据域 Byte1按位解析 BMS_STOP_REASON { 0: 达到SOC目标值, 1: 达到总电压设定值, 2: 达到单体电压设定值, 3: 收到充电机中止报文, 4: 绝缘故障, 5: 输出连接器过温, 6: 检测点电压异常, 7: 其他故障, } def parse_stop(payload: bytes) - list[str]: payload 为 BST/CST 报文的 4 字节数据域返回命中的原因列表。 flag payload[0] return [BMS_STOP_REASON[i] for i in range(8) if (flag i) 1] print(parse_stop(bytes([0b00010001, 0, 0, 0]))) # [达到SOC目标值, 绝缘故障]参数上要注意两点位序必须和所用标准版本的定义一致不同版本的位分配有差异另外这是原因而不是故障比如达到 SOC 目标值是正常收尾把它统计成故障会让兼容性报告失真。位定义建议直接从项目实际使用的标准文本抄进代码常量表不要凭记忆写。4.4 一份可复用的兼容性测试用例表用例表的价值在于可重复执行。每条用例只改一个变量其余保持固定跑完记录通过与否和首条异常报文。用例编号测试项设置方法判定标准DC-01常温正常充电25 ℃SOC 30% 起充走完全流程无中止DC-02低温冷车首插-10 ℃ 静置 8 h 后插枪握手在超时窗口内完成DC-03高 SOC 边界SOC 95% 起充正常收尾或温和降功率DC-04通信中断恢复充电中拔掉 CAN 线 3 s 再恢复中止原因明确不误报DC-05极端电压需求模拟单体电压触及上限走参数配置降流不硬切DC-06绝缘边界人为降低绝缘电阻报绝缘故障且不合闸DC-07重复插拔连续插拔 50 次无一次卡在辨识阶段5. 批量兼容性分析与 ISO 15118 演进下的测试关注点5.1 用脚本把一批充电日志变成兼容性矩阵单次日志能定位一个故障几十次日志才能看出规律。桩厂和车厂做互认时最有说服力的交付物是一张车型 × 桩型的矩阵表每格标明走到了哪个阶段、在哪一步失败。做法是把日志批量解码按阶段打标签后汇总。import glob import pandas as pd import cantools DB cantools.database.load_file(gbt27930.dbc) ID2NAME {m.frame_id: m.name for m in DB.messages} STAGES [CHM, BHM, CRM, BRM, BCP, CRO, BRO, CCS, CST] def scan_log(path: str) - dict: 扫描一份 candump -L 日志返回本次充电各阶段是否出现。 seen set() with open(path, encodingutf-8, errorsignore) as f: for line in f: parts line.split() if len(parts) 3 or not parts[2].startswith(can0): continue try: fid int(parts[3].split(#)[0], 16) except (IndexError, ValueError): continue name ID2NAME.get(fid) if name in STAGES: seen.add(name) return {s: (PASS if s in seen else MISS) for s in STAGES} rows {} for log in glob.glob(logs/*.log): rows[log] scan_log(log) df pd.DataFrame(rows).T # 行日志文件列阶段 df[结论] df.apply(lambda r: 通过 if r[STAGES[:8]].eq(PASS).all() else 不通过, axis1) df.to_excel(compatibility_matrix.xlsx) print(df)ID2NAME从 DBC 反查报文名避免手写 ID 表写错阶段列表决定了矩阵的列要按项目实际关心的流程调整。日志行的分割位置和 candump 的-L输出格式强相关换别的抓包工具时要同步改解析逻辑。生成的矩阵直接看哪一列全是MISS那一列对应的报文就是系统性偏差比逐份日志翻要快一个数量级。5.2 ISO 15118 落地后测试关注点会怎么变GB/T 27930 走的是 CAN 加固定长度报文ISO 15118 换了一套完全不同的骨架传输层用电力线载波承载 IPv6应用层是结构化的消息集支持即插即充和双向能量传输安全上引入 TLS 和证书体系。对测试来说最直接的变化是从逐帧对时序变成看会话和认证——关注点转向链路协商是否成功、会话建立耗时、证书校验失败时的降级行为。台架也要跟着变。CAN 分析仪之外得多一路抓以太网侧报文的能力绝缘和预充这些高压安全项仍然保留但报文层要看的东西完全不同。过渡期最现实的做法是两条链路并行测桩同时支持两套协议时先确认它在两套协议下对同一辆车的表现是否一致再单独验证切换逻辑有没有死锁。5.3 长期维护的兼容性回归清单回归清单要能跟着标准版本和车型迭代更新我一般按三层维护。第一层是必跑项覆盖正常充电、低温首插、高 SOC 边界、通信中断恢复这四条任何版本发布前都跑一遍。第二层是互认项按车型 × 桩型矩阵抽组合重点覆盖不同厂商的功率模块和不同电池包。第三层是历史缺陷项每一个修过的兼容性问题都固化成一条用例防止回退。每次跑完把矩阵和上一版做一次差异比对只有新增的不通过项才需要人介入。另外留一份报文基线把通过时的关键报文序列存下来出现新问题时先做一次自动比对通常几十秒就能定位到第一帧异常的位置。本文还有配套的精品资源点击获取