class_loader
是一个独立于ROS的软件包,用于在运行时加载插件。插件是从运行时库(即共享对象、动态链接库)中加载的可动态加载的类。使用class_loader
,不必将他们的应用程序与包含类的库显式链接——相反,class_loader
可以在任何时候打开一个包含导出类的库,而无需应用程序事先了解该库或包含类定义的头文件。插件可用于扩展/修改应用程序行为,而无需应用程序源代码。
class_loader
只能加载被注册为可导出的插件,所以第一步是#include <class_loader/register_macro.hpp> CLASS_LOADER_REGISTER_CLASS(Derived, Base)
- 运行时加载插件
#include <class_loader/class_loader.h> //class_loader headers #include "MyBase.h" //Defines class MyBase int main() { class_loader::ClassLoader loader("libMyLibrary.so"); //Instantiate a class_loader::ClassLoader object passing the path and name of the library to open std::vector<std::string> classes = loader.getAvailableClasses<MyBase>(); //Query the class for exported classes which have an interface defined by some base class (MyBase in the example) for(unsigned int c = 0; c < classes.size(); ++c) { //Create/destroy objects of said exported classes boost::shared_ptr<MyBase> plugin = loader.createInstance<MyBase>(classes[c]); plugin->someMethod(); //call some function //'plugin' will automatically be deleted when it goes out of scope } }
pluginlib
是一个 基于class_loader
的ROS软件包,用于从 ROS 包中加载和卸载插件。
创建基类包
ros2 pkg create --build-type ament_cmake polygon_base --dependencies pluginlib --node-name area_node
打开dev_ws/src/pokygon_base/include/polygon_base/regular_polygon.hpp
编辑
创建一个抽象类RegularPolygon
#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
基类必须提供无参构造函数,所以关于多边形的边长没有通过构造函数而是通过单独编写的initialize函数传参。
我们需要将此header提供给其他类,因此打开dev_ws/src/polygon_base/CMakeLists.txt
编辑
所以在ament_target_dependencies
之后添加以下几行
install(
DIRECTORY include/
DESTINATION include
)
并且在ament_package
命令之前添加
ament_export_include_directories(
include
)
创建插件包
$ ros2 pkg create --build-type ament_cmake polygon_plugins --dependencies polygon_base pluginlib --library-name polygon_plugins
注册/导出 插件
为了允许插件类能够被动态加载,必须通过PLUGIN_EXPORT_CLASS
将其标记为一个导出类(需要包含头文件pluginlib/class_list_macros.h
)。
插件源代码
打开dev_ws/src/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特殊 的部分是最后三行,它调用了一些将类注册为实际插件的宏。PLUGINLIB_EXPORT_CLASS
宏的参数:
- 插件类的完全限定类型,在本例中为
polygon_plugins::Square
. - 基类的完全限定类型,在本例中为
polygon_base::RegularPolygon
.
编译插件库
在CMakeLists.txt文件中添加库
add_library(polygon_plugins src/polygon_plugins.cpp)
target_include_directories(polygon_plugins PUBLIC
<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
<INSTALL_INTERFACE:include>)
target_link_libraries(polygon_plugins ${catkin_LIBRARIES})
如果是ROS2可以通过以下指令代替target_link_libraries()
ament_target_dependencies(polygon_plugins
polygon_base
pluginlib
)
插件描述文件
插件描述文件是存储插件所有重要信息的机器可读文件,包含插件所在库的名字,插件的名字,插件的类型及其继承的基类名。
ROS1中功能包生成的库保存在devel/lib
目录下,ROS2中功能包生存的库保存在install/<package-name>/lib
目录下。
创建dev_wc/src/polygon_plugins/plugins.xml
<!--ROS1 <library path="lib/libpolygon_plugins"> -->
<library path="polygon_plugins"><!--ROS2-->
<class name="polygon_plugins/Square" type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon">
<description>This is a square plugin.</description>
</class>
<class name="polygon_plugins/Triangle" type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
<description>This is a triangle plugin.</description>
</class>
</library>
在ROS包系统中注册插件
为了让pluginlib能够找到ROS包中的所有可用的插件,每个包必须显式指定其导出的插件和包含这些插件的库。
ROS2是通过CMakeLists.txt
导出插件。这是和ROS1不同的地方,ROS1是通过package.xml
导出的。
ROS1
在package.xml中添加以下语句,其中polygon_base
是基类的包名,plugin= xml描述文件
<export>
<polygon_base plugin="${prefix}/plugins.xml">
</export>
为了使上述导出命令正常工作,提供插件的包必须直接依赖于包含插件基类的包,还要在package.xml
文件中加油
<build_depend>polygon_base</build_depend>
<run_depend>polygon_base</run_depend>
或
<depend>polygon_base</depend>
ROS2
在dev_ws/src/polygon_plugins/CMakeLists.txt
文件的find_package(pluginlib REQUIRED)
之后添加:
pluginlib_export_plugin_description_file(polygon_base plugins.xml)
install(
TARGETS polygon_plugins
EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
在ament_packge()
之前,添加
ament_export_libraries(
polygon_plugins
)
ament_export_targets(
export_${PROJECT_NAME}
)
使用插件
pluginlib
提供了一个在class_loader.h/hpp
头文件中的ClassLoader
类,方便快速使用插件类。
//头文件
#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是关键
- 它是用基类模板化的
- 第一个参数是基类包名的字符串,即
polygon_base
- 第二个参数是基类插件的完全限定类型,即
polygon_base::RegularPolygon
有多种方法可以实例化类的实例,一种方法是使用createSharedInstance()
创建实例并获取共享指针。