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,帮你理清思路:

CAN_H CAN_L 120Ω 120Ω ECU 1 ECU 2 ECU 3 Autoware 控制器 CAN总线拓扑结构:多节点共享双绞线,两端各接120Ω终端电阻

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。

调试流程一般分三步:

  1. 连接硬件:把CAN转USB设备插到电脑,接好CAN_H和CAN_L,确认终端电阻。
  2. 配置参数:设置波特率。大部分车用底盘是250kbps或500kbps。我建议先从500k开始试,不行再换。
  3. 监听报文:打开工具,看能不能收到数据。如果一片空白,先检查硬件连接。

下面是一个用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调试没有捷径,多动手、多踩坑,自然就熟了。

可可精品资料库