ROS:pluginlib

简介: 在计算机领域,插件是很常用的术语。插件是一种模块化的软件,可以在现有应用软件的基础上增加一些新的功能。

插件(plugin)介绍

1、插件定义:

在计算机领域,插件是很常用的术语。插件是一种模块化的软件,可以在现有应用软件的基础上增加一些新的功能。

2、插件的优点:

我们不需要再主应用程序中编写所有功能。相反我们只需要在主应用中建立一个软件架构,并接收新的插件。通过这种方法,我们可以任意扩展软件功能。

3、ROS中的插件(plugin):

我们也需要为自己的机器人应用安装一些插件,当我们开始为机器人开发复杂的ROS应用程序时,插件将是我们扩充应用功能的一个不错的选择。
ROS系统提供了pluginlib插件框架,该框架可以动态地加载或卸载插件。插件可以是一个库,也可以是一个类。pluginlib代表一组C++库,该库可以帮助我们编写插件,也可以在需要时加载或卸载某一插件。

plugin文件是一组运行时库,如共享对象库(.so)或动态链接库(.DLL)。它们是在不链接到主应用程序代码的情况下编译生成的。插件是与主应用软件没有任何依赖关系的独立实体。

插件的主要优点是我们可以在不对应用代码做太多修改的情况下扩展应用软件的功能。

下面使用pluginlib创建一个简单的插件,并且可以看到使用ROS pluginlib创建一个插件所涉及的所以步骤。

使用pluginlib创建一个简单的计算器应用。通过使用插件来增加计算器的各个功能。

4、插件机制使用的大概步骤

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

使用pluginlib为计算器应用创建插件

与编写一段简单的代码相比,使用插件创建一个计算器应用是一项略微烦琐的任务。然而,示例的主要目的是展示如何在不修改主应用代码的情况下为计算器添加新功能。

1、创建功能包 plugin_calculator

功能包依赖 主要依赖 pluginlib

$ catkin_create_pkg plugin_calculator pluginlib roscpp std_msgs

成功创建后如下
在这里插入图片描述
依赖pluginlib ,会在include文件夹下多创建一个功能包名字的文件夹

2、创建 calculator_base.h 头文件

在plugin_calculator/include/plugin_calculator文件下创建calculator_base.h头文件
该文件主要功能是 创建一个抽象类作为基类,所有插件都将继承该基类
声明插件中常用的函数或方法

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

#ifndef PLUGINLIB_CALCULATOR_CALCULTOR_BASE_H_
#define PLUGINLIB_CALCULATOR_CALCULTOR_BASE_H_

namespace calculator_base  //可以在此命名空间中添加更多类来扩展此基类的功能
{
  class calc_functions //声明 calc_functions 类 封装了 插件使用的函数
  {
    public:
    
        //实现的主要方法
      virtual void get_numbers(double number1, double number2) = 0;//检索到两个数字作为计算器的输入
      virtual double operation() = 0;//定义我们想要执行的数学运算
      
      virtual ~calc_functions(){}

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

3、创建calculator_plugins.h头文件

在plugin_calculator/include/plugin_calculator文件下创建calculator_plugins.h头文件
该文件的主要用途是定义计算器插件的完整功能

这些插件可以命名为Add、Sub、Mul、Div。

每个插件被定义为一个类,它继承了calculator_base.h头文件中的calc_functions类

#ifndef PLUGINLIB_CALCULATOR_CALCULTOR_PLUGINS_H_
#define PLUGINLIB_CALCULATOR_CALCULTOR_PLUGINS_H_
#include <pluginlib_calculator/calculator_base.h>//包含了 用于获取计算器的基本函数
#include <cmath>

namespace calculator_plugins 
{

  class Add : public calculator_base::calc_functions
  {
    public:
    Add()
    {
        number1_ = 0;
        number2_ = 0;
    }

    void get_numbers(double number1, double number2)//继承的函数 的定义  用于检索两个数字输入参数的计算
    {
        try{
            number1_ = number1;
            number2_ = number2;
           }
           
        catch(int e)
        {
        std::cerr<<"Exception while inputting numbers"<<std::endl;
        }
    }    

    double operation()  //继承的函数 的定义 执行想要的运算  执行加法运算
    {
        return(number1_+number2_);
    }

    private:
      double number1_;
      double number2_;

  };

//同理其它所有插件的定义

  class Sub : public calculator_base::calc_functions
  {
    public:
    Sub()
    {
        number1_ = 0;
        number2_ = 0;

    }

    void get_numbers(double number1, double number2)
    {
        try{

            number1_ = number1;
            number2_ = number2;
           }

        catch(int e)
        {
        std::cerr<<"Exception while inputting numbers"<<std::endl;
        }

    }    

    double operation()
    {

        return(number1_- number2_);
    }

    private:
      double number1_;
      double number2_;

  };


  class Mul : public calculator_base::calc_functions
  {
    public:
    Mul()
    {
        number1_ = 0;
        number2_ = 0;

    }

    void get_numbers(double number1, double number2)
    {
        try{

            number1_ = number1;
            number2_ = number2;
           }

        catch(int e)
        {
        std::cerr<<"Exception while inputting numbers"<<std::endl;
        }

    }    

    double operation()
    {

        return(number1_ * number2_);
    }

    private:
      double number1_;
      double number2_;

  };



  class Div : public calculator_base::calc_functions
  {
    public:
    Div()
    {
        number1_ = 0;
        number2_ = 0;

    }

    void get_numbers(double number1, double number2)
    {
        try{

            number1_ = number1;
            number2_ = number2;
           }

        catch(int e)
        {
        std::cerr<<"Exception while inputting numbers"<<std::endl;
        }

    }    

    double operation()
    {

        if(number2_ == 0)
            return(0);
        else        
            return(number1_ / number2_);
    }

    private:
      double number1_;
      double number2_;

  };
  
};
#endif

4、用calculator_plugins.cpp导出插件

到目前为止,创建了一些标准的C++类。现在,将开始执行pluginlib使用中特定的工作

为了动态地加载这个插件,我们必须使用一个特定的宏 PLUGINLIB_EXPORT_CLASS 来导出每个类。
这个宏必须存在于由插件类组成的任何CPP文件中。
已经定义了插件类,并且在这个文件中,仅定义宏语句即可。

在plugin_calculator/src文件夹下创建 calculator_plugins.cpp 文件

在 PLUGINLIB_EXPORT_CLASS 中,需要提供插件的类名和基类

#include <pluginlib/class_list_macros.h>
#include <pluginlib_calculator/calculator_base.h>
#include <pluginlib_calculator/calculator_plugins.h>

PLUGINLIB_EXPORT_CLASS(calculator_plugins::Add, calculator_base::calc_functions);//在 PLUGINLIB_EXPORT_CLASS 中,需要提供插件的类名和基类
PLUGINLIB_EXPORT_CLASS(calculator_plugins::Sub, calculator_base::calc_functions);
PLUGINLIB_EXPORT_CLASS(calculator_plugins::Mul, calculator_base::calc_functions);
PLUGINLIB_EXPORT_CLASS(calculator_plugins::Div, calculator_base::calc_functions);

其中

#include <pluginlib/class_list_macros.h>

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

5、用 calculator_loader.cpp 实现插件加载器

插件加载器节点实现加载每个插件,将数字输入到每个插件后从插件中获取结果
在plugin_calculator/src文件夹下创建 calculator_loader.cpp 文件

在下面有创建插件的描述文件xml,在本cpp中的
在这里插入图片描述
要与xml文件中的
在这里插入图片描述
一致。

//这些是加载插件必需的头文件
#include <boost/shared_ptr.hpp>
#include <pluginlib/class_loader.h>
#include <pluginlib_calculator/calculator_base.h>

int main(int argc, char** argv)
{

//pluginlib 提供了 ClassLoader 类 ,它位于class_loader.h文件中,用于在运行时加载类。
//需要为加载器和计算器基类提供一个名称作为参数
  pluginlib::ClassLoader<calculator_base::calc_functions> calc_loader("pluginlib_calculator", "calculator_base::calc_functions");

  try
  {
     //使用ClassLoader对象创建add类的实例  (pluginlib_calculator和上面的名称一致)
     //"pluginlib_calculator/Add" 与calculator_plugins.xml 中的一致
    boost::shared_ptr<calculator_base::calc_functions> add = calc_loader.createInstance("pluginlib_calculator/Add");//记住用法即可
    
    //提供输入并在插件中执行操作
    add->get_numbers(10.0,10.0);
    double result = add->operation();

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


  try
  {
    boost::shared_ptr<calculator_base::calc_functions> sub = calc_loader.createInstance("pluginlib_calculator/Sub");

    sub->get_numbers(10.0,10.0);
    double result = sub->operation();

    ROS_INFO("Substracted result: %.2f", result);
  }
  catch(pluginlib::PluginlibException& ex)
  {
    ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
  }




  try
  {
    boost::shared_ptr<calculator_base::calc_functions> mul = calc_loader.createInstance("pluginlib_calculator/Mul");
    mul->get_numbers(10.0,10.0);
    double result = mul->operation();

    ROS_INFO("Multiplied result: %.2f", result);
  }
  catch(pluginlib::PluginlibException& ex)
  {
    ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
  }




  try
  {
    boost::shared_ptr<calculator_base::calc_functions> div = calc_loader.createInstance("pluginlib_calculator/Div");
    div->get_numbers(10.0,10.0);
    double result = div->operation();

    ROS_INFO("Division result: %.2f", result);
  }
  catch(pluginlib::PluginlibException& ex)
  {
    ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
  }

  return 0;
}

6、创建插件描述文件:calculator_plugins.xml,将插件加入ROS中

生成计算器、加载器代码之后,下一步必须在名为Plugin Description Flie的XML文件中描述此软件包内的插件列表。插件描述文件包含了软件包所含插件的所有信息,例如类的名称、类的类型、基类等。

插件描述文件是一个基于插件的软件包的重要文件,因为它有助于ROS系统的自动查找、加载插件。它还包含诸如插件描述之类的信息。

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

<library path="lib/libpluginlib_calculator"> <!-- 插件的库路径  --> <!-- 怎么确定的还不知道???  -->
   
    <!-- 类名(新的名称)  -->    <!-- 类的类型 (实际的类 与calculator_plugins.h中的类名一致) -->    <!-- 基类  -->
  <class name="pluginlib_calculator/Add" type="calculator_plugins::Add" base_class_type="calculator_base::calc_functions">
    <description>This is a add plugin.</description>  <!-- 描述  -->
  </class>

  <class name="pluginlib_calculator/Sub" type="calculator_plugins::Sub" base_class_type="calculator_base::calc_functions">
    <description>This is a sub plugin.</description>
  </class>

  <class name="pluginlib_calculator/Mul" type="calculator_plugins::Mul" base_class_type="calculator_base::calc_functions">
    <description>This is a mul plugin.</description>
  </class>

  <class name="pluginlib_calculator/Div" type="calculator_plugins::Div" base_class_type="calculator_base::calc_functions">
    <description>This is a div plugin.</description>
  </class>

</library>

7、使用ROS软件包系统注册插件

为了让pluginlib在ROS系统查找到所有基于插件的软件包。我们应该在package.xml中导出插件描述文件。如果不包含此插件,ROS系统将无法找到软件包内的插件。

在package.xml中添加export标签,如下:

  <export>
    <pluginlib_calculator plugin="${prefix}/calculator_plugins.xml" />
  </export>

无论在编译还是在运行时,当前这个软件包应该直接依赖其自身

标签的名称pluginlib_calculator 应该与插件的基类base_class所在的包对应。
其中plugin属性应设置为指向上面步骤中创建的的XML文件。

8、编辑CMakeLists.txt文件,构建插件动态链接库

与其它普通ROS节点的另一个区别就是CMakeLists.txt文件中包含的编译指令。

为了编译计算器插件和加载器节点,要在CMakeLists.txt 文件中加入下面几行,以构建插件库

## pluginlib_tutorials library
add_library(pluginlib_calculator src/calculator_plugins.cpp)
target_link_libraries(pluginlib_calculator ${catkin_LIBRARIES})

## polygon_loader executable
add_executable(calculator_loader src/calculator_loader.cpp)
target_link_libraries(calculator_loader ${catkin_LIBRARIES})

至此完成了所有配置
可以进行 编译

catkin_make

9、查询软件包中的插件列表

下面命令将查询软件包中的插件列表

$ rospack plugins --attrib=plugin pluginlib_calculator 

结果
在这里插入图片描述
其中
在这里插入图片描述
和xml中的
在这里插入图片描述
一致

10、运行插件加载器

启动roscore后 使用如下命令执行 calculator_loader

$ rosrun pluginlib_calculator calculator_loader 

结果
在这里插入图片描述

相关实践学习
Docker镜像管理快速入门
本教程将介绍如何使用Docker构建镜像,并通过阿里云镜像服务分发到ECS服务器,运行该镜像。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
相关文章
|
2月前
|
机器学习/深度学习 网络协议 中间件
[ROS2] --- ROS diff ROS2
[ROS2] --- ROS diff ROS2
100 0
|
2月前
[ROS2] --- ROS2安装
[ROS2] --- ROS2安装
53 0
|
10月前
|
Ubuntu Python
ROS Melodic Arbotix报错
ROS Melodic Arbotix报错
|
10月前
|
Ubuntu 算法 网络协议
ROS简介
机器人是一个系统工程,它涉及机械、电子、控制、通信、软件等诸多学科。以前,开发一个机器人需要设计机械、画电路板、写驱动程序、设计通信架构、组装集成、调试、以及编写各种感知决策和控制算法,每一个任务都需要花费大量的时间。然而随着技术进步,机器人产业分工开始走向细致化、多层次化,如今的电机、底盘、激光雷达、摄像头、机械 臂等元器件都由不同厂家专门生产,社会分工加速了机器人行业的发展。而各个部件的集成就需要一个统一的软件平台,在机器人领域,这个平台就是机器人操作系统 ROS。
|
C++ Python
ROS学习-理解ROS节点
ROS学习-理解ROS节点
206 0
ROS学习-理解ROS节点
|
Ubuntu
ROS学习-了解ROS的文件结构
ROS学习-了解ROS的文件结构
118 0
|
Ubuntu 开发工具
ROS学习-使用rosed来编辑ROS中的文件
ROS学习-使用rosed来编辑ROS中的文件
149 0
|
C++ Python
ROS快速入门第一讲——ROS的工作空间创建
ROS快速入门第一讲——ROS的工作空间创建
198 0
ROS快速入门第一讲——ROS的工作空间创建
|
Ubuntu 数据可视化 机器人
加快你ROS安装的一篇文章
加快你ROS安装的一篇文章
192 0
加快你ROS安装的一篇文章
|
Shell Python
ROS小技巧
ROS小技巧
123 0