☰
ROS 2 Node节点详解:一个Node从启动到执行,到底发生了什么?
2026/9/29 2:06:39 网站建设 项目流程

上一篇文章我们从整体架构出发,介绍了 ROS 2 为什么会成为机器人软件开发的重要基础框架,并沿着Node、Topic、Service、Action、DDS、QoS、Executor这条技术链路,对 ROS 2 的基本组成进行了梳理。

如果把 ROS 2 看成一座城市,那么 Node 就像城市中的一个个功能单元。

摄像头需要一个 Node,激光雷达需要一个 Node,定位需要一个 Node,路径规划需要一个 Node,运动控制也需要一个 Node。

但真正开始开发 ROS 2 之后,一个看似简单的问题很快就会出现:

Node 到底是什么?

很多 ROS 2 入门教程会告诉我们:

Node 是 ROS 2 中最基本的执行单元。

这句话没有错,但如果只停留在这个层面,其实很难真正理解 ROS 2。

因为一个 Node 并不是凭空运行的。

一个 Node 启动以后,最终还是要变成操作系统中的进程、线程和任务;Node中的Subscription、Timer、Service等事件,又会转化成Callback;Callback最终交给Executor组织执行;Executor背后的线程最终还是需要从操作系统获得CPU时间。

也就是说,一个看起来简单的:

ROS 2 Node

背后实际上连接着:

Node ↓ Process ↓ Thread ↓ Callback ↓ Executor ↓ Linux Scheduler ↓ CPU

如果机器人只是做一个简单的Demo,这些底层细节可能暂时不重要。

但当机器人进入高速运动控制、工业机器人、机械臂、人形机器人、移动机器人等复杂场景以后,这条链路就会越来越重要。

因为机器人真正需要关注的,不仅是:

“这个Node能不能运行?”

还包括:

“这个Node什么时候运行?”

“它能不能按预期频率运行?”

“当多个Node同时工作时,谁优先执行?”

“一个Callback执行太久,会不会影响另一个关键Callback?”

“CPU被其他任务占用时,实时控制任务怎么办?”

而这些问题,已经从 ROS 2 应用层逐渐进入操作系统层。

所以,这一篇我们就从一个最基础的 ROS 2 Node 出发,一层层把它拆开,看看一个 Node 从启动到真正执行,中间到底发生了什么,以及为什么机器人实时性最终无法绕开操作系统。


一、ROS 2 Node到底是什么?为什么机器人软件需要Node?

在 ROS 2 中,Node 是非常核心的概念。

一个 Node 通常承担一个相对明确的功能。

例如:

camera_node lidar_node imu_node localization_node planner_node controller_node

可以把它理解成:

机器人系统 │ ├── 摄像头 Node ├── 激光雷达 Node ├── IMU Node ├── 定位 Node ├── 规划 Node └── 控制 Node

这样设计最大的意义,是把复杂机器人系统拆成多个相对独立的功能模块。

例如一个移动机器人:

摄像头 ↓ camera_node ↓ 视觉识别 ↓ perception_node ↓ localization_node ↓ planner_node ↓ controller_node ↓ 电机

每个 Node 都可以专注自己的事情。

摄像头 Node 不需要知道路径规划算法到底怎么实现。

路径规划 Node 也不需要知道摄像头底层驱动是怎么工作的。

只要双方遵守约定的数据接口,就可以通过 ROS 2 的通信机制进行协作。

这种设计对于机器人软件开发非常重要。

因为机器人软件通常具有非常明显的模块化特点。

例如:

感知模块

负责:

  • 摄像头

  • 激光雷达

  • 深度相机

  • IMU

  • GPS

定位模块

负责:

  • 位姿估计

  • 里程计

  • SLAM

  • 多传感器融合

规划模块

负责:

  • 全局路径规划

  • 局部路径规划

  • 轨迹规划

  • 避障

控制模块

负责:

  • 速度控制

  • 位置控制

  • 关节控制

  • 电机控制

如果这些东西全部写在一个程序中,系统会越来越难维护。

因此 Node 带来的第一层价值就是:

把机器人复杂的软件系统拆成可以独立开发和协作的功能模块。

但这里需要特别注意:

Node是逻辑层面的模块,并不等于一个独立的CPU。

更不意味着:

一个Node就一定对应一个线程。

也不意味着:

一个Node就一定对应一个进程。

这正是很多初学者容易混淆的地方。

Node是 ROS 2 对机器人软件功能组织的一种抽象。

真正运行它的时候,还需要落到底层的进程和线程。


Node和Process到底是什么关系?

例如我们写一个简单的 ROS 2 程序:

int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared<MyNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

当这个程序启动以后,操作系统首先看到的并不是:

“这里来了一个ROS 2 Node。”

操作系统看到的是一个进程。

例如Linux中可以看到:

ps -ef

或者:

top

看到对应的进程。

也就是说,可以粗略理解成:

ROS 2层 ↓ Node Linux层 ↓ Process

但是一个进程里面又可以包含多个线程。

所以更完整的关系是:

ROS 2 Node ↓ Process ↓ Thread ↓ Callback

当然,真实系统会比这个模型复杂,因为一个进程里可以运行多个Node,一个Node也可能涉及多个执行线程。

但这个层次关系对于理解ROS 2实时性非常重要。


二、一个Node启动以后发生了什么?从main函数一直走到Callback

理解Node最好的方式,不是死记定义,而是跟踪它的一次完整运行过程。

假设我们有一个非常简单的机器人控制Node:

controller_node

它负责每隔1ms读取机器人状态,然后计算控制指令。

代码可能类似:

auto node = std::make_shared<ControllerNode>(); rclcpp::spin(node);

当程序运行起来之后,大致会经历下面几个层次。


第一步:操作系统启动进程

Linux首先创建一个进程。

例如:

controller_node ↓ Linux Process

此时操作系统负责:

  • 分配虚拟地址空间

  • 建立页表

  • 创建进程控制结构

  • 加载程序代码

  • 建立运行环境

对于普通应用程序来说,这些东西可能完全透明。

但是实时机器人系统必须开始关注一个问题:

进程什么时候真正获得CPU?

因为:

创建成功 ≠ 立即执行。

进程被创建出来以后,还需要由操作系统调度器决定:

什么时候让它运行。

这就进入了线程调度。


第二步:创建线程

一个进程至少需要一个执行线程。

例如:

controller_node │ └── main thread

线程才是真正被操作系统调度的对象。

也就是说:

Process ↓ Thread ↓ CPU

如果CPU当前正在运行其他线程,那么你的控制线程就需要等待调度。

假设机器人控制周期要求:

1ms

那么真正的问题就来了:

如果控制线程本应该在:

10.000ms

执行。

但由于CPU正在处理其他任务,直到:

10.400ms

才真正开始运行。

那么这里就已经出现了:

400μs

的调度延迟。

如果偶尔出现一次,系统可能仍然正常。

但如果延迟不断变化:

100μs 300μs 80μs 700μs 150μs 2ms

那么系统就产生了明显的:

Jitter,也就是执行抖动。

这对于机器人控制非常重要。

因为机器人不是只要求:

“平均每1ms执行一次。”

而是往往更加关注:

“最坏情况下到底会延迟多少?”


第三步:ROS 2创建Node

线程启动以后,程序开始初始化ROS 2。

例如:

auto node = std::make_shared<ControllerNode>();

这一步实际上完成了很多事情。

Node需要建立自己的:

  • Node name

  • Namespace

  • Publisher

  • Subscription

  • Timer

  • Service

  • Parameter

  • Callback

例如:

controller_node │ ├── /joint_states Subscription ├── /cmd_vel Publisher ├── control_timer └── parameters

此时Node在ROS 2系统中已经“注册”了自己的身份和通信接口。

如果它订阅:

/joint_states

那么当新的关节状态数据到达时,就会产生相应的处理事件。

例如:

/joint_states ↓ Subscription ↓ Callback

问题再次出现:

Callback什么时候执行?

这时候就必须理解Executor。


三、Callback和Executor:Node真正开始“动起来”的地方

很多ROS 2初学者容易认为:

Topic收到消息 → Callback自动执行。

从使用角度看,这样理解并没有问题。

但如果要深入理解ROS 2,就必须进一步问:

到底是谁执行这个Callback?

答案就是:

Executor。

Executor是ROS 2执行机制中的重要组成部分,它负责组织和执行Node中的各种回调。

例如一个Node可能同时存在:

Timer Callback Subscription Callback Service Callback Action Callback

可以想象一个机器人控制Node:

Controller Node │ ├── Timer Callback │ └── 每1ms执行 │ ├── Joint State Callback │ └── 接收关节状态 │ ├── Parameter Callback │ └── 参数变化 │ └── Diagnostics Callback └── 状态监控

这些Callback并不是全部同时执行。

最终仍然需要执行线程。

因此可以进一步理解:

ROS 2 Callback ↓ Executor ↓ Executor线程 ↓ Linux Scheduler ↓ CPU

这条链路非常重要。

因为很多机器人实时问题,并不是发生在:

“算法写错了。”

而是发生在:

“算法虽然正确,但是没有在预期时间执行。”


SingleThreadedExecutor:所有Callback排队执行

最简单的一种方式就是SingleThreadedExecutor。

可以简单理解为:

一个Executor ↓ 一个执行线程 ↓ 多个Callback

例如:

Callback A Callback B Callback C Callback D

可能变成:

A → B → C → D

假设:

A = 激光雷达数据处理 B = 摄像头处理 C = 控制算法 D = 状态监测

如果A执行了很长时间,那么C就需要等待。

例如:

A ████████████████ 5ms B ████ 1ms C ██ 0.5ms

如果C原本要求:

1ms周期

那么A就可能对C造成影响。

这就是单线程执行模型下非常典型的问题:

一个Callback的执行时间可能影响其他Callback的响应时间。

对于普通机器人任务,这种影响可能并不严重。

但对于实时控制任务来说,就需要更加谨慎。


MultiThreadedExecutor:多个线程并行执行

因此ROS 2还提供了MultiThreadedExecutor。

可以简单理解为:

Executor │ ├── Thread 1 → Callback A ├── Thread 2 → Callback B ├── Thread 3 → Callback C └── Thread 4 → Callback D

这样多个Callback就有机会并行运行。

听起来问题解决了。

但实际上,新的问题又出现了:

多个线程并行以后,资源竞争怎么办?

例如:

Thread 1 ↓ 读取机器人状态 Thread 2 ↓ 修改机器人状态

两个线程如果同时访问共享数据,就可能需要:

Mutex Lock Synchronization

于是:

Thread A ↓ 获取Lock ↓ 访问资源 Thread B ↓ 等待Lock

这时候,线程之间又产生了等待。

对于普通应用来说,这属于常规并发问题。

但对于实时机器人来说,情况更加复杂。

因为等待时间本身可能是不确定的。


Callback Group:ROS 2如何管理Callback之间的并发?

ROS 2还提供了Callback Group机制。

可以简单理解为:

开发者可以告诉ROS 2,哪些Callback之间允许并行,哪些Callback之间需要互斥。

例如:

Callback Group A │ ├── Sensor Callback └── State Callback Callback Group B │ ├── Control Callback └── Timer Callback

不同Callback Group可以采用不同的并发关系。

其中常见的概念包括:

  • Mutually Exclusive

  • Reentrant

Mutually Exclusive可以理解为:

同一组中的相关Callback不同时执行。

Reentrant则允许更加灵活的并发执行。

这对于构建复杂机器人软件非常重要。

因为随着Node数量增加,Callback数量也会快速增加。

最终系统可能变成:

几十个Node ↓ 上百个Callback ↓ 多个Executor ↓ 多个线程 ↓ 多个CPU核心

到了这个阶段,机器人系统实际上已经不再是一个简单的“ROS程序”。

它已经成为一个真正的:

多任务并发系统。

而多任务系统最终绕不开一个问题:

CPU到底应该把时间给谁?


四、当ROS 2进入机器人实时控制,真正的问题开始出现在Linux内核

现在把前面的链路重新画一遍:

机器人控制算法 ↓ ROS 2 Node ↓ Callback ↓ Executor ↓ Thread ↓ Linux Scheduler ↓ CPU

到了这里,ROS 2和操作系统之间的边界就非常清楚了。

ROS 2可以决定:

“这个Callback需要执行。”

但是它并不能独立决定:

“这个Callback必须在CPU上立刻执行。”

真正决定线程什么时候运行的,是操作系统调度器。

这就是为什么:

ROS 2实时性 ≠ 仅仅优化ROS 2代码。

假设:

控制线程 优先级高

同时:

视觉线程 普通优先级

以及:

日志线程 普通优先级

如果底层操作系统没有针对实时任务进行合理配置,那么高优先级任务仍然可能受到:

  • 中断

  • 内核线程

  • CPU迁移

  • 调度延迟

  • 锁竞争

  • 内存访问

  • 系统服务

等因素影响。

因此机器人实时系统通常会进一步关注:

1. 实时调度策略

例如:

SCHED_FIFO SCHED_RR SCHED_DEADLINE

不同调度策略适用于不同的实时任务模型。

例如一个高优先级机器人控制线程,可以采用实时调度策略运行。


2. CPU Affinity

如果一个控制线程可以运行在:

CPU 3

那么可以减少它在多个CPU之间迁移的可能。

例如:

CPU 0 → 普通任务 CPU 1 → 感知 CPU 2 → 规划 CPU 3 → 控制

这就是CPU亲和性。

但这里必须注意一个很重要的概念:

CPU绑定并不等于CPU核心隔离。

如果只是把控制线程绑定到CPU 3:

Control Thread ↓ CPU 3

并不意味着CPU 3上没有其他工作。

CPU 3仍然可能受到:

IRQ Kernel Thread Timer RCU System Task

等因素影响。

所以更加严格的实时系统还需要进一步研究:

CPU核心隔离。


3. IRQ Affinity

机器人系统中大量硬件设备会产生中断。

例如:

Ethernet CAN PCIe USB GPIO Sensor Motor Controller

设备产生中断以后,CPU需要响应。

如果这些中断全部集中在机器人控制核心上,那么就可能造成:

实时控制任务 ↓ 正在运行 硬件中断 ↓ 突然到来 CPU ↓ 处理中断 控制任务 ↓ 延迟

因此实时系统通常需要进一步考虑:

中断应该由哪个CPU核心处理?

这就是IRQ affinity。

于是我们可以看到:

ROS 2应用层的一个简单控制Callback,往下其实连接了一整套复杂的操作系统机制。


五、为什么机器人最终需要关注实时操作系统?ROS 2与望获OS如何连接起来?

到了这里,我们可以回答文章开头的问题:

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

可以把整个过程简化成:

① Node启动 ↓ ② Linux创建进程 ↓ ③ 创建线程 ↓ ④ ROS 2初始化Node ↓ ⑤ 创建Publisher / Subscription / Timer ↓ ⑥ DDS接收或发送数据 ↓ ⑦ 产生Callback ↓ ⑧ Executor管理Callback ↓ ⑨ Executor线程等待CPU ↓ ⑩ Linux Scheduler进行调度 ↓ ⑪ CPU执行Callback ↓ ⑫ 控制算法产生结果 ↓ ⑬ 数据发送给执行器

真正值得注意的是:

第⑧步以后,ROS 2应用层与操作系统底层开始高度耦合。

如果只是普通机器人Demo,系统可能完全没有必要追求极端的实时性。

但是对于以下场景:

  • 工业机器人

  • 机械臂

  • 运动控制

  • 高速移动机器人

  • 人形机器人

  • 无人系统

  • 智能制造设备

  • 高精度执行机构

情况会发生变化。

因为这些系统往往同时存在:

AI计算 视觉处理 传感器采集 路径规划 运动控制 网络通信 状态监测 日志系统

这些任务全部运行在同一个计算平台上。

于是:

CPU资源 ↓ 多个任务竞争

而机器人控制任务又有明确的时间约束。

因此真正的问题逐渐变成:

如何让关键控制任务获得更加确定的执行环境?

这也是实时Linux需要解决的问题。

而对于望获OS而言,这里就是望获rtLinux可以切入的技术层。

需要强调的是:

望获rtLinux不是ROS 2的替代品。

两者属于不同的软件层。

可以简单理解成:

┌─────────────────────────────┐ │ AI / Robot Application │ ├─────────────────────────────┤ │ ROS 2 │ │ Node / Topic / Action │ │ Executor / QoS │ ├─────────────────────────────┤ │ DDS / Middleware │ ├─────────────────────────────┤ │ 望获rtLinux │ │ 实时调度 / 核心隔离 / 资源管理 │ ├─────────────────────────────┤ │ 国产CPU / SoC │ └─────────────────────────────┘

ROS 2负责解决机器人软件开发和模块协作问题。

望获rtLinux则处于更加底层的操作系统层,关注实时任务运行所需要的操作系统基础能力。

尤其值得关注的是:

核心隔离。

对于机器人系统来说,如果所有任务都混在同一组CPU资源上:

视觉 AI 日志 网络 控制 系统服务

那么一个任务的负载变化,就可能影响另一个任务。

而核心隔离的思路,就是尽可能将不同类型的任务进行资源边界划分。

例如:

CPU 0 系统任务 网络 后台服务 CPU 1 AI / 感知 CPU 2 ROS 2规划 CPU 3 实时控制

这里的意义并不是简单地“把程序绑到某个CPU”。

真正的核心隔离需要综合考虑:

CPU Affinity IRQ Affinity Kernel Thread RCU Timer Scheduler Housekeeping

等多个层面的资源和任务。

因此,从ROS 2 Node继续向下研究,会发现一个非常有意思的技术关系:

Node ↓ Callback ↓ Executor ↓ Thread ↓ Scheduler ↓ CPU

ROS 2解决的是上层机器人软件如何组织和执行。

操作系统解决的是底层线程如何获得资源、如何调度以及如何减少不同任务之间的干扰。

这两层共同决定了一个机器人软件系统最终的运行特性。

对于机器人企业而言,这意味着未来的软件架构可能不再只是:

“ROS 2能不能运行起来?”

而会进一步变成:

“ROS 2能不能在一个具有确定性资源管理能力的操作系统上稳定运行?”

尤其是当机器人从实验室Demo进入真实工业环境之后,这个问题会更加明显。

因为实验室环境通常允许开发者使用:

普通PC 普通Linux 普通网络 普通调度

只要功能能够运行即可。

但是进入产品阶段以后,系统需要进一步考虑:

稳定性 实时性 可靠性 资源隔离 故障恢复 长期运行 国产芯片适配

于是,ROS 2与实时操作系统之间就建立起了一个更加明确的关系:

ROS 2 负责机器人软件生态 + 望获rtLinux 负责底层实时运行环境

这并不是说所有ROS 2机器人都必须使用望获rtLinux。

不同机器人、不同控制周期、不同硬件平台和不同安全要求,会对应不同的系统架构。

但对于需要更加严格实时性、确定性和资源隔离能力的机器人系统而言,从ROS 2向实时操作系统进一步延伸,是一个非常值得研究的技术方向。


写在最后:一个Node背后,其实是一整套操作系统机制

很多时候,我们看到ROS 2程序可能只有几行:

auto node = std::make_shared<MyNode>(); rclcpp::spin(node);

看起来非常简单。

但真正运行的时候,背后已经经历了:

Node ↓ Process ↓ Thread ↓ Callback ↓ Executor ↓ Scheduler ↓ CPU

这也是为什么学习ROS 2不能只停留在API层面。

如果只知道:

“怎么创建Node。”

只能算是入门。

如果进一步理解:

“Node中的Callback是怎么执行的?”

就开始进入ROS 2运行机制。

如果继续理解:

“Executor如何管理Callback?”

就开始进入ROS 2执行模型。

如果再继续研究:

“Executor线程什么时候获得CPU?”

就已经进入Linux调度。

而如果继续追问:

“如何让机器人控制任务获得更加确定的CPU执行环境?”

那么就自然进入:

实时Linux、CPU核心隔离、中断隔离、实时调度以及实时操作系统。

这也是整个“ROS 2 × 实时操作系统”系列接下来要继续研究的核心。

下一篇,我们将继续解决一个非常实际的问题:

ROS 2中的Topic、Service和Action到底有什么区别?为什么机器人中的激光雷达、控制指令和导航任务不能使用同一种通信方式?

从三种通信模型出发,我们会进一步进入DDS、QoS、Reliable、Best Effort、Deadline以及机器人实时数据链路,并继续分析这些通信机制和底层实时操作系统之间的关系。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询