ROS2 Composable Node 学习
本文是一个可直接上手的组件化速查文档,重点解释:
- 什么是 Composable Node(组件节点)
- 何时有收益、何时收益不明显
rclcpp_components_register_node的标准用法- 单库多组件、双库组件两种组织方式
- 启动与排查方法
1. 什么是 Composable Node
在 ROS2 中,Composable Node 的核心思想是:
- 节点不一定编译成独立可执行程序(
add_executable) - 节点可以编译成可加载插件(共享库
.so) - 由容器进程(如
component_container_mt)在运行时加载多个组件节点
简单理解:
- 传统模式:一个节点一个进程
- 组件模式:多个节点在一个进程(容器)里运行
2. 什么时候有优势
组件化优势主要在“一个容器中有多个组件”时体现:
- 减少进程间通信开销
- 可结合
use_intra_process_comms降低拷贝成本 - 统一启动、统一管理多个节点
如果只有一个组件在容器中运行,收益通常不明显,更多是架构预留。
3. 组件化最小闭环(必须具备)
要让组件能跑起来,需要以下 4 步:
- 组件类继承
rclcpp::Node - 在
.cpp文件末尾使用RCLCPP_COMPONENTS_REGISTER_NODE(YourClass) - CMake 编译共享库并调用
rclcpp_components_register_node(...) - 使用
ComposableNodeContainer在 launch 中加载组件
4. rclcpp_components_register_node 用法详解
4.1 基本语法
rclcpp_components_register_node(<target>
PLUGIN "namespace::ClassName"
[EXECUTABLE <wrapper_executable_name>]
)
说明:
<target>:add_library(... SHARED ...)定义的共享库 targetPLUGIN:组件类全限定名(命名空间 + 类名)EXECUTABLE:可选,生成一个包装可执行文件,便于直接运行该组件
4.2 单组件示例
add_library(my_components SHARED
src/talker_component.cpp
)
ament_target_dependencies(my_components
rclcpp
rclcpp_components
std_msgs
)
rclcpp_components_register_node(my_components
PLUGIN "demo::TalkerComponent"
EXECUTABLE talker_component_node
)
4.3 一个库里注册多个组件(推荐入门)
add_library(my_components SHARED
src/talker_component.cpp
src/listener_component.cpp
)
ament_target_dependencies(my_components
rclcpp
rclcpp_components
std_msgs
)
rclcpp_components_register_node(my_components
PLUGIN "demo::TalkerComponent"
EXECUTABLE talker_component_node
)
rclcpp_components_register_node(my_components
PLUGIN "demo::ListenerComponent"
EXECUTABLE listener_component_node
)
这个写法表示:
- 同一个
.so中导出两个组件 - 各组件可被容器加载
- 也可分别通过各自包装可执行入口启动
4.4 两个库分别注册组件(按需)
add_library(talker_component SHARED src/talker_component.cpp)
add_library(listener_component SHARED src/listener_component.cpp)
ament_target_dependencies(talker_component rclcpp rclcpp_components std_msgs)
ament_target_dependencies(listener_component rclcpp rclcpp_components std_msgs)
rclcpp_components_register_node(talker_component
PLUGIN "demo::TalkerComponent"
)
rclcpp_components_register_node(listener_component
PLUGIN "demo::ListenerComponent"
)
适合组件依赖差异大、需要独立复用/发布时使用。
5. 类代码示例(必须配合注册宏)
#include <rclcpp/rclcpp.hpp>
#include "rclcpp_components/register_node_macro.hpp"
namespace demo {
class DemoComponent : public rclcpp::Node {
public:
explicit DemoComponent(const rclcpp::NodeOptions & options)
: Node("demo_component", options) {}
};
} // namespace demo
RCLCPP_COMPONENTS_REGISTER_NODE(demo::DemoComponent)
注意:
- 构造函数签名建议固定为
const rclcpp::NodeOptions & - 注册宏通常放在
.cpp文件末尾
6. Launch 加载两个组件示例
from launch import LaunchDescription
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
def generate_launch_description():
return LaunchDescription([
ComposableNodeContainer(
name='demo_container',
namespace='',
package='rclcpp_components',
executable='component_container_mt',
composable_node_descriptions=[
ComposableNode(
package='my_pkg',
plugin='demo::TalkerComponent',
name='talker',
extra_arguments=[{'use_intra_process_comms': True}],
),
ComposableNode(
package='my_pkg',
plugin='demo::ListenerComponent',
name='listener',
extra_arguments=[{'use_intra_process_comms': True}],
),
],
output='screen',
)
])
7. use_intra_process_comms 指南
use_intra_process_comms 的作用可以概括为:
- 让同一进程内(同一容器内)的发布订阅优先走进程内通道
- 减少序列化和内存拷贝开销
- 在高频、大消息链路中通常可降低延迟和 CPU 占用
要点:
- 它不是组件化的必要条件,不开也能使用组件
- 它不保证所有场景都达到“严格零拷贝”
- 主要对“同容器内通信”有效,跨进程通信仍按常规路径走
7.1 什么时候建议开启
建议开启:
- 一个容器中有多个组件,且彼此通信频繁
- 消息体较大(图像、点云、大数组)或话题频率高
- 平台资源紧张(CPU/内存压力较大)
可暂不开启(或先不开):
- 只有单组件,几乎没有容器内节点间通信
- 调试阶段希望行为更接近跨进程部署
- 先追求稳定,再做性能优化验证
7.2 如何打开(Launch 方式,最常用)
在 ComposableNode 中加入:
ComposableNode(
package='my_pkg',
plugin='demo::TalkerComponent',
name='talker',
extra_arguments=[{'use_intra_process_comms': True}],
)
如果有多个组件,建议每个组件都显式配置,避免行为不一致。
7.3 如何打开(代码方式)
如果你在 C++ 中手动创建节点/组件,可通过 NodeOptions 打开:
rclcpp::NodeOptions options;
options.use_intra_process_comms(true);
然后把该 options 传给节点构造函数。
7.4 如何验证是否值得开
建议通过对比实验判断收益:
- 同样场景跑两组:
True与False - 观察端到端延迟、CPU、内存、丢帧率(或消息滞后)
- 如果收益明显,再固化为默认配置
8. 常用检查命令
ros2 component list /demo_container
ros2 node list
ros2 topic list
如果容器正常但组件没加载,优先检查:
PLUGIN名称是否与类全限定名完全一致- 是否缺失
RCLCPP_COMPONENTS_REGISTER_NODE(...) - 共享库是否被正确安装
- launch 中
package与plugin是否写对
9. 常见误区
-
误区 1:两个组件必须两个组件库
不是。一个组件库可以注册多个组件。 -
误区 2:用了组件就一定性能提升明显
不一定。单组件场景提升通常有限。 -
误区 3:有
rclcpp_components_register_node就不需要注册宏
错。CMake 注册和代码注册宏都需要。
end 2026.03.14,by yongtang

1252

被折叠的 条评论
为什么被折叠?



