feat(level1): ROS2 完全体 12 包 / 80 测试 / 23 文档 / 工程化 / Docker 分组
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# 18 · 组合节点 (Composable Node) 完全指南
|
||||
|
||||
> **目标**: 理解 ROS2 Composable Node 设计,能把多个节点合并到同一进程,降低延迟与开销。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [1. 是什么](#1-是什么)
|
||||
- [2. 为什么需要](#2-为什么需要)
|
||||
- [3. 工作原理](#3-工作原理)
|
||||
- [4. C++ 实现(.so 库)](#4-c-实现so-库)
|
||||
- [5. Container 启动](#5-container-启动)
|
||||
- [6. Python 等价做法](#6-python-等价做法)
|
||||
- [7. 性能对比](#7-性能对比)
|
||||
- [8. 何时用](#8-何时用)
|
||||
- [9. 推荐阅读](#9-推荐阅读)
|
||||
|
||||
---
|
||||
|
||||
## 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`**:
|
||||
|
||||
```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`**:
|
||||
|
||||
```cmake
|
||||
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 启动
|
||||
|
||||
```bash
|
||||
# 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 文件
|
||||
|
||||
```python
|
||||
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)。但有等价做法:
|
||||
|
||||
```python
|
||||
# 同进程多节点(共享内存,但仍走 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. 推荐阅读
|
||||
|
||||
- [ROS2 Composition 设计稿](https://design.ros2.org/articles/composition.html)
|
||||
- [ROS2 Humble Composition 教程](https://docs.ros.org/en/humble/Tutorials/Intermediate/Launch/Using-Event-Handlers.html)
|
||||
- [ros2 component CLI](https://docs.ros.org/en/humble/Tutorials/Intermediate/Composition.html)
|
||||
- [`py_lifecycle_composable` 包](../src/py_lifecycle_composable/README.md)
|
||||
Reference in New Issue
Block a user