ROS1 - Node vs. Nodelets
在ROS1中可以编写node或者nodelet,ROS1中的节点被编译为可执行文件,ROS1中的nodelet被编译为shared lib,然后在运行时由容器进程加载。
我们可以简单的理解nodelet是一组统一管理的nodes。
ROS2 - Unified API
ROS2 中,我们推荐的代码编写方式类似于nodelet-在ROS2中成为Component,这样来说涉及的代码修改更少,因为可以比较顺利的把ROS1中的概念移植到ROS2中,在ROS2中将两种API(Node、Nodelet)整合为一种API。
因为我们目前将进程布局作为运行时的决策,我们可以选择:
- 在多进程中运行多个节点,优点是进程之间是隔离的,然后简化单个节点之间的调试难度;
- 在单进程中运行多个节点,优点是开销较低,进程内的节点通信更加高效。
此外,ros2 launch系统可以通过launch文件来执行这些操作。
编写 Component
因为Component内置于shared lib中,因为Component没有主函数,可以参考demos中Component中Talker的代码,Component通常是rclcpp::Node
的子类,由于Component不受线程的控制,因此我们不应在构造函数中执行长时间执行或者阻塞的任务。此外,可以使用定时器来获取定期的通知。此外,也可以创建Publisher和Subscriber,Server和Client。
Component类使用rclcpp_components
中的宏来注册自己,这是一个非常重要的特性。当这个Component的静态库加载到正在运行的进程时,使进程可以发现该Component,并将其视作一个入口点。
此外,一旦创建了Component,就需要在索引中注册该组件,使得其他工具可以发现这个Component。
CMakeList.txt的例子:
add_library(talker_component SHARED
src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")
如果想在package中将这个component导出为shared lib,并且在其他的package链接时使用这个package里面的component就需要在CMakeList.txt中添加代码。
注意:为了component_container可以发现期望的component,需要从提供相应组件的workspace中从shell启动或者执行component。
Using Components
composition package为用户提供了很多使用方法,其中较为常见的三种是:
- 先启动一个通用的容器进程,之后使用容器进程提供的
load_node
service向进程中动态加载节点。load_node
service会通过命令行参数读取package name,library name,之后在这个容器进程中执行这个可执行文件。这个service可以通过命令行调用,也可以通过编程的方式调用。 - 创建一个定制化的可执行文件,这个文件中包含了多个节点,这些节点可以在编译期确定下来,这个方法需要每个Component都有一个hpp文件;
- 创建一个launch文件,使用
ros2 launch
命令创建一个容器进程,并在其中运行多个组件。