1、ROS2与自定义消息概述:为什么需要自定义消息?ROS2消息系统架构简介。
各位同学,咱们开始上课。
今天聊的话题,是ROS2开发里最基础、也最绕不开的一个环节——自定义消息。
说实话,我刚开始接触ROS2的时候,也纳闷过:ROS2不是自带了一大堆消息类型吗?什么Twist、Odometry、LaserScan……够用了吧?为什么还要自己定义?
嗯,这个问题问得好。我当年在做一个多传感器融合的项目时,就吃过这个亏。当时图省事,硬把IMU和GPS的数据塞进一个现成的消息结构里,结果代码写得跟打补丁似的,后期维护起来简直想哭。从那以后,我就养成了一个习惯:能用自定义消息,就别凑合。
1.1 为什么需要自定义消息?
说白了,ROS2自带的那些消息类型,是给通用场景准备的。但实际项目里,你的数据往往很“个性”。
举个例子:
- 你做了一个机械臂,需要传递每个关节的目标角度、当前角度、力矩、温度。ROS2里没有现成的“JointStateWithTemperature”吧?
- 你搞自动驾驶,要发一个包含障碍物ID、速度、类型、置信度的结构体。用标准消息拼凑?那代码可读性就太差了。
所以,自定义消息的核心价值就两条:
- 语义清晰——看到消息名,就知道里面装了什么。
- 数据结构紧凑——只传你需要的数据,不浪费带宽。
核心观点: 自定义消息不是炫技,而是工程化的必然选择。它让你的代码自文档化,也让团队协作更顺畅。
1.2 ROS2消息系统架构简介
聊完了“为什么”,咱们再聊聊“是什么”。
ROS2的消息系统,其实没那么玄乎。你想想看,它本质上就是一个跨进程的数据传输协议。
整个架构可以拆成三层:
| 层级 | 作用 | 我的一点理解 |
|---|---|---|
| 接口定义层 | 用.msg、.srv、.action文件描述数据结构 | 就像写合同,把数据格式定死 |
| 代码生成层 | 编译时自动生成对应语言的代码 | 省去手写序列化/反序列化的麻烦 |
| 通信传输层 | 基于DDS实现发布/订阅、服务/客户端 | 底层靠DDS,可靠又灵活 |
这里我要特别强调一下代码生成层。我记得刚开始用ROS2时,有个同事问我:“为什么我改了.msg文件,重新编译后还是旧结构?”
我当时就告诉他:ROS2的消息是编译期生成的。你改了.msg,必须重新build,否则代码里用的还是老版本。这个坑,我踩过不止一次。
1.3 消息类型的三兄弟
ROS2里,接口文件分三种:
- .msg —— 用于话题通信,发布/订阅模式。
- .srv —— 用于服务通信,请求/响应模式。
- .action —— 用于动作通信,带反馈的长时间任务。
咱们这门课,主要聚焦在.msg和.srv上。动作通信稍微复杂一些,但原理相通。
小技巧: 如果你不确定该用哪种,记住一个原则——单向数据流用话题,双向请求用服务,长时间带反馈用动作。我在做机器人导航时,路径规划用动作,传感器数据用话题,控制指令用服务,分工明确。
1.4 一个简单的自定义消息长什么样?
光说不练假把式。咱们直接看个例子。
假设我要定义一个“带时间戳的超声波传感器数据”:
# Ultrasonic.msg
std_msgs/Header header
float32 range # 距离,单位米
float32 confidence # 置信度,0.0~1.0
uint8 sensor_id # 传感器编号
你看,就这么几行。但它的好处是:任何节点只要include了这个消息,就知道里面有什么。不用猜,不用看文档。
我习惯把自定义消息放在一个单独的包里,比如叫robot_msgs。这样所有节点都能引用,不会出现循环依赖。
注意: 自定义消息的包名,建议全小写,用下划线分隔。比如my_robot_msgs,而不是MyRobotMsgs。ROS2的命名规范很严格,大小写搞错了,编译会报错。
1.5 为什么我推荐你从自定义消息开始学?
很多新手一上来就学launch文件、学参数服务器,结果写出来的节点之间数据传得乱七八糟。
我个人建议:先定义好消息结构,再写节点逻辑。就像盖房子,先画好图纸,再砌砖。
你想想看,如果消息结构清晰,节点之间的接口就稳定了。后期改逻辑,只要不改消息,其他节点基本不用动。这就是解耦的好处。
好了,这一章咱们把“为什么需要自定义消息”和“ROS2消息系统长什么样”讲清楚了。下一章,我会手把手带你创建一个自定义消息包,并编译运行起来。
记住:好的架构,从定义消息开始。