ROS中的pluginlib使用总结

原文里面有几个小错误,导致package编译出错,这里进行了修改(红色部分),本文是作者亲测通过的。后面附有代码的下载链接。

1 简介

pluginlib是一个C++库,可以实现为一个ROS包动态的加载和卸载插件。这里的插件通常是一些功能类,且以运行时可动态加载的库(如共享对象,动态链接库)的形式存在。借助pluginlib的帮助,用户不必关心自己的应用程序该如何链接包含自己想使用的的class的库(如定义class的头文件在哪里,如何定义的),因为pluginlib会在你调用时自动打开你需要的插件库(Note:需要提前将插件库注册到pluginlib)。使用插件来扩展或者修改应用程序的功能非常方便,不用改动源码重新编译应用程序,通过插件的动态加载即可完成功能的扩展和修改。


2 插件编写

    pluginlib利用了C++多态的特性,不同的插件只要使用统一的接口,便可以替换使用。这样用户通过调用在插件中实现的统一的接口函数,不需要更改程序,也不需要重新编译,更换插件即可实现功能修正。

    利用pluginlib编写插件的方法大致包括如下四步:

  1. 创建插件基类,定义统一接口(如果为现有接口编写插件,则跳过该步)
  2. 编写插件类,继承插件基类,实现统一接口
  3. 导出插件,并编译为动态库
  4. 将插件加入ROS系统,使其可识别和管理
2.1 创建插件基类

首先,创建工作空间,创建后如下所示



其次,开始编写插件基类,基类所在头文件polygon_base.h放在include/my_plugin_test下即可,

  1. #ifndef POLYGON_BASE_H_  
  2. #define POLYGON_BASE_H_  
  3. namespace polygon_base  
  4. {  
  5.     class Polygon{   
  6.         public:  
  7.             Polygon() {};  
  8.             virtual ~Polygon() {};  
  9.             virtual void init(float side_len) = 0;  
  10.             virtual float area() = 0;  
  11.       };  
  12. };  
  13. #endif

2.2 创建插件类

所插件类的头文件polygon_plugin.h在放在include/my_plugin_test目录下。

  1. #ifndef POLYGON_PLUGIN_H_  
  2. #define POLYGON_PLUGIN_H_  
  3. #include <cmath>  
  4. #include <my_plugin_test/polygon_base.h>  
  5.   
  6. namespace polygon_plugin{  
  7.   
  8.     class Square: public polygon_base::Polygon{  
  9.         public:  
  10.             Square() {};  
  11.             virtual ~Square() {};  
  12.             virtual void init(float side_len)  
  13.             {  
  14.                 this->side_len = side_len;  
  15.             }  
  16.             virtual float area()  
  17.             {  
  18.                 return (side_len * side_len);  
  19.             }  
  20.         private:  
  21.             float side_len;  
  22.     };  
  23.   
  24.     class Triangle: public polygon_base::Polygon{  
  25.         public:  
  26.             Triangle() {};  
  27.             virtual ~Triangle() {};  
  28.             virtual void init(float side_len)  
  29.             {  
  30.                 this->side_len = side_len;  
  31.             }  
  32.             virtual float area()  
  33.             {  
  34.                 return 0.5 * (side_len * ( sqrt( (side_len * side_len) - (0.5 * side_len)*(0.5 * side_len) ) ) );  
  35.             }  
  36.   
  37.         private:  
  38.             float side_len;  
  39.     };  
  40. };  
  41. #endif  

3 导出插件,并编译为动态链接库

3.1导出插件

   利用 pluginlib 库提供的宏操作注册插件,并且编译为动态链接库。

  在src目录下添加polygon_plugin.cpp,

[plain] view plain copy
  1. #include <pluginlib/class_list_macros.h>  
  2. #include <my_plugin_test/polygon_base.h>  
  3. #include <my_plugin_test/polygon_plugin.h>  
  4.   
  5.  //mark Square and Triangle as the exported class 
  6. PLUGINLIB_EXPORT_CLASS(polygon_plugin::Triangle, polygon_base::Polygon)  
  7. PLUGINLIB_EXPORT_CLASS(polygon_plugin::Square, polygon_base::Polygon) 

如果要实现class可动态加载,必须要将其标记为可导出的class。通过特定的宏PLUGINLIB_EXPORT_CLASS可以完成导出,该宏通常放置于cpp文件的底部。这个宏第一个参数是插件类全名(含namespace),第二个参数是插件基类全名(含namespace)。

3.2 编译为动态链接库

    要将插件编译为动态链接库,需要相应修改CMakeLists.txt文件,添加如下几行:

[plain] view plain copy
  1. include_directories(  
  2.        include  
  3.        ${catkin_INCLUDE_DIRS}  
  4.  )  
  5.        
  6.  ## Declare a C++ library  
  7.  add_library(polygon_plugin  
  8.        src/polygon_plugin.cpp  
  9.  ) 

4 将插件加入ROS系统,使其可识别和管理

4.1 创建插件描述文件

   插件描述文件是一个XML格式的文件,用于存储插件的重要信息(如,插件库路径,插件名称,插件类类型,插件基类类型)。

  我们在my_plugin_test目录下,创建名为polygon_plugin.xml的文件,

[html] view plain copy
  1. <library path="lib/libpolygon_plugin">  
  2.          <class type="polygon_plugin::Triangle" base_class_type="polygon_base::Polygon">  
  3.              <description>This is a triangle plugin.</description>  
  4.          </class>  
  5.          <class type="polygon_plugin::Square" base_class_type="polygon_base::Polygon">  
  6.              <description>This is a square plugin.</description>  
  7.          </class>  
  8. </library>  

这里标签library和其属性path一起定义了主package相对于插件库的路径,一个插件库可以包含多个不同的插件类(如这里是2个插件类)。

这里的标签class用以描述插件库中的插件类,属性type指定插件类的类型(必须全名),属性base_class_type指定插件基类的类型(必须全名),属性description描述插件类的功能。

注意:插件描述文件还有一个标签class_libraries这里没有使用,其可以实现在一个插件描述文件包含多个库,该标签无属性。


4.2 注册插件到ROS系统

    为确保pluginlib可以查到ROS系统所有插件,定义插件的package必须显式的指定哪个包导出了什么插件。

这通常在package.xml文件中定义,

[html] view plain copy
  1. <export>  
  2.          <my_plugin_test plugin="${prefix}/polygon_plugin.xml" />  
  3. </export>  

这里标签my_plugin_test是定义插件基类的package名称,属性plugin是前面定义的插件描述符文件。

注意:如果插件类与基类不在同一package,为了使插件的export生效,还必须添加对插件基类所在package的依赖。如果是在同一个package,则不能加下面两句话,否则会报错!!!

 <build_depend>my_plugin_test</build_depend>
 <exec_depend>my_plugin_test</exec_depend>

5 check插件是否在ROS下可以查看

    在catkin_make执行成功之后,source  devel/setup.bash,然后运行如下命令如果能正确看到输出polygon_plugin.xml则ok。

   rospack plugins --attrib=plugin my_plugin_test


6 调用插件

6.1 在src目录下创建my_plugin_loader.cpp

  1. #include <ros/ros.h>  
  2. #include <pluginlib/class_loader.h>  
  3. #include <my_plugin_test/polygon_base.h>  
  4.   
  5.   
  6. int main(int argc, char ** argv)  
  7. {  
  8.     ros::init(argc, argv, "my_plugin_loader");  
  9.     ros::NodeHandle nh;  
  10.   
  11.     float side_len = 5.0;  
  12.      
  13.     std::string param_name = "polygon_plugin";  
  14.     std::string plugin_class;  
  15.       
  16.     if(!nh.getParam(param_name.c_str(), plugin_class))  
  17.     {  
  18.         ROS_ERROR("can't get param");  
  19.         return 0;  
  20.     }  
  21.   
  22.  /** 
  23.      * Define plugin loader object(polygon_loader) for loading my plugin. 
  24.      * param1: the path of plugin package, param2:the base class of plugin class with full name 
  25.      */
  26.     pluginlib::ClassLoader<polygon_base::Polygon> polygon_loader("my_plugin_test""polygon_base::Polygon");  
  27.       
  28.     try  
  29.     {  
  30.        /*Based on input param to create the corresponding plugin instance by ClassLoader*/
  31.         boost::shared_ptr<polygon_base::Polygon> polygon_cal = polygon_loader.createInstance(plugin_class);  
  32.   
  33.         polygon_cal->init(side_len);  
  34.   
  35.         ROS_INFO("plugin class is %s, area is %f",plugin_class.c_str(), polygon_cal->area());  
  36.     }  
  37.   
  38.     /*catch exception ClassLaoder object(polygon_loader) exception*/
  39.     catch(pluginlib::PluginlibException& ex)  
  40.     {  
  41.         ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());  
  42.     }  
  43.   
  44.     return 0;  
  45. }

6.2 修改CMakeLists.txt,添加
[plain] view plain copy
  1. ## Declare a C++ executable  
  2. add_executable(my_plugin_loader src/my_plugin_loader.cpp)
  3.   
  4. ## Add cmake target dependencies of the executable  
  5. ## same as for the library above  
  6. # add_dependencies(my_plugin_test_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})  
  7.   
  8. ## Specify libraries to link a library or executable target against  
  9. target_link_libraries(my_plugin_loader  
  10.   ${catkin_LIBRARIES}  
  11. )

6.3 创建启动文件

在my_plugin_test目录下创建launch文件夹添加文件my_plugin_loader.launch

[html] view plain copy
  1. <launch>  
  2.     <!--plugin select-->  
  3.     <!--<param name="polygon_plugin" value="polygon_plugin::Square"/> -->  
  4.     <param name="polygon_plugin" value="polygon_plugin::Triangle"/>  
  5.     <node name="my_plugin_loader" pkg="my_plugin_test" type="my_plugin_loader" output="screen"/>  
  6. </launch>

最后目录工作空间目录结构如下所示:


6.4 测试

方式一:通过启动文件

roslaunch my_plugin_test my_plugin_loader.launch

输出:
... logging to /home/siriansu/.ros/log/f05ccba8-a4a7-11e6-9012-001fc69be782/roslaunch-siriansu-nuc-24328.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://siriansu-nuc:40746/

原文地址:http://blog.csdn.net/x_r_su/article/details/53117572


SUMMARY
========

PARAMETERS
 * /polygon_plugin: polygon_plugin::T...
 * /rosdistro: kinetic
 * /rosversion: 1.12.5

NODES
  /
    my_plugin_loader (my_plugin_test/my_plugin_loader)

ROS_MASTER_URI=http://localhost:11311

core service [/rosout] found
process[my_plugin_loader-1]: started with pid [24346]
[ INFO] [1478842570.904729245]: plugin class is polygon_plugin::Triangle, area is 10.825317
[my_plugin_loader-1] process has finished cleanly
log file: /home/siriansu/.ros/log/f05ccba8-a4a7-11e6-9012-001fc69be782/my_plugin_loader-1*.log
all processes on machine have died, roslaunch will exit
shutting down processing monitor...
... shutting down processing monitor complete
done


方式二:命令行方式

rosparam set polygon_plugin polygon_plugin::Square

rosrun my_plugin_test my_plugin_loader

输出:

[ INFO] [1478842687.012935017]: plugin class ispolygon_plugin::Square, area is25.000000

rosparam set polygon_plugin polygon_plugin::Triangle

rosrun my_plugin_test my_plugin_loader

输出:

[ INFO] [1478844657.917205466]: plugin class ispolygon_plugin::Triangle, area is10.825317
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值