ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CAN总线技术详解:从差分信号到关节电机控制

2026/9/12 9:05:04 拓冰建站 浏览量
CAN总线技术详解:从差分信号到关节电机控制 上周去朋友修车厂帮忙我把示波器两个通道分别搭到一台老款SUV的OBD接口6脚和14脚上几秒钟后就看到了那串熟悉的2V差分波形。很多搞软件的朋友第一次接触CAN总线都会发懵这两根线不是应该一根发0、一根发1吗其实不一样CAN走的是一对双绞线之间的电压差。这篇文章就把我这些年调试CAN总线、解析车辆协议、用CAN控制机器人关节电机的经验完整梳理一遍。不管你是刚入行的嵌入式工程师还是想读懂自己车上数据的玩家或者正在折腾达妙这类CAN关节电机都应该能从里面找到能直接抄作业的内容。1. 先说清楚CAN总线解决的是“多节点可靠通信”这个问题1.1 为什么汽车和机器人都在同一条总线上在CAN普及之前汽车内部ECU之间基本靠密密麻麻的点对点线束。每个传感器、每个开关都要单独拉线到控制器线束又粗又重接头多到排查故障能让人崩溃。而且发动机舱里面电磁环境极差火花塞点火、继电器吸合、电机启停都会产生强烈干扰用普通模拟信号通信很难保证稳定。CAN总线Controller Area Network最早是Bosch在1986年前后提出的核心思路就是改变“一个信号一对线”的拓扑所有节点挂到同一对双绞线上两两之间都能通信不再需要中央调度器点名。数据靠报文ID区分内容靠优先级仲裁决定谁先发送节点之间共享同一条物理通道。放到整车上看线束重量大幅下降装配和维护成本也低很多。到今天底盘、动力、车身控制器里依然大量跑CAN娱乐系统和辅助驾驶则慢慢转向车载以太网但CAN在安全关键信息传输上仍然是不可替代的老底子。机器人行业其实也是同一个逻辑。四足机器人每条腿三四个关节电机如果每个电机都用PWM或独立串口和主控一对一连接线缆数量会迅速失控而且调试时根本分不清哪根线对应哪个关节。换到CAN上所有电机就是挂在总线上的节点每台电机只需要两根差分信号线主控侧一个CAN控制器就能挂几十个节点。更重要的是电机越多总线的扩展成本几乎不增加同步性还更好。你会发现在汽车和机器人这两个看似八竿子打不着的领域底层通信方案最后都收敛到CAN不是偶然就是因为它适合“多节点、短报文、强实时”的场景。1.2 总线设计到底把那几件事做到了回头看CAN能流行三十多年的原因其实非常简单一对双绞线两条线之间的电压差代表信号这天然抗干扰多个节点同时发送时靠ID仲裁自动解决竞争不需要主机协调节点自带错误检测和重发机制通信可靠性高任何节点损坏不至于让整个总线彻底瘫痪。这些特性叠加起来正好匹配汽车振动、高温、电磁干扰极强的环境也匹配机器人高速实时控制的需求。影响范围也是全方位的。整车层面BMS电池管理、电机控制器、车身控制器、仪表、ADAS传感器都依赖CAN报文交互零部件层面OBD诊断口、改装配件、电机控制器几乎全都对外开放CAN接口工具链层面示波器、CAN卡、协议分析仪、HIL台架都围绕CAN建立了成熟生态。一条总线从物理层到应用层都形成了完整标准这就是它至今还在大规模应用的底气。2. 物理层核心CAN总线的电压差是怎么产生和改变的2.1 显性和隐性它不是普通的高低电平很多刚从串口转过来的人都会有个惯性思维发送0就把某根线拉低发送1就把某根线拉高。但CAN总线完全不是这套逻辑。它走的是差分信号真正的信息在CAN_H和CAN_L两条线之间的电位差上而不是某条线对地的绝对电平。隐性位recessive逻辑1CAN_H和CAN_L都被拉到2.5V附近差分电压接近0V显性位dominant逻辑0CAN_H被推到约3.5VCAN_L被拉到约1.5V差分电压约2V总线仲裁时显性位覆盖隐性位。只要任意一个节点输出显性位整条总线的状态就是显性。为什么用差分不用单端因为汽车和工业现场的干扰都是共模形式的外部电磁干扰几乎同时耦合到两根线上。接收器只关心两根线的差值共模干扰在相减时就被抵消了。这就是CAN能在火花塞点火、电机启停这种恶劣环境里依然稳定通信的根本原因。2.2 收发器内部到底做了什么要理解电压差怎么改变得看CAN收发器内部的输出级结构。以常见的TJA1050、SN65HVD230这类ISO 11898-2高速收发器为例隐性状态收发器输出级的两个驱动管都处于高阻或关闭状态CAN_H和CAN_L靠着终端电阻网络被偏置到2.5V左右此时差分电压为0V显性状态发送逻辑让上管把CAN_H往上推同时下管把CAN_L往下拉形成约2V的差分电压发送完毕后收发器释放总线终端电阻又迅速把两条线拉回2.5V。所以你看总线上并没有一个“绝对电平标准”只有“相对差值标准”。调试时如果只看单根线波形看到的是在2.5V上下抖动的方波意义不大只有把两个通道做差才能看到干净的2V幅值信号。这也是为什么示波器测量CAN一定要做差分测量或者用两个通道做数学相减。波特率和线缆长度也会影响这个电压变化过程。波特率越高、线缆越长信号在线上传播时的反射和不连续就越明显波形上升沿/下降沿会出现振铃直接影响接收端的采样判断。这也是CAN对终端电阻和布线要求特别高的原因。2.3 终端电阻、波特率和布线怎么搭配CAN总线的差分阻抗典型值是120Ω所以需要在总线物理两端各接一个120Ω终端电阻来做阻抗匹配信号到达线端时能量被吸收不会反射回来形成振铃。实际整车ECU内部很多自带终端电阻但默认不一定启用。调试时拿到一条未知总线第一步先用万用表量CAN_H和CAN_L之间的电阻如果测到约60Ω说明两端120Ω都已接入如果只有120Ω说明只接了一端如果接近0Ω那大概率有短路或接错线千万不能直接上电。节点数量和线缆长度也有约束。ISO 11898标准建议单段总线尽量不要超过30个节点1Mbps下总线长度应控制在40米以内降到125kbps时长度可以放到数百米。每个节点通过“短桩线”接入总线的部分越短越好最好不超过1米。桩线一长阻抗不连续点就会明显高速通信时错误帧会突然变多。这类物理层问题在示波器上看得最清楚后面测试章节会细说。3. 数据链路层帧格式、仲裁、错误重发3.1 报文的哪些字节能信哪些不能CAN的帧格式初看很复杂实际开发中真正需要关心的主要就两块帧ID和数据段。以标准帧CAN 2.0A为例一帧包含SOF起始位、仲裁段11位ID、控制段、数据段最多8字节、CRC段、ACK段和EOF结束位。扩展帧CAN 2.0B把ID扩展到29位规则类似只是ID更长。数据段的字节排列和长度由用户自己定义这里的“协议解析”本质就是搞清楚每个字节代表什么物理量、换算系数是多少、有没有偏移量。比如一个16位的转速值可能高位在前、也可能低位在前厂商的协议文档里会明确写清楚。没有文档的时候就只能靠实际增减物理量、观察数据变化来逆向推断——这也是做车辆协议逆向最花时间的地方。有个容易混淆的细节标准帧和扩展帧的ID表示方式不同。标准帧ID一般写成0x000到0x7FF三位十六进制扩展帧ID写成0x00000000到0x1FFFFFFF八位十六进制比如J1939里常见的0x18FEF100。看报文时一定要确认CAN卡当前配置的是标准帧还是扩展帧否则ID会全乱套。3.2 总线仲裁为什么ID小的就先走CAN总线没有“主站”任何节点想发就能发。那万一几个节点同时发怎么办CAN靠仲裁机制解决显性位0在总线上会覆盖隐性位1。每个节点发送帧ID时同时监听总线如果发现自己发送的位是隐性位、但总线上实际状态是显性位说明自己仲裁输了立刻停止发送转为接收状态。这条规则的直接推论就是帧ID越小在逐个bit比较的过程中越晚输优先级就越高。所以“ID即优先级”不是约定俗成而是物理机制决定的结果。在自定义车辆或设备协议时关键报文一定要分配小ID比如碰撞信号、刹车信号、急停信号否则总线繁忙时它们可能一直被其他报文压住系统响应会受影响。3.3 错误帧为什么那么让人头疼CAN节点自带五种错误检测机制位错误、填充错误、CRC错误、格式错误、ACK错误。任何一个节点发现问题就会在总线上发送错误帧发送节点收到错误帧后进入重发流程。这套机制保证了单条消息不丢但也带来一个副作用如果总线上某个节点频繁发坏帧其他节点会不断报错重发错误帧计数蹭蹭往上涨总线利用率被拉垮。远程帧RTR位在实际工程中很少用到主流方案都是周期发送数据帧接收方做超时判断。如果你在自定义协议里看到有文档坚持用远程帧做请求-响应要多留个心眼因为很多CAN卡驱动对远程帧支持得并不好调试会浪费大量时间。4. 从总线到应用OBD、UDS、J1939、CANopen和CAN FD4.1 车辆协议栈的分层逻辑CAN总线本身只是传输通道它不关心数据内容是什么。真正意义上的“车辆协议”是指寄居在CAN帧数据域之上的应用层规范谁来发、多久发一次、每个字节怎么解释。不同行业、不同车型差异很大这就是“车辆协议解析”容易让人迷糊的地方。乘用车诊断排放相关用OBD-IISAE J1979整车厂深度诊断用UDSISO 14229商用车、工程机械、农用机械多用J1939基于29位扩展ID工业控制、机器人、医疗设备常用CANopen靠对象字典加PDO/SDO传输实时数据和配置。不用被这些缩写吓住它们只是在同一个CAN物理层之上换了不同的“ID规划规则”和“数据段格式”而已。你学会解析其中一种换到另一种只是换张对照表。4.2 实操用USB-CAN适配器读发动机转速想入门车辆协议最省钱的方案是一块几十到一两百元的USB-CAN适配器再配一个免费的上位机软件。接线不复杂标准OBD-II接口里6号脚是CAN_H14号脚是CAN_L地线接4号脚或5号脚其他针脚暂时不用管。注意高端车型或新能源车很多已经上CAN FD普通CAN适配器无法解析CAN FD的64字节报文需要换支持CAN FD的适配器。接线完成先设置波特率。车辆CAN最常见的波特率是500kbps接着依次试250kbps、125kbps、1Mbps。判断波特率选对的标准是能看到稳定周期出现、ID分布有规律的报文而且错误帧很少。然后就可以发送诊断请求了。比如OBD模式01PID 0x0C表示读发动机转速请求帧这样构造发送ID0x7DF数据02 01 0C 00 00 00 00 0002表示后面还有2个字节01是模式01当前数据0C是PID发动机ECU会返回类似这样的响应返回ID0x7E8数据04 41 0C 1A F8 00 00 0004表示4个数据字节41 0C是“响应相同PID”真正的转速值在最后两个字节1A F8换算公式是0x1AF8 / 4也就是6904 / 4 1726单位是rpm。为什么除以4OBD-II规范里转速分辨率为0.25 rpm/bit所以需要值除以4。这种逐字节换算的功夫就是车辆协议解析最日常的形态。这里必须强调一句如果你测试的是自己拥有或明确授权的车辆通过诊断接口读取数据完全没问题但如果涉及写入ECU、刷写参数务必遵守当地法规和整车厂条款绝对不要对公共道路车辆做非授权写入操作。4.3 UDS和J1939在实践中怎么用UDSISO 14229是整车厂最常用的一对一诊断协议。服务号很好记0x10切换会话模式0x22按ID读数据0x2E按ID写数据0x19读故障码0x14清除故障码。实际排障时你经常看到的是一连串请求响应先发0x10 03进入扩展诊断会话再发0x22 F1 90读取VIN码ECU就会返回一长串ASCII字符的车辆识别号。只要理解了“请求服务参数”和“肯定/否定响应”的规则UDS抓包就很容易读懂。J1939则更多用于商用车和工程机械。它使用29位扩展IDID的一部分字段被拆成PGN参数组编号和源地址。比如报文ID 0x18FEF100前3位优先级是6然后保留位和数据页接着是PDU格式字段0xFEPDU特定字段0xF1最后源地址0x00。PGN的计算方式是(0xFE 8) 0xF1 0xFEF1 65265对应车速相关参数组。注意很多初学者会把ID和PGN混为一谈实际上一个PGN可以出现在不同源地址的多个ID中查表时必须以PGN为准。4.4 CAN FD到底改了什么传统CAN 2.0每帧最多8字节车速、转向这类报文够用但遇到固件刷写、大数据量帧传输就太局促了。CAN FDCAN with Flexible Data-rate在这个基础上做了两件大事数据段长度扩展到64字节数据段的波特率可以比仲裁段高得多比如仲裁段1Mbps、数据段5Mbps。这意味着同样的物理线束单位时间能搬的数据多了很多。但要注意CAN FD帧格式和CAN 2.0并不完全兼容。同一根总线上如果挂了一个只支持CAN 2.0的老节点会让CAN FD通信失效。所以实际升级时要么整条总线上所有节点全部换成CAN FD要么使用“混合模式”让老节点不上线。买CAN卡前先确认目标总线是不是CAN FD否则抓包解析会踩大坑。5. 实操总线测量与测试方法5.1 示波器测电压差的接法和波形看点我调试CAN总线时示波器是排障的最终武器。接法不复杂通道1接CAN_H通道2接CAN_L然后把显示模式切到数学减法CH1-CH2看到的波形才是真正的差分信号。没有差分探头也能这么替代只要两个通道的探头地都夹在同一个GND参考点上就行。接好之后先看静态电压正常情况下CAN_H和CAN_L都在2.5V附近如果一侧明显偏低或偏高先怀疑收发器供电、终端电阻或线路对地短路。然后触发发送观察差分波形显性位时应接近2V隐性位时接近0V。如果波形上升沿出现明显的振铃或者2V的平稳段有毛刺多半是终端电阻缺失、总线过长或分支线太长。采样点也是隐蔽的问题源。CAN控制器的采样点一般设置在位时间的70%到90%之间整车厂常用75%到80%很多开发板默认在75%。如果总线距离长、波特率较高采样点不合适会出现“偶尔丢帧”这种很难复现的毛病。遇到这种情况逐个尝试67%、75%、80%、87.5%的采样点配置配合错误帧计数变化通常能定位到位时序不匹配的问题。5.2 用CAN卡和SocketCAN做协议解析Linux下用SocketCAN是最舒服的解析方式。插上USB-CAN适配器后先启用接口sudo ip link set can0 up type can bitrate 500000然后直接抓包candump can0屏幕上会实时滚动类似can0 18FF50E5 [8] 01 02 03 04 05 06 07 08这样的报文含义依次是接口名、报文ID、数据长度和8字节数据。想过滤指定ID加上-i参数想保存日志用-l或者直接重定向到文件。真正做协议解析时光看滚动输出没用。我会把报文汇总成一张表ID、周期、数据长度、方向、用途。看到周期稳定的ID先标记为“规律报文”然后配合车辆动作变化比如开关钥匙、踩刹车、开空调观察哪些字节在变就能反推出很多信号的物理含义。这招对没有文档的旧车协议逆向特别有效。5.3 总线利用率和错误帧排查总线利用率反映的是总线上实际被占用的时间比例。一般建议长期负载不要超过60%到70%瞬时峰值可以接近90%但长期跑到80%以上低优先级报文的延迟就会明显恶化。利用率的统计在商用CAN卡和大部分调试软件里都能看到。错误帧排查有固定套路。看到错误帧计数快速增长时按顺序做四件事确认波特率匹配不要凭感觉直接抓包看报文能否稳定出现检查CAN_H和CAN_L之间的电阻是否在60Ω左右用模块隔离法把疑似节点从总线断开观察错误帧是否消失最后才去查线缆和端子很多问题出在插头进水、压线松脱这类物理故障上。我自己就吃过这类亏。有次整车测试时错误帧刷屏查了半天才发现是防水插头进水导致CAN_L对地阻抗异常。从那以后凡是恶劣环境下的测试我都先量CAN_H/CAN_L对地电压和线间电阻再上电这个习惯能省掉很多不必要的排查时间。6. 达妙电机与CAN关节控制从指令到精准运动6.1 为什么关节电机用CAN而不是串口或PWM回到机器人关节控制。关节电机对实时性和同步性的要求比普通电机高很多四足机器人每个关节都要在毫秒级周期内更新目标位置和力矩所有关节还要尽量同步动作。串口一对一通信每加一个电机就要多占一路串口电机一多主控接口根本不够用PWM能控制速度和方向但没法回读编码器反馈闭环控制还要额外接一大堆线。CAN就是关节电机的天然搭档。一条总线上挂几台电机统一用每个电机独立的CAN ID来区分就能够以1kHz甚至更高频率周期发送控制帧还能同时收回各电机的角度、速度、力矩反馈。很多无刷关节电机比如达妙、宇树早期系列、MIT Cheetah项目用的电机都支持CAN控制网上能找到的机器人开源项目里一大半都是CAN方案。6.2 控制帧、反馈帧和ID分配关节电机控制里最常见的是一套类似MIT开源四足机器人用的控制协议。虽然细节因厂商而异但框架大致相同控制端向某个基础ID加上电机ID的方向发送控制帧比如ID 0x140 电机ID电机在另一组ID上返回反馈帧比如ID 0x240 电机ID控制帧数据区通常包含目标位置、目标速度、位置环刚度KP、速度环阻尼KD、力矩前馈t_ff反馈帧数据区包含当前角度、当前速度、当前力矩可能还有温度。以常见的CAN 2.0编码方式为例有些实现用12字节或16字节的数据区每个物理量经过固定缩放系数转成整数放到特定字节位置。CAN FD模式下有的厂商直接用24字节数据区里面装6个float分别是p_des、v_des、kp、kd、t_ff和预留字段。实际使用时每种电机的数据布局和缩放系数都不一样。第一件事绝对要去查所购型号的手册或官方示例代码不要拿别的型号的协议套用。曾经有人拿MIT电机协议去控制另一家电机结果使能指令发错电机力矩输出异常差点把支架打坏这个教训值得记住。6.3 从目标值到关节运动的完整流程假设你手里有一台支持CAN的达妙无刷关节电机希望让它走到指定角度大致流程如下配置电机ID和波特率。多数电机有专门的配置模式或调试上位机ID会存在电机内部的Flash里。同时设置总线波特率确保所有节点统一。配置完成后重启电机。使能电机。使能指令通常是一帧固定ID、固定数据。未使能时发送力矩指令通常无效所以这一步别跳过。设置控制模式。如果要用位置-速度-力矩前馈模式需要提前把电机切到对应模式。不同型号可能有位置模式、速度模式、力矩模式的区别。周期性发送控制帧。主控按照控制周期例如1kHz即每1ms发一帧向目标ID发送控制数据。数据里的kp、kd、t_ff会被电机驱动器实时使用。读取反馈帧形成闭环。精准关节控制不是“发完目标角度就不管”而是每个周期都读取电机反馈值计算误差后再生成下一帧的控制量。举个例子你想让机械臂从当前位置平滑运动到目标角度典型做法是在每个控制周期里计算当前位置与目标位置的误差按比例生成速度指令再结合当前速度误差生成力矩前馈。外层位置环在主控上执行内层电流环在电机驱动器里执行这样的两级闭环结构是机器人关节控制的主流方式。6.4 KP、KD和前馈的调参经验调参是有套路的不是瞎试。先调KP再调KD。KP决定对位置误差的响应力度太小电机软绵绵跟不上路径太大会出现高频啸叫和振荡。我的习惯是先把KD设成0KP从小慢慢往上加直到出现轻微振荡然后把KP稍微退回一点。加KD消除振荡。KD相当于速度阻尼能压住超调。注意KD也不是越大越好过大的KD会让整个系统变得迟钝手上看起来“发硬”。用前馈抵消重力。机械臂的关节如果长期扛着一个负载只靠位置环去“憋”会发热非常严重。提前估算重力矩把力矩前馈t_ff按负载情况填进控制帧电机就能在比较小的误差下轻松维持位置。用日志复盘参数。把控制帧的目标角度和反馈帧的实际角度同时录下来放到同一张图表里看是滞后还是过冲一目了然。有次我调一台桌面机械臂4号关节总是过冲10度查来查去发现是主控的任务调度抖动日志打印阻塞了发送线程。改成异步日志后抖动消失关节跟踪立刻变稳。7. 常见问题速查与实战心得调试CAN总线和车辆协议走得多了总能遇到几个老朋友。下面这张表是我最常用来快速定位问题的故障现象大概率原因排查与解决完全收不到任何报文波特率不匹配、接线接反、节点未上电量CAN_H对CAN_L电阻确认约60Ω再确认供电和波特率能看到报文但错误帧刷屏终端电阻缺失、线缆过长、采样点不准补两端120Ω电阻缩短总线检查波形边沿单节点偶发丢帧该节点短桩线过长、供电不稳缩短分支线给该节点加强电源滤波CAN_H和CAN_L对地电压异常总线对地短路或收发器损坏断开节点逐段量对地电阻解析出的ID和实际抓包不一样标准帧与扩展帧ID混淆确认CAN配置里的标准帧/扩展帧选项CAN FD报文被解析成错误帧适配器不支持CAN FD换支持CAN FD的适配器仲裁段和数据段波特率分开设置还有几个杂七杂八但非常实用的经验在线调试时一定要把原始报文存成日志文件不要只截图。很多问题就藏在一帧偶发错误帧里现场可能来不及盯事后复盘比现场抓瞎有效得多。每条总线至少配一个能显示时间戳、总线负载、错误计数的调试工具。不用纠结是免费还是商业关键是能看见“错误收敛趋势”。错误帧计数清零前不要轻易宣布系统正常。做模块隔离时不要一次断开一堆节点最好采用二分法先断开一半看错误帧是否消失再继续缩小范围这样定位速度最快。最后再分享一个我做CAN协议多年的个人体会CAN总线表面看就是两根线、一个差分电压、一串十六进制ID但真正决定系统稳定性的往往不是协议本身而是终端电阻、采样点、线缆长度这些物理细节。车辆协议解析的核心也始终是把“物理信号—报文ID—数据字节”这三层关系一一对应清楚。这个能力一旦练出来不管以后是解析汽车ECU、写OBD工具还是给机器人装关节电机底层那套“电压差、仲裁、错误重发”的哲学都是完全一致的。把这套基础吃透能帮你少走很多弯路。