ros2 从零开始17 编写可组合节点
ros2 从零开始17 编写可组合节点
前言
背景
之前我们提到,同一个进程有多个节点,这些节点之间的通信是线程同步通信,基于此通信效率很高效。本节我们讨论一下可组合节点。
本节也会对比之前的普通节点和组合节点的区别。
起点
假如我们有一个普通的节点,包含main函数的。如下:
namespace palomino { class VincentDriver : public rclcpp::Node { // ... }; } int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<palomino::VincentDriver>()); rclcpp::shutdown(); return 0; }其CMakeList文件也通常如下:
# ... add_executable(vincent_driver src/vincent_driver.cpp) # ... install(TARGETS vincent_driver DESTINATION lib/${PROJECT_NAME} )工程更新
假如要将它修改成组合节点,则需要有几项修改。
1. 添加包依赖
package.xml应该依赖于,比如rclcpp_components ,所以在package.xml里面添加如下:
<depend>rclcpp_components</depend>你也可以手动添加如下依赖<build_depend>和<exec_depend>
2. 类定义变化
类定义唯一需要改变的是,需要一个带参数的构造函数,如下:
VincentDriver(const rclcpp::NodeOptions & options) : Node("vincent_driver", options) { // ... }3. 没有main函数。
去掉main函数,替换为下面的代码
#include <rclcpp_components/register_node_macro.hpp> RCLCPP_COMPONENTS_REGISTER_NODE(palomino::VincentDriver)如果你的main函数使用了MultiThreadedExecutor,则确保你的运行容器同样也是多线程的(multithreaded)。后续章节会讨论。
4. CMakeList变动
4.1 第一步,需要添加依赖rclcpp_components,如下:
find_package(rclcpp_components REQUIRED)4.2 第二步,编译目标变化。
编译目标从add_executable(编译进程)变成add_library(编译链接库), 如下,add_executable已被我注释掉:
#add_executable(vincent_driver src/vincent_driver.cpp) add_library(vincent_driver_component SHARED src/vincent_driver.cpp)4.3 第三步,替换旧目标的其他选项,应用到新目标上。
例如ament_target_dependencies(vincent_driver …)变成 ament_target_dependencies(vincent_driver_component rclcpp_components …), 同时别忘记了多出来的rclcpp_components。同样,下面注释掉的是之前配置项,未被注释的是当前使用的
#ament_target_dependencies(vincent_driver tutorial_interfaces) ament_target_dependencies(vincent_driver_component tutorial_interfaces rclcpp_components)4.4 第四步,添加一个声明,注册你的组合节点:
rclcpp_components_register_node( vincent_driver_component PLUGIN "palomino::VincentDriver" EXECUTABLE vincent_driver )其中rclcpp_components_register_node有三个参数:
- 组合节点,这里是vincent_driver_component
- 组合节点的命名空间,这里是palomino::VincentDriver
- 组合节点名,这里是vincent_driver
4.5 第五步,也是最后一步,将CMake中运行在旧目标上的安装命令改为安装库版本。
旧的安装命令(注释掉的部分)是将vincent_driver安装到lib/${PROJECT_NAME}目录中, 而现在的安装命令是直接安装到lib目录中。
# install(TARGETS vincent_driver # DESTINATION lib/${PROJECT_NAME} # ) ament_export_targets(export_vincent_driver_component) install(TARGETS vincent_driver_component EXPORT export_vincent_driver_component ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin )运行你的节点
经过上面修改后,用colcon进行编译及安装,这里不再赘述。
关于合成节点的深入介绍,请参见合成教程。按如下编写你的启动脚本。PS:根据官方教程做,到运行节点时翻车了,运行一直报错,以为是哪里写的有问题,而官方的python启动脚本又描述很少。后来看是python启动脚本的原因。这里附上的可运行的python脚本
#!/usr/bin/env python3fromlaunchimportLaunchDescriptionfromlaunch_ros.actionsimportComposableNodeContainerfromlaunch_ros.descriptionsimportComposableNodefromlaunch.actionsimportExecuteProcess,TimerActionfromlaunch.substitutionsimportFindExecutabledefgenerate_launch_description():# 创建组件节点容器container=ComposableNodeContainer(name='my_container',namespace='',package='rclcpp_components',executable='component_container',composable_node_descriptions=[ComposableNode(package='palomino',plugin='palomino::VincentDriver',name='VincentDriver',extra_arguments=[{'use_intra_process_comms':True}]),],output='screen',)returnLaunchDescription([container,])实际运行效果:
root@bc2bf85b2e4a:~/ros2_ws# ros2 launch src/palomino/launch/launch.py [INFO] [launch]: All log files can be found below /root/.ros/log/2026-04-27-06-53-18-169175-bc2bf85b2e4a-19235 [INFO] [launch]: Default logging verbosity is set to INFO [INFO] [component_container-1]: process started with pid [19247] [component_container-1] [INFO] [1777272798.537723957] [my_container]: Load Library: /root/ros2_ws/install/palomino/lib/libvincent_driver_component.so [component_container-1] [INFO] [1777272798.547384246] [my_container]: Found class: rclcpp_components::NodeFactoryTemplate<palomino::VincentDriver> [component_container-1] [INFO] [1777272798.547458915] [my_container]: Instantiate class: rclcpp_components::NodeFactoryTemplate<palomino::VincentDriver> [INFO] [launch_ros.actions.load_composable_nodes]: Loaded node '/VincentDriver' in container '/my_container' [component_container-1] [INFO] [1777272799.063800446] [VincentDriver]: Publishing: '0' [component_container-1] [INFO] [1777272799.563895176] [VincentDriver]: Publishing: '1' [component_container-1] [INFO] [1777272800.063988902] [VincentDriver]: Publishing: '2' [component_container-1] [INFO] [1777272800.564248809] [VincentDriver]: Publishing: '3'总结
可组合节点没有main函数,需要通过容器或者其他的方式才能加载和运行,下一章节将继续讨论其运行方式。
