ROS2 Jazzy:编写可组合节点(C++)
起始状态
假设你有一个常规的rclcpp::Node
可执行文件,你希望它能和其他节点在同一个进程中运行,以实现更高效的通信。
让我们从一个直接继承Node
且定义了主函数的类开始:
cpp
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;
}
通常在 CMake 中,这段代码会被编译成一个可执行文件:
cmake
# ...
add_executable(vincent_driver src/vincent_driver.cpp)
# ...
install(TARGETS vincent_driver
DESTINATION lib/${PROJECT_NAME}
)
代码更新
添加包依赖
你的package.xml
文件应该添加对rclcpp_components
的依赖,如下所示:
xml
<depend>rclcpp_components</depend>
或者,你也可以分别添加build_depend
和exec_depend
。
类定义
你可能需要对类定义进行修改,就是确保类的构造函数接受一个NodeOptions
参数:
cpp
VincentDriver(const rclcpp::NodeOptions & options) : Node("vincent_driver", options)
{
// ...
}
不再需要主函数
用pluginlib
风格的宏调用替换你的main函数:
cpp
#include <rclcpp_components/register_node_macro.hpp>
RCLCPP_COMPONENTS_REGISTER_NODE(palomino::VincentDriver)
注意事项
如果被替换的main函数中包含MultiThreadedExecutor
,请务必注意并确保容器节点是多线程的。详情请参阅下面的相关部分。
CMake 修改
第一步
在CMakeLists.txt
中添加rclcpp_components
作为依赖:
cmake
find_package(rclcpp_components REQUIRED)
第二步
用add_library
替换add_executable
,并使用一个新的目标名称:
cmake
add_library(vincent_driver_component src/vincent_driver.cpp)
第三步
将其他使用旧目标的构建命令替换为作用于新目标的命令。例如,ament_target_dependencies(vincent_driver ...)
要改为ament_target_dependencies(vincent_driver_component ...)
。
第四步
添加一个新命令来声明你的组件:
cmake
rclcpp_components_register_node(
vincent_driver_component
PLUGIN "palomino::VincentDriver"
EXECUTABLE vincent_driver
)
第五步,也是最后一步
将 CMake 中所有作用于旧目标的安装命令修改为安装库版本。例如,将目标安装到lib/${PROJECT_NAME}
替换为库安装命令:
cmake
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
)
运行节点
关于节点组合的详细内容,请参考《在单个进程中组合多个节点》教程。简单来说,如果你在 Python 启动文件中有如下代码:
python
from launch_ros.actions import Node
# ..
ld.add_action(Node(
package='palomino',
executable='vincent_driver',
# ..
))
你可以将其替换为:
python
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
# ..
ld.add_action(ComposableNodeContainer(
name='a_buncha_nodes',
namespace='',
package='rclcpp_components',
executable='component_container',
composable_node_descriptions=[
ComposableNode(
package='palomino',
plugin='palomino::VincentDriver',
name='vincent_driver',
# ..
extra_arguments=[{'use_intra_process_comms': True}],
),
]
))
注意事项
如果你需要多线程支持,不要将executable
设置为component_container
,而是设置为component_container_mt
。
作者:小芝,一个干了二十多年的C++开发。开发过桌面软件,干过古早功能手机游戏开发,也弄过客户端APP。现在对AI开发和机器人开发有兴趣,同时也在了解产品相关知识。若喜欢本文,欢迎点赞、在看、留言交流。
为了防失联,欢迎关注本智践行(gh_157cc95c7163)。
或加小芝微信chunjuz2025,防失联。