3. 传感器通信协议
3.1 为什么机器人工程师必须理解底层通信协议
在 ROS2 中,传感器数据最终以 sensor_msgs/msg 下的标准消息类型(如 LaserScan、Imu、Image)发布到话题上。但消息只是“集装箱”,真正决定数据能否准时、完整到达的,是传感器与主控之间的物理链路与通信协议。I2C、SPI、UART、CAN 是当前机器人领域最常用的四种串行通信接口,它们在速率、时序、拓扑、可靠性和 ROS2 集成方式上差异显著。选错协议或配置不当,轻则数据丢帧,重则总线死锁甚至损坏器件。
i2c-tools、spidev、serial、SocketCAN)直接暴露协议特性。理解协议是排查“驱动能加载但数据异常”类问题的前提。
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, ®, 1);
write(fd, &val, 1);
// 循环读取加速度数据(寄存器 0x3B 起 6 字节)
while (rclcpp::ok()) {
uint8_t buf[6];
reg = 0x3B;
write(fd, ®, 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 实战案例:多协议传感器融合的轮式机器人
假设一个室内巡检机器人底盘集成以下传感器:
- MPU6050 IMU(I2C,地址 0x68)→ 发布
sensor_msgs/Imu - TF-Luna 激光测距(UART,115200bps)→ 发布
sensor_msgs/Range - 两个直流电机驱动器(CAN,ID 0x201/0x202)→ 发布
sensor_msgs/JointState
在 ROS2 中,三个驱动节点分别独立运行,通过 robot_state_publisher 或 tf2 进行坐标变换。底层协议差异被完全封装在驱动节点内部,上层导航节点只消费标准消息。
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 查看状态 |
i2cdetect,SPI 用 spidev_test,UART 用 minicom,CAN 用 candump),确认底层链路通畅后再编写 ROS2 驱动,可节省大量调试时间。
理解四种协议的物理特性和时序差异,是编写可靠 ROS2 驱动的基础。I2C 适合低速多从机,SPI 适合高速大数据量,UART 适合简单点对点,CAN 适合多节点实时控制。在实际项目中,应根据传感器输出频率、数据量、实时性要求和布线复杂度综合选型,并在驱动层做好错误处理与诊断上报,才能保证 ROS2 系统的长期稳定运行。