2. ROS2基础回顾

在进入任务通信与行为编排之前,有必要把 ROS2 的通信骨架重新梳理一遍。ROS2 不是“一个进程跑到底”的框架,而是由大量可独立部署、可动态组合的计算单元通过中间件互相协作。理解这些单元各自解决什么问题、在什么场景下使用,是后续设计任务编排逻辑的前提。

一、核心概念与通信模型

ROS2 中最基本的运行单元是节点(Node)。一个节点通常承担一项相对独立的职责,例如读取激光雷达、发布里程计、执行路径规划。节点通过话题(Topic)、服务(Service)、动作(Action)和参数(Parameter)四类接口与外界交互,它们分别对应不同的通信语义。

  • 话题:发布/订阅模型,单向、异步、多对多,适合高频连续数据流,如传感器数据、状态广播。
  • 服务:请求/响应模型,同步、一对一,适合短时、确定性的查询或命令,如“查询地图”“切换模式”。
  • 动作:基于话题与服务构建的长时任务模型,支持目标、反馈、结果和取消,适合导航、抓取等耗时任务。
  • 参数:节点级配置项,可在启动时设置,也可在运行时动态调整,如速度上限、控制增益。

这四类接口并非彼此替代,而是互补。选择错误的通信方式,往往会导致系统在实时性、可观测性或可取消性上出现问题。

ROS2 计算图:节点、通信接口与运行时支撑 节点 Node 独立职责 · 可组合 · 可分布式 话题 Topic 发布/订阅 · 异步 · 多对多 服务 Service 请求/响应 · 同步 · 一对一 动作 Action 目标/反馈/结果/取消 长时任务 · 可抢占 参数 Parameter 启动配置 · 运行时调整 速度上限 · 控制增益 执行器 Executor 单线程 / 多线程 / 回调组 生命周期节点 未配置→非活跃→活跃→终止 节点通过四类接口通信,执行器决定回调调度方式,生命周期节点提供受控状态迁移

二、执行器:回调如何被调度

节点中注册的订阅回调、服务回调、定时器回调并不会自动并发执行,它们由执行器(Executor)统一调度。ROS2 提供单线程执行器和多线程执行器两种基本形态。单线程执行器按顺序处理回调,逻辑简单但容易因某个耗时回调阻塞整个节点;多线程执行器允许并行处理,但需要配合回调组(Callback Group)来避免数据竞争。

常见的做法是把互斥的回调放入同一个 MutuallyExclusive 回调组,把可并行的回调放入 Reentrant 回调组。若不加区分地把所有回调都设为可重入,共享状态就可能被并发修改,导致难以复现的偶发故障。

// 创建互斥回调组,保证同一组内回调不会并发执行
auto cb_group = create_callback_group(
  rclcpp::CallbackGroupType::MutuallyExclusive);

// 订阅时绑定回调组
auto sub_opt = rclcpp::SubscriptionOptions();
sub_opt.callback_group = cb_group;
subscription_ = create_subscription<std_msgs::msg::String>(
  "topic", 10, callback, sub_opt);

三、生命周期节点:让启动过程可控

普通节点一旦启动就立即开始工作,这在复杂系统中会带来隐患:传感器尚未就绪、参数尚未校验、下游节点尚未上线,数据就已经开始流动。生命周期节点通过状态机解决这一问题,其典型状态包括:

  1. Unconfigured:节点已创建,但尚未配置。
  2. Inactive:已完成配置,但不处理数据。
  3. Active:正常处理数据、发布消息。
  4. Finalized:已终止,等待销毁。

状态之间通过 configure、activate、deactivate、cleanup、shutdown 等转换触发。这样,系统可以按依赖顺序逐个激活节点,避免“半成品”数据进入计算链路。

四、典型场景与选型对照

假设我们要实现一个“移动机器人巡逻”功能:激光雷达节点持续发布扫描数据,定位节点订阅扫描并发布位姿,巡逻控制节点接收目标点并控制底盘。这里,雷达到定位、定位到控制适合用话题;控制节点查询“当前是否允许移动”适合用服务;而“前往某个巡逻点并返回结果”则适合用动作。

通信方式 语义 典型场景 注意事项
话题 Topic 发布/订阅,异步 传感器数据、状态广播 注意队列深度与丢帧
服务 Service 请求/响应,同步 查询、模式切换 避免长阻塞,防止死锁
动作 Action 目标/反馈/结果 导航、抓取、巡检 需处理取消与超时
参数 Parameter 键值配置 速度上限、增益 动态修改需回调校验
选型口诀:连续流用话题,短查询用服务,长任务用动作,可调项用参数。若一个任务需要“过程反馈 + 可取消 + 最终结果”,优先考虑动作而不是服务。

五、常见误区与复盘

  • 用服务做长任务:服务调用会阻塞客户端,若任务耗时较长,容易造成超时或界面卡死,应改用动作。
  • 忽视执行器配置:默认单线程执行器下,一个耗时回调会拖慢整个节点,必要时使用多线程执行器并划分回调组。
  • 生命周期节点只配不用:若没有按依赖顺序激活,仍可能出现数据竞争,应结合 launch 的 event handler 控制激活顺序。
  • 参数修改不校验:运行时修改参数若不做范围检查,可能直接导致控制异常,应设置参数回调进行合法性验证。
回顾要点:节点是职责边界,话题/服务/动作/参数是四类通信契约,执行器决定回调调度,生命周期节点提供受控启动。把这五者放在一起看,才能理解 ROS2 系统为何既能灵活组合,又能保持行为可预测。