一、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 全景图:四种通信方式的关系

下面这张图,是我每次培训必画的。它把四种通信方式放在一起对比,你看完就能明白什么时候该用哪个。

ROS2 四种通信方式全景图 节点 A 话题 (Topic) 异步广播,一对多 服务 (Service) 同步请求-应答 动作 (Action) 异步任务 + 反馈 节点 B 发布 订阅 请求 响应 目标 反馈/结果 核心原则:话题用于数据流,服务用于同步调用,动作用于长时间任务。节点之间通过名称解耦。

1.6 如何选择?一张决策表搞定

你可能会问:这么多通信方式,我到底该用哪个?别急,我总结了一张决策表,你照着选就行:

场景 推荐方式 原因
激光雷达/摄像头数据持续发布 话题 异步、高频率、一对多
查询机器人当前状态 服务 一次请求一次响应,简单直接
导航到目标点(耗时 10 秒以上) 动作 需要持续反馈进度,不能阻塞
触发一次地图保存 服务 执行时间短,不需要反馈
电机速度指令持续下发 话题 高频、实时、不需要确认
机械臂执行复杂轨迹 动作 长时间执行,需要中途取消和反馈

我的经验: 刚开始学 ROS2 时,我总喜欢用服务,觉得「有来有回」才踏实。后来发现,服务用多了,节点之间耦合太紧,一个服务卡住整个系统都动不了。现在我的原则是:能用话题就用话题,需要反馈才用动作,只有简单查询才用服务。

好了,这一章的内容就到这里。四种通信方式——节点、话题、服务、动作——它们的核心区别和应用场景,你应该已经清楚了。记住那张全景图和决策表,后面写代码的时候随时回来翻一翻。