第 13.2 節

CAN通信

0瀏覽次數0訪問次數--跳出率--平均停留

Linux CAN 通信

CAN 通信是机器人、车辆、工业控制和电机驱动中非常常见的总线通信方式。

相比串口通信,CAN 更适合多个设备挂在同一条总线上进行通信。例如一个机器人底盘中,可能有多个电机驱动器、底盘控制板、传感器节点共同连接在 CAN 总线上。

在实际项目中,CAN 通信常见于:

  • 控制电机驱动器
  • 读取电机编码器反馈
  • 连接底盘控制板
  • 多个控制节点之间通信
  • 车辆总线通信
  • 工业设备和运动控制系统

对于移动机器人底盘、电机控制、自动驾驶线控系统等方向来说,CAN 是非常重要的一类通信方式。

CAN 通信是什么

CAN,全称 Controller Area Network,是一种多主机总线通信协议。

和串口点对点通信不同,CAN 总线上可以挂多个节点。每个节点都可以发送消息,其他节点根据 CAN ID 判断是否需要处理这条消息。

CAN 通信中常见概念包括:

概念说明
CAN ID用来标识一帧 CAN 消息的含义或来源
标准帧11 位 CAN ID
扩展帧29 位 CAN ID
数据区经典 CAN 一帧最多 8 字节数据
CAN FDCAN 的扩展版本,支持更长数据区和更高数据速率
波特率常见有 250K、500K、1M
终端电阻CAN 总线两端通常需要 120Ω 终端电阻

在 Linux 中,CAN 设备通常不是 /dev/ttyUSB0 这种设备文件,而是被抽象成网络接口,例如:

can0

可以使用下面的命令查看:

ip link

Linux 下常见 CAN 工具和库

Linux 下 CAN 通信的核心是 SocketCAN。

SocketCAN 是 Linux 内核提供的 CAN 通信框架,它把 CAN 设备抽象成类似网络接口的形式,例如 can0can1

常见工具和库包括:

方案类型特点适合场景
SocketCANLinux 原生 CAN 接口Linux 标准方案,通用、稳定、工程常用推荐作为主线学习
can-utilsSocketCAN 命令行工具集提供 candumpcansendcangen 等工具调试 CAN 总线
libsocketcanSocketCAN 辅助库便于配置和管理 CAN 接口需要程序内配置 CAN 参数时使用
ros2_socketcanROS 2 封装库将 SocketCAN 封装进 ROS 2 生态纯 ROS 2 项目或参考实现
ros2_canopenROS 2 CANopen 协议栈适合 CANopen 设备电机驱动器使用 CANopen 协议时
厂商 SDK厂商提供的驱动库和具体硬件绑定使用特定 USB-CAN、PCIe-CAN 设备时

各方案对比

方案优点缺点
SocketCANLinux 原生、通用、稳定、适合长期学习需要理解 socket 编程和 CAN 帧结构
can-utils调试方便,命令简单主要用于命令行测试,不是完整工程封装
libsocketcan配置 CAN 接口方便不是主要的数据收发方案
ros2_socketcan和 ROS 2 生态结合方便离开 ROS 2 后复用性较差
ros2_canopen适合 CANopen 设备,协议栈完整只适合 CANopen 场景,学习成本较高
厂商 SDK对特定硬件支持好通用性较差,容易绑定厂商

本教程建议选择

本教程建议优先选择:

SocketCAN + can-utils

原因是:

  • SocketCAN 是 Linux 下 CAN 通信的标准方案
  • 不依赖 ROS 2,适合普通 C++、OpenCV、Qt、嵌入式 Linux 项目
  • can-utils 非常适合调试和验证 CAN 设备
  • 后续可以封装成普通 C++ driver
  • 可以自然接入 ros2_control hardware_interface

推荐的学习顺序是:

1. 使用 ip link 配置 can0
2. 使用 candump 接收 CAN 帧
3. 使用 cansend 发送 CAN 帧
4. 理解 struct can_frame
5. 使用 C++ SocketCAN 进行收发
6. 封装自己的 CanDriver 类
7. 接入 ROS 2 或 ros2_control

推荐的工程结构是:

上层项目:ROS 2 / OpenCV / Qt / 普通 C++ 程序
        ↓
自己封装的 CanDriver 类
        ↓
SocketCAN
        ↓
can0
        ↓
电机驱动器 / STM32 / 底盘控制板

与 ROS 2 的关系

学习 SocketCAN,并不代表不用 ROS 2。

在 ROS 2 项目中,可以将 CAN 通信封装成一个普通 C++ 类,然后在 ROS 2 节点或 ros2_control hardware_interface 中调用。

例如:

ros2_control controller
        ↓
hardware_interface
        ↓
自己封装的 CanDriver
        ↓
SocketCAN
        ↓
can0
        ↓
电机驱动器

这样做的好处是,CAN 通信代码不会和 ROS 2 强绑定。以后即使写普通 C++ 项目、Qt 上位机或者 OpenCV 控制程序,也可以继续复用同一套 CAN driver。

什么时候使用 ros2_socketcan

ros2_socketcan 并不是不能用。

如果项目本身就是纯 ROS 2 架构,并且希望快速把 CAN 帧转换成 ROS 2 topic,那么 ros2_socketcan 是一个可以考虑的方案。

但是对于底层驱动学习和长期工程复用来说,仍然建议先掌握 SocketCAN。

可以这样理解:

SocketCAN:底层能力
ros2_socketcan:ROS 2 封装

先学 SocketCAN,再看 ros2_socketcan 会更容易理解。

什么时候使用 ros2_canopen

如果你的电机驱动器或工业设备使用的是 CANopen 协议,那么就不能只把它当作普通 CAN 帧通信来看待。

CANopen 是建立在 CAN 之上的高层协议,涉及对象字典、PDO、SDO、NMT 等概念。

这种情况下,可以考虑学习:

ros2_canopen

但如果你的设备只是自定义 CAN 协议,例如自己规定 CAN ID 和数据格式,那么优先学习 SocketCAN 就足够了。

本章学习目标

学习完本章后,应该能够掌握:

  • CAN 通信的基本概念
  • Linux 下 can0 设备的使用方式
  • 使用 ip link 配置 CAN 波特率
  • 使用 candumpcansend 调试 CAN 总线
  • 理解 CAN ID、标准帧、扩展帧、数据区等概念
  • 使用 C++ SocketCAN 进行 CAN 帧收发
  • 将 CAN 通信封装成可复用的 C++ 类
  • 为后续接入 ROS 2、ros2_control、电机驱动器控制打基础
音乐页