ROS实践系列03-消息与服务
ROS消息与服务基础
消息(Message)
- 作用 :ROS节点间通信的“数据载体”,把数据从一个节点传递到另一个节点(比如控制指令、传感器数据等)。
- 定义位置 :写在
.msg文件 中,例如geometry_msgs/Twist(常用于控制机器人的线速度、角速度)。 - 数据类型 :支持基础类型(
int32、float64、string等)、 数组 (如保存多个相同类型的数据)、 嵌套消息 (自定义消息中嵌套其他消息,形成“结构体”般的复杂结构)。
服务(Service)
- 通信模式 : 请求 - 响应 机制,类似“发请求→等待处理→拿结果”的流程(同步通信)。
- 定义位置 :写在
.srv文件 中,例如std_srvs/Trigger(常用于“触发操作并返回状态”,比如让机器人执行某个动作后,返回“成功/失败”)。 - 结构 :分为 请求 (可携带参数,告诉服务端“怎么执行”)和 响应 (返回操作结果、状态信息,比如成功/失败、错误原因等)。
话题与服务的对比
话题(基于消息的通信) :
- 特点: 异步通信 (“广播式”)。
- 场景:适合 数据流传输 (如摄像头持续发布图像、激光雷达发布点云、传感器持续采集数据),订阅节点按自身节奏处理数据,不强求“实时响应”。
服务 :
- 特点: 同步通信 (“点对点打电话要结果”)。
- 场景:适合 请求 - 响应场景 (如让机械臂运动到目标位置、查询传感器当前状态),必须等“结果返回”后再执行下一步。
核心区别总结
话题像“持续发广播”,适合“数据流式”的持续传输;服务像“打电话问事情”,适合“我要结果再继续”的请求 - 响应场景。两者的 通信模式 和 适用场景 完全不同,是ROS中实现节点间协作的两大核心机制~
消息文件.msg的编写
消息文件格式规范
基本结构与数据类型
字段定义格式 :
数据类型 变量名( 无逗号,空格分隔 )
✅ 正确 :int32 id
❌ 错误 :int32 id,(类似 C 语法,ROS 不支持)支持的数据类型 (ROS 内置):
类型 说明 示例 bool布尔值 bool is_activeint8/uint88 位整数 int8 ageint16/uint1616 位整数 uint16 countint32/uint3232 位整数 int32 idint64/uint6464 位整数 int64 timestampfloat32/float64浮点数 float64 valuestring字符串 string nametime时间戳(ROS 1/2) time timestampduration时长(ROS 1/2) duration timeout
依赖声明(关键修正)
- ROS 1 :
无需在.msg文件中声明依赖 !依赖通过package.xml和CMakeLists.txt管理。示例:若需使用
std_msgs/String,在包的package.xml中添加<depend>std_msgs</depend>。
注释规范
所有注释以
#开头 :1
2
3
4
5
6
7# 人信息消息
# 作者:ROS Team
# 日期:2023-10-01
string name # 姓名(小写+下划线)
uint8 age # 年龄(0-255)
string gender # 性别("male" / "female")
命名规范
| 项目 | 规范 | 示例 |
|---|---|---|
| 消息文件名 | 全小写 + 下划线分隔( *.msg ) |
person.msg |
| 字段名 | 全小写 + 下划线分隔 | name , age , gender |
| 禁止 | 首字母大写、驼峰式、数字开头 | ❌ PersonName , ❌ 1st_name |
为什么? ROS 严格要求命名规范,避免与 C++/Python 代码冲突,提高可读性。
关键注意事项
文件位置 :
- 消息文件必须放在 包的
msg/目录下 (如my_package/msg/Person.msg)。 - 未按此结构放置,ROS 无法编译。
- 消息文件必须放在 包的
字段唯一性 :
- 同一消息中 禁止重复字段名 (如
int32 id和int32 id重复)。
- 同一消息中 禁止重复字段名 (如
基本类型限制 :
- 不能使用自定义类型(如
MyCustomType),需通过依赖的.msg文件定义。
- 不能使用自定义类型(如
创建msg消息
创建消息包
我们将创建一个new_msg包,并且定义新的消息
1 | cd ~/Tika_pkg/src #进入src目录中 |
修改package.xml
接下来,查看package.xml, 确保它包含一下两条语句:
1 | <build_depend>message_generation</build_depend> |

修改CMakeLists.txt
在CMakeLists.txt文件中,利用find_packag函数,增加对message_generation的依赖,这样就可以生成消息了。 你可以直接在COMPONENTS的列表里增加message_generation,就像这样:
1 | find_package(catkin REQUIRED COMPONENTS |

另外在CMakeLists.txt文件中,继续找到以下代码块,去掉注释符号#用你的.msg文件替代Message*.msg,或者将下面的内容复制粘贴
1 | add_message_files( |



接下来,添加generate_messages()函数到如下位置
1 | ## Generate actions in the 'action' folder |

编译msg节点
我们回到 ~/Tika_ws下执行编译命令:
1 | catkin_make -j1 |
预期输出结果如下:
1 | tika@tika-virtual-machine:~/Tika$ catkin_make -j1 |
使用rosmsg检查
通过rosmsg show命令,检查ROS是否能够识消息。
1 | rosmsg show [message type] |
例如,我将会运行以下命令:
1 | rosmsg show new_msg/num |
你将会看到如下输出

消息类型由两部分组成:
- Package名 :消息所在的包(例如
new_msg) - 消息名 :具体消息名称(例如
num)
如果忘记包名,可直接省略包名,使用消息名进行查询:
1 | rosmsg show num |
执行后将显示:
1 | [new_msg/num]: |

服务文件(.srv)的编写
服务文件格式
服务文件分为请求和响应两部分:
- 请求部分 :定义输入参数
- 响应部分 :定义输出结果
请求和响应部分字段定义与消息文件类似,支持多种数据类型和数组。
示例与规范
示例:AddTwoInts 服务
1 | # 请求部分 |
说明 :定义一个AddTwoInts服务,请求包含两个整数,响应返回它们的和。
规范要求
- 服务文件名以小写字母开头
- 请求和响应字段名应清晰描述功能,便于理解和使用
- 例如:
add_two_ints.srv(文件名),a、b(请求字段),sum(响应字段)
注意事项
- 字段名唯一性 :请求和响应部分字段名不能重复,避免冲突,确保服务正常工作。
- 依赖声明 :服务文件依赖其他消息类型时,需在
package.xml中正确声明依赖,确保编译通过。 - 类型支持 :支持ROS标准数据类型(如
int32、float64、string等)及自定义消息类型。 - 结构清晰 :服务文件应保持简洁,仅包含必要的请求和响应字段。
创建服务文件
在上面msg的package中创建一个服务:
1 | roscd new_msg |
这次我们不再手动创建服务,而是从其他的package中复制一个服务。 roscp是一个很实用的命令行工具,它实现了将文件从一个package复制到另外一个package的功能。
使用方法:
1 | roscp [package_name] [file_to_copy_path] [copy_path] |
现在我们可以从rospy_tutorials package中复制一个服务文件了:
1 | roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv |
修改CMakeLists.txt
在CMakeLists.txt文件中增加了对message_generation的依赖。:
1 | \# Do not just add this line to your CMakeLists.txt, modify the existing line |
message_generation 对msg和srv都起作用
在上一节创建消息的步骤中已经执行过,所以会有内容。
同样,跟msg文件类似,你也需要在CMakeLists.txt文件中做一些修改。查看上边的说明,增加额外的依赖项。删掉#,去除对下边语句的注释:
1 | # add_service_files( |
用你自己的srv文件名替换掉那些Service*.srv文件:
1 | ## Generate services in the 'srv' folder |
如图所示:


编译srv节点
参考 2.2.4-编译msg节点 的编译
我们回到 ~/Tika_ws下执行编译命令:
1 | catkin_make -j1 |
使用rossrv检查
通过rosmsg show命令,检查ROS是否能够识该服务。
使用方法:
1 | rossrv show <service type> |
例子:
1 | rossrv show new_msg/AddTwoInts |
你将会看到:
1 | int64 a |
跟rosmsg类似, 你也可以不指定具体的package名来查找服务文件:
1 | tika@tika-virtual-machine:~/Tika$ rossrv show AddTwoInts |
实际操作:
消息的发布与订阅
编写发布节点
在我们前几节的功能包(new_msg)中,运行如下命令,创建 scripts 来存放我们的python代码
1 | mkdir scripts |
然后运行以下命令
1 | wget https://raw.githubusercontent.com/ros/ros_tutorials/noetic-devel/rospy_tutorials/001_talker_listener/talker.py --no-check-certificate |
这个命令让我们会下载一份talker.py的文件,这个文件是ros的一个最简单的发布者的示例
然后利用如下命令给这份文件升权
1 | chmod +x talker.py |

然后我们利用VS code打开这份文件
1 | code talker.py |

图上这些部分是talker.py的核心代码,我写了一份带注释的方便理解
1 | ################################################################################ |
为talker.py修改CMakeLists.txt
将以下内容添加到CMakeLists.txt中。这样可以确保正确安装了python脚本,并使用了正确的python解释器。
1 | catkin_install_python(PROGRAMS scripts/talker.py |

订阅器节点编写
运行以下命令下载代码,同时对文件进行升权:
1 | roscd new_msg/scripts |
同样的,我也对核心代码部分写了注释
1 |
|
listener.py的代码与talker.py相似,不同之处在于,我们引入了一种基于回调的新机制来订阅消息。
1 | rospy.init_node('listener', anonymous=True) |
这个声明,您的节点订阅的话题是类型的std_msgs.msgs.String。收到新消息时,将以消息作为第一个参数来调用回调。
我们还稍微改变了对rospy.init_node()的调用。我们添加了anonymous = True关键字参数。ROS要求每个节点都有唯一的名称。如果出现一个具有相同名称的节点,它将碰撞前一个节点。这样一来,故障节点就可以轻松地从网络上踢出去。该匿名=真标志告诉rospy为节点生成一个唯一的名称,以便您可以有多个listener.py节点轻松运行。
最后添加的rospy.spin()只是使您的节点无法退出,直到该节点已关闭。与roscpp不同,rospy.spin()不会影响订户回调函数,因为它们具有自己的线程。
为listener.py修改CMakeLists.txt
找到我们为talker.py修改CMakeLists.txt的字段

在第一行末尾加上 scripts/listener.py ,整个字段的就是
1 | catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py |

需要注意的是 scripts/talker.py 和 scripts/listener.py 中间有一个空格
编译节点
我们使用CMake作为构建系统,即使对于Python节点,也必须使用它。这是为了确保创建用于消息和服务的自动生成的Python代码。因为CMake不直接编译项目,而是通过CMakeLists.txt配置文件生成适用于不同平台的构建文件(如Makefile、Visual Studio工程文件等),这些文件再由实际的编译工具(如make、MSBuild)执行编译过程。
转到根目录并运行catkin_make:
1 | cd ~/Tika_ws |
测试消息发布器和订阅器
我们新建终端,运行如下命令
1 | cd ~/Tika_ws/ |
然后我们再次新建一个终端,在工程根目录下运行如下命令,以启动”talker”的发布器节点
1 | rosrun new_msg talker.py |
会出现如下情况

我们编写了一个名为”listener”的订阅器节点。再打开另外一个终端运行:
1 | rosrun new_msg listener.py |
运行结果如下,便是成功

至此,消息的发布和订阅以全部完成。
ROS服务通信:编写Service和Client
创建Server节点
我们将创建一个简单的service节点(“server”),该节点将接收到两个整形数字,并返回它们的和。进入前几章的教程中所创建的new_msg包所在的目录,打开一个终端运行:
1 | cd ~/Tika_ws/src/new_msg |
接下来创建server.py,运行如下命令
1 | cd scripts |
此时我们会打开VS code界面,把如下内容复制进去并保存
1 | #!/usr/bin/env python |
方便起见,我依旧是写了一份带注释的版本
1 | #!/usr/bin/env python |
然后给这份文件升权
1 | chmod +x server.py |
创建Client节点
运行如下命令
1 | roscd new_msg/scripts |
将如下内容粘贴到VS code
1 | #!/usr/bin/env python |
同样的,我也写了一份带注释的
1 | #!/usr/bin/env python |
然后给文件升权
1 | chmod +x client.py |
为Service和Client修改CMakeLists.txt
确保如下部分是非注释态

删除重复的
generate_messages()调用- 删除了第 68 行的空调用
generate_messages()
- 删除了第 68 行的空调用
添加带依赖声明的
generate_messages()调用- 添加了明确指定依赖项的调用:
1
2
3
4generate_messages(
DEPENDENCIES
std_msgs
)
修改原因
- CMake 规则要求 :每个 ROS 包中只能调用一次
generate_messages()函数 - 依赖声明必要性 :为了正确生成服务代码,需要显式声明对标准消息包
std_msgs的依赖 - 代码生成 :只有正确配置后,
catkin_make才能生成服务对应的 Python 模块,解决导入错误
编译节点
回到我们的工作空间根目录并执行编译
1 | catkin_make -j1 |
然后更新一下配置文件
1 | source devel/setup.bash |
测试 Service和Client
首先打开一个终端运行:
1 | roscore |
然后再次新建一个终端运行
1 | cd ~/Tika && source devel/setup.bash && rosrun new_msg server.py |

再次新建一个终端,并且运行Client并附带一些参数:
1 | rosrun new_msg client.py 6 9 |
出现下图的结果便是整个服务通信已完成

- 标题: ROS实践系列03-消息与服务
- 作者: 亓翎_Re-Tikara
- 创建于 : 2026-03-15 18:27:11
- 更新于 : 2026-03-15 20:24:25
- 链接: https://re-tikara.fun/Blog/posts/d418d54e/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
