如果把一台机器人拆开来看,真正复杂的往往不是机械结构,而是藏在机器人内部的软件系统。

一台看起来能够完成“看见目标—理解环境—规划路径—运动控制”的机器人,背后可能同时运行着摄像头驱动、激光雷达驱动、IMU数据处理、定位算法、地图管理、路径规划、运动控制、状态监测、故障处理等几十甚至上百个软件模块。这些模块既需要独立运行,又需要不断交换数据。

例如,一个机器人要完成自主移动,摄像头需要不断提供图像,激光雷达需要不断提供点云,IMU需要持续输出姿态信息,定位模块需要融合这些传感器数据,规划模块根据地图和当前位置计算路径,控制模块再根据规划结果向电机发送控制指令。

如果所有代码都写在一个巨大的程序里,理论上当然可以实现,但随着机器人越来越复杂,这种方式很快就会遇到问题:模块之间高度耦合、代码难以维护、故障难以定位、功能难以复用,一个模块出现问题甚至可能影响整个系统。

ROS 2正是在这样的背景下成为机器人软件开发的重要基础框架。

不过,很多刚开始接触ROS 2的人容易产生一个误解:

ROS 2是不是机器人操作系统?

从严格意义上说,答案是否定的。

ROS 2更准确地说,是一个面向机器人应用开发的软件框架和通信基础设施。它提供了节点、Topic、Service、Action、参数、生命周期、执行器等一整套机制,并通过DDS等底层通信机制把分布式机器人软件组织起来。

而真正负责CPU调度、内存管理、中断处理、设备驱动以及底层资源隔离的,仍然是操作系统。

理解这一点非常重要。

因为当机器人从实验室中的功能Demo逐渐进入工业现场、人形机器人、机械臂、移动机器人等实际应用环境之后,开发者最终会发现:机器人软件框架解决了“软件模块如何组织和通信”的问题,但机器人能不能稳定、确定、可靠地运行,还会进一步受到底层操作系统的影响。

因此,理解ROS 2,不应该只停留在“怎么创建一个Node、怎么发布一个Topic”。如果希望真正理解ROS 2在机器人系统中的价值,就需要沿着这样一条技术链路去认识它:

Node → Topic/Service/Action → DDS → QoS → Executor → Linux → 实时操作系统。

本文就从这条链路出发,系统梳理ROS 2的基本架构,以及它与Linux、实时操作系统之间到底是什么关系。


一、ROS 2到底解决了什么问题?为什么机器人需要这样的软件框架?

在理解ROS 2之前,可以先想象一下传统机器人软件开发方式。

假设现在需要开发一台移动机器人。

机器人至少包含下面这些模块:

摄像头
激光雷达
IMU
轮速计
GPS
定位
地图
路径规划
运动控制
电机驱动
状态监控

最简单的实现方式,是把这些功能全部放在一个程序里面:

main()
{
    初始化摄像头();
    初始化激光雷达();
    初始化IMU();

    读取传感器();

    定位();

    地图更新();

    路径规划();

    运动控制();

    电机控制();
}

机器人功能比较简单的时候,这种方式并不是完全不可行。

但随着系统规模扩大,问题会越来越明显。

首先是模块耦合。

例如定位算法发生变化,需要修改整个主程序;如果更换激光雷达,又需要修改数据读取部分;如果运动控制算法发生变化,又可能影响整个程序。

其次是模块复用困难。

今天开发的是轮式机器人,明天可能开发机械臂,后天又开发人形机器人。不同机器人虽然硬件不同,但很多软件模块其实具有高度共通性,例如传感器处理、坐标变换、导航、规划等。

如果所有东西都写在一个大型程序里,很难实现真正意义上的模块复用。

第三是分布式计算越来越常见。

现代机器人不一定只有一个CPU。

视觉算法可能运行在GPU平台,运动控制运行在实时CPU,导航运行在另外一个计算单元,AI模型甚至运行在独立的AI加速器上。

这时候机器人软件已经天然变成一个分布式系统。

因此,一个现代机器人软件框架需要解决的问题,不只是“怎么写代码”,而是:

不同软件模块如何独立运行?

模块之间如何通信?

不同设备上的程序如何通信?

大量任务如何被统一管理?

如何处理传感器、控制器和执行器之间的数据流?

如何保证关键任务能够及时执行?

ROS 2就是围绕这些问题建立起来的。

可以把ROS 2简单理解为机器人软件之间的一层“组织基础设施”。

它本身并不负责替机器人完成导航、视觉识别或者运动规划,而是为这些算法和功能模块提供统一的运行和通信环境。

从结构上看,可以简单理解成:

机器人应用
│
├── 感知
├── 定位
├── 建图
├── 规划
├── 控制
└── 设备驱动
        │
        ↓
      ROS 2
        │
├── Node
├── Topic
├── Service
├── Action
├── Parameter
├── Lifecycle
└── Executor
        │
        ↓
       DDS
        │
        ↓
     操作系统
        │
        ↓
       CPU

这也是理解ROS 2非常重要的一条主线。


二、Node、Topic、Service和Action:ROS 2是如何把机器人拆开的?

ROS 2最基础的概念之一就是Node。

1. Node:机器人软件中的基本功能单元

Node可以理解为ROS 2系统中的一个独立功能模块。

例如:

camera_node
lidar_node
imu_node
localization_node
planner_node
controller_node

摄像头Node负责采集摄像头数据。

激光雷达Node负责采集雷达数据。

定位Node负责处理传感器信息并计算机器人位置。

规划Node负责根据目标位置和环境生成运动路径。

控制Node则根据路径计算具体的运动指令。

这样,一个复杂的机器人系统就被拆成了多个相对独立的软件模块。

这实际上体现了ROS 2非常重要的设计思想:

模块化。

模块化带来的直接好处就是软件可以独立开发、独立测试、独立部署。

例如开发团队A负责视觉,团队B负责定位,团队C负责运动控制。

只要各个模块按照约定的数据接口进行通信,就不需要了解其他模块内部到底是怎么实现的。

这对于复杂机器人项目非常重要。

但Node只是第一步。

多个Node拆开以后,新的问题马上出现:

Node之间怎么通信?

这就涉及ROS 2的Topic、Service和Action。


2. Topic:适合持续产生的数据

Topic是ROS 2中非常常见的通信机制。

它采用典型的发布/订阅模式。

例如:

激光雷达Node
      │
      │ publish
      ↓
   /scan
      │
      ├────────→ 定位Node
      │
      ├────────→ 建图Node
      │
      └────────→ 障碍物检测Node

激光雷达Node不断发布数据。

其他Node根据自己的需要订阅这个Topic。

这种模式特别适合传感器数据。

例如:

  • 摄像头图像

  • 激光雷达

  • IMU

  • 里程计

  • GPS

  • 机器人状态

这些数据具有一个共同特点:

数据会不断产生。

例如一个摄像头每秒产生30帧图像。

如果按照传统函数调用方式处理,就需要一个模块主动调用另一个模块。

而Topic采用发布/订阅模式之后,数据生产者和数据消费者可以进一步解耦。


3. Service:适合请求—响应

Service和Topic的最大区别,是它更适合一次性的请求和响应。

例如:

客户端
  │
  │ request
  ↓
服务端
  │
  │ response
  ↓
客户端

例如机器人需要执行:

  • 清空地图

  • 查询某个参数

  • 设置某个状态

  • 执行一次简单操作

这时候Service就比较合适。


4. Action:适合长时间运行的机器人任务

Action则适用于更加复杂的任务。

例如:

“让机器人移动到这个位置。”

这个动作可能需要几秒、几十秒甚至更长时间。

如果使用普通Service,那么调用方发出请求以后,可能需要一直等待最终结果。

Action则可以提供:

Goal
 ↓
执行
 ↓
Feedback
 ↓
执行中
 ↓
Result

例如:

目标:
移动到坐标(10,5)

反馈:
当前位置(2,1)

反馈:
当前位置(5,2)

反馈:
当前位置(8,4)

结果:
到达目标位置

Action还可以处理取消等情况。

因此可以简单理解:

Topic
→ 持续数据流

Service
→ 请求/响应

Action
→ 长时间任务

这三个通信机制共同构成ROS 2机器人软件通信的重要基础。


三、DDS和QoS:ROS 2为什么能够支持复杂机器人通信?

当Node越来越多以后,一个更加底层的问题出现了:

Node之间的数据到底怎么传?

ROS 2并没有简单地自己重新设计一套底层网络协议,而是建立在DDS等通信中间件机制之上。

可以把它简单理解成:

ROS 2应用
   ↓
rclcpp / rclpy
   ↓
RMW
   ↓
DDS
   ↓
网络 / 进程间通信

其中RMW,也就是ROS Middleware Interface,可以理解为ROS 2与具体中间件实现之间的一层抽象。

这样做的一个好处是:

ROS 2上层开发者不需要直接处理底层通信细节。

例如开发者只需要关注:

publisher->publish(message);

而不用自己处理:

  • 网络连接

  • 数据序列化

  • 数据传输

  • 发现机制

  • 节点匹配

  • 数据可靠性

这些工作由底层通信机制完成。

但是机器人系统有一个特殊问题:

并不是所有数据都应该采用同一种通信策略。

例如激光雷达。

假设激光雷达每秒产生大量数据。

如果丢失其中一帧数据,机器人可能仍然可以继续运行。

但是对于某些关键控制数据,数据可靠性要求就可能完全不同。

因此ROS 2提供了QoS,也就是Quality of Service。

QoS可以理解为:

开发者可以告诉通信系统:这类数据应该以什么方式传输和保存。

其中比较重要的策略包括:

  • Reliability

  • Durability

  • History

  • Depth

  • Deadline

  • Lifespan

例如Reliability可以关注:

Best Effort
Reliable

Best Effort更强调尽可能快速传递,不一定保证每一条数据都成功到达。

Reliable则更加关注数据可靠到达。

这就是为什么ROS 2不是简单地“把数据从A搬到B”。

它实际上在通信层面提供了更加复杂的数据服务策略。

但是这里又出现一个非常值得注意的问题。

QoS能够解决通信层面的确定性吗?

只能解决其中一部分。

因为即使数据能够可靠传递,也不能说明:

数据到达之后,CPU一定能够在规定时间内处理它。

这就是ROS 2实时性问题的起点。

例如:

传感器
  ↓
DDS
  ↓
ROS 2
  ↓
Executor
  ↓
Callback
  ↓
控制算法

假设传感器数据已经成功到达。

但是此时CPU正在执行另外一个任务。

那么这个Callback什么时候真正获得CPU?

如果系统中存在大量后台任务、网络中断、内核线程、系统服务,甚至其他高负载计算任务,那么数据从“到达”到“真正开始处理”之间就可能产生延迟。

因此:

通信可靠性 ≠ 调度确定性。

这是理解ROS 2与实时操作系统关系的关键。


四、Executor:ROS 2真正开始面对“任务调度”的地方

前面我们已经知道:

Node负责组织功能。

Topic、Service、Action负责通信。

DDS负责底层通信机制。

但一个非常重要的问题仍然没有回答:

这些任务到底什么时候执行?

这就轮到Executor登场了。

ROS 2中的很多操作最终都会转化为需要执行的Callback。

例如:

Timer Callback
Subscription Callback
Service Callback
Action Callback

假设一个机器人同时存在:

摄像头Callback
激光雷达Callback
IMU Callback
定位Callback
控制Callback
状态监测Callback

那么CPU不可能无限并行执行这些任务。

它需要进行调度。

Executor就是ROS 2用于组织和执行这些Callback的重要机制。

最典型的是:

SingleThreadedExecutor

和:

MultiThreadedExecutor

SingleThreadedExecutor可以理解成:

多个Callback最终由一个线程执行。

结构类似:

Callback A
    ↓
Callback B
    ↓
Callback C
    ↓
Callback D

优点是结构简单。

但问题也很明显:

如果Callback A执行时间比较长,那么后面的Callback都可能受到影响。

例如:

激光雷达Callback
       ↓
复杂点云处理
       ↓
执行5ms
       ↓
控制Callback等待

如果控制任务本身要求1ms一个周期,那么这种等待就可能造成问题。

MultiThreadedExecutor则可以使用多个线程执行Callback:

线程1 → 激光雷达

线程2 → 摄像头

线程3 → 定位

线程4 → 控制

看起来问题解决了。

但新的问题又出现了:

多个线程之间如何协调?

例如两个Callback同时访问同一份数据。

这时候需要:

  • Mutex

  • Lock

  • Condition

  • Callback Group

  • Synchronization

一旦涉及锁,就会出现新的实时性问题。

例如一个低优先级任务持有锁:

低优先级任务
      ↓
     Lock
      ↓
访问共享资源

此时高优先级控制任务也需要这个资源:

高优先级控制任务
      ↓
等待Lock

如果与此同时又存在一个中优先级任务不断运行:

中优先级任务
      ↓
持续占用CPU

那么就可能形成经典的:

优先级反转。

这已经不再是简单的ROS 2 API问题,而开始进入操作系统调度领域。

这也是为什么研究ROS 2实时性,最终必须理解Linux调度器。


五、ROS 2不是操作系统:从机器人软件框架走向实时操作系统

到这里,可以把前面的技术链条完整串起来:

机器人应用
      ↓
ROS 2 Node
      ↓
Topic / Service / Action
      ↓
QoS
      ↓
DDS
      ↓
Executor
      ↓
线程
      ↓
Linux Scheduler
      ↓
CPU

ROS 2解决了大量机器人软件工程问题。

但是它并没有取代操作系统。

真正决定一个线程什么时候获得CPU的,是底层操作系统调度机制。

真正处理硬件中断的,是操作系统。

真正管理内存、进程、线程和设备资源的,也是操作系统。

因此,一个更加完整的机器人软件架构应该理解为:

┌─────────────────────────────┐
│        机器人应用层          │
│ AI / 感知 / 规划 / 控制      │
├─────────────────────────────┤
│            ROS 2             │
│ Node / Topic / Action        │
│ QoS / Executor / Lifecycle  │
├─────────────────────────────┤
│       DDS / Middleware       │
├─────────────────────────────┤
│      实时操作系统 / Linux     │
│ Scheduler / IRQ / Memory    │
│ CPU Isolation / Drivers     │
├─────────────────────────────┤
│       CPU / MCU / SoC        │
└─────────────────────────────┘

这时候就可以理解为什么机器人行业开始越来越关注实时操作系统。

因为机器人系统有一个与普通服务器软件不同的特点:

它不仅需要把事情做对,还需要在规定时间内把事情做完。

例如一个机器人控制任务要求:

控制周期:1ms

理想情况:

0ms    → 采集数据
0.1ms  → 状态计算
0.3ms  → 控制算法
0.5ms  → 输出控制
1ms    → 下一周期

但是实际系统可能变成:

0ms    → 采集数据
0.1ms  → 等待CPU
0.7ms  → 获得CPU
0.8ms  → 执行算法
1.4ms  → 输出控制

平均来看,也许机器人依然可以工作。

但是问题在于:

实时系统真正关注的不是平均值,而是最坏情况下会发生什么。

这就是:

Worst-case latency

以及:

Jitter

需要研究的问题。

如果控制周期偶尔从1ms变成10ms、20ms甚至更长,那么对于高速运动控制系统来说,系统行为就可能出现不可预测性。

因此,机器人实时性不能只依赖ROS 2应用层。

还需要进一步优化:

  • Linux内核抢占

  • 实时调度策略

  • CPU affinity

  • CPU isolation

  • IRQ affinity

  • 内核线程

  • 内存管理

  • 锁机制

  • 中断延迟

  • 调度延迟

这也正是实时Linux存在的重要意义。


而对于面向机器人、工业控制、智能制造等场景的实时操作系统而言,更进一步的问题是:

如何让关键任务尽可能不受到普通任务的干扰?

例如:

CPU 0
普通Linux任务
网络
日志
系统服务
后台任务

CPU 1
机器人感知

CPU 2
路径规划

CPU 3
实时控制

如果机器人最关键的控制任务能够运行在相对独立的CPU核心上,那么普通后台任务就不应该轻易干扰控制任务。

这就涉及一个非常重要的技术概念:

核心隔离。

核心隔离不是简单地把一个进程绑定到某个CPU上。

它涉及:

  • CPU affinity

  • IRQ affinity

  • housekeeping CPU

  • 内核线程

  • RCU

  • timer

  • scheduler tick

  • 中断处理

等多个层面的协同。

这也是为什么从ROS 2继续深入到机器人实时系统之后,最终会进入操作系统底层。

从这个角度看,ROS 2和实时操作系统其实并不是两个互相竞争的东西。

它们解决的是不同层次的问题:

ROS 2
解决:
机器人软件如何组织
机器人模块如何通信
机器人任务如何协同

实时操作系统
解决:
任务如何调度
资源如何隔离
中断如何处理
关键任务如何获得确定性

两者组合起来,才构成一个更加完整的机器人软件运行环境。

对于望获 OS而言,这也正是其可以切入机器人产业链的技术位置。

望获 OS定位于全栈国产嵌入式硬实时操作系统研发与技术服务,其技术方向包括实时Linux、实时操作系统以及核心隔离、高安全、高可靠等能力,并面向国产芯片与工业生态提供支持。

当ROS 2逐渐进入工业机器人、机械臂、人形机器人、智能制造等更加复杂的应用场景之后,ROS 2上层的软件能力与底层实时操作系统之间的关系会越来越值得关注。

例如:

AI感知
   ↓
ROS 2
   ↓
规划
   ↓
ros2_control
   ↓
实时控制
   ↓
实时Linux
   ↓
核心隔离
   ↓
CPU / IRQ资源
   ↓
国产处理器

在这条链路中,ROS 2解决的是机器人软件开发和模块协同问题,而实时操作系统承担的是底层任务调度和系统资源管理。

这也是未来机器人软件基础设施值得进一步研究的方向。


写在最后:理解ROS 2,不能只停留在“会用”

很多人学习ROS 2的路径通常是:

安装ROS 2
 ↓
创建Workspace
 ↓
创建Node
 ↓
发布Topic
 ↓
订阅Topic
 ↓
运行机器人

这对于入门当然非常重要。

但如果真正进入机器人产品研发阶段,仅仅“会用ROS 2”是不够的。

随着系统复杂度增加,需要继续理解:

Node
 ↓
通信
 ↓
DDS
 ↓
QoS
 ↓
Executor
 ↓
线程
 ↓
Linux Scheduler
 ↓
CPU

这条链路实际上对应了机器人软件从应用层一路向下进入操作系统的过程。

尤其对于工业机器人、人形机器人、机械臂、移动机器人等系统而言,当控制周期越来越短、任务数量越来越多、AI计算负载越来越高时,机器人软件面对的问题已经不只是:

“功能能不能实现?”

而会逐渐变成:

“关键任务能不能按时完成?”

这也是ROS 2进入工业级机器人之后,一个非常值得深入讨论的问题。

下一篇,我们将继续沿着这条技术链向下深入:

ROS 2 Node到底是什么?

我们将不再停留在“Node就是一个节点”这种入门级解释,而是从Node进程、线程、Callback、Executor、Callback Group以及Linux任务调度几个层面分析:

一个ROS 2 Node从启动到真正获得CPU执行,中间到底发生了什么?

理解这个问题,也就真正开始进入ROS 2实时性的世界。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐