ROS pluginlib 插件

ROS插件机制

简介

pluginlib是一个C++库,用于从ROS包中加载和卸载插件插件库。插件是动态加载的类,它们从运行时库(即共享对象,动态链接库)加载。pluginlib可以在任何时候打开包含导出类的库,而不需要应用程序事先了解库或包含类定义的头文件。插件可用于扩展/修改应用程序行为,而无需修改应用程序源代码。

编写和使用简单的插件

ROS wiki 参考:pluginlib/Tutorials/Writing and Using a Simple Plugin

本篇对ROS wiki中的Writing and Using a Simple Plugin插件机制教程翻译、逐步实现、讲解。

教程中创建了一个多边形的抽象类作为基类。
并实现了两个子类(矩形、三角形)作为插件提供实际功能(triangle_plugin、rectangle_plugin)——计算面积。

插件机制使用的大概步骤如下:

  1. 创建一个抽象基类,定义统一通用接口

如果基于现有基类实现插件,则不需要这个步骤。比如navigation中的costmap_2d包中已经提供了代价地图costmap_2d::Layer的基类。nav_core包中提供了nav_core::BaseGlobalPlannernav_core::BaseLocalPlannernav_core::RecoveryBehavior三个基类。分别用于全局路径规划、局部路径规划、复位行为加载。

  1. 创建plugin类,继承基类,实现统一的接口。
  2. 注册插件。
  3. 编译生成插件的动态链接库。
  4. 将插件加入ROS中。

下载pluginslib

sudo apt-get install ros-$ROS_DISTRO-pluginlib

创建示例包

创建pluginlib_tutorials_包同时添加roscpp``pluginlib依赖

cd ~/eduRobot_ws/src/
catkin_create_pkg pluginlib_tutorials_ roscpp pluginlib

或者直接用roboware studio在~/eduRobot_ws/src/目录下直接创建包,并添加roscpp``pluginlib依赖。

创建基类

我们将创建一个基类,所有插件都将继承该基类。对于这个例子,将创建两个不同种类的多边形RegularPolygon对象并使用它们,因此我们需要创建RegularPolygon抽象类作为基类。创建~/eduRobot_ws/src/pluginlib_tutorials_/include/pluginlib_tutorials_/polygon_base.h,polygon_base.h中内容如下:

#ifndef PLUGINLIB_TUTORIALS__POLYGON_BASE_H_
#define PLUGINLIB_TUTORIALS__POLYGON_BASE_H_

namespace polygon_base
{
  class RegularPolygon
  {
    public:
      virtual void initialize(double side_length) = 0;
      virtual double area() = 0;
      virtual ~RegularPolygon(){}

    protected:
      RegularPolygon(){}
  };
};
#endif

此处需要特别注意ROS中使用pluginlib类,要求构造函数没有参数。因此
需要使用initialize()函数来初始化对象。
initialize(double side_length)用于初始化并以多边形边长为参数
area()用于输出多边形面积

创建插件类

下面我们将在这个例子中将基于基类RegularPolygon创建两个插件类,第一个是Triangle,第二个是Square。

创建include/pluginlib_tutorials_/polygon_plugins.h文件。

#ifndef PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_
#define PLUGINLIB_TUTORIALS__POLYGON_PLUGINS_H_

#include <pluginlib_tutorials_/polygon_base.h>
#include <cmath>
namespace polygon_plugins
{
  class Triangle : public polygon_base::RegularPolygon
  {
    public:
      Triangle(){}
      void initialize(double side_length){
        side_length_ = side_length;
      }
      double area(){
        return 0.5 * side_length_ * getHeight();
      }
      double getHeight(){
        return sqrt((side_length_ * side_length_) - ((side_length_ / 2) * (side_length_ / 2)));
      }
    private:
      double side_length_;
  };//polygon_base::RegularPolygon

  class Square : public polygon_base::RegularPolygon
  {
    public:
      Square(){}
      void initialize(double side_length){
        side_length_ = side_length;
      }
      double area(){
        return side_length_ * side_length_;
      }
    private:
      double side_length_;
  };//polygon_base::RegularPolygon
};//polygon_plugins
#endif

Triangle类与Square类分别实现了等边三角形、正方形的初始化:initialize()、面积计算:area()Triangle类还有getHeight()函数,用于等边三角形的高的计算。

注册插件

到目前为止,我们刚刚创建了一些标准的C++类。现在,我们将开始执行pluginlib使用中特定的工作,
将Triangle和Square类声明为插件。
创建src/polygon_plugins.cpp内容粘如下:

#include <pluginlib/class_list_macros.h>
#include <pluginlib_tutorials_/polygon_base.h>
#include <pluginlib_tutorials_/polygon_plugins.h>

PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon)
PLUGINLIB_EXPORT_CLASS(polygon_plugins::Square, polygon_base::RegularPolygon)
#include <pluginlib/class_list_macros.h>

首先包含pluginlib宏,允许我们将类注册为插件。

PLUGINLIB_EXPORT_CLASS(polygon_plugins::Triangle, polygon_base::RegularPolygon)
PLUGINLIB_EXPORT_CLASS(polygon_plugins::Square, polygon_base::RegularPolygon)

在这里,我们将TriangleSquare类注册为插件。让我们来看看PLUGINLIB_EXPORT_CLASS()宏的参数:
以Triangle为例:

  1. 插件类的类型全称,在本例中为 polygon_plugins::Triangle
  2. 基类的类型全称,在本例中为 polygon_base::RegularPolygon

构建插件动态链接库

将以下内容添加到CMakeLists.txt文件中,以构建插件库:

include_directories(include)
add_library(polygon_plugins src/polygon_plugins.cpp)

然后,就可以用catkin_make编译工作空间。

将插件加入ROS中

上面的步骤使得插件的实例一旦加载就可以创建插件的实例对象,但是插件加载器仍然需要一种方法来查找该库并知道该库中的引用内容。为此,我们还将创建一个XML文件,并修改package.xml。该文件与package.xml一起,可以为ROS提供有关我们的插件的所有必要信息。
创建~/eduRobot_ws/src/pluginlib_tutorials_/polygon_plugins.xml,内容如下:

<library path="lib/libpolygon_plugins">
  <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
    <description>This is a triangle plugin.</description>
  </class>
  <class type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon">
    <description>This is a square plugin.</description>
  </class>
</library>
<library path="lib/libpolygon_plugins">

第一行中给出了包含插件类的动态链接库的相对路径。这个库就是上面在CMakeLists.txt中添加的编译产物。

<class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
    <description>This is a triangle plugin.</description>
  </class>

Triangle为例,<class>标签声明了我们希望从我们的库导出插件。参数如下:

  • type:插件的类型全称。对我们来说,这是polygon_plugins::Triangle
  • base_class:插件基类的类型全称。对我们来说,就是polygon_base::RegularPolygon
  • description:插件的描述。描述其作用。

完成polygon_plugins.xml文件的创建后,还需要将这个文件在package.xml中声明,将如下内容加入~/eduRobot_ws/src/pluginlib_tutorials_/package.xml

  <export>
    <pluginlib_tutorials_ plugin="${prefix}/polygon_plugins.xml"/>
  </export>

标签的名称pluginlib_tutorials_应该与插件的基类base_class所在的包对应。在这种情况下,基类和继承的插件类存在于同一个包中,但在大多数实际情况中,情况并非如此。
其中plugin属性应设置为指向上面步骤中创建的的XML文件。

这样,插件的创建、声明就完成了。
要验证操作是否正常,请首先catkin_make构建工作区,然后尝试运行以下rospack命令:

rospack plugins --attrib=plugin pluginlib_tutorials_

没有异常的话你会看到如下输出:

pluginlib_tutorials_ /home/<user_name>/eduRobot_ws/src/pluginlib_tutorials_/polygon_plugins.xml

使用插件

我们已经成功创建并导出了一些RegularPolygon插件,现在来使用它们。创建src/polygon_loader.cpp内容如下:

#include <pluginlib/class_loader.h>
#include <pluginlib_tutorials_/polygon_base.h>
int main(int argc, char** argv)
{
  pluginlib::ClassLoader<polygon_base::RegularPolygon> poly_loader("pluginlib_tutorials_", "polygon_base::RegularPolygon");
  try
  {
    boost::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createInstance("polygon_plugins::Triangle");
    triangle->initialize(10.0);

    boost::shared_ptr<polygon_base::RegularPolygon> square = poly_loader.createInstance("polygon_plugins::Square");
    square->initialize(10.0);

    ROS_INFO("Triangle area: %.2f", triangle->area());
    ROS_INFO("Square area: %.2f", square->area());
  }
  catch(pluginlib::PluginlibException& ex)
  {
    ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
  }
  return 0;
}

首先我们需要include pluginlib/class_loader.h头文件,以及polygon_base.h
我们创建一个名为poly_loader的ClassLoader对象,它对应的是插件的基类,我们将用它来加载插件。它需要两个参数,第一个是包含插件基类的包名,在我们的例子中是pluginlib_tutorials_。第二个是基类的完全限定类型,在我们的例子中是polygon_base::RegularPolygon

接下来可以通过createInstance()函数来关联对应插件,
细节可以查看plugin的API文档:
http://docs.ros.org/api/pluginlib/html/classpluginlib_1_1ClassLoader.html

boost::shared_ptr<polygon_base::RegularPolygon> triangle = poly_loader.createInstance("polygon_plugins::Triangle");
    triangle->initialize(10.0);

我们加载了polygon_plugins::Triangle插件。由于插件构造函数不能有参数,因此我们在构造对象之后立即调用initialize函数来初始化对象。

再在CMakeLists.txt中添加polygon_loader可执行文件:

add_executable(polygon_loader src/polygon_loader.cpp)
add_dependencies(polygon_loader ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(polygon_loader ${catkin_LIBRARIES})

接下来catkin_make编译后,运行polygon_loader:

rosrun pluginlib_tutorials_ polygon_loader

有如下输出:

[ INFO] [1554170430.374275315]: Triangle area: 43.30
[ INFO] [1554170430.374307944]: Square area: 100.00
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值