ROS2 Jazzy:关于组合的概念

139 阅读4分钟

ROS1 中的节点与节点组件

在 ROS1 中,你可以将代码编写为 ROS 节点或 ROS 节点组件(Nodelet)。ROS1 节点会被编译为可执行文件,而 ROS1 节点组件则被编译为共享库,然后由容器进程在运行时加载。

ROS2 中统一的 API

在 ROS2 中,推荐的组件代码编写方式类似于nodelet,在ROS2中称之为组件 ComponentComponent 的本质是一种模块化、可插拔的代码单元,既可独立运行为节点,也可动态加载到容器进程中,这使得向现有代码添加常见概念(如生命周期:继承 rclcpp_lifecycle::LifecycleNode)变得更容易。

节点和组件拥有不同的 API 是 ROS1 中最大的缺点,但在 ROS2 中可以避免,因为两种方法都使用相同的 API,也就是说 Component 同样继承自 rclcpp::Node。与普通节点使用相同的基类,使得编写 Component 的代码与普通节点完全兼容。通过类加载器(Class Loader) 和 组件容器(Component Container),Component 可被动态加载到同一进程,而不需要修改代码。

虽然仍然可以使用类似节点的“编写自己的main函数”的风格,但通常不推荐这样做。这是因为显式编写 main 函数创建节点,然后硬编码初始化逻辑,灵活性较差。所以除非有特殊需求(如极简原型),否则都推荐以 Component 形式编写节点。特别是需要动态启停组件、多组件进程共享、生命周期管理时,Component 更高效且易于维护

另外,为了使进程布局成为部署时决策,用户可以在以下两种方式中选择一个:

  • 在独立进程中运行多个节点,享受进程/故障隔离的优势,也能更轻松地调试单个节点;
  • 在单个进程中运行多个节点,具有更低的开销,并能选择更高效的通信方式(具体可参的《配置高效的进程内通信》一文)。

此外,ros2 launch 可用于通过专门的启动动作自动化这些操作。

组件容器

组件容器是一个宿主进程,允许你在运行时在同一进程空间内加载和管理多个组件。

目前,可用的通用组件容器类型如下:

  • component_container
    最通用的组件容器,使用单个单线程执行器(SingleThreadedExecutor)执行所有组件。
  • component_container_mt
    使用单个多线程执行器(MultiThreadedExecutor)执行组件的容器。
  • component_container_isolated
    为每个组件使用专用执行器的容器:可以是单线程执行器(默认)或多线程执行器。
    有关执行器类型的更多信息,请参考《执行器》一文中的执行器类型部分。而更多有关每个组件容器选项的信息,请参考《在单进程中组合多个节点》一文中的组件容器类型。

编写组件

由于组件仅能编译为共享库,因此它没有主函数(参见 Talker 源代码h:ttps://github.com/ros2/composition/blob/humble/composition/src/talker_component.cpp)。组件通常是 rclcpp::Node 的子类。由于组件不控制线程,因此不应在其构造函数中执行任何长时间运行或阻塞的任务,而是使用定时器来获取周期性通知。此外,它可以创建发布者、订阅者、服务端和客户端。

要使得一个类成为组件,该类应该使用 rclcpp_components 包中的宏(RCLCPP_COMPONENTS_REGISTER_NODE)在代码中进行自我注册。它充当了一个入口点,这使得组件库被加载到正在运行的进程中时可以被发现。

此外,组件创建后,必须在Cmake中向索引注册才能被工具发现。

add_library(talker_component SHARED src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# 若要在同一共享库中注册多个组件,可多次调用
# rclcpp_components_register_nodes(talker_component "composition::Talker2")

有关示例,请查看《编写可组合节点》一文。

注意

为了让组件容器能够找到所需的组件,必须从已加载相应工作空间的终端执行或启动容器。

使用组件

composition 包有几种不同的组件使用方法。最常见的三种方法是:

  1. 启动通用容器进程并调用容器提供的 load_node ROS 服务
    这个 ROS 服务将加载通过包名和库名指定的组件,并在运行的进程中启动执行它。除了以编程方式调用 ROS 服务外,还可以使用命令行工具通过传递命令行参数来调用 ROS 服务。
  2. 创建包含编译时已知的多节点的自定义可执行文件
    这种方法要求每个组件都有一个头文件(第一种情况并非严格需要)。
  3. 创建启动文件并使用 ros2 launch 创建加载了多个组件的容器进程

实际应用

可以试一试《在单个进程中组合多个节点》一文中提到的各种方式。


关注【智践行】,发送 【机器人】 获得机器人经典学习资料