Files
ROS2_learn/doc/18-composable.md
T

5.8 KiB

18 · 组合节点 (Composable Node) 完全指南

目标: 理解 ROS2 Composable Node 设计,能把多个节点合并到同一进程,降低延迟与开销。


目录


1. 是什么

Composable Node = 把多个 ROS 节点装到同一个进程(共享内存)。

传统模式:
  ┌──────────┐   ┌──────────┐   ┌──────────┐
  │ Process1 │   │ Process2 │   │ Process3 │
  │ Node A   │   │ Node B   │   │ Node C   │
  └──────────┘   └──────────┘   └──────────┘
       │ DDS │        │ DDS │        │ DDS │
       └─────┴────────┴────────┴────────┘
       (跨进程通信,微秒级延迟)

Composable 模式:
  ┌────────────────────────────┐
  │     Single Process         │
  │  Node A │ Node B │ Node C  │
  │     (shared memory)        │
  └────────────────────────────┘
       │ DDS │
       (只有出/入本进程时走 DDS)

2. 为什么需要

场景 传统模式延迟 Composable 延迟 提升
5 节点 pipeline ~500μs ~50μs 10x
高频 sensor fusion ~1ms ~100μs 10x
大量小消息 频繁拷贝 共享指针 CPU 降 30%

其他好处:

  • 启动快(避免 fork)
  • 内存共享(零拷贝)
  • 调试简单(单进程,单 gdb)

3. 工作原理

ROS2 Composable = C++ 共享库(.so) + Container 进程

  1. 把节点代码编译成 .so 库(libmy_component.so)
  2. Container 进程(component_container_mt)加载 .so
  3. Container 实例化组件(无需 fork)
  4. 组件间用进程内 publish/subscribe(不经过 DDS)

4. C++ 实现(.so 库)

my_pkg/src/my_component.cpp:

#include "rclcpp_components/register_node_macro.hpp"

class MyComponent : public rclcpp::Node
{
public:
    explicit MyComponent(const rclcpp::NodeOptions & options)
    : Node("my_component", options) {}
};

RCLCPP_COMPONENTS_REGISTER_NODE(MyComponent)

CMakeLists.txt:

add_library(my_component SHARED src/my_component.cpp)
ament_target_dependencies(my_component rclcpp)
rclcpp_components_register_node(my_component "my_component")

install(TARGETS my_component
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
)

5. Container 启动

# 1. 单线程 container(调试用)
ros2 component standalone --container-type standalone

# 2. 多线程 container(生产用)
ros2 component standalone --container-type multithreaded

# 3. 在已有 container 里加载组件
ros2 component load <container_name> <package_name> <component_name>

# 例:
ros2 component load /ComponentManager my_pkg my_component

Launch 文件

from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode

container = ComposableNodeContainer(
    name='my_container',
    namespace='',
    package='rclcpp_components',
    executable='component_container_mt',
    composable_node_descriptions=[
        ComposableNode(
            package='my_pkg',
            plugin='my_pkg::MyComponent',
            name='node_a',
        ),
        ComposableNode(
            package='my_pkg',
            plugin='my_pkg::MyComponent',
            name='node_b',
        ),
    ],
)

6. Python 等价做法

Python 不支持真正的 Composable Node(必须用 C++ .so)。但有等价做法:

# 同进程多节点(共享内存,但仍走 DDS 内部)
import rclpy
from rclpy.executors import MultiThreadedExecutor

rclpy.init()
node_a = NodeA()
node_b = NodeB()

executor = MultiThreadedExecutor(num_threads=4)
executor.add_node(node_a)
executor.add_node(node_b)
executor.spin()

Python 多节点同进程 + MultiThreadedExecutor 是 ROS2 Python 等价的 Composable 做法。

7. 性能对比

维度 传统多进程 C++ Composable Python 多线程
启动时间 慢(每个进程 fork) 快(动态加载)
进程间延迟 ~100μs (DDS) ~5μs (shared mem) ~10μs
内存 每进程独立 共享 共享
调试 gdb attach 多个 gdb 单进程 gdb 单进程
灵活性 高(可单独 kill) 低(同进程)

8. 何时用

用 Composable:

  • 同一 pipeline 多个节点(image → process → control)
  • 高频消息流(>100Hz)
  • 延迟敏感(机器人控制回路)

不用 Composable:

  • 节点可独立部署(某些在 PC,某些在 RK3506)
  • 需要单独 kill 重启某些节点
  • 节点崩溃隔离(传统模式崩溃只影响一个进程)

9. 推荐阅读



📖 阅读路径导航

💡 这是仓库 doc/ 下所有文档的推荐阅读顺序。返回 README 总导航

本文预计阅读时间: 25 分钟 📍 当前位置: 第 9 / 24 篇