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 

结果
在这里插入图片描述

相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
相关文章
|
6月前
|
机器学习/深度学习 网络协议 中间件
[ROS2] --- ROS diff ROS2
[ROS2] --- ROS diff ROS2
151 0
|
3月前
|
机器人 Shell Python
ROS2教程05 ROS2服务
这篇文章是关于ROS2(Robot Operating System 2)服务的教程,涵盖了服务的概念、特性、命令行工具的使用,以及如何编写服务的服务器和客户端代码,并提供了测试服务通信机制的示例。
97 4
ROS2教程05 ROS2服务
|
3月前
|
存储 自然语言处理 机器人
ROS2教程06 ROS2行动
这篇文章是关于ROS2(Robot Operating System 2)行动(Action)通信机制的教程,包括行动的概念、特点、命令行工具的使用,以及如何编写行动的客户端和服务器代码,并介绍了如何测试行动通信。
116 4
ROS2教程06 ROS2行动
|
3月前
|
算法 数据可视化 机器人
ROS2教程01 ROS2介绍
本文是ROS2(机器人操作系统的下一代)的介绍教程,内容包括ROS2的诞生背景、核心功能、特点、框架以及与ROS1的比较。文章涵盖了ROS2的通信系统、框架和工具、生态系统、全球性社区支持、完全开源、跨平台特性、多机协同能力、实时系统支持和更强的稳定性。此外,还提供了ROS2架构的详细介绍资源链接,适合对ROS2感兴趣的读者学习和了解。
171 1
|
3月前
|
传感器 自然语言处理 机器人
ROS2教程03 ROS2节点
本文是关于ROS2(机器人操作系统2)节点的教程,涵盖了节点的概念、特性、使用方法,以及如何编写、测试和使用ROS2节点相关的命令行工具。文章介绍了节点的独立性、任务执行、跨硬件分布和多语言编写能力。详细解释了如何启动节点、查看节点信息、编写节点代码(包括面向过程和面向对象的方法),以及如何为功能包添加依赖和入口点。此外,还探讨了重映射节点名称和使用节点命令行工具的方法,如 `ros2 node info` 和 `ros2 node list`。适合已安装ROS2 Humble和Ubuntu 22.04操作系统,并具有Shell基础知识的读者学习。
102 1
|
3月前
|
XML 数据可视化 机器人
08 ROS的其他常见工具
本文概述了ROS(机器人操作系统)中的一些常见工具包,包括rqt工具箱、Rviz三维可视化工具、Gazebo物理仿真环境和rosbag数据记录与回放工具的使用方法和功能。
48 0
|
6月前
[ROS2] --- ROS2安装
[ROS2] --- ROS2安装
219 0
|
Ubuntu Python
ROS Melodic Arbotix报错
ROS Melodic Arbotix报错
|
C++ Python
ROS学习-理解ROS节点
ROS学习-理解ROS节点
529 0
ROS学习-理解ROS节点
|
C++ Python
ROS快速入门第一讲——ROS的工作空间创建
ROS快速入门第一讲——ROS的工作空间创建
242 0
ROS快速入门第一讲——ROS的工作空间创建