3. ROS2 基础概念:Node、Topic、Service、Action、Parameter

好,咱们正式开始接触 ROS2 的核心概念了。

说实话,我当年刚学 ROS 的时候,被这些名词搞得一头雾水。什么节点、话题、服务……听着像计算机网络课,又像操作系统课。后来做项目做多了才明白,ROS2 本质上就是一套 进程间通信(IPC) 的框架。你想想看,机器人上有那么多传感器、电机、算法模块,它们之间怎么说话?这就是 ROS2 要解决的事。

一句话总结:ROS2 是一个分布式通信框架。节点是“人”,话题是“广播”,服务是“一问一答”,动作是“带反馈的长任务”,参数是“配置文件”。

下面我一个个拆开讲。每个概念我都会结合项目里的实际场景来说,这样你理解起来会更接地气。

ROS2 核心通信机制 Node(节点) Topic(话题) 发布/订阅,异步通信 Service(服务) 请求/响应,同步通信 Action(动作) 带反馈的长时间任务 Parameter(参数) 运行时配置与调参 一个节点可以同时拥有多个 Publisher、Subscriber、Service Server/Client、Action Server/Client 和 Parameter 所有通信都基于 DDS(数据分发服务)实现,天然支持分布式部署

3.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()

你看,核心就三步:初始化、创建节点、spin 保持运行。spin 这个函数很有意思,它让节点一直活着,随时处理进来的数据。

3.2 话题(Topic)—— 机器人的“广播”

话题是 ROS2 里最常用的通信方式。它是 异步的、一对多的。

说白了,就是有人(发布者)在广播,其他人(订阅者)想听就听。发布者不知道谁在听,订阅者也不知道谁在发。这种解耦设计,在机器人系统里非常实用。

我举个例子。激光雷达节点每隔 0.1 秒发布一次扫描数据,话题名叫 /scan。定位节点订阅 /scan,建图节点也订阅 /scan。它们各取所需,互不干扰。

关键点:话题通信是“发出去就不管了”。发布者只管发,不关心有没有人收到。订阅者只管收,不关心是谁发的。这种模式适合周期性数据,比如传感器数据、状态信息。

代码示例,发布者:

class TalkerNode(Node):
    def __init__(self):
        super().__init__('talker')
        self.publisher = self.create_publisher(String, 'chatter', 10)
        timer_period = 0.5
        self.timer = self.create_timer(timer_period, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = 'Hello, ROS2!'
        self.publisher.publish(msg)

订阅者:

class ListenerNode(Node):
    def __init__(self):
        super().__init__('listener')
        self.subscription = self.create_subscription(
            String, 'chatter', self.listener_callback, 10)

    def listener_callback(self, msg):
        self.get_logger().info(f'收到: {msg.data}')

避坑指南:我曾经在项目里把队列大小(上面的 10)设成了 1,结果数据一多就丢包。后来才明白,队列大小要按你的处理速度来设。处理慢就设大点,但别太大,否则内存会爆。一般传感器数据设 10~50 就够用。

3.3 服务(Service)—— 一问一答

话题是广播,服务就是 打电话。你打过去,对方接起来,你问一句,他答一句,然后挂断。

服务是 同步的、一对一的。客户端发请求,服务端处理完返回响应。这期间客户端会等着。

什么时候用服务?比如你让机器人去抓一个杯子。你发一个请求:“去抓那个杯子”,机器人抓完了回复你:“抓到了”或者“没抓到”。这种需要确认结果的操作,用服务就很合适。

定义服务接口(.srv 文件):

int64 a
int64 b
---
int64 sum

服务端代码:

class AddTwoIntsServer(Node):
    def __init__(self):
        super().__init__('add_two_ints_server')
        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

我的经验:服务调用不要用在高频场景。比如控制电机,每秒要发几百次指令,用服务就不行——客户端等响应的时间太长了。这种场景应该用话题。服务适合那种“偶尔调用一次,但需要确认结果”的操作。

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

动作是服务的 升级版。

你想想看,如果让机器人从 A 点导航到 B 点,这个过程可能要几十秒。用服务的话,你发一个请求,然后干等几十秒?太傻了。用话题的话,你又不知道任务到底完成了没有。

动作就是来解决这个问题的。它有三个部分:

  • Goal(目标):告诉机器人你要干什么
  • Feedback(反馈):机器人一边干活一边告诉你进度,比如“走了 30% 了”
  • Result(结果):任务完成后的最终结果

动作接口定义(.action 文件):

# 目标
geometry_msgs/PoseStamped target_pose
---
# 结果
bool success
string message
---
# 反馈
float32 progress

什么时候用动作?导航、机械臂运动、扫地机器人回充……只要是长时间执行、需要中途反馈、还能随时取消的任务,都用动作。

我记得有一次做巡检机器人,导航到每个点位大概要 2 分钟。一开始我用服务,结果 UI 界面卡死了。换成动作后,界面可以实时显示进度条,用户体验好太多了。

3.5 参数(Parameter)—— 机器人的“配置文件”

参数就是节点的配置项。比如激光雷达的扫描频率、PID 控制器的系数、导航的最大速度……这些值如果写死在代码里,每次改都要重新编译,太麻烦了。

ROS2 的参数系统允许你在 运行时动态修改 这些值。

声明参数:

self.declare_parameter('max_speed', 0.5)
self.declare_parameter('pid_kp', 1.2)

读取参数:

max_speed = self.get_parameter('max_speed').value

在启动时加载参数文件(YAML 格式):

# config/params.yaml
my_robot_node:
  ros__parameters:
    max_speed: 0.8
    pid_kp: 1.5
    pid_ki: 0.01

注意:参数虽然可以动态修改,但不是所有参数都适合运行时改。比如激光雷达的波特率,改了之后需要重新初始化硬件,这种参数最好在启动时设好就别动了。我一般把参数分为“热参数”(运行时可以改)和“冷参数”(启动时固定),在代码里用注释标清楚。

3.6 五种机制的对比

最后,我用一张表帮你理清这五种机制的区别。这张表我每次培训新人都会拿出来讲,很实用。

机制 通信模式 同步/异步 典型场景 是否带反馈
Topic 发布/订阅 异步 传感器数据、状态广播 否
Service 请求/响应 同步 开关控制、查询状态 一次性结果
Action 目标/反馈/结果 异步 导航、机械臂运动 持续反馈 + 最终结果
Parameter 键值对 同步/异步 配置项、调参 否

嗯,到这里,ROS2 的五大基础概念就讲完了。你可能会觉得有点抽象,没关系,后面每一章都会用到这些概念。等你亲手写几个节点、跑几个话题之后,这些概念就会变成你的肌肉记忆。