Skip to content

关于节点组合

在 ROS 1 中,你可以将代码编写为 ROS 节点(node) 或 ROS nodelet。 ROS 1 节点被编译为可执行文件, 而 nodelet 则被编译为共享库,在运行时由一个容器进程加载。

在 ROS 2 中,推荐的代码编写方式类似于 nodelet——我们称之为 Component(组件)。 这样更容易向现有代码中添加通用概念,例如 生命周期(life cycle)。 ROS 1 中最大的缺点是编写节点和 nodelet 需要使用两套不同的 API,而在 ROS 2 中,两种方式共用同一套 API,这个问题得以避免。

注意:仍然可以使用 node 风格的”编写自己的 main”方式,但对于常见情况不推荐这样做。

由于进程布局是在部署时才决定的,用户可以灵活选择:

  • 在独立进程中运行多个节点,从而获得进程隔离、故障隔离等优势,也更容易单独调试某个节点
  • 在单个进程中运行多个节点,从而降低通信开销,并可选地使用更高效的进程内通信(Intra Process Communication)

此外,可以通过 ros2 launch 使用专门的 launch 动作(action)来自动化上述操作。

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

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

  • component_container

    • 使用单个 SingleThreadedExecutor 来执行组件的组件容器。
  • component_container --executor-type multi-threaded

    • 使用单个 MultiThreadedExecutor 来执行组件的组件容器。
  • component_container --executor-type events-cbg

    • 使用单个 EventsCBGExecutor 来执行组件的组件容器。
  • component_container --executor-type single-threaded --isolated

    • 为每个组件使用专用 executor 的组件容器:可用选项包括 SingleThreadedExecutor(默认)、MultiThreadedExecutor 和 EventsCBGExecutor。

对于使用 MultiThreadedExecutor 或 EventsCBGExecutor 的隔离和非隔离组件容器,可以通过 ROS 参数 thread_num 配置事件处理线程的数量。 在隔离模式下,每个专用 executor 都将使用 thread_num 个线程创建。

有关 Executor 类型的更多信息,请参见 Executor 类型(TypesOfExecutors)。 有关每种组件容器选项的更多信息,请参见组合教程中的组件容器类型(ComponentContainerTypes)。

由于组件只被构建为共享库,它没有 main 函数(参见 Talker 源代码)。 组件通常是 rclcpp::Node 的子类。 由于它不控制线程,因此不应在其构造函数中执行任何长时间运行或阻塞的任务。 相反,它可以使用定时器来获取周期性通知。 此外,它可以创建发布者(publisher)、订阅者(subscriber)、服务端和客户端。

让一个类成为组件的关键在于,它需要使用 rclcpp_components 包中的宏完成自注册(参见源代码的最后一行)。 这样,当共享库被加载到运行中的进程时,组件就可以被发现——它相当于一个入口点。

此外,一旦创建了组件,就必须将其注册到索引中,以便工具可以发现它。

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")

示例教程请参考 编写可组合节点(Writing a Composable Node)教程。

注意:为了让 component_container 能够找到所需的组件,它必须从一个已经 source 了相应工作空间的 shell 中执行或启动。

ROS 2 提供了两个用于注册组件的 CMake 宏,各自用于不同的目的:

此宏注册一个组件并生成一个独立的可执行文件。 当你既想要可组合性,又希望能够将节点作为独立进程运行时,请使用此宏。

add_library(talker_component SHARED src/talker_component.cpp)
rclcpp_components_register_node(talker_component
PLUGIN "composition::Talker"
EXECUTABLE talker)

此宏注册一个或多个组件用于运行时组合,但不创建独立可执行文件。 当你只想要一个在运行时加载到组件容器中的纯组件库时,请使用此宏。

add_library(talker_component SHARED src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")

composition 包包含了多种使用组件的方法。 三种最常见的方法是:

  1. 启动一个 通用容器进程,并调用容器提供的 ROS 服务 load_node。 ROS 服务将加载通过包名和库名指定的组件,并在运行中的进程内开始执行它。 除了以编程方式调用 ROS 服务外,你还可以使用 命令行工具,通过传入命令行参数来调用 ROS 服务。

  2. 创建一个 自定义可执行文件,包含多个在编译时已知的节点。 此方法要求每个组件都有一个头文件(在第一种情况下并非严格需要)。

  3. 创建一个 launch 文件,并使用 ros2 launch 创建一个加载了多个组件的容器进程。

试试 组合示例(Composition demos)。