一、ROS2 通信模型全景:节点、话题、服务、动作
各位好,我是老周。做机器人系统集成这些年,我见过太多人在 ROS2 通信模型上栽跟头。说白了,节点、话题、服务、动作这四个东西,就是 ROS2 的「四根柱子」。你把这四根柱子搞明白了,后面写节点、调通信、做分布式,基本就顺了。
我个人习惯,每次带新人,第一件事就是让他们画一张通信全景图。为什么?因为 ROS2 本质上就是一个「分布式节点工厂」——每个节点干自己的活,通过不同的通信方式互相配合。你想想看,如果连节点之间怎么说话都不清楚,那代码写得再漂亮也跑不起来。
1.1 节点(Node)—— 一切的基础
节点是什么?说白了,就是一个独立的可执行单元。每个节点只负责一件事:比如激光雷达节点只管发激光数据,里程计节点只管算位置,导航节点只管规划路径。
我记得有一次,一个团队把传感器驱动、数据处理、控制逻辑全写在一个节点里。结果呢?改一个功能就要重新编译整个项目,调试的时候日志混在一起根本分不清谁是谁。这就是典型的「节点设计没想清楚」。
节点设计黄金法则: 一个节点只做一件事,并且把它做好。
ROS2 中创建节点非常简单,Python 示例:
import rclpy
from rclpy.node import Node
class MyRobotNode(Node):
def __init__(self):
super().__init__('my_robot_node')
self.get_logger().info('节点已启动!')
def main(args=None):
rclpy.init(args=args)
node = MyRobotNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
1.2 话题(Topic)—— 异步广播,一对多
话题是 ROS2 里最常用的通信方式。它的本质是「发布-订阅」模式:发布者把数据扔到话题上,订阅者自己去取。发布者和订阅者互不知道对方的存在,完全解耦。
我在项目中遇到过一个问题:两个节点用话题通信,但数据总是丢包。查了半天,发现是发布频率太高,而订阅者的回调函数处理太慢。嗯,这里要注意——话题是异步的,发布者不会等订阅者处理完再发下一条。
| 特性 | 说明 |
|---|---|
| 通信模式 | 发布-订阅(异步) |
| 数据流向 | 单向,发布者 → 订阅者 |
| 典型场景 | 传感器数据、状态信息、日志 |
| QoS 策略 | 可配置可靠性、历史记录、深度 |
避坑指南: 我曾经在调试激光雷达数据时,发现订阅者收到的数据总是延迟 2 秒。后来才发现是 QoS 的深度设置太小,导致老数据被覆盖了。建议传感器类话题用 SensorDataQoS(),控制类话题用 ReliableQoS()。
1.3 服务(Service)—— 同步请求-应答
服务和话题最大的区别是什么?服务是同步的,有来有回。你发一个请求,服务器处理完给你一个响应,期间客户端会阻塞等待。
说白了,话题适合「一直发」的数据流,服务适合「问一次、答一次」的场景。比如:让机械臂移动到某个位置、查询当前电池电量、触发一次地图保存。
服务定义使用 .srv 文件:
# SetGoal.srv
---
bool success
string message
服务端实现:
from example_interfaces.srv import AddTwoInts
class AddService(Node):
def __init__(self):
super().__init__('add_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)
def add_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info(f'收到请求: {request.a} + {request.b} = {response.sum}')
return response
注意: 服务是同步阻塞的。如果你的服务处理逻辑耗时较长(比如超过 1 秒),建议改用动作(Action),否则会阻塞整个节点的事件循环。
1.4 动作(Action)—— 带反馈的异步任务
动作是 ROS2 里最「高级」的通信方式。它结合了话题和服务的特点:像服务一样有目标(Goal)和结果(Result),又像话题一样能持续反馈进度(Feedback)。
我做过一个机器人送餐项目,导航到目标点这个任务,用服务就不合适——因为导航可能要 30 秒,客户端不能一直等着。用动作就完美了:客户端发送目标点,服务端持续反馈「走到哪了」,最后告诉客户端「到了」。
| 通信方式 | 同步/异步 | 反馈机制 | 适用场景 |
|---|---|---|---|
| 话题 | 异步 | 无 | 传感器数据流 |
| 服务 | 同步 | 无 | 一次性查询/触发 |
| 动作 | 异步 | 有(持续反馈) | 长时间任务 |
动作定义使用 .action 文件:
# NavigateToPose.action
# 目标
geometry_msgs/PoseStamped target_pose
---
# 结果
bool success
float32 duration
---
# 反馈
float32 progress_percent
string status_message
1.5 全景图:四种通信方式的关系
下面这张图,是我每次培训必画的。它把四种通信方式放在一起对比,你看完就能明白什么时候该用哪个。
1.6 如何选择?一张决策表搞定
你可能会问:这么多通信方式,我到底该用哪个?别急,我总结了一张决策表,你照着选就行:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 激光雷达/摄像头数据持续发布 | 话题 | 异步、高频率、一对多 |
| 查询机器人当前状态 | 服务 | 一次请求一次响应,简单直接 |
| 导航到目标点(耗时 10 秒以上) | 动作 | 需要持续反馈进度,不能阻塞 |
| 触发一次地图保存 | 服务 | 执行时间短,不需要反馈 |
| 电机速度指令持续下发 | 话题 | 高频、实时、不需要确认 |
| 机械臂执行复杂轨迹 | 动作 | 长时间执行,需要中途取消和反馈 |
我的经验: 刚开始学 ROS2 时,我总喜欢用服务,觉得「有来有回」才踏实。后来发现,服务用多了,节点之间耦合太紧,一个服务卡住整个系统都动不了。现在我的原则是:能用话题就用话题,需要反馈才用动作,只有简单查询才用服务。
好了,这一章的内容就到这里。四种通信方式——节点、话题、服务、动作——它们的核心区别和应用场景,你应该已经清楚了。记住那张全景图和决策表,后面写代码的时候随时回来翻一翻。