【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/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(motion_control_system::SpinMotionController, motion_control_system::MotionController)
步骤 5:配置插件描述文件(XML)
- 声明插件库与类信息,供 pluginlib 在运行时查找使用。
<library path="spin_motion_controller"><classname="motion_control_system/SpinMotionController"type="motion_control_system::SpinMotionController"base_class_type="motion_control_system::MotionController"><description>Spin Motion Controller</description></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_controllerARCHIVE DESTINATION libLIBRARY DESTINATION libRUNTIME DESTINATION bin
)# 安装插件描述文件(用于 pluginlib 运行时查找)
pluginlib_export_plugin_description_file(motion_control_system spin_motion_plugins.xml
)
运行流程
-
加载器通过包名
motion_control_system
找到安装路径:install/share/motion_control_system/spin_motion_plugins.xml
-
解析 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