【ROS2】ROS2 插件开发流程(基于 pluginlib)

概述

在 ROS 2 系统中,pluginlib 是一个支持运行时动态加载 C++ 插件的框架,广泛用于控制器、导航模块、传感器驱动等模块化场景。相比传统的静态类继承方式,pluginlib 允许开发者在不修改主程序的前提下,通过字符串指定并加载不同的功能模块,实现高度解耦与可扩展设计。

本篇文档系统地梳理了 ROS 2 插件开发的完整流程,包括:

  • 如何定义接口类与派生类;
  • 如何将插件注册并编译为 .so 共享库;
  • 如何配置 XML 描述文件供运行时识别;
  • 如何在主程序中通过字符串加载并调用插件类;
  • 插件在编译期和运行期的完整链路逻辑。

最终目标是实现这样一种能力:

在运行时仅通过字符串名称,即可加载并调用任意插件类,无需修改或重新编译主程序。

本流程结合实际 CMake 与 pluginlib 的接口调用,详细解释了插件的“构建 → 导出 → 注册 → 加载 → 调用”全周期流程。

一、插件开发概述

ROS 2 中的 pluginlib 提供了运行时插件加载机制,广泛应用于控制器、导航、硬件驱动等场景。整个插件的开发流程包括以下六个步骤:

步骤 1:定义接口(基类)

  • 创建一个纯虚类,用于定义通用行为接口。
  • 必须至少有一个纯虚函数,用于派生类重写。
class MotionController {
public:
  virtual void start() = 0;
  virtual void stop() = 0;
  virtual ~MotionController() {}
};

步骤 2:派生具体实现类

  • 创建实现类,继承基类,使用 override 明确重写。
class SpinMotionController : public MotionController {
public:
  void start() override;
  void stop() override;
};

步骤 3:编写实现 .cpp

  • 实现类中声明的函数逻辑。
void SpinMotionController::start() {
  std::cout << "SpinMotionController::start" << std::endl;
}
void SpinMotionController::stop() {
  std::cout << "SpinMotionController::stop" << std::endl;
}

步骤 4:注册插件类

  • 使用 PLUGINLIB_EXPORT_CLASS 宏注册类,便于 pluginlib 在运行时创建其实例。
#include 
PLUGINLIB_EXPORT_CLASS(motion_control_system::SpinMotionController, motion_control_system::MotionController)

步骤 5:配置插件描述文件(XML)

  • 声明插件库与类信息,供 pluginlib 在运行时查找使用。
<library path="spin_motion_controller">
  <class
    name="motion_control_system/SpinMotionController"
    type="motion_control_system::SpinMotionController"
    base_class_type="motion_control_system::MotionController">
    <description>Spin Motion Controllerdescription>
  class>
library>
  • path: 动态库文件名,不含 lib 前缀和 .so 后缀,即对应libspin_motion_controller.so库,这个库的创建设置在CMakeLists中有描述,后面会提到。

  • name: 插件的唯一标识符,用于加载时传入。

  • type: 实际 C++ 实现类名。

  • base_class_type: 必须与 ClassLoader 的基类一致。


步骤 6:动态加载插件

pluginlib::ClassLoader<motion_control_system::MotionController> loader("motion_control_system", "motion_control_system::MotionController");
auto controller = loader.createSharedInstance("motion_control_system/SpinMotionController");
controller->start();
controller->stop();

二、编译与运行机制

编译流程

CMakeLists.txt 中配置如下:

find_package(pluginlib REQUIRED)

# 创建动态库目标
add_library(spin_motion_controller SHARED src/spin_motion_controller.cpp)

# 指定依赖项,自动添加头文件路径和链接信息
ament_target_dependencies(spin_motion_controller pluginlib)

# 安装插件库(仅 .so 有效)
install(TARGETS spin_motion_controller
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin
)

# 安装插件描述文件(用于 pluginlib 运行时查找)
pluginlib_export_plugin_description_file(
  motion_control_system spin_motion_plugins.xml
)

运行流程

  1. 加载器通过包名 motion_control_system 找到安装路径:

    install/share/motion_control_system/spin_motion_plugins.xml
    
  2. 解析 XML:

    • 找到 name 对应的类;

    • 映射到 .so 动态库路径;

    • 使用 dlopen 动态加载 .so 文件;

    • 使用 dlsym 获取构造函数;

    • 创建类实例并调用接口。


三、关键概念补充

项目 含义
.so 共享库,支持运行时加载(pluginlib 的必要前提)
pluginlib_export_plugin_description_file(...) 安装 XML 到 share/包名/,供运行时读取
ament_target_dependencies(...) 为目标添加头文件路径、链接库、编译选项
createSharedInstance() 基于插件唯一标识符(字符串)动态构造类实例
ClassLoader 插件加载核心类,内部使用 dlopen + dlsym

四、插件运行示例结构

# 编译
colcon build --packages-select motion_control_system
source install/setup.bash

# 运行
./install/motion_control_system/lib/motion_control_system/test_plugin motion_control_system/SpinMotionController

五、目录结构参考

motion_control_system/
├── CMakeLists.txt
├── package.xml
├── spin_motion_plugins.xml
├── include/
│   └── motion_control_system/
│       ├── motion_control_interface.hpp
│       └── spin_motion_controller.hpp
└── src/
    ├── spin_motion_controller.cpp
    └── test_plugin.cpp

插件加载流程图:

ClassLoader → 查找包路径 → 读取 XML → 找到插件类 → dlopen(.so)dlsym 获取构造函数 → 创建对象

参考

ROS2机器人开发:从入门到实践 (桑欣)
Chatgpt
Claude

你可能感兴趣的:(机器人,ROS2,c++)