4. ROS2 核心概念:节点、话题、服务、动作,以及如何用命令行验证
好,咱们进入 ROS2 最核心的部分了。
说实话,我刚接触 ROS 那会儿,被这些概念绕得有点晕。节点、话题、服务、动作……名字都挺像,到底谁是谁?
后来我明白了,你不需要一次性记住所有细节。你只需要知道:ROS2 本质上是一个“软件零件”的通信框架。每个零件(节点)怎么说话、说什么、跟谁说,就是由这四个概念来定义的。
这一章,我带你一个一个拆开看。而且我会告诉你,怎么用命令行亲手验证它们——这才是真正“上手”的关键。
一句话总结:
- 节点 = 一个能干活的小程序(比如“手臂控制器”)
- 话题 = 广播喇叭(一个人说,多人听)
- 服务 = 打电话(一问一答,即时响应)
- 动作 = 派任务(有进度反馈,可中途取消)
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 秒呢?你总不能一直阻塞等着吧?
动作的机制是:
- 发送目标(比如“走到门口”)
- 持续接收反馈(比如“已走 30%”、“已走 60%”)
- 最终收到结果(“已到达”)
- 支持中途取消(“停!别走了”)
说白了,动作 = 服务 + 话题。它用服务来发送目标和接收结果,用话题来发布反馈。
我在项目中遇到过:人形机器人走路这个动作,如果用服务,你发一个“走 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这四个命令要烂熟于心 - 遇到通信问题,先用命令行确认“数据到底有没有发出来”