2021SC@SDUSC
ROS源代码阅读(13)
这篇我们主要看一下插件机制(Plugin)
使用Plugin可以更加方便对一个节点、功能包进行修改,添加功能,而无需修改此节点、功能包的源代码,大大提升了代码复用的能力。
官方的解释是:pluginlib是一个C++库,用于从ROS包中加载和卸载插件。插件是从运行库(即共享对象、动态链接库)加载的动态可加载类。使用 pluginlib 时,您不必显式将其应用程序与包含类的库关联起来——相反, pluginlib 可以在任何时候打开包含导出类的库,而应用程序无需事先意识到库或包含类定义的标头文件。插件可用于扩展/修改应用程序行为,而无需应用程序源代码
ROS中应用Plugin的一个例子是在 move_base 节点中,通过插件机制选择不同的 Global planner Local planner 和 Recovery behavior,为什么要选择不同的planner? 因为plan的算法有很多种,常见的规划算法就有A*、Dj等,所以需要使用插件机制方便不同算法下的planner进行切换。
我们可以看Plugin的实现:
#include <pluginlib>/class_list_macros.h
PLUGINLIB_EXPORT_CLASS(plugin class, base class)
申明基类,确定接口,注意基类的构造函数的参数应为空,所以为了对基类进行初始化,应该使用 如 initialized()等函数进行类的初始化,若使用已有接口类,则省略此步骤,创建插件,插件多以类的形式出现,所以即创建插件类,该类需要继承基类,并覆盖基类的共有接口。插件注册,通过使用Pluginlib中的宏定义,对新创建的插件进行注册
include_directories(include)
add_library(my_plugins_lib src/my_plugins.cpp)
这里修改CMakeList文件,添加内容,构建插件动态链接库
<library path="lib/libmy_plugins_lib">
<class name="{plugin class name}" type="{plugin class type}" base_class_type="{base class type}">
<description>This is an example.</description>
</class>
</library>
编译文件,在devel/lib中生成库文件libmy_plugins_lib
添加插件描述文件 my_plugins.xml
<export>
<{base_class_package_name} plugin="${prefix}/my_plugins.xml"/>
</export>
为了使Pluginlib在ROS的所有功能包中查询到plugin,需要在每个功能包中指出该功能包导出了哪些plugin和哪些功能包使用这些plugin
<build_depend>base_class_package_name</build_depend>
<run_depend>base_class_package_name</run_depend>
在plugin所在功能包的package.xml中添加对基类的依赖
这样,一个plugin就实现了,下面我们看一下move_base对插件的使用
#include <pluginlib/class_loader.h>
#include <nav_core/base_local_planner.h> //base class
#include <nav_core/base_global_planner.h> //base class
...
pluginlib::ClassLoader<nav_core::BaseGlobalPlanner> bgp_loader_;
pluginlib::ClassLoader<nav_core::BaseLocalPlanner> blp_loader_;
pluginlib::ClassLoader<nav_core::RecoveryBehavior> recovery_loader_;
...
boost::shared_ptr<nav_core::BaseLocalPlanner> tc_;
boost::shared_ptr<nav_core::BaseGlobalPlanner> planner_;
...
bgp_loader_("nav_core", "nav_core::BaseGlobalPlanner"),
blp_loader_("nav_core", "nav_core::BaseLocalPlanner"),
recovery_loader_("nav_core", "nav_core::RecoveryBehavior"),
...
try {
planner_ = bgp_loader_.createInstance(global_planner);
planner_->initialize(bgp_loader_.getName(global_planner), planner_costmap_ros_);
}
catch (const pluginlib::PluginlibException& ex) {
ROS_FATAL("Failed to create the %s planner, are you sure it is properly registered and that the containing library is built? Exception: %s", global_planner.c_str(), ex.what());
exit(1);
}
try {
tc_ = blp_loader_.createInstance(local_planner);
ROS_INFO("Created local_planner %s", local_planner.c_str());
tc_->initialize(blp_loader_.getName(local_planner), &tf_, controller_costmap_ros_);
}
catch (const pluginlib::PluginlibException& ex) {
ROS_FATAL("Failed to create the %s planner, are you sure it is properly registered and that the containing library is built? Exception: %s", local_planner.c_str(), ex.what());
exit(1);
}
从上面可以看到,include pluginlib中的Classloader类,使用Classloader实例化一个基类对象,然后将my_loader指向插件对象,并初始化,这样就使用了插件。
对于插件的另一个例子参考ROS Global Planner