
1. 为什么CAN总线是车载系统的“神经系统”做车辆研发、机器人控制或者后装诊断这些年几乎每天都要跟CAN总线打交道。从发动机ECU、变速箱、ABS到车窗、空调再到新能源汽车的电池管理系统BMS和电机控制器内部互相通信的骨干基本都是CAN。说它是车载系统的“神经系统”一点都不夸张车上所有电子单元ECU靠它交换状态、指令和诊断信息一个信号错了或者一条总线断了轻则某个功能失效重则整车控制器直接进入故障保护状态。这篇文章写的是我对CAN协议和车辆协议的理解包括协议分层的思路、物理层电压变化、常见的测试手法再拿达妙电机这类智能关节模块做例子讲清楚怎么通过CAN总线把位置、速度、扭矩控制起来。无论你是刚入门的车辆电子工程师还是做机器人嵌入式开发或者只是想把OBD诊断搞明白都应该能从里面找到能直接上手的东西。需要先说清楚CAN总线本身不是某一款芯片的功能而是一整套国际标准协议。从头捋一遍你会发现它其实不复杂难的是面对真实总线上那种各种干扰、电平波动和ID管理混乱的时候还能快速把问题定位出来。这也是我写这篇文章的目的不只是讲“协议长什么样”更希望让大家知道“为什么这么设计”以及“现场出了问题怎么判断”。2. 物理层拆解CAN总线的电压差到底是怎么变的2.1 CAN_H和CAN_L两条线的分工很多人第一次学CAN看波形就蒙了为什么明明叫“数字总线”示波器上看到的却不是干净利落的0V/3.3V方波这是因为CAN物理层用的是差分信号不是单端电平。所谓差分就是同时使用两根线来传输一个逻辑电平一条叫CAN_H一条叫CAN_L。接收端真正关心的不是单根线对地的电压而是两根线的电压差CAN_H电压减去CAN_L电压。这样做最大的好处是抗干扰能力强。车上的点火线圈、电机驱动器、电火花干扰到处都是如果靠一根线对地传输地噪声很容易把信号淹没而差分信号在双绞线里受到的干扰基本是共模的两个线同时被抬升或拉低差值却几乎不变接收端依然能准确判断电平。一台正常工作的CAN总线静止状态下CAN_H和CAN_L都应该在2.5V附近这叫隐性电平。当某个节点要发送数据时发送器会主动把CAN_H往上拉、CAN_L往下压形成大约2V的差值这叫显性电平。在示波器上看起来不是“满幅跳变”而是两条线从中间往上下两边分开再合拢。这跟RS232那种0V到5V的跳变完全不同一开始不习惯很正常。2.2 显性电平与隐性电平的电压差计算按ISO 11898-2标准隐性状态下CAN_H和CAN_L的典型电压都在2.5V左右电压差接近0V显性状态下CAN_H被拉到不低于3.5VCAN_L被拉到不高于1.5V电压差约为2V。芯片数据手册里通常会给2V左右的典型差分电压实际使用示波器量到1.5V到3V都算正常前提是总线拓扑和终端电阻没出问题。这里有一个关键点显性位是“主动驱动”的由发送节点的CAN收发器同时开路上拉CAN_H、下拉CAN_L相当于用两个管子把差分对硬生生撑开。隐性位则相反大家都不出力靠总线两端的120欧终端电阻把两个电平“拉拢”回2.5V附近。所以从能量角度看发送显性位的时候功耗更大总线上也更容易出现振铃发送隐性位则是自然回落。在实际调试中我经常用万用表直接测CAN_H和CAN_L之间的电阻来判断物理层状态正常端接的总线在整车断电或者断开所有节点的情况下CAN_H和CAN_L之间的直流电阻应该在60欧左右——因为总线两端各有一个120欧电阻并联。如果你量到120欧说明只有一端接了终端电阻量到无穷大那就说明至少有一端是断开的。这招在排查“总线静默”问题的时候屡试不爽。2.3 为什么差分会随负载和节点数量变化总线上节点一多差分输出就会受到负载影响。CAN收发器的输出能力有限当总线上有几十个节点并接再加上线缆的分布电容发送显性位时电压差下降、上升沿变缓都很正常。反过来总线过短而终端电阻又没装好波形上会出现明显的过冲振铃。所以测量波形不能只看“有没有约2V差”还要看边沿斜率、振铃幅度和差分电压的稳定区间。我还遇到过一种情况总线上明明有波但接收端就是报错。后来发现是某个节点的CAN_H和CAN_L接反了导致整个总线的差分电压在特定帧期间被反向拉低。用示波器量时光看单根线电压根本发现不了必须同时看CAN_H和CAN_L两路信号才能看到“两根线往同一方向跑”这种明显错误。这也是我一直建议初学者养成“默认抓差分波形”习惯的原因。3. 数据链路层CAN报文到底长什么样3.1 标准帧与扩展帧的结构差异物理层搞定电平之后接下来就是CAN协议的核心——数据链路层。这一层规定了怎么把一帧数据打包、怎么让多个节点同时发送而不冲突、以及怎么保证出错的数据不会一直发送。目前市面最常见的是CAN 2.0协议分为标准帧和扩展帧两种格式。标准帧的标识符ID是11位最多可以区分2048个不同ID扩展帧的标识符是29位能区分5亿多个ID。二者在帧头格式上不同但在总线上可以混合共存因为协议通过IDE位来区分节点可以同时接收两种格式。一帧CAN报文从结构上拆开看主要包括这些部分字段长度作用SOF1 bit帧起始固定显性电平同步所有节点ID11/29 bit标识符决定优先级和数据含义RTR1 bit区分数据帧和远程帧DLC4 bit数据长度0到8字节DATA0-8字节实际要传输的数据CRC15 bit循环冗余校验检验数据是否正确ACK2 bit应答接收节点确认收到EOF7 bit帧结束看到这里你可能会问一帧最多才8字节够用吗这就是CAN设计的巧妙之处。它牺牲了单帧数据量换来了高实时性和高可靠性。对于转速、车速、门锁状态这种短数据8字节绰绰有余。需要传输大数据的场景协议栈会在应用层做分包和重组把逻辑上的一大块数据拆成多帧发送。3.2 仲裁机制为什么高优先级永远抢线CAN总线上所有节点共享一对线任何时刻都可能有两个节点同时开始发送。那到底听谁的CAN的答案很优雅硬件级仲裁发送过程中直接“赛跑”。在帧起始后每个节点发送自己的ID位同时监听总线电平。CAN的物理层规定显性电平差分约2V等于逻辑0隐性电平等于逻辑1。由于显性电平会“覆盖”隐性电平——只要有一个节点拉低总线其他节点读到的就是0——所以当多个节点同时发送时谁在这一位上是显性谁就赢了遇到隐性位的节点发现总线电平跟自己发的不一致立刻退出仲裁转为接收状态。这就是为什么ID数值越小优先级越高。因为ID都是高位先发数值小的ID在前面位次的“0”更多更容易压过数值大的ID。实际整车通讯里优先级高的数据比如安全气囊触发、碰撞信号都会分配很小的ID保证在总线拥堵时也能第一时间抢占线路。这个机制也决定了ID规划非常讲究不能只考虑“不重复”还要考虑“谁应该先走”。3.3 位填充、CRC与错误处理机制总线信号在传输过程中可能会被电机火花、开关浪涌等外部干扰打乱。为了应对这种情况CAN协议设计了多层防线。一个是位填充。协议规定连续发送5个相同电平后发送方必须自动插入一个相反电平接收方在解码时再把这个填充位去掉。这样做有两个作用一是给接收端的时钟同步提供跳变沿因为连续长时间不变的电平会让采样点失去参考二是避免长串相同位被误判成空闲状态或产生其他歧义。另一个是CRC校验。每帧报文末尾都带15位CRC校验码接收端用同样的算法计算如果对不上就直接丢弃本帧并且返回错误帧。假如一个节点反复收到错误帧它会进入bus-off状态暂时退出总线避免“一颗老鼠屎坏了一锅粥”。这种设计让CAN在真实工业环境中非常皮实也不会因为单点故障导致全车通信瘫痪。4. 车辆协议全景OBD-II、UDS与J19394.1 从OBD-II接口入手最直观如果你手里有辆乘用车找一下方向盘下方的OBD接口通常是一个16针梯形插座。这个接口就是接入车辆CAN总线最直接的入口。OBD-II原本是为了满足排放检测法规而设计后来扩展成了通用的车辆诊断入口。标准定义了几种不同的物理层协议但在2008年后的绝大多数新车上走的基本都是CAN引脚6是CAN_H引脚14是CAN_L。用USB-CAN分析仪插上OBD接口把波特率设成500kbps马上就能在软件里看到总线上源源不断的报文。最常见的那些ID比如0x0C、0x1A、0x2C这些不同车企定义不一样但都能通过读取数据的变化来猜测对应含义。我一开始调车的时候就是把转向灯、车窗、刹车踏板逐项操作一边操作一边盯报文列表很快就能摸清楚哪些ID对应哪些功能。这里要提醒新手OBD接口虽然暴露了总线但并不是所有ECU都会在上面跑。很多整车厂会把动力CAN、车身CAN、舒适CAN分成多条独立总线OBD接口只接到其中一条诊断总线上。想测其他总线就得找到对应的接点或者通过网关转发。网关的存在也是CAN网络设计中的关键一环它的主要任务就是隔离不同速率、不同安全等级的总线段防止一个区域出问题导致全车瘫痪。4.2 UDS诊断协议怎么读故障码光能看到CAN报文还不够真正要读故障码、做ECU刷写就得聊UDSUnified Diagnostic ServicesISO 14229。UDS是跑在CAN上层的一套应用协议它定义了如0x10会话控制、0x22按ID读数据、0x2E写数据、0x31例程控制、0x3E保持连接等诊断服务。UDS的通信模型是典型的请求-应答式诊断仪发送一个请求帧ECU回复一个响应帧。请求帧和响应帧的ID通常是成对出现的比如请求ID是0x7E0响应ID就是0x7E8。如果发动机ECU出故障诊断仪通过发送0x19服务请求读取故障码ECU会返回DTCDiagnostic Trouble Code列表像P0101这种“质量空气流量传感器电路范围/性能问题”就属于这类。很多后装设备、车载T-Box、自动驾驶盒子本质上都是靠着UDS这条通道去读车速、里程、电池状态和校准信号。做这个方向的开发建议先从0x22和0x19这两个服务入手理解了会话管理和安全等级之后再往上碰0x2E和刷写类服务。4.3 J1939与商用车上的CAN如果从乘用车转到商用车你会发现虽然底层还是CAN但上层协议换成了SAE J1939。J1939用在卡车、工程机械、农用设备等环境它的特点是波特率固定为250kbpsID定义和乘用车完全不同。J1939把29位扩展ID做了详细划分前几位是优先级中间是PDU格式和PDU特定域后面是源地址和目标地址。它把发动机转速、水温、油位这些参数都定义成了标准参数组PGN比如发动机液位/压力归为一个PGN电子发动机控制器1又归为另一个。这种标准化的好处是只要设备支持J1939无论装在哪家的卡车底盘上都能读到一致的数据。我做工程机械项目时经常遇到一个坑就是客户拿乘用车OBD工具去测商用车怎么都搜不到报文。原因就是波特率不对J1939是250kbps乘用车大多是500kbps工具设置不对自然抓不到数据。所以拿到一辆车先别急着猜看协议文档或者用支持自动波特率识别的分析仪扫一遍能省下不少时间。4.4 车企私有CAN网络规划逻辑除了标准协议每家车企还会在量产车型上做自己的私有CAN网络规划。比如很多新能源车会把整车分为动力CAN、车身CAN、自动驾驶域CAN各段速率不同、物理位置不同之间用域控制器或网关做数据路由。这样既能降低单条总线负载率又能控制不同安全等级的数据互相隔离。私有协议的部分一般IP、源码拿不到只能靠逆向抓包。我的经验是先看周期报文周期固定且ID稳定的多半是状态广播类比如车速、转速再看事件报文只有在操作时才出现的多半是门锁、车窗、灯光控制类。把这些报文分类之后再结合硬件原理图去猜字节含义。这个过程需要耐心但也是理解车辆协议最有效的练习方式。5. 实战CAN总线测试与波形分析5.1 测试CAN总线需要哪些基础设备CAN总线测试听起来复杂但基础工具其实不复杂。我认为至少需要这三样一个双通道或四通道示波器带宽100MHz以上就足够大多数车载CAN测试重点是用差分探头或两个无源探头同时抓CAN_H和CAN_L一个USB-CAN分析仪从几十块的入门款到几千块的专业款都有配合上位机软件可以抓包、发包、看错误帧一个万用表用来测终端电阻、通断和电平检查示波器用来观察物理层质量CAN分析仪用来观察数据层交互万用表用来做基础电气检查。三者是互补关系缺一个都可能让你在现场绕远路。很多人图方便只带一个分析仪结果遇到“有报文但数据乱”的情况完全无从下手因为根本看不到波形长什么样。5.2 用示波器抓CAN波形的完整步骤抓CAN波形我是这么操作的。先把示波器通道1接CAN_H通道2接CAN_L地线夹接车辆接地注意不要直接夹信号地要确认公共地关系。把时间轴调到50微秒/格电压轴调到1V/格左右。如果是500kbps波特率单bit时间是2微秒一个完整标准帧大约100多微秒刚好能铺满两到三格屏幕。触发方式建议设成下降沿或上升沿触发用CAN_H通道做触发源。如果总线上一直在跑周期性报文很快就能看到连续的帧波形。我习惯先把光标放在两个显性位之间测一下单bit时间再用1除以bit时间验证波特率有没有猜错。比如测出一个bit是2微秒那波特率就是500kbps如果是4微秒就是250kbps。波形抓到现在重点看三件事差分电压幅度是不是正常边沿是不是干净有没有明显振铃隐性回退是否平缓有没有长时间漂移。如果发现波形上有“塌陷”的显性位大概率是有节点在错误地驱动总线或者在总线上发生了多个节点同时发送但仲裁失败的瞬态。5.3 典型CAN故障波形快速判断表我从这些年调试经历里整理了一张速查表遇到问题可以先对照一下现象可能原因快速验证方法总线完全静默无任何波形总线短路、无节点供电万用表测CAN_H对CAN_L电阻、测供电电压有波形但差分幅度明显偏低终端电阻缺失、节点过多、线缆过长检查总线上两端120欧电阻断开部分节点波形边沿过冲且振铃严重缺少终端电阻或接线走线过长在总线末端加终端电阻检查拓扑是否“菊花链”偶发错误帧但波形看着正常某一节点地电位不稳、接头松动单独检查每个节点的地线紧固接头单根线电压几乎不变收发器损坏、差分线短路到地或电源分别测CAN_H和CAN_L对地电压做CAN测试时一定要记得波形是“物理层结果”错误帧是“数据层结果”。物理层的小问题通常会先在错误帧计数里爆发比如总线上CRC错误突然变多先说“数据层有事”然后才轮到示波器上找原因。反过来如果错误帧频繁出现而波形看起来完全正常就要想是不是协议层配置错了比如波特率虽对但采样点位置不合适。5.4 错误帧和bus-off现象怎么排查CAN总线上出现错误帧在分析仪里通常能看到红色的错误计数器跳动。这里要区分两种情况一种是某个节点内部错误计数器高导致它主动报错另一种是总线外部的信号质量问题导致错误。判断方法很简单把疑似故障的节点单独拆下来用分析仪模拟总线上另一个节点通信看错误帧是不是依旧存在。如果消失说明问题不在这个节点而是它和总线的交互环节。最棘手的是bus-off状态。节点发送错误太多错误计数器超过255节点会主动离线不再参与通信。这个状态会让ECU看起来“死掉”一样而且很多ECU掉线之后需要重新上电或回车钥匙才能恢复。所以在测试台上调试时我习惯在分析软件里周期性发送一个“心跳”报文一旦哪个节点离线马上能通过心跳消失的时间点判断它是什么时候进的bus-off再配合示波器去看那个时间点前后的波形。6. 达妙电机通过CAN实现精确关节控制6.1 为什么机器人关节控制也选CAN最近两年做四足机器人、机械臂、人形机器人的朋友经常会提起达妙电机这类一体化关节模组。它们体积小、力矩密度高内部集成了电机、减速器、编码器和驱动电路对外只留一个CAN接口和数据线。那为什么偏偏选CAN而不是RS485或者以太网核心原因是实时性和同步性。机器人关节通常需要1kHz甚至更高的控制频率每个控制周期内主控都要给所有关节下发目标位置、速度或力矩同时收回当前角度、速度等信息。CAN总线在多主通信上没有主从关系任何一个节点随时可以发数据非常适合这种周期性指令加分散反馈的场景。而且CAN帧的仲裁机制天然保证了优先级紧急的安全停止指令可以用低ID抢占总线响应时间非常有保障。达妙电机这种关节模组内部通常已经是“电机加编码器加驱动器”的闭环而外部的CAN控制其实是叠加了一个“上层位置环”或“速度环”。主控只需要告诉它目标值电机内部的算法会自动去跟不需要主控高频率读取编码器再做PID。这也是我为什么建议初学者不要一上来就在主控里做高频电流环先把CAN通信跑通再用电机自带的闭环能力后面再根据负载情况决定要不要自定义控制算法。6.2 达妙电机的CAN报文格式与ID规划达妙电机不同型号的寄存器映射和报文ID会略有差异我在调一款常见关节模组时用到的典型配置可以给大家参考但真做项目时务必以对应手册为准。我习惯把主控下发数据的ID规划成0x140到0x1FF这一段的扩展ID区域每台电机分配一个固定的控制ID。比如三台电机分别占0x141、0x142、0x143主控按控制周期依次或按优先级发送。电机反馈的ID一般映射到0x240到0x2FF区间比如0x241、0x242、0x243主控通过接收这些ID获取当前角度、速度和力矩数据。从报文内容上看控制帧通常是8字节前面两个字节是目标位置或目标速度的高低位中间两个字节是速度或力矩设定值最后两个字节是刚度或阻尼参数。不同模式位置模式、速度模式、力矩模式下每个字段的意义会切换在程序里就必须非常小心地解析别把力矩字段当成速度字段去填。6.3 控制帧和反馈帧的实操用法我举个具体例子。假设我用CAN分析仪往ID 0x141发一帧数据目标位置设为90度速度设为30转/分再让内部刚度设成中等那8字节可能看起来像这样0x1 0x41 : 0x20 0x4E 0x00 0x1E 0x00 0x0A 0x00 0x00这里面0x204E是角度编码值经过换算后的结果0x001E是速度0x000A是刚度系数。实际怎么换算还是得查对应型号的手册因为不同型号的编码器分辨率不同角度值和原始数据之间的比例可能差好几倍。电机返回的反馈帧同样8字节前两个字节是当前角度的编码值中间是当前速度后面两个字节是当前真实力矩。主控收到之后可以先判断ID是不是自己的目标电机再按同样的换算式把这个原始值转成实际角度。比如反馈原始值是0x1020分辨率是每圈16384那当前角度的计算方式就是0x1020除以16384再乘以360度。这里有一个调试要点控制周期不能太短。很多人一上来就把发送间隔设成100微秒10kHz结果总线上全是报文CPU也忙不过来最后控制效果反而变差。我建议先从1kHz开始也就是每1毫秒发一次控制帧观察关节跟踪效果再逐步提高。对大多数机械结构和电机响应模型来说1kHz到2kHz已经足够平滑没必要盲目追求高频。6.4 达妙电机CAN控制的坑与调试建议我在调达妙电机时踩过的几个坑列出来给大家参考。第一个坑是波特率不匹配。模块出厂可能默认波特率不是你以为的那个值用分析仪自动识别波特率或查阅手册确认。波特率配错的表现是主控发送之后电机毫无反应分析仪上全是错误帧甚至完全抓不到任何有效数据。第二个坑是终端电阻。如果只有一台电机和一个主控在总线上只有两个节点也依然需要在两端各接一个120欧电阻。不少人在实验桌上只用一根短杜邦线连接忽略终端电阻结果高速通信时波形振铃严重开环测试正常闭环位置控制一上去就抖动。解决方式很简单在CAN分析仪或主控板一端接焊好的120欧电阻电机端如果设计上没有内置终端电阻可以在连接器附近外接一个注意不是所有电机模块都内置。第三个坑是ID冲突。如果总线上有两个电机配置成了同一个ID控制帧发出去两个电机都会响应反馈帧也会从两个节点同时发回来导致仲裁混乱位置和力矩读出来的数据完全不可信。上电之前最好先用分析仪扫一遍总线上的ID列表确认没有重复的源地址再开始联调。7. 我调试CAN总线这么多年的几条心得CAN总线知识看起来零散实际上是一条非常清晰的链路物理层电平决定信号能不能传数据链路层决定帧能不能收应用层协议决定数据有没有意义。很多新手把大量时间花在背帧结构上反而忽略了用示波器看波形、用万用表查线路这些基本功到现场一测就露馅。我自己的习惯是遇到任何CAN通信问题先问三件事——波特率对不对终端电阻在不在ID有没有冲突。这三点排查完八成的基础通信问题都能解决。剩下两成再去看时序、采样点、接地和信号完整性。如果你现在正准备做车辆协议分析或者机器人关节控制项目我的建议是从一个最简单的实验开始拿一个CAN分析仪接上一台达妙电机先把ID和波特率配置对然后试着只发送一帧固定控制数据观察电机有没有动作。等这一步通了再一点一点加控制环优化相信你很快就会对CAN总线有真正的掌控感。