创建和使用插件

class_loader是一个独立于ROS的软件包,用于在运行时加载插件。插件是从运行时库(即共享对象、动态链接库)中加载的可动态加载的类。使用class_loader,不必将他们的应用程序与包含类的库显式链接——相反,class_loader可以在任何时候打开一个包含导出类的库,而无需应用程序事先了解该库或包含类定义的头文件。插件可用于扩展/修改应用程序行为,而无需应用程序源代码。

  1. class_loader只能加载被注册为可导出的插件,所以第一步是
    #include <class_loader/register_macro.hpp>
    CLASS_LOADER_REGISTER_CLASS(Derived, Base)
    
  2. 运行时加载插件
    #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
      }
    }
    

详细参考class_loader wiki

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宏的参数:

  1. 插件类的完全限定类型,在本例中为polygon_plugins::Square.
  2. 基类的完全限定类型,在本例中为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()创建实例并获取共享指针。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Shilong Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值