ROS 2 Node节点详解:一个Node从启动到执行,到底发生了什么?

发布时间:2026/9/29 2:06:40
ROS 2 Node节点详解:一个Node从启动到执行,到底发生了什么? 上一篇文章我们从整体架构出发介绍了 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等事件又会转化成CallbackCallback最终交给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 的通信机制进行协作。这种设计对于机器人软件开发非常重要。因为机器人软件通常具有非常明显的模块化特点。例如感知模块负责摄像头激光雷达深度相机IMUGPS定位模块负责位姿估计里程计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_sharedMyNode(); 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最好的方式不是死记定义而是跟踪它的一次完整运行过程。假设我们有一个非常简单的机器人控制Nodecontroller_node它负责每隔1ms读取机器人状态然后计算控制指令。代码可能类似auto node std::make_sharedControllerNode(); 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_sharedControllerNode();这一步实际上完成了很多事情。Node需要建立自己的Node nameNamespacePublisherSubscriptionTimerServiceParameterCallback例如controller_node │ ├── /joint_states Subscription ├── /cmd_vel Publisher ├── control_timer └── parameters此时Node在ROS 2系统中已经“注册”了自己的身份和通信接口。如果它订阅/joint_states那么当新的关节状态数据到达时就会产生相应的处理事件。例如/joint_states ↓ Subscription ↓ Callback问题再次出现Callback什么时候执行这时候就必须理解Executor。三、Callback和ExecutorNode真正开始“动起来”的地方很多ROS 2初学者容易认为Topic收到消息 → Callback自动执行。从使用角度看这样理解并没有问题。但如果要深入理解ROS 2就必须进一步问到底是谁执行这个Callback答案就是Executor。Executor是ROS 2执行机制中的重要组成部分它负责组织和执行Node中的各种回调。例如一个Node可能同时存在Timer Callback Subscription Callback Service Callback Action Callback可以想象一个机器人控制NodeController 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 GroupROS 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 ExclusiveReentrantMutually 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 3Control 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 ↓ CPUROS 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_sharedMyNode(); 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以及机器人实时数据链路并继续分析这些通信机制和底层实时操作系统之间的关系。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询