5.5 KiB
5.5 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 进程。
- 把节点代码编译成 .so 库(
libmy_component.so) - Container 进程(
component_container_mt)加载 .so - Container 实例化组件(无需 fork)
- 组件间用进程内 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 重启某些节点
- 节点崩溃隔离(传统模式崩溃只影响一个进程)