ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

GB/T 27930-2015直流充电CAN报文交互详解:从握手到统计

GB/T 27930-2015直流充电CAN报文交互详解:从握手到统计 干充电桩和BMS联调这行最怕的不是高压大电流而是CAN总线上两个节点都好好的却谁都等不到谁。早年间我在实验室调一台直流桩和一套动力电池包现象特别典型物理连接都正常绝缘检测也过了但上位机一直报“握手超时”。拿CANoe一抓桩在不停发握手报文BMS却像没听见一样。查到最后是桩侧波特率被配置成了500k而GB/T 27930-2015规定的是250k。就这么一个基础参数能让整个充电流程卡死在第一步。这篇博文想把直流充电中最常被问的“报文到底怎么交互”完整讲清楚。内容主要围绕非车载直流充电机与BMS之间的CAN通信覆盖物理层参数、四阶段充电流程、关键报文字段、超时与异常处理、多帧报文、实测抓包分析以及我在现场调试中遇到的坑。适合刚接触BMS软件或充电桩嵌入式开发的工程师也适合做充电测试、售后故障分析的人。读完你至少能回答一个问题某一帧充电报文没到系统为什么会卡在某个阶段。1. 先看这套协议管的是哪一段链路物理层和报文基础1.1 直流充电通信在整车网络里的位置很多刚开始接触BMS的人会混淆一个概念车上到处是CAN网络发动机、车身、仪表、BMS都在上面是不是随便拉一路CAN就能做充电通信不是。GB/T 27930-2015管的是车辆充电接口到直流充电机之间的通信链路而不是车内的动力CAN。具体走的是直流充电枪座里的CAN_H和CAN_L这两根针脚也就是GB/T 20234.3定义的直流充电接口。这意味着BMS在充电过程中是直接和桩对话不经过车内网关也不经过VCU。实际台架联调时BMS会有一个专门对外通信的CAN通道或者通过多路CAN板卡把一路口分出来接充电口。调试中如果发现桩收不到BMS报文先确认BMS是否把报文发到了对外通道而不是发到了车内总线上。这也解释了一个常见现象用OBD口去抓充电报文经常抓不到数据。因为OBD口通常接的是车内动力CAN或诊断CAN直流充电口那一路CAN是独立的需要从充电座后方或BMS对外接口引出。1.2 CAN参数波特率、帧格式、终端电阻GB/T 27930-2015在物理层和数据链路层采用的是CAN 2.0B协议关键参数如下参数要求说明波特率250 kbit/s现场最多的问题之一配成500k就握手失败帧格式29位扩展帧不是标准帧过滤ID时要注意数据场长度最长8字节长数据需要多帧机制终端电阻120Ω两端并联后约60Ω缺失会导致信号反射、丢帧波特率这个坑我在现场踩过不止一次。有些桩的主控板既支持CAN也支持其他高速总线出厂配置不小心把CAN波特率设成了500k或者1M车上BMS按250k收结果就是桩能看到自己发的帧却完全收不到BMS回的任何报文。这时候用示波器看CAN_H和CAN_L的位时间能快速判断正常的250k位宽是4微秒500k是2微秒。终端电阻问题则隐蔽一些。如果桩侧没有正确接入120Ω终端电阻BMS发出的报文在总线末端反射波形振铃严重容易出现偶发CRC错误。抓包时会看到大量错误帧但又不是完全不通。我常用的检查方式是断开整车端直接在充电座CAN针脚上用万用表量电阻理论上应该接近60Ω。另外要注意共地问题。直流充电时车辆和桩之间虽然有PE连接但CAN收发器参考地如果不在同一电位会导致共模电压异常。很多现场毛刺和偶发丢帧都是这个原因特别是长线束接测试工装时最好是桩端和BMS端都用隔离CAN收发器。1.3 周期与超时为什么报文节奏是协议的灵魂如果把充电通信当成两个人对话报文周期就是说话节奏。GB/T 27930-2015里的报文周期分两类一类是快速实时报文比如充电阶段的需求和输出反馈周期做到50ms级别另一类是状态类报文比如SOC、单体电压、温度周期通常250ms。还有一部分握手、统计报文只在特定时刻发一次。超时判定则是对“对方是否还在线”的确认。协议不会要求收到每一帧都处理但会规定连续多长时间收不到某类报文就判定通信中断。通用做法是按报文周期的3倍做超时比如50ms周期的报文250ms内没收到就算超时250ms周期的报文1秒内没收到就算超时。不同车厂和桩厂在实现上会有调整但思路是一致的先容错再中断。我做联调时习惯把超时参数作为单独一张表列出来而不是散落在代码里。因为现场出问题首先要回答的就是“卡在哪个阶段哪个报文超时了”。没有这张表排查效率会低很多。2. 四阶段充电流程从握手到统计每一步谁在说话2.1 握手阶段版本对齐是第一个门槛直流充电流程不是一插枪就马上灌电双方要先确认“你会不会说同一种话”。握手阶段主要由CHM充电机握手报文和BHMBMS握手报文完成。典型顺序是充电机完成物理连接检测和绝缘检测后开始周期发送CHM报文中包含充电机支持的通信协议版本号。BMS收到CHM后如果版本能接受就回复BHM里面也带回版本信息。如果双方版本不对应BMS会进入错误处理或者桩侧在超时后主动中止。实际测试中这一阶段最容易出现的问题是“BMS一直没收到CHM”。桩以为自己发了但BMS验收滤波没放行对应ID或者桩的报文周期和BMS期望不一致。我建议调试时先把CAN验收滤波器配置成全接收确认能抓到所有报文后再逐步收窄否则容易把自己绕进去。握手完成意味着双方确认了通信关系但还没确认“电压电流怎么充”。接下来进入参数配置。2.2 参数配置阶段BMS提需求充电机亮底牌握手之后BMS要发BRMBMS辨识报文把自己是谁说清楚内容包括电池类型、额定容量、生产厂商信息等。这份报文内容较长超过单帧8字节所以要用多帧方式传输这也是很多调试新手第一次接触多帧报文的地方。随后双方会进行时间同步CTS和BTS按50ms周期相互发送。时间同步的目的是让桩和BMS在统计充电数据时基于同一个时钟基准避免出现“充电时间对不上”的扯皮。真正决定能不能充电的参数协商从BCP开始。BMS发送BCP告诉充电机电池的额定容量、总电压、最高允许充电电压、最高允许充电电流等信息。充电机收到后结合自身能力回复CSP和CML。CML有点像“充电机的底牌”告诉BMS自己能输出的最大电压、最小电压和最大电流。如果BMS的需求高于充电机能力充电阶段会在限制范围内运行。比如BMS说最高允许充电电压500V但桩最大只能输出400V那后续充电需求按400V来协商。这一阶段的所有参数都必须落在双方都能接受的范围否则不会进入充电阶段。2.3 充电阶段50ms的实时你来我往参数配置完成后充电机开始输出电压电流系统进入充电阶段也是报文最密集的阶段。BMS作为控制方周期性发送BCL给出充电需求电压和充电需求电流。充电机按这个需求闭环输出同时通过CCS把实际输出电压、输出电流反馈给BMS。BMS还会周期发送BCS和BSM分别上报充电电压、充电电流、当前SOC以及单体最高最低电压和温度极值。这组报文里BCL和CCS的周期通常为50ms是控制闭环的关键BCS和BSM周期通常为250ms用于状态监测。BMS不是发了需求就不管它会持续看CCS反馈的实际值是否接近需求值。如果桩输出跟不上、超调过大或者突然中断BMS都会判断为异常并触发中止。充电阶段最容易出问题的地方在“需求来回跳变”。比如BMS计算SOP时出现波动导致BCL里需求电流在短时间内大幅度变化桩侧执行机构来不及响应输出电压过冲。这种问题在报文上看不出明显错误但波形就是不稳定。后来我们统一在台架上做了需求斜率限制才解决。2.4 结束与统计阶段不是断电就完事充满或者因为故障、人为原因停止充电时BMS会发送BST里面带停止原因。充电机收到后停止输出然后发送CST。双方都发过中止报文后再交换统计报文BMS发BSS桩发CSS内容包含充电电量、充电时长、起始SOC、结束SOC等。这个阶段经常被忽略但它对充电账单和电池追溯很重要。CSS里的累计充电电量是计费依据BSS里的最高最低电压/温度则会被写进充电历史供后续分析。如果通信在充电中途异常中断统计报文没送出去桩这边通常会按本地数据补一条记录。注意一个细节不是BST一发完就立刻切断。为了保证安全中止充电后还要等待电压泄放、接触器分段完成通信链路保持一段时间后才彻底断开。现场有些故障是“BMS发了BST就立刻关CAN发送”导致桩没收到CST桩那边会认为通信中断反而触发更严重的保护动作。2.5 一屏话看懂全过程如果把四阶段翻译成人话大概是这样的对话BMS我是2015版协议能跟你握手吗BHM回CHMBMS我是某厂家电池额定容量120Ah最大允许500V/200A。BRM/BCP桩我只能输出200V到750V最大电流150A。CSP/CMLBMS好那按400V/120A给我充。BCL桩已输出400.2V/119.8A。CCSBMS继续充当前SOC 62%。BCS/BSM……循环直到充满……BMS已充满停止原因达到SOC阈值。BST桩输出已停止。CST双方本次共充入45.2kWh时长72分钟。BSS/CSS这个剧本能帮你快速在脑子里建立整条链路。后面任何一帧报文异常你都能定位到是哪句话没说清楚。3. 高频报文逐帧拆解BCP/BCL/BCS/BSM/CCS里到底有什么3.1 BCP电池“自我介绍”BCP是参数配置阶段的核心很多充电机把BCP当作能否充电的判据。典型字段包括电池类型、电池额定容量、电池总电压、最高允许充电电压、最高允许充电电流、最高允许充电总电压等。字段常见长度单位/分辨率说明电池类型1字节枚举三元、铁锂、锰酸锂等电池额定容量2字节1Ah/bit满充容量电池当前总电压2字节0.1V/bit握手后测量值最高允许充电电压2字节0.1V/bit全生命周期上限最高允许充电电流2字节0.1A/bit全生命周期上限最高允许充电总电压2字节0.1V/bit含连接器压降考虑这里要提醒一下不同整车厂的DBC里可能对BCP做了扩展比如加了温度上下限、充电模式支持标志。调试时不要只依赖标准里的字段表必须以实际车型的DBC为准。曾经遇到过一次很奇怪的问题BMS发了BCP桩就是不进入下一阶段抓包数据全部符合预期。后来把BCP原始字节逐个拆开才发现BMS在“最高允许充电总电压”字段上少乘了10倍填出来的值变成了5000V桩内部校验直接判定非法丢弃了这帧报文没有任何报错。要不是用DBC做了逐信号比对这个问题单看十六进制数据非常难发现。3.2 BCLBMS每秒钟下的充电指令BCL是充电阶段BMS发给充电机的指令报文周期通常50ms频率相当高。核心内容就两个充电需求电压、充电需求电流外加一个充电模式。字段常见长度单位/分辨率说明充电需求电压2字节0.1V/bitBMS期望的输出电压充电需求电流2字节0.1A/bitBMS期望的输出电流充电模式1字节枚举恒流、恒压、恒功率等很多人对BCL有个误解以为BMS给了需求电流桩就必须立刻按这个值输出。实际上桩还要结合自己的输出能力矩阵调整。CML里已经定义了桩的最小/最大输出电压和最大输出电流BCL的值超出这个范围时桩会按边界值输出同时通过CCS把实际值反馈给BMS。BMS如果发现桩总是达不到需求会进一步调整需求或触发异常。我自己的经验是调BCL时一定要在CANoe或脚本里记录“需求值-实际反馈值”的偏差曲线。偏差一直很大多半是桩的功率模块响应跟不上或者是BMS的SOP计算太激进。3.3 BCS与BSM充电过程中的体检报告BCS是BMS周期性上报的充电状态典型内容包括充电电压测量值、充电电流测量值、当前SOC。桩可以根据BCS判断电池是否接近充满但最终是否停止由BMS决定。字段常见长度单位/分辨率说明充电电压测量值2字节0.1V/bit实际采样值充电电流测量值2字节0.1A/bit实际采样值当前电池SOC1字节1%/bit0-100%BSM则偏重电池健康状态包括单体最高电压、单体最低电压、最高温度、最低温度、绝缘状态等。桩拿到BSM后会做安全判断比如最高单体电压超过允许值即使BCL还是在请求大电流桩也可能会限流或主动中止。单体电压和温度的字段通常不只是数值本身还带“位置编号”。比如“最高单体电压所在电池组号/单体序号”这个信息对电芯一致性问题定位很有帮助。我在分析充电异常数据时经常会扫BSM里的温度极值变化趋势看是否出现温差过大导致充电降功率。3.4 CCS充电机的应答CCS是充电机在充电阶段发送给BMS的状态反馈报文相当于对BCL的应答。字段包括充电机输出电压、输出电流等。BMS通过CCS来判断桩是否真正执行了需求也是闭环控制的反馈量。字段常见长度单位/分辨率说明充电机输出电压2字节0.1V/bit输出端实际电压充电机输出电流2字节0.1A/bit输出端实际电流如果CCS的周期波动大BMS侧的超时判断容易误触发。我在测试中遇到过桩端负载较重时CCS发送周期从50ms漂到了120msBMS按超时逻辑报了通信中断。最终查下来是桩主控任务的优先级设置不当CAN发送被其他任务抢占。这类问题在报文层面看起来像偶发丢帧实际是发送任务调度问题。3.5 字段换算示例从16进制到物理量CAN报文里的原始值都是整数要转成物理量需要乘以分辨率。比如BCL原始数据里需求电压字段有两个字节值为0x0FA0换算方法是voltage_raw 0x0FA0 # 十进制4000 current_raw 0x03E8 # 十进制1000 voltage voltage_raw * 0.1 current current_raw * 0.1 print(f{voltage:.1f} V) print(f{current:.1f} A)输出结果是400.0V和100.0A。这个换算逻辑新手容易搞混特别是有些字段还有偏移量。好在GB/T 27930-2015里大部分电压电流字段没有偏移直接用原始值乘分辨率即可。但解析协议时务必先看DBC或出厂协议文档不要想当然。4. 时序细节与异常跳转握手之后还会遇到哪些坑4.1 报文发送时序全景从物理连接完成到充电结束整个过程中的报文时序大致如下阶段发送方报文周期下一步触发握手桩CHM250msBMS回BHM握手BMSBHM250ms进入BRM辨识BMSBRM单次多帧桩确认辨识信息时间同步桩CTS50msBMS回BTS时间同步BMSBTS50ms进入BCP参数配置BMSBCP100ms或单次桩回CSP/CML参数配置桩CSP/CML100ms或单次双方确认充电BMSBCL50ms桩按需输出充电桩CCS50msBMS闭环控制充电BMSBCS/BSM250ms桩监控状态结束BMSBST单次桩停止输出结束桩CST单次数据统计统计双方BSS/CSS单次充电记录这里有一个容易误解的地方桩不一定严格按照“收到BCP后才发CML”的方式实现。有些桩在握手完成后就会周期性发送CSP/CMLBMS要做的是兼容这种提前广播而不是等CML才发BCP。我在测试中就见过BMS因为没收到CML就死等而桩其实一直在发CML只是验收滤波把ID滤掉了。4.2 超时与重发一帧报文丢了怎么办CAN通信不是万无一失的总会有偶发干扰、CRC错误、仲裁丢失。GB/T 27930-2015的超时设计不是“丢一帧就完蛋”而是留出容错余量。典型逻辑50ms周期报文连续3到5帧未收到判定通信超时进入中止流程。250ms周期报文连续2到3个周期未收到判定超时。对单次报文如果规定时间内没收到响应可能是对方掉线或协议状态不对。举个例子充电阶段BMS每50ms发一次BCL桩如果连续200ms没收到桩会认为BMS控制失效主动限流或中止输出。反过来BMS如果连续收不到CCS也会认为桩失去控制能力发送BST。我在实际测试中会专门做“丢帧注入”实验在CANoe里屏蔽特定报文看对方在几帧后触发超时。这有助于确认超时参数设置是否合理也能验证故障上报是否准确。4.3 多帧报文BRM的处理超过8字节怎么传BRM里包含电池厂商、电池类型、容量等信息总长度超过8字节单帧装不下。按GB/T 27930-2015要求需要采用多帧传输机制。抓包时你会看到BRM不是一帧而是拆成首帧、连续帧、流控帧等好几个CAN帧并且都在同一个CAN ID下传输。多帧传输的过程可以这样理解发送方先发一个首帧里面带“后面还有多少字节”的信息接收方收到首帧后决定自己能否接收并返回流控帧告诉发送方“我准备好了你连续发吧每次发多少帧”发送方再按流控要求连续发送若干个连续帧直到全部发完。调试中BRM多帧不通的常见原因有三个接收方没有正确处理流控帧导致对方一直等流控。连续帧序号循环计数错位比如从0开始而不是从1开始。首帧里声明的数据总长度与实际发送字节数不一致接收方认为没发完。建议第一次跑BRM时直接用CANoe的“多帧分析”窗口看链路层完整过程不要只看应用层的解析结果。4.4 故障场景下的交互BST/CST不是发着玩的当BMS检测到过压、过流、过温、绝缘故障或者通信中断时会发送BST并携带原因。桩收到BST后必须停止输出并回CST。同理桩如果自身出故障也会发CSTBMS收到后进入停止状态。这里我要特别提醒一件事BST里的停止原因不是让桩“参考”的而是让桩“执行”的。有些BMS代码里把原因只当作上报字段桩那边却在等CST回执两边配合就容易出问题。我处理过一起事故BMS发了“过压”的BST桩因为CST发送逻辑阻塞没有及时停止输出导致电池电压继续升高。虽然最终没有造成严重后果但暴露了异常链路处理不完备的问题。故障处理逻辑需要在台架上反复演练不能只在正常充电流程里跑通了就觉得没问题。我会列一张“异常注入测试表”覆盖BMS过压、过流、过温、绝缘故障、桩侧急停、CAN通信中断等场景每个场景至少验证一次BST/CST交互和硬件切断动作。5. 实测抓包复盘一次完整充电的CAN日志长什么样5.1 从握手到输出的报文时间线下面是我在实验室台架上抓的一次完整充电过程片段ID做了脱敏处理但阶段关系和时序是真实的。时间戳方向报文关键数据00:00:00.000桩→BMSCHM协议版本号0x0100:00:00.015BMS→桩BHM协议版本号0x0100:00:00.028BMS→桩BRM(首帧)数据总长度22字节00:00:00.032桩→BMS流控帧允许连续发送00:00:00.040BMS→桩BRM(连续帧)电池类型、容量等00:00:00.060桩→BMSCTS时钟2025-05-06 08:12:3000:00:00.075BMS→桩BTS时钟2025-05-06 08:12:3000:00:00.100BMS→桩BCP额定容量120Ah00:00:00.160桩→BMSCML最大电压750V最大电流150A00:00:00.210桩→BMSCSP参数配置完成00:00:00.250BMS→桩BCL需求电压400V需求电流100A00:00:00.300桩→BMSCCS输出电压400.2V输出电流99.8A00:00:00.500BMS→桩BCSSOC62%00:00:00.550BMS→桩BSM最高单体电压4.15V最高温度32℃从抓包看整个流程非常紧凑每个阶段都在几十毫秒内完成。如果某个阶段停留时间过长比如连续几秒重复发BCP而桩不回CSP基本可以断定该阶段交互有问题。5.2 用一段Python脚本把原始值换算成电压电流抓包拿到的只是十六进制原始值没法直接看。我通常写个简单脚本批量解析把关键报文的数据转成可读的物理量。下面是解析BCL的示例can_frame { id: 0x186, # 示例ID实际以DBC为准 data: [0xC0, 0x0F, 0x44, 0x03, 0x01, 0x00, 0x00, 0x00] } # 假设第0-1字节是需求电压第2-3字节是需求电流第4字节是充电模式 voltage_raw (can_frame[data][1] 8) | can_frame[data][0] current_raw (can_frame[data][3] 8) | can_frame[data][2] voltage voltage_raw * 0.1 current current_raw * 0.1 print(f需求电压: {voltage:.1f} V) print(f需求电流: {current:.1f} A)这段代码的输出是“需求电压: 400.0 V需求电流: 100.0 A”。实际项目中我会用CANoe的CAPL脚本或者Python-can库直接接总线解析原理都一样拿到原始字节按DBC信号定义做位拆解和缩放。解析脚本最怕DBC和实际报文不一致。我吃过大亏因为用了旧版DBC把BCS里的SOC信号位定义解读反了显示一直跳到负数排查了整整半天才发现是DBC版本没更新。所以抓包解析前永远先确认协议树版本。5.3 一次“参数配置阶段卡死”的排查过程有一回测试桩一直发CTSBMS也回了BTS但接下来BMS发完BCP后桩没有任何回应也不发CSP/CML。抓包看数据本身没有任何错误BCP的CRC也正常就是桩不认。排查步骤是这样的先查物理层CAN波形干净无错误帧排除波特率和终端电阻问题。查协议状态机桩的状态确实在“参数配置”不是卡在握手。用DBC解析BCP所有字段发现“最高允许充电电压”字段原始值是0x4E20换算后是2000V。电池包怎么可能允许2000V明显是标定数据错位。查BMS标定表发现该字段引用的数据源错误把“电池总电压”和“最高允许充电电压”两个变量接反了。修正标定后重新上电BMS再发BCP桩正常回复CSP流程通过。这个问题的隐蔽点在于桩收到非法范围的数据后不一定会上报错误而是直接把报文丢弃。BMS这边看到的是“我发了你为什么不回”桩那边看到的是“你发的数据不合法我不处理”。如果没有完整的数据解析光看报文交互会陷入死循环。6. 把协议跑稳的经验清单调试工具与避坑建议6.1 抓包工具与DBC没有协议树就别谈分析做GB/T 27930-2015联调最基础的工具就是CAN抓包分析仪和配套软件。常用组合有CANoe、CANalyzer、PCAN、周立功CANPro、Vehicle Spy等有些还配合CANscope做物理层诊断。抓包时要关注的不只是“有没有报文”还有时间戳精度、总线负载率、错误帧计数。没有DBC文件时抓到的报文只是ID和十六进制数据很难解读。至少要维护一份信号矩阵表把报文ID、方向、周期、信号字节位置、分辨率和偏移量列清楚。我在项目里要求在联调前由BMS和桩双方各提交一份协议树文件比对一致后才能开测。这个动作能提前解决大量“两边字段理解不一致”的问题。抓包时一定开启时间戳功能。判断超时问题必须精确到毫秒级不能用看画面闪烁的方式估算。CANoe里我会把相对时间戳加进窗口显示方便直接看相邻帧间隔。6.2 现场调试最容易翻车的五个点波特率不匹配表现为握手阶段双方都发报文但都收不到回包。用示波器量位宽250k对应4us。终端电阻缺失偶发错误帧、COM口误码率偏高。断电后在CAN针脚量电阻应该接近60Ω。CAN_H/CAN_L反接看起来完全没数据但总线电平正常。试错成本低检查线序再上电。报文ID验收滤波配置错误BMS侧只接收特定ID桩发的CHM被过滤掉导致BMS认为桩不在线。先用全接受模式验证。报文周期抖动主控任务调度不当50ms周期的报文实际变成了120ms对方误判超时。抓包统计相邻帧间隔超差就要看软件调度。6.3 新老协议兼容遇到2011版车辆怎么处理GB/T 27930-2015是2015年发布替代2011版的但路上还有不少按2011版通信的老车。2011版和2015版在报文定义、参数配置流程上有差异充电桩要做好兼容才能避免“插上老车充不了电”的投诉。识别方法通常在握手阶段CHM和BHM里带协议版本号桩解析到版本号后切换对应的状态机和报文解析表。测试时要专门准备一台老协议模拟器验证两套协议切换逻辑是否可靠。另外2023年后又发布了新版标准虽然目前还是过渡期但新开发的项目最好把协议版本做成可配置项方便后续平滑升级。协议软件里不要把版本号写死这是我在多个项目里被坑出来的教训。干联调这些年我最大的体会是协议文档再厚落不到总线数据上都是空的。每次解析报文我都习惯把DBC和原始日志一起归档标清楚车型、桩型号、软件版本、测试时间。这样即使过了半年再翻出来也能源源本本还原当时的现场状态。对正在做BMS或充电桩通信开发的朋友建议从一次完整的抓包开始把CHM到CSS的每一帧都过一遍弄懂每个字段是谁发的、给谁看、超时会怎样。把这些基础啃下来碰到任何“充不进电”的故障你都有一条清晰的排查主线。
返回列表