我是做嵌入式出身,这几年从车载BMS调试一路做到机械臂关节控制,绕来绕去始终没离开CAN总线。很多人一听到“车辆协议”就以为是搞OBD诊断、读故障码,实际上CAN总线是整车电子的命根子,从发动机、ABS到车窗升降,只要需要可靠通信的节点,几乎都挂在CAN上。现在人形机器人火起来,达妙电机这类关节执行器也大量用CAN做实时控制,协议栈和车载ECU通信本质上是同一套东西。这篇我打算把CAN总线的基础知识、物理层电压变化、协议帧结构、波形质量判读、以及实际控制电机的链路串起来讲一遍,适合刚接触总线通信的工程师,也适合做机器人、机械臂项目想快速上手CAN的人参考。
1. 为什么搞懂CAN总线,是车载和机器人项目绕不过的一关
1.1 从“线束爆炸”到两根双绞线
回到上世纪80年代,传统汽车的电控单元只要一多,线束就变成噩梦。每个传感器、执行器都要单独拉线到对应的ECU,一辆车动辄几百根线,重量大、装配难、故障率高。Bosch在1986年前后推出CAN总线,核心目的就一个:用一对双绞线把车上的ECU串成一个网络,大家在这根共享总线上发报文。这样做的好处显而易见——线束数量大幅下降,而且数据天然是共享的,不需要每个节点之间都点对点连线。
这个思路放到现在依然不过时。我们做一个多关节机械臂,如果是RS-232那种点对点方式,6个关节电机就要6组线,还得统一收发,乱成一团。用CAN之后,所有电机并联在同一对线上,每个节点有不同的ID,主控制器发一帧数据,所有电机都能收到,但只有ID匹配的那个会动作。这套逻辑和汽车ECU网络完全一样。
1.2 CAN、LIN、RS-485,选型时为什么优先CAN
很多人会问:RS-485也是差分信号,也能多点挂接,为什么车载和机器人控制都优先选CAN?关键在于CAN不只有物理层,它自带完整的协议层——帧格式、优先级仲裁、错误检测、错误恢复,这些是RS-485没有的。RS-485只规定了电气特性,至于怎么组帧、怎么避免两个节点同时抢总线,那需要你自己写协议,稍不留神就出现数据碰撞、接收错乱。
LIN总线虽然成本低,但只有单主架构,适合车窗、门锁这种对实时性要求不高的低速场景,速率一般在20kbps左右,跑实时控制完全不够。CAN标准速率最高1Mbps,CAN FD还能到5Mbps以上,对绝大多数控制场景绰绰有余。
| 总线 | 物理介质 | 典型速率 | 多主能力 | 协议完整性 |
|---|---|---|---|---|
| LIN | 单线 | 10-20kbps | 不支持 | 简单校验 |
| RS-485 | 双绞线差分 | 最高数十Mbps | 需自组协议 | 无内建协议 |
| CAN | 双绞线差分 | 最高1Mbps | 支持 | 帧格式+仲裁+错误处理完备 |
| CAN FD | 双绞线差分 | 最高5Mbps以上 | 支持 | 帧格式扩展+更优CRC |
1.3 CAN的应用边界:从乘用车到机器人的共同基础
我遇到过不少做应用层开发的同学,觉得CAN是车辆工程师才需要弄懂的东西,直到他们拿到一个机器人关节电机样品,翻开手册看到“CAN接口”才意识到问题没那么简单。事实是,CAN总线已经成了车载网络、工业现场、医疗设备、机器人关节控制的通用语言。它的抗干扰能力、实时性、组网灵活性,让很多需要确定性通信的场景都把CAN当作默认选项。
2. 物理层入门:CAN电压差到底怎么产生、怎么变化
2.1 两条线的状态切换原理
CAN总线物理介质是CAN_H和CAN_L两条线,信号靠这两条线之间的差分电压来承载。总线上只有两个状态:显性(Dominant)和隐性(Recessive)。打个不太精确但好记的比方:显性相当于有人站出来发言,隐性相当于所有人沉默。
先说隐性状态。当总线上没有节点发送数据时,所有收发器的输出级都处于高阻状态,两条线会通过终端电阻网络被拉到同一个电平——就是通常说的2.5V左右。此时CAN_H和CAN_L之间的差分电压接近0V,对应逻辑电平“1”。
再说显性状态。某个节点开始发送数据时,收发器内部会拉起CAN_H、拉下CAN_L。标准高速CAN下,CAN_H通常被推到3.5V左右,CAN_L被拉到1.5V左右,差分电压约2V,对应逻辑电平“0”。这就是很多人问的“can总线的电压差是怎么改变的”——本质是一个收发器在CPU命令下切换了内部输出级的驱动状态。
2.2 终端电阻和共模电压,两个容易被忽略的关键点
CAN总线两端必须各接一个120Ω终端电阻。这个电阻不是摆设:它有两个作用。第一,吸收信号到达线末端时产生的反射,避免波形出现振铃;第二,为显性电平提供电流回路。没有终端电阻的CAN网络,波形会“糊”掉,差分电平也可能不够,远距离传输更是直接罢工。
还有一个容易踩坑的点是共模电压。CAN的物理层是差分传输,理论上抗共模干扰,但共模电压不能无限制漂移。收发器内部有共模输入范围,超过范围照样报错。在实际项目里,尤其是机器人关节电机这种带PWM驱动的场景,电机功率线和CAN通信线如果绑在一起走,共模噪声会非常可观。我自己的习惯是:通信线单独走槽,双绞尽量靠近,远离大电流PWM线至少10cm,必要时加磁环。
2.3 从示波器测量理解“电压差”的真实情况
如果你给CAN网络接上示波器,用两个通道分别看CAN_H和CAN_L对地的电压,会看到显性时CAN_H往上跳、CAN_L往下掉,隐性时两条线回到同一个电平。如果用差分探头或者示波器的数学通道做CH1-CH2,就能直接看到差分电压波形。理想状态下,显性差分电平应该在1.5V到2.5V之间,隐性接近0V。
但实际总线不是理想环境。节点数越多、线缆越长、终端匹配越差,波形畸变越明显。差分电压只要低于接收器的判定阈值(比如有些收发器要求差分电压大于0.9V才识别为显性),通信就会出错。这就是为什么靠电压差判断通信好坏,比单纯看逻辑“0”和“1”更真实。
3. 协议层拆解:帧格式、仲裁和位填充怎么配合
3.1 一个标准数据帧的完整结构
CAN 2.0A标准帧从SOF(帧起始)开始,紧接着是11位标识符,再往后是RTR位、IDE位、r0保留位、4位DLC数据长度码,然后就是0到8字节数据,接下来是15位CRC校验、ACK应答槽、EOF帧结束,再后面还有3位IFS帧间隔。整个帧看起来复杂,但核心就两个任务:把数据送达,以及保证多个节点同时发帧时不冲突。
我在项目里通常直接用外设寄存器或者库函数发送,不用手工拼帧,但理解帧结构依然很重要。比如排查问题时,如果TJA1050这类收发器连续报错,你得能看懂总线上的数据是不是从SOF开始的完整帧,CRC位置对不对,ACK是主机回的还是从机回的——这些信息在协议分析仪上看一目了然,不懂帧结构就无从下手。
3.2 非破坏性仲裁:为什么ID越小优先级越高
CAN总线最大的亮点是多主仲裁机制。以前用RS-485时,两个节点同时发声就会产生数据冲突,很麻烦。CAN的做法是:允许所有节点同时发送,在仲裁段通过显性位覆盖隐性位的机制,大家一路“比较”下去,ID小的节点自然胜出,而且胜出方不需要重发,这就是“非破坏性仲裁”。
举个例子,节点A发送ID=0x123,节点B发送ID=0x456。二进制展开后,A的ID更小,在高位比较时A的位保持显性、B的位被迫收手,B自动转为接收状态。这个机制对实时控制意义重大——优先级高的报文可以抢占总线,保证关键数据(比如刹车指令、关节急停)永远第一时间发出去。
3.3 位填充是什么,为什么它能防错
CAN协议规定:发送方在连续发出5个相同电平之后,必须插入一个相反电平的填充位;接收方发现连续6个相同位,就说明总线上出现了异常帧。这个机制两个作用:一是保证接收端的锁相环电路可以持续从电平跳变中提取时钟信号,不至于长时间没有跳变导致位同步漂移;二是作为错误检测手段之一,连续6个相同位会被判定为位填充错误,节点会主动发出错误帧。
做高速CAN传输时,我特别关注位填充的副作用——它会降低有效数据率。如果数据区恰好是长串0或者长串1,填充位会让实际要传的位数量变多。所以如果你想精确估算总线负载率,不能只看DLC,还要把最坏情况的位填充算进去。
3.4 采样点和波特率:1Mbps不是想上就能上
初学者常犯的错误是直接把波特率配到1Mbps,结果通信要么时好时坏,要么完全不通。原因是波特率不仅取决于总线速度,还取决于节点间的位同步能力。CAN的每一位时间由同步段、传播段、相位缓冲段1、相位缓冲段2组成,采样点一般配置在70%~80%的位置——也就是一位时间的后段。这样做的目的是等信号从线缆一端传到另一端再稳定下来后去采样,避免采到跳变边缘。
如果总线长度过长、波特率过高,信号还在线上传播,接收端已经到采样点了,这帧必然错。经验上,1Mbps时总线长度一般控制在几十米内,250kbps时可以到200米以上。实测中最靠谱的做法是:先用CAN分析仪做回环测试,再逐步提高波特率,同时看误帧率,而不是直接按手册理想参数配置。
4. 拿到CAN波形后,怎么快速判断通信质量
4.1 示波器抓取CAN波形的两种方式
CAN波形最直接的获取方式是示波器。如果你有差分探头,那最好,直接用差分通道接CAN_H和CAN_L。如果没有差分探头,也可以把CH1接到CAN_H、CH2接到CAN_L,然后用示波器的数学通道做CH1-CH2,效果几乎等同。触发方式建议设为下降沿触发,因为SOF是从隐性跳到显性的过程,在差分通道上表现为一个明显的负跳变。
抓波形的时候要注意:总线空闲时,差分通道应该是一条接近0V的直线;一旦节点开始发帧,就会出现一连串有规律的高低电平跳变。如果你看到电平杂乱无章、幅度忽大忽小,大概率是有节点在重复发错误帧,需要马上停下来排查。
4.2 健康波形长什么样
一个健康的CAN波形应该满足这几个条件:
- 隐性差分电平在0V附近,不能长期偏到正或负;
- 显性差分电平在1.5V~2.5V之间,且跳变沿干净;
- 每一位的宽度基本一致,和波特率匹配;
- 显性到隐性、隐性到显性的边沿没有明显振铃和反射台阶;
- 总线空闲段没有被周期性噪声污染。
我习惯把波形文件存下来,下次遇到同样问题时直接对比。不同节点的波形可能会有细微差异,但如果差分电平偏低、边沿过缓,大概率是终端电阻缺失、总线分支过长或者线缆阻抗不匹配。
4.3 波形异常对应的典型故障
| 波形表现 | 可能原因 | 检查方向 |
|---|---|---|
| 显性差分电压低于1V | 终端电阻缺失/节点过多 | 测量总线两端直流电阻是否约60Ω |
| 边沿出现振铃 | 分支线路过长/无终端匹配 | 缩短分支,在远端补终端电阻 |
| 电平不对称,CAN_H和CAN_L幅值差距大 | 收发器损坏或线缆短路 | 断电测量CAN_H与CAN_L间阻值 |
| 波形整体被噪声覆盖 | 共模干扰/接地问题 | 检查地线,通信线远离功率线 |
| 波形存在毛刺的“台阶” | 采样点配错或信号反射 | 降低波特率或调整采样点位置 |
4.4 用波形文件做离线分析的排查方式
现在很多CAN分析工具支持把波形导出成CSV、binlog或log格式,然后再离线回放分析。我在做整车网络问题定位时,经常让现场人员把CAN波形文件发回来,我这边直接导入电脑,放大看每一位。波形文件的好处是可以反复看,不需要现场复现。你可以把关注的时间窗口扩大,看是不是周期性错误;也可以把波形转成二进制位流,手动比对帧ID和数据内容。这比在现场盯着示波器屏幕猜有效率得多。
5. 从车载到底层机器人:以达妙电机为例看CAN控制链路
5.1 为什么关节电机都选CAN而不是EtherCAT或PWM
机器人关节电机对控制的实时性和同步性要求很高,但也不是非要上EtherCAT这种千兆级实时总线。CAN在机械臂场景的优势非常明显:一对双绞线就能串联多个关节,1MHz波特率下每毫秒就能发一帧控制指令,足够支撑最常见的关节位置、速度、力矩闭环。再加上CAN本身具备抗干扰能力,整体方案简单、成本低、可靠性高。
达妙电机这类产品在机器人圈用得很多,内部集成了驱动器和编码器,对外提供CAN接口。主控制器通过CAN给电机发送期望位置、速度、力矩等参数,电机把实际编码器位置、速度和力矩回传给主控,形成闭环。整个交互方式和汽车上ECU与传感器之间的CAN通信,本质上是同一条链路。
5.2 控制帧和反馈帧:一主多从的典型结构
达妙电机的CAN通信是典型的一主多从结构。主机分配每个电机一个CAN ID,发送控制帧时,ID标明是发给哪个电机的。反馈帧则由电机主动上传,ID是电机自己的编号,主机根据反馈帧的内容更新每个关节的状态。这种结构只要总线负载率控制好,电机数量再多也不乱。
具体到控制帧的内容,厂家手册通常会定义8字节的控制字,常见字段包括目标位置、目标速度、输出力矩、Kp和Kd刚度系数等。需要注意的是,不同型号的电机关节数据格式可能完全不一样。有的把位置放在前4字节,用int32表示;有的是小端浮点格式。我自己的做法是拿到电机后先发一帧空命令,观察反馈帧的原始字节,再对照手册确认字段顺序和单位,不要上来就套模板。位置单位可能是0.01度,也可能是rad的1000倍,这个换算错了,电机转起来就是灾难。
5.3 实现精准关节控制的几个关键细节
第一,控制周期要稳定。电机控制帧必须以固定周期发送,通常做到1kHz,也就是1ms发一次。如果主控系统因为任务调度导致发帧间隔抖动,电机响应就会产生顿挫感。所以在MCU里我一般把CAN发送放到定时器中断里,保证时序尽可能稳定。
第二,浮点转整型的处理。CAN帧里的数据本质是字节,如果你的控制量是浮点数,要么约定按比例换算成整型,要么直接按IEEE 754格式把float的字节塞进帧里。前者在嵌入式里更快,后者更灵活。我倾向用uint32_t和float的联合体做内存映射转换,避免人为移位导致大小端问题。
第三,反馈回来的编码器数据要做滤波。电机的编码器读数在某些工况下会有轻微抖动,尤其是低速启动和换向瞬间。直接拿原始值做闭环容易造成力矩波动,我会做一个简单的低通滤波或者中值滤波,实测下来效果立竿见影。
5.4 多关节并联时的总线布线和终端处理
多根关节电机接在同一条CAN总线上,物理接线非常像汽车上的ECU链:一条主干线从主控出发,依次经过每个电机节点,最后在末端接一个120Ω终端电阻。很多人只给主控端接了终端电阻,漏了最远端,结果就是一两米短距离还看不出来,稍微长一点波形就开始振铃,通信疯狂报错。
另外,我看到不少机器人项目把CAN线当成普通杜邦线随便飞线接。杜邦线本身阻抗不稳定,距离一长高频衰减大,很容易丢帧。自己做样机可以用双绞线或者屏蔽双绞线,接头用带锁的接插件,排查起来能省很多事。用CAN分析仪先看实测波形,确认边沿和电平正常后再装到结构件里,不然出了问题拆装一次非常费劲。
5.5 一套亲测有效的电机CAN调试流程
拿到达妙电机或者类似CAN电机,我建议按这个顺序逐步调通:
- 先用CAN分析仪配合官方上位机,确认电机的ID、波特率和通信正常,这一步不要急着写代码;
- 主控端接好CAN收发器,发一个最简单的读ID指令,确认主控和电机能双向通信;
- 将控制帧从“力矩模式”起步,一点点加峰值力矩,确认电机能响应;
- 切换到位置模式,先设置一个很小的目标角度,观察运行轨迹和反馈值是否对齐;
- 最后才把多电机挂载到同一条总线上,逐个分配ID,再做联动测试。
整个过程中,只要反馈帧的数据和电机实际动作对不上,就回头用示波器看波形,确认总线上有没有错误帧。这个排查链路我用了很多年,基本能覆盖九成以上的CAN通信问题。
做CAN总线这些年,我最大的体会是:这个协议看起来是上世纪的东西,但实际项目里遇到的大部分问题根本不是协议本身的bug,而是物理层没处理好、波形没看明白。不管是整车网络还是机器人关节电机,只要你把电压差、终端电阻、帧结构、采样点这几个关键点吃透,CAN总线其实是一个非常好用又可靠的通信方案。最后再分享一个小建议:新项目调CAN,第一步别急着写满屏代码,先用示波器把波形看明白,把物理层搞扎实,后面所有问题都会简单很多。