4. ROS2 核心概念:节点、话题、服务、动作,以及如何用命令行验证

好,咱们进入 ROS2 最核心的部分了。

说实话,我刚接触 ROS 那会儿,被这些概念绕得有点晕。节点、话题、服务、动作……名字都挺像,到底谁是谁?

后来我明白了,你不需要一次性记住所有细节。你只需要知道:ROS2 本质上是一个“软件零件”的通信框架。每个零件(节点)怎么说话、说什么、跟谁说,就是由这四个概念来定义的。

这一章,我带你一个一个拆开看。而且我会告诉你,怎么用命令行亲手验证它们——这才是真正“上手”的关键。

一句话总结:

  • 节点 = 一个能干活的小程序(比如“手臂控制器”)
  • 话题 = 广播喇叭(一个人说,多人听)
  • 服务 = 打电话(一问一答,即时响应)
  • 动作 = 派任务(有进度反馈,可中途取消)
ROS2 核心概念关系图 ROS2 通信框架 节点 (Node) 话题 (Topic) 服务 (Service) 动作 (Action) 节点是基础,话题/服务/动作是通信方式 一个节点可以同时发布话题、提供服务和执行动作

4.1 节点(Node)—— 系统的“细胞”

节点是什么?说白了,就是 ROS2 系统里一个独立的可执行程序。

每个节点只干一件事。比如,一个节点专门读取激光雷达数据,另一个节点专门控制电机。我早期做项目时,喜欢把所有功能塞进一个程序里,结果改一处崩全局。后来学乖了——一个节点只做一件事,做好它。

我的习惯:每个传感器、每个执行器、每个算法模块,都单独做成一个节点。这样调试时,我可以单独重启某个节点,而不影响整个系统。

命令行验证:

# 查看当前所有运行中的节点
ros2 node list

# 查看某个节点的详细信息
ros2 node info /your_node_name

你试试看。启动一个 ROS2 程序后,运行 ros2 node list,你会看到一串名字。每个名字就是一个活着的节点。

4.2 话题(Topic)—— 异步广播通信

话题是 ROS2 里最常用的通信方式。它的模式是:发布者(Publisher)往一个“频道”里发数据,订阅者(Subscriber)从同一个频道里收数据。

就像收音机。电台只管播,你只管听。电台不知道谁在听,你也不知道谁在播。这就是“异步”和“解耦”。

我在项目中遇到过:有一次,IMU 数据突然延迟了。我以为是代码问题,查了半天。后来用 ros2 topic hz 一看,发现发布频率从 100Hz 掉到了 20Hz。原来是 USB 线接触不良。命令行一查就定位了问题。

常用命令:

# 列出所有话题
ros2 topic list

# 查看某个话题的数据内容(实时打印)
ros2 topic echo /topic_name

# 查看话题的发布频率
ros2 topic hz /topic_name

# 查看话题的详细信息(类型、发布者、订阅者)
ros2 topic info /topic_name

# 手动发布数据到话题(调试神器)
ros2 topic pub /topic_name std_msgs/msg/String "data: 'hello'"

我曾经踩过的坑:用 ros2 topic echo 时,如果话题数据量很大(比如点云),终端会刷屏卡死。建议先 ros2 topic info 看看消息类型,再用 --once 参数只打印一条。

4.3 服务(Service)—— 同步请求-响应

话题是“说了不管有没有人听”,服务是“你问我答,必须等回复”。

什么时候用服务?比如你让机械臂“移动到位置 A”,你需要知道它到底到没到。这时候用服务:你发请求,它回响应。

关键区别:

特性 话题(Topic) 服务(Service)
通信模式 发布/订阅(异步) 请求/响应(同步)
是否阻塞 不阻塞 阻塞等待结果
适用场景 传感器数据、状态广播 触发式操作、查询
多对多 支持(1对N,N对1) 通常1对1

命令行验证:

# 列出所有服务
ros2 service list

# 查看服务类型
ros2 service type /service_name

# 调用服务(手动触发)
ros2 service call /service_name your_interfaces/srv/YourService "{request_field: value}"

我建议:调试阶段,多用 ros2 service call 手动触发服务。这样可以不写代码就验证服务端逻辑是否正确。我曾经用这个方法,在 10 分钟内定位了一个服务端死锁问题。

4.4 动作(Action)—— 带反馈的长任务

动作是 ROS2 里最“高级”的通信方式。它解决了一个痛点:服务只能一问一答,但如果任务要跑 10 秒呢?你总不能一直阻塞等着吧?

动作的机制是:

  1. 发送目标(比如“走到门口”)
  2. 持续接收反馈(比如“已走 30%”、“已走 60%”)
  3. 最终收到结果(“已到达”)
  4. 支持中途取消(“停!别走了”)

说白了,动作 = 服务 + 话题。它用服务来发送目标和接收结果,用话题来发布反馈。

我在项目中遇到过:人形机器人走路这个动作,如果用服务,你发一个“走 5 步”的请求,然后整个程序卡住 3 秒等它走完。这期间你没法处理其他事。换成动作后,你可以一边接收“第 1 步完成”“第 2 步完成”的反馈,一边随时准备发“紧急停止”。

命令行验证:

# 列出所有动作
ros2 action list

# 查看动作的详细信息
ros2 action info /action_name

# 发送动作目标(并查看反馈)
ros2 action send_goal /action_name your_interfaces/action/YourAction "{goal_field: value}" --feedback

注意:动作的接口定义比话题和服务复杂。你需要先有 .action 文件定义好目标、反馈、结果三个部分。别急,后面章节我会专门讲怎么自定义接口。

4.5 四者对比:什么时候用哪个?

很多新手会问:我到底该用话题、服务还是动作?

我的经验是:

  • 持续不断的数据流(传感器、状态)→ 用 话题
  • 一次性查询或触发(开关、设置参数)→ 用 服务
  • 长时间执行的任务(导航、走路、抓取)→ 用 动作

你想想看,如果你用话题去发一个“抓取”指令,你没法知道它抓没抓到。如果你用服务去发一个“走 10 米”的指令,中间想取消都难。选对通信方式,代码能少一半 bug。

4.6 综合实战:用命令行跑通一个完整流程

光说不练假把式。我带你走一遍完整的命令行验证流程。

步骤 1:启动一个示例节点

# 启动 C++ 版的话题发布节点
ros2 run demo_nodes_cpp talker

步骤 2:新开终端,查看节点和话题

ros2 node list
# 应该看到 /talker

ros2 topic list
# 应该看到 /chatter

ros2 topic info /chatter
# 类型: std_msgs/msg/String

步骤 3:监听话题数据

ros2 topic echo /chatter
# 你会看到每秒打印一次 "Hello World: N"

步骤 4:手动发布数据

ros2 topic pub /chatter std_msgs/msg/String "data: '你好,机器人'"
# 你会看到 talker 节点也在收这条消息(如果它订阅了的话)

步骤 5:调用服务示例

# 启动服务端
ros2 run demo_nodes_cpp add_two_ints_server

# 新终端调用服务
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5, b: 3}"
# 返回: sum: 8

步骤 6:发送动作目标

# 启动动作服务端(需要安装 action_tutorials)
ros2 run action_tutorials_cpp fibonacci_action_server

# 发送目标
ros2 action send_goal /fibonacci action_tutorials_interfaces/action/Fibonacci "{order: 5}" --feedback
# 你会看到反馈: [0, 1, 1, 2, 3] 逐步出现

核心要点:

  • 节点是基础,话题/服务/动作是通信方式
  • 话题用于持续数据流,服务用于即时查询,动作用于长任务
  • 命令行是调试利器——echo、hz、call、send_goal 这四个命令要烂熟于心
  • 遇到通信问题,先用命令行确认“数据到底有没有发出来”