3、CAN通信基础:CAN协议原理、CAN报文结构、CAN总线调试工具使用
各位同学,咱们今天聊CAN通信。
说实话,在自动驾驶这个圈子里混,你要是搞不懂CAN,那基本等于开车不会挂挡。我当年刚入行时,第一个任务就是调通一辆线控底盘的CAN通信,折腾了整整三天,最后发现是终端电阻没焊好。嗯,从那以后,我对CAN总线就多了一份敬畏。
3.1 CAN协议原理:为什么是它?
CAN(Controller Area Network)是博世在80年代搞出来的。你想想看,一个80年代的技术,到现在还在汽车上活得好好的,说明它确实有两把刷子。
核心特点就三个:
- 多主通信:总线上任何一个节点都能主动发消息,不需要主机来调度。这跟I2C、SPI那种主从模式完全不一样。
- 差分信号:CAN_H和CAN_L两根线,靠电压差传数据。抗干扰能力极强,我见过在电机驱动器旁边跑CAN,一点问题没有。
- 非破坏性仲裁:多个节点同时发数据怎么办?谁的ID优先级高(数值小),谁就继续发,低的自动退让。这个机制非常优雅。
重要概念:CAN的物理层
CAN总线两端必须各接一个120Ω的终端电阻。我遇到过好几次,调试时发现数据全是乱码,最后发现是终端电阻没接或者接错了位置。记住:没有终端电阻,信号会反射,通信必挂。
咱们来看一张CAN总线的基本结构图,我手绘了一个SVG,帮你理清思路:
3.2 CAN报文结构:数据到底怎么装的?
CAN报文分两种:标准帧(11位ID)和扩展帧(29位ID)。在自动驾驶领域,大部分线控底盘用的是标准帧,但有些商用车会用扩展帧。我个人习惯,先搞清楚标准帧再说。
标准CAN报文长这样:
| SOF | ID | RTR | IDE | r0 | DLC | Data (0-8字节) | CRC | ACK | EOF |
|---|---|---|---|---|---|---|---|---|---|
| 1 bit | 11 bit | 1 bit | 1 bit | 1 bit | 4 bit | 0-64 bit | 15 bit | 2 bit | 7 bit |
这里我重点说几个关键字段:
- ID(标识符):决定优先级。数值越小,优先级越高。比如方向盘控制报文ID通常是0x0C1,而车速报文可能是0x1A0。你想想看,方向盘控制肯定比车速显示紧急,所以ID更小。
- DLC(数据长度码):表示后面Data字段有几个字节。范围0-8。注意,有些设备会发DLC=0的空帧,这在诊断协议里很常见。
- Data(数据段):最多8个字节。怎么解析?完全看协议定义。比如一个车速信号,可能定义在Byte0和Byte1,小端模式,单位0.01km/h。
我的经验:解析CAN报文时,一定要搞清楚大小端。我见过一个团队,因为大小端搞反了,车速显示300多km/h,吓得测试员以为车要起飞了。后来发现就是字节顺序反了。
3.3 CAN总线调试工具使用:实战必备
纸上谈兵没意思,咱们直接说工具。做CAN调试,你至少得会用一个工具。我个人最常用的是PCAN-USB和Kvaser,配合PCAN-View或者Bus Master。
调试流程一般分三步:
- 连接硬件:把CAN转USB设备插到电脑,接好CAN_H和CAN_L,确认终端电阻。
- 配置参数:设置波特率。大部分车用底盘是250kbps或500kbps。我建议先从500k开始试,不行再换。
- 监听报文:打开工具,看能不能收到数据。如果一片空白,先检查硬件连接。
下面是一个用Python通过python-can库读取CAN报文的示例,我在Autoware项目里经常这么干:
import can
# 初始化CAN接口
bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=500000)
# 发送一个报文
msg = can.Message(arbitration_id=0x0C1, data=[0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], is_extended_id=False)
bus.send(msg)
# 接收报文
for msg in bus:
print(f"ID: {hex(msg.arbitration_id)}, Data: {msg.data.hex()}")
# 这里可以加解析逻辑
if msg.arbitration_id == 0x1A0:
speed = (msg.data[0] << 8) | msg.data[1]
print(f"当前车速: {speed * 0.01} km/h")
警告:千万不要在车辆行驶过程中随意发送CAN报文!我曾经见过有人调试时误发了刹车报文,车直接急刹,差点出事故。调试时最好用仿真模式,或者把车架起来。
3.4 避坑指南:我踩过的那些坑
做CAN调试这么多年,我总结了几条血泪教训:
- 终端电阻必须接:不接的话,长距离通信必出乱码。我见过有人用一根网线当CAN线,跑了50米,没接终端电阻,数据全废。
- 波特率必须一致:总线上所有节点的波特率必须一样。我曾经把ECU设成250k,调试工具设成500k,折腾了两小时才发现。
- 地线必须共地:CAN虽然用差分信号,但各个节点的GND必须连在一起。不共地的话,共模电压会漂,严重时烧毁收发器。
- 报文ID不要冲突:同一个总线上,两个节点发相同ID的报文,虽然仲裁机制能处理,但数据会乱。我建议在项目初期就做好ID分配表。
核心总结:CAN通信是自动驾驶控制接入的基石。你搞懂了CAN协议原理、报文结构,再配上趁手的调试工具,基本就能应对90%的线控底盘适配工作。剩下的10%,靠经验和耐心。
好了,这一章就到这里。记住,CAN调试没有捷径,多动手、多踩坑,自然就熟了。