Beginner:Client libraries-10 创建并使用插件
目标:学习创建和加载一个简单的插件使用pluginlib
背景
本教程来自于
http://wiki.ros.org/pluginlib and Writing and Using a Simple Plugin Tutorial.pluginlib是一个c++库,用于从ROS包中加载和卸载插件。插件是从运行库(即共享对象、动态链接库)加载的可动态加载类。使用pluginlib,不必将其应用程序与包含类的库显式链接——相反,pluginlib可以在任何时候打开包含导出类的库,而应用程序事先不知道该库或包含类定义的头文件。插件对于在不需要应用程序源代码的情况下扩展/修改应用程序行为非常有用。
任务
在本教程中,需要创建两个包,一个定义了基类,另一个提供插件。基类定义一个通用的多边形类,然后插件定义具体的形状。
1、创建基类包
ros2 pkg create --build-type ament_cmake polygon_base --dependencies pluginlib --node-name area_node
创建头文件:polygon_base/include/polygon_base/regular_polygon.hpp
里面内容如下:
#ifndef POLYGON_BASE_REGULAR_POLYGON_HPP #define POLYGON_BASE_REGULAR_POLYGON_HPP namespace polygon_base { class RegularPolygon { public: virtual void initialize(double side_length) = 0; virtual double area() = 0; virtual ~RegularPolygon(){} protected: RegularPolygon(){} }; } // namespace polygon_base #endif // POLYGON_BASE_REGULAR_POLYGON_HPP
为了让其他类可以使用这个头文件,在CMakeLists.txt中ament_target_dependencies命令之后编辑如下:
install( DIRECTORY include/ DESTINATION include )
在命令ament_package之前添加:
ament_export_include_directories(
include
)
2、创建一个插件包
ros2 pkg create --build-type ament_cmake polygon_plugins --dependencies polygon_base pluginlib --library-name polygon_plugins
2.1 source 代码为了插件
在文件/polygon_plugins/src/polygon_plugins.cpp中添加如下
#include <polygon_base/regular_polygon.hpp> #include <cmath> namespace polygon_plugins { class Square : public polygon_base::RegularPolygon { public: void initialize(double side_length) override { side_length_ = side_length; } double area() override { return side_length_ * side_length_; } protected: double side_length_; }; class Triangle : public polygon_base::RegularPolygon { public: void initialize(double side_length) override { side_length_ = side_length; } double area() override { return 0.5 * side_length_ * getHeight(); } double getHeight() { return sqrt((side_length_ * side_length_) - ((side_length_ / 2) * (side_length_ / 2))); } protected: double side_length_; }; } #include <pluginlib/class_list_macros.hpp> PLUGINLIB_EXPORT_CLASS(polygon_plugins::Square, polygon_base::RegularPolygon) PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon)
上面实现了一个正方形和三角形,保存边,计算面积。最后三行说明了插件,会调用一些定义来注册实际的类为插件。
参数宏PLUGINLIB_EXPORT_CLASS:
1.插件类的完全类型:
polygon_plugins::Square
2.基类的完全类型:
polygon_base::RegularPolygon
2.2 插件声明XML
上面的步骤使得我们的插件实例可以在加载它们所在的库后创建,但插件加载程序仍然需要一种方法来找到该库并知道在该库中引用什么。为此,我们还将创建一个XML文件,该文件与包清单中的一个特殊导出行一起,使ROS工具链可以获得有关插件的所有必要信息。
创建插件xml文件
polygon_plugins/plugins.xml
<library path="polygon_plugins"> <class type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon"> <description>This is a square plugin.</description> </class> <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon"> <description>This is a triangle plugin.</description> </class> </library>
注意:
1.library标记提供了一个库的相对路径,该库包含我们想要导出的插件。在ROS 2中,这只是库的名称。在ROS 1中,它包含前缀lib,有时包含lib/lib(即lib/libpolygon_plugins),但这里更简单。
2.class标记声明了一个我们想要从库中导出的插件。让我们来了解一下它的参数:
type:插件的完全限定类型,对我们来说就是,polygon_plugins::Square。
base_class: 插件的完全限定基类类型。对我们来说,这就是polygon_base::RegularPolygon。
description:插件及其功能的描述。
name:过去有一个name属性,但现在不再需要它了。
2.3 CMake Plugin 声明
最后通过CMakeLists.txt来导出你的插件。与ROS1不同,ros1是通过package.xml。
在CMakeLists.txt中添加且在find_package(pluginlib REQUIRED)之后
pluginlib_export_plugin_description_file(polygon_base plugins.xml)
pluginlib_export_plugin_description_file命令的参数:
1.带有基类的包:polygon_base
2.声明xml的相对路径:plugins.xml
3.使用插件
现在是时候使用插件了。这可以在任何包中完成,但这里我们将在基本包中完成。
编辑文件polygon_base/src/area_node.cpp
#include <pluginlib/class_loader.hpp> #include <polygon_base/regular_polygon.hpp> int main(int argc, char** argv) { // To avoid unused parameter warnings (void) argc; (void) argv; pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("polygon_base", "polygon_base::RegularPolygon"); try { std::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createSharedInstance("polygon_plugins::Triangle"); triangle->initialize(10.0); std::shared_ptr<polygon_base::RegularPolygon> square = poly_loader.createSharedInstance("polygon_plugins::Square"); square->initialize(10.0); printf("Triangle area: %.2f\n", triangle->area()); printf("Square area: %.2f\n", square->area()); } catch(pluginlib::PluginlibException& ex) { printf("The plugin failed to load for some reason. Error: %s\n", ex.what()); } return 0; }
ClassLoader是要理解的关键类,在class_loader.hpp头文件中定义:
它是用基类模板化的,即polygon_base::RegularPolygon。
第一个参数是基类的包名称的字符串,即polygon_base。
第二个参数是具有插件的完全限定基类类型的字符串,即polygon_base::RegularPolygon
有许多方法可以实例化类的实例。在本例中,我们使用共享指针。我们只需要使用插件类的完全限定类型调用createSharedInstance,在本例中为polygon_plugins::Square。
重要提示:定义该节点的polygon_base包不依赖于polygon_plugins类。插件将被动态加载,而不需要声明任何依赖项。此外,我们正在用硬编码的插件名称实例化类,但您也可以使用参数等动态地进行实例化
4、编译和运行
colcon build --packages-select polygon_base polygon_plugins ros2 run polygon_base area_node
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2016-06-03 无名管道通讯编程
2016-06-03 执行程序---system
2016-06-03 exit--进程退出;wait--进程等待;execl--执行程序
2016-06-03 创建一个子进程---vfork
2016-06-03 多进程程序设计
2016-06-03 进程控制理论
2016-06-03 时间编程