1、ROS2与自定义消息概述:为什么需要自定义消息?ROS2消息系统架构简介。

各位同学,咱们开始上课。

今天聊的话题,是ROS2开发里最基础、也最绕不开的一个环节——自定义消息

说实话,我刚开始接触ROS2的时候,也纳闷过:ROS2不是自带了一大堆消息类型吗?什么Twist、Odometry、LaserScan……够用了吧?为什么还要自己定义?

嗯,这个问题问得好。我当年在做一个多传感器融合的项目时,就吃过这个亏。当时图省事,硬把IMU和GPS的数据塞进一个现成的消息结构里,结果代码写得跟打补丁似的,后期维护起来简直想哭。从那以后,我就养成了一个习惯:能用自定义消息,就别凑合

1.1 为什么需要自定义消息?

说白了,ROS2自带的那些消息类型,是给通用场景准备的。但实际项目里,你的数据往往很“个性”。

举个例子:

  • 你做了一个机械臂,需要传递每个关节的目标角度、当前角度、力矩、温度。ROS2里没有现成的“JointStateWithTemperature”吧?
  • 你搞自动驾驶,要发一个包含障碍物ID、速度、类型、置信度的结构体。用标准消息拼凑?那代码可读性就太差了。

所以,自定义消息的核心价值就两条:

  1. 语义清晰——看到消息名,就知道里面装了什么。
  2. 数据结构紧凑——只传你需要的数据,不浪费带宽。

核心观点: 自定义消息不是炫技,而是工程化的必然选择。它让你的代码自文档化,也让团队协作更顺畅。

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消息系统长什么样”讲清楚了。下一章,我会手把手带你创建一个自定义消息包,并编译运行起来。

记住:好的架构,从定义消息开始