3. 传感器通信协议

3.1 为什么机器人工程师必须理解底层通信协议

在 ROS2 中,传感器数据最终以 sensor_msgs/msg 下的标准消息类型(如 LaserScan、Imu、Image)发布到话题上。但消息只是“集装箱”,真正决定数据能否准时、完整到达的,是传感器与主控之间的物理链路与通信协议。I2C、SPI、UART、CAN 是当前机器人领域最常用的四种串行通信接口,它们在速率、时序、拓扑、可靠性和 ROS2 集成方式上差异显著。选错协议或配置不当,轻则数据丢帧,重则总线死锁甚至损坏器件。

核心判断:ROS2 节点层看不到底层协议差异,但驱动层(如 i2c-tools、spidev、serial、SocketCAN)直接暴露协议特性。理解协议是排查“驱动能加载但数据异常”类问题的前提。
四种串行协议在 ROS2 传感器集成中的定位 I2C 2线(SDA+SCL) 100k~1Mbps 多从机地址寻址 IMU/温湿度/ADC ROS2: linux/i2c-dev SPI 4线(MOSI/MISO/SCK/CS) 10~80Mbps 片选独立寻址 激光雷达/高速ADC ROS2: spidev UART 2线(TX+RX) 0.3~12Mbps 点对点异步 GPS/串口屏/旧雷达 ROS2: serial CAN 2线(CANH+CANL) 0.125~8Mbps 多主多从+仲裁 电机驱动/车规传感器 ROS2: SocketCAN ROS2 驱动层 i2c-tools / spidev / pyserial / python-can → 封装为 C++ 或 Python 驱动节点 ROS2 消息层 sensor_msgs/Imu · sensor_msgs/LaserScan · sensor_msgs/JointState · custom_msgs

3.2 四种协议核心原理与选型对比

3.2.1 I2C:多从机低速总线

I2C 使用 SDA(数据) 和 SCL(时钟) 两根线,所有从机并联在总线上,通过 7 位或 10 位地址区分设备。主机发起通信时先发送起始条件 + 从机地址 + 读写位,从机应答后逐字节传输。由于是开漏输出,必须外接上拉电阻(典型 4.7kΩ)。

  • 优点:接线少、支持多设备、地址可配置(部分芯片支持 A0/A1 引脚)。
  • 缺点:速率低(标准模式 100kbps,快速模式 400kbps),总线电容限制设备数量,无错误检测机制。
  • ROS2 场景:IMU(如 MPU6050)、气压计(BMP280)、距离传感器(VL53L0X)。

3.2.2 SPI:高速全双工主从总线

SPI 采用 四线制:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。每个从机独占一根 CS 线,主机通过拉低对应 CS 选中设备。通信时主机产生时钟,数据在时钟边沿同步移位,支持全双工。

  • 优点:速率极高(可达 80Mbps+)、无地址帧开销、全双工。
  • 缺点:每增加一个设备需多一根 CS 线;无应答机制,需软件确认。
  • ROS2 场景:激光雷达(如 RPLIDAR A1 内部)、高速 ADC、LCD 显示屏、外部 Flash。

3.2.3 UART:异步点对点串口

UART 只需 TX 和 RX 两根线,双方需预先约定波特率(如 115200)。数据帧包含起始位、数据位(通常 8 位)、可选校验位和停止位。无时钟线,靠起始位同步。

  • 优点:简单可靠、几乎任何 MCU 都支持、可通过 USB-TTL 转接直接连 PC。
  • 缺点:点对点(不支持多机直接并联)、速率受限于波特率精度、无流控时可能丢数据。
  • ROS2 场景:GPS 模块(NMEA 协议)、串口激光雷达、飞控(PX4/ArduPilot 的 TELEM 口)。

3.2.4 CAN:差分多主实时总线

CAN 使用 CANH 和 CANL 差分线,通过 报文 ID 优先级仲裁 实现多主通信。节点发送数据时同时监听总线,若检测到更高优先级 ID 则自动退避。CAN 帧包含 ID、DLC、数据域和 CRC 校验,错误帧自动重发。

  • 优点:抗干扰强(差分信号)、实时性高(仲裁机制保证低 ID 优先)、错误检测完善。
  • 缺点:速率上限低于 SPI(CAN FD 最高 8Mbps)、需收发器芯片(如 TJA1050)、协议栈相对复杂。
  • ROS2 场景:电机驱动器(如 Dynamixel 部分型号)、车规传感器、底盘控制(线控转向/刹车)。
特性 I2C SPI UART CAN
信号线数 2 (SDA, SCL) 4+ (MOSI, MISO, SCK, CS×N) 2 (TX, RX) 2 (CANH, CANL)
通信模式 半双工 全双工 全双工 半双工
典型速率 100k~1Mbps 10~80Mbps 0.3~12Mbps 125k~8Mbps
多设备支持 是(地址寻址) 是(片选) 否(点对点) 是(ID 仲裁)
错误检测 无(仅 ACK) 无(需软件) 可选校验位 CRC + 错误帧
ROS2 典型包 i2c-tools spidev serial SocketCAN

3.3 ROS2 驱动集成方法流程

无论哪种协议,在 ROS2 中集成的流程都遵循“打开设备 → 配置参数 → 循环读写 → 发布消息”的框架。以下以 I2C 和 CAN 为例展示典型代码模式。

3.3.1 I2C 传感器驱动(以 MPU6050 为例)

// 使用 Linux i2c-dev 接口
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int fd = open("/dev/i2c-1", O_RDWR);
ioctl(fd, I2C_SLAVE, 0x68);  // MPU6050 地址

// 唤醒传感器:向寄存器 0x6B 写 0
uint8_t reg = 0x6B, val = 0x00;
write(fd, &reg, 1);
write(fd, &val, 1);

// 循环读取加速度数据(寄存器 0x3B 起 6 字节)
while (rclcpp::ok()) {
    uint8_t buf[6];
    reg = 0x3B;
    write(fd, &reg, 1);
    read(fd, buf, 6);
    // 拼接为 int16 并发布 sensor_msgs/Imu
}

3.3.2 CAN 总线接入 ROS2(SocketCAN)

# 1. 启用 can 接口
sudo ip link set can0 up type can bitrate 500000
# 2. 查看总线状态
candump can0

# 3. Python 驱动示例(使用 python-can)
import can
import rclpy
from sensor_msgs.msg import JointState

bus = can.interface.Bus(channel='can0', bustype='socketcan')
while rclpy.ok():
    msg = bus.recv(timeout=0.1)
    if msg.arbitration_id == 0x201:  # 电机反馈帧
        # 解析数据并发布
        pass
关键配置检查清单:
  • I2C:确认设备地址(i2cdetect -y 1)、上拉电阻、总线速率。
  • SPI:确认时钟极性和相位(CPOL/CPHA)与传感器数据手册一致。
  • UART:核对波特率、数据位、停止位、流控(通常关闭)。
  • CAN:终端电阻(120Ω)、波特率、帧格式(标准/扩展)。

3.4 实战案例:多协议传感器融合的轮式机器人

假设一个室内巡检机器人底盘集成以下传感器:

  1. MPU6050 IMU(I2C,地址 0x68)→ 发布 sensor_msgs/Imu
  2. TF-Luna 激光测距(UART,115200bps)→ 发布 sensor_msgs/Range
  3. 两个直流电机驱动器(CAN,ID 0x201/0x202)→ 发布 sensor_msgs/JointState

在 ROS2 中,三个驱动节点分别独立运行,通过 robot_state_publisher 或 tf2 进行坐标变换。底层协议差异被完全封装在驱动节点内部,上层导航节点只消费标准消息。

常见误区:不要试图在 ROS2 节点中直接操作寄存器而不检查返回错误。I2C 的 read() 返回 -1 时可能是设备未应答;CAN 的 recv() 超时可能是总线关闭。务必在驱动中加入错误重试和状态上报(如 diagnostic_msgs)。

3.5 排障复盘:协议集成中的典型问题

现象 可能原因 排查方法
I2C 设备检测不到 地址错误、上拉电阻缺失、SDA/SCL 接反 用 i2cdetect 扫描;万用表测电压
SPI 数据全为 0xFF MISO/MOSI 接反、CPOL/CPHA 不匹配 示波器看时序;尝试四种模式组合
UART 乱码 波特率不一致、地线未共地 逻辑分析仪抓波形;确认共地
CAN 总线无通信 缺少终端电阻、波特率不匹配、ID 冲突 ip -details link show can0 查看状态
复盘建议:在正式集成前,先用命令行工具验证物理层(I2C 用 i2cdetect,SPI 用 spidev_test,UART 用 minicom,CAN 用 candump),确认底层链路通畅后再编写 ROS2 驱动,可节省大量调试时间。

理解四种协议的物理特性和时序差异,是编写可靠 ROS2 驱动的基础。I2C 适合低速多从机,SPI 适合高速大数据量,UART 适合简单点对点,CAN 适合多节点实时控制。在实际项目中,应根据传感器输出频率、数据量、实时性要求和布线复杂度综合选型,并在驱动层做好错误处理与诊断上报,才能保证 ROS2 系统的长期稳定运行。