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 # 位置描述
定义好之后,怎么用呢?三步走:
- 在功能包的
msg/目录下创建WeatherSensor.msg文件 - 在
CMakeLists.txt里添加rosidl_generate_interfaces调用 - 在
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 知识体系总览
说了这么多,我画张图帮大家理一理思路:
这张图把本章的核心内容串起来了。从消息类型基础出发,到自定义消息、sensor_msgs标准格式,再到接口包的工程实践,每一步都是环环相扣的。
好了,这一章的内容就到这里。记住一句话:消息接口是ROS2传感器接入的“通用语言”。把这块搞明白了,后面写传感器驱动、做数据处理,都会顺手很多。