4. 传感器数据接口标准:ROS2消息类型详解

各位同学,欢迎来到第四章。前面我们聊了传感器怎么连、驱动怎么写,但有个关键问题一直没解决——传感器数据到了ROS2系统里,到底长什么样?

说白了,这就是个“数据格式”的问题。你想想看,一个激光雷达发出来的是点云,一个IMU发出来的是加速度和角速度,一个摄像头传出来的是图像。如果每个传感器都用自己的“方言”,那系统就没法玩了。ROS2的解决方案就是——统一消息接口。

核心思想:所有传感器数据,都必须按照ROS2定义好的消息格式来封装。这样,无论是谁写的驱动,无论是哪个厂家的传感器,数据到了ROS2里都能被正确理解和处理。

4.1 ROS2消息类型基础

ROS2的消息类型,本质上就是一个结构化数据定义。它用.msg文件来描述,里面包含各种字段,每个字段都有明确的类型。

我个人习惯把消息类型分成三类:

  • 基础类型:int32、float64、string这些,跟编程语言里的基本类型差不多
  • 复合类型:由基础类型组合而成,比如Header(时间戳+坐标系)
  • 数组类型:用来表示序列数据,比如激光雷达的一圈扫描点

嗯,这里要注意:ROS2的消息类型是强类型的。你定义了一个int32,就不能往里塞float64。这跟Python那种动态类型不一样,但好处是——数据在传输过程中不会出现“类型错乱”的问题。

4.2 自定义消息接口(.msg)

很多时候,ROS2自带的那些消息类型不够用。比如你做了一个特殊的传感器,输出的是“温度+湿度+气压”三合一数据。这时候就需要自己定义消息了。

我给大家看一个我项目里用过的例子:

# WeatherSensor.msg
std_msgs/Header header
float64 temperature    # 温度,单位:摄氏度
float64 humidity       # 湿度,单位:百分比
float64 pressure       # 气压,单位:百帕
string location        # 位置描述

定义好之后,怎么用呢?三步走:

  1. 在功能包的msg/目录下创建WeatherSensor.msg文件
  2. 在CMakeLists.txt里添加rosidl_generate_interfaces调用
  3. 在package.xml里声明rosidl_default_generators依赖

小技巧:我建议你在定义消息时,一定要写注释。特别是单位、坐标系、取值范围这些信息。不然过两个月你自己都看不懂这个字段是啥意思了——别问我怎么知道的。

4.3 传感器通用消息格式(sensor_msgs)

ROS2专门为传感器数据准备了一个消息包——sensor_msgs。这里面定义好了各种常见传感器的数据格式。

我挑几个最常用的给大家说说:

消息类型 适用传感器 核心字段
sensor_msgs/Image 摄像头 width, height, encoding, data
sensor_msgs/LaserScan 激光雷达 angle_min, angle_max, ranges, intensities
sensor_msgs/Imu 惯性测量单元 orientation, angular_velocity, linear_acceleration
sensor_msgs/PointCloud2 3D激光雷达/深度相机 width, height, point_step, row_step, data
sensor_msgs/JointState 关节编码器 name, position, velocity, effort

我曾经在一个项目里踩过坑——用sensor_msgs/Image传深度图,结果发现编码格式选错了,图像数据全乱了。后来才搞清楚,深度图要用32FC1编码,彩色图用rgb8或bgr8。这些细节,文档里都有,但你不踩一次坑真记不住。

4.4 接口包创建与使用

在实际项目中,我习惯把消息定义单独放在一个包里,叫做xxx_interfaces。这样做的好处是:

  • 消息定义和业务逻辑分离,方便复用
  • 多个功能包可以共享同一个消息定义
  • 版本管理更清晰

创建接口包的步骤:

# 1. 创建接口包
ros2 pkg create --build-type ament_cmake sensor_interfaces

# 2. 创建msg目录
mkdir sensor_interfaces/msg

# 3. 编写消息文件(比如刚才的WeatherSensor.msg)

# 4. 修改CMakeLists.txt
# 添加:
# rosidl_generate_interfaces(${PROJECT_NAME}
#   "msg/WeatherSensor.msg"
# )

# 5. 修改package.xml
# 添加:
# <build_depend>rosidl_default_generators</build_depend>
# <exec_depend>rosidl_default_runtime</exec_depend>
# <member_of_group>rosidl_interface_packages</member_of_group>

# 6. 编译
colcon build --packages-select sensor_interfaces

避坑指南:我曾经因为忘记在package.xml里加<member_of_group>rosidl_interface_packages</member_of_group>这一行,结果编译出来的消息在其他包里死活找不到。折腾了半天才发现是这个问题。所以,这行一定要加。

使用的时候,在别的功能包的package.xml里加上依赖:

<depend>sensor_interfaces</depend>

然后在代码里就可以直接引用了:

from sensor_interfaces.msg import WeatherSensor

msg = WeatherSensor()
msg.temperature = 25.5
msg.humidity = 60.0
msg.pressure = 1013.25
msg.location = "实验室A区"

4.5 知识体系总览

说了这么多,我画张图帮大家理一理思路:

ROS2传感器数据接口标准知识体系 ROS2消息接口 消息类型基础 基础类型 | 复合类型 | 数组类型 自定义消息(.msg) 定义 → 注册 → 编译 → 使用 sensor_msgs Image | LaserScan | Imu | PointCloud2 接口包创建与使用 创建包 → 编写msg → 编译 → 依赖引用 核心原则:统一格式、强类型、模块化 所有传感器数据 → ROS2消息 → 标准化处理

这张图把本章的核心内容串起来了。从消息类型基础出发,到自定义消息、sensor_msgs标准格式,再到接口包的工程实践,每一步都是环环相扣的。

好了,这一章的内容就到这里。记住一句话:消息接口是ROS2传感器接入的“通用语言”。把这块搞明白了,后面写传感器驱动、做数据处理,都会顺手很多。