Pluginlib Understanding and Example

Source: Internet
Author: User
Tags deprecated
1. Written at the front

Because in a source code, about in the Ros platform concrete implementation, uses the plugin. In order to understand more deeply what is a ghost, so the pluginlib to do some learning, the following content, most of the site is able to find, is a comprehensive wiki on the introduction, as well as from the source to see the content and their own attempts. Hope to help a little bit for everyone.

Pluginlib is a library implemented using C + + to dynamically load or unload plugin in the Ros package. Plugin A class that can be loaded dynamically from a runtime, such as a shared object, a dynamic-link library, that satisfies some conditions. Plugin has an advantage in extending or modifying the application's behavior, do not need to know the source of the original class, perhaps you are writing code, also do not know which plugin you will use, but at runtime through the parameter loading to determine the specific plugin, in the following a little related examples, Can experience the use of plugin feeling (feel very general, ha ha ...). 2. Example

Imagine a scene, assuming that there is now a Ros pack polygon_interface_package containing a polygon base class, and there are two different polygon, one of which is rectangle, that exists in Rectangle_ The plugin package, the other one is triangle, exists in the Triangle_plugin package. We want both rectangle and triangle to be supported by the system. 2.1 registering/exporting a Plugin

In order to be able to load a class dynamically, the class must be a labeled and imported into a class of the system. This process needs to be done using a macro pluginlib_export_class. Typically, this macro is placed at the end of the implementation file (. cpp). Similar to the following:

    #include <pluginlib/class_list_macros.h>
    #include <polygon_interface_package/polygon.h>
    # Include <rectangle_package/rectangle.h>

    //Specific parameter meanings, see below
    pluginlib_export_class (rectangle_namespace:: Rectangle, Polygon_namespace::P Olygon)
2.2 The Plugin Description File

Each plugin needs to be described by a plugin file, which is an XML-formatted machine-readable file. Contains some necessary information, such as the path, plug-in type, plug-in name, and so on. Similar to the following:

    <library path= "Lib/librectangle" >
      <class type= "Rectangle_namespace::rectangle" Polygon_namespace::P olygon ">
      <description> this is
      a rectangle plugin
      </description>
      </class>
    </library>

For more detailed information, you can view: Http://wiki.ros.org/pluginlib/PluginDescriptionFile 2.3 Register Plug-ins with Ros Package system

In order for Pluginlib to be able to query all available plug-ins, you need to add the export tag block to the package.xml file. Similar to the following:

    <export>
      <polygon_interface_package plugin= "${prefix}/rectangle_plugin.xml"/>
    </export >

A noteworthy place to add the following information to the build and run dependencies in order for the export command to execute correctly:

    <build_depend>polygon_interface_package</build_depend>
    <run_depend>polygon_interface_ Package</run_depend>
2.4 Querying ROS Package System for Available Plugins

You can use the Rospack command to query for a very simple command, similar to the following:

    $ rospack Plugins--attrib=plugin Nav_core

All plug-ins imported in the Nav_core package will be returned. 2.5 Step by step 2.5.1 Create plugin

First of all, it is of course to create a Ros Package in your own workspace to try. Enter the following command, in turn.

    $ ROSCD
    $ CD. /SRC
    $ catkin_create_pkg plugin_test roscpp pluginlib

OK, now it's time to start writing code. Create a new file in the Include/plugin_test folder Polygon_base.h, and then copy the following code to affirm our base class.

    #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

To create a new file in the Include/plugin_test folder Polygon_plugins.h, copy the following code to confirm our plugin:

    #ifndef pluginlib_tutorials__polygon_plugins_h_ #define Pluginlib_tutorials__polygon_plugins_h_ #include <p Lugin_test/polygon_base.h> #include <cmath> namespace Polygon_plugins {class Triangle:publi C Polygon_base::regularpolygon {Public:triangle () {} void Initialize (double side_lengt
          h) {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_;

      }; 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_;
    };
    }; #endif

Create the file Polygon_plugins.cpp in the SRC folder and copy the code below to register our plugin.

    #include <pluginlib/class_list_macros.h>
    #include <plugin_test/polygon_base.h>
    #include < Plugin_test/polygon_plugins.h>

    Pluginlib_export_class (Polygon_plugins::triangle, Polygon_base:: Regularpolygon)
    Pluginlib_export_class (Polygon_plugins::square, Polygon_base::regularpolygon)

In the CMakeLists.txt document, add the following add_library statement. It is noteworthy that in the CMakeLists.txt file, you need to add the Include directory in Include_directories, otherwise the two header files we wrote earlier will not be found.

    Include_directories (
      ${catkin_include_dirs}
      INCLUDE
    )

    ... Add_library (Polygon_plugins src/polygon_plugins.cpp)

As mentioned earlier, I also need to edit information about Plug-ins, in the Plugin_test home directory, create a polygon_plugins.xml file, copy the following entry:

    <library path= "Lib/libpolygon_plugins" >
      <class type= "Polygon_plugins::triangle" 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>

Add the following in the export tag block in the package.xml file in the same directory:

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

Run the following instructions on the command line, and the corresponding output information is as follows:

    $ rospack Plugins--attrib=plugin plugin_test
    plugin_test/home/silence/workspace/catkin_ws/src/plugin_test/ Polygon_plugins.xml

If you get a similar output, it means everything is fine. 2.5.2 Use plugin 2.5.2.1 Basic Usage Experience

Under the SRC directory, create a new Polygon_loader.cpp file for testing and copy the following:

 #include <pluginlib/class_loader.h> #include <plugin_test/polygon_base.h> int main (int argc, char** argv) {pluginlib::classloader<polygon_base::regularpolygon> Poly_loader ("plug

      In_test "," Polygon_base::regularpolygon "); try {boost::shared_ptr<polygon_base::regularpolygon> triangle = poly_loader.createinstance ("Polygon_p
        Lugins::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 ());  The catch (pluginlib::P luginlibexception& ex) {ros_error ("The plugin failed to load for some reason.
      Error:%s ", Ex.what ());
    return 0; }

and add the following statement to the CMakeLists.txt, then $ catkin_make.

    Add_executable (Polygon_loader src/polygon_loader.cpp)
    target_link_libraries (Polygon_loader ${catkin_ Libraries})

After the compilation succeeds, the running node should get similar output as follows.

    $ rosrun plugin_test Polygon_loader [
    info] [1477584281.637794959]: Triangle area:43.30
    [INFO] [ 1477584281.637923253]: Square area:100.00

The above code is relatively simple, do not do too much explanation. 2.5.2.2 Another kind of experience

In the SRC directory, create a new Polygon_loader_v1.cpp file for testing and copy the following:

    #include <pluginlib/class_loader.h> #include <plugin_test/polygon_base.h> #include <ros/ros.h&
    Gt #include <sstream> #include <string> #include <vector> template <typename t> std:
        : String to_string (T value) {Std::ostringstream OS;
        OS << value;
    return Os.str ();
      int main (int argc, char** argv) {ros::init (argc, argv, "POLYGON_LOADER_V1");

      Ros::nodehandle N_;
      Std::vector<std::string> Class_names;
      int class_index = 1;
        while (true) {std::string class_name;
        std::string param_name = std::string ("Polygon_loader_v1/derive_class_" + to_string (class_index++));
        if (!n_.getparam (Param_name.c_str (), class_name)) break;
      Class_names.push_back (class_name);
        } if (Class_names.empty ()) {ros_error ("ROS parameter ERROR");
      return 0; } pluginlib::classloader<pOlygon_base::regularpolygon> Poly_loader ("Plugin_test", "Polygon_base::regularpolygon"); try {for (Class_index = 0; Class_index < class_names.size (); ++class_index) {std::string CLA
          Ss_name = Class_names[class_index];
          Boost::shared_ptr<polygon_base::regularpolygon> plugin = poly_loader.createinstance (class_name);
          Plugin->initialize (10.0);
        Ros_info ("The Polygon (%d/%d) area:%.2f", Class_index + 1, class_names.size (), Plugin->area ());  The catch (pluginlib::P luginlibexception& ex) {ros_error ("The plugin failed to load for some Reason.
      Error:%s ", Ex.what ());
      } ros_info ("Waiting \ Ctrl + c\");
      while (Ros::ok ()) {;
    return 0; }

and add the following statement to the CMakeLists.txt, then $ catkin_make.

    Add_executable (polygon_loader_v1 src/polygon_loader_v1.cpp)
    target_link_libraries (Polygon_loader_v1 ${catkin _libraries})

Create the Class_loader.launch file by creating a new launch folder in the Plugin_test directory, copying the following.

    <launch>

        <!--plugin params-->
        <param name= "Polygon_loader_v1/derive_class_1" value= " Polygon_plugins::triangle "/>
        <param name=" polygon_loader_v1/derive_class_2 "value=" Polygon_plugins:: Square "/>

        <node name= polygon_loader_v1" pkg= "Plugin_test" type= "POLYGON_LOADER_V1" output=
             "screen" />

    </launch>

After the successful compilation, run the launch file, because in the code, we initialized the Ros node, and in the launch file we added Ros parameter, after the launch of launch file, will automatically open the Roscore, the output will be more confusing, Similar output should be obtained as follows.

    $ roslaunch plugin_test class_loader.launch ...

    Auto-starting new Master
    Process[master]: Started with PID [16890]
    ros_master_uri=http://localhost:11311

    setting/run_id to Bfd33a96-9cf8-11e6-a0bb-78acc03c5a93
    Process[rosout-1]: Started with PID [16903]
    Started core service [/rosout]
    Process[polygon_loader_v1-2]: Started with PID [16906]
    [INFO] [ 1477650287.294727014]: The Polygon (1/2) area:43.30
    [INFO] [1477650287.294864723]: The Polygon (2/2) area:100.
    [INFO] [1477650287.294899464]: Waiting "Ctrl + C"

The above code is relatively simple, do not do too much explanation. 3. Advanced

Pluginlib's source code See link: https://github.com/ros/pluginlib. Open the Include folder, which you can see, containing only a small number of files. Screenshot below:



3.1 class_list_macros.h

The structure is clear and several important macros are stated in Class_list_macros.h and are defined as follows:

    #include <class_loader/class_loader.h>/** * @macro This version is deprecated in favor of pluginlib_ Declare_class * @param-class_name-an alias for the CLASS (no special characters allowed) (ignored as of Pluginli B 1.9) * @param-class_type-the Real class name with namespace qualifier (e.g. animals::lion) * @param-base _class_type-the real base class type from which Class_type inherits */#define PLUGINLIB_REGISTER_CLASS (Class_n  Ame, Class_type, base_class_type) \ class_loader_register_class_with_message (Class_type, Base_class_type, "in File" __file__ "Pluginlib Warning:pluginlib_register_class is deprecated, to use Pluginlib_export_class instead. You can run the script ' plugin_macro_update ' provided with Pluginlib into your package source folder to automatically and re cursively Update legacy Macros. Base = base_class_type, Derived = Derived_class_type ")/** * @macro This version are the most in use and requireS package name in addition to fields in Pluginlib_register_class * @param-pkg-the package that exports the Plugi N (ignored as of pluginlib 1.9) * @param-class_name-an alias for "Class (no special characters allowed) (Igno
     RED as of Pluginlib 1.9) * @param-class_type-the Real class name with namespace qualifier (e.g. animals::lion) * @param-base_class_type-the Real base class type from which Class_type inherits */#define PLUGINLIB_DECL Are_class (pkg, class_name, class_type, base_class_type) \ class_loader_register_class_with_message (Class_type, Base_ Class_type, "pluginlib warning:in file" __file__ "Pluginlib_declare_class is deprecated, in use PLUGINLIB_EXPORT_CL Ass instead. You can run the script ' plugin_macro_update ' provided with Pluginlib into your package source folder to automatically and re  cursively Update legacy Macros. Base = base_class_type, Derived = Derived_class_type ")/** * @macro This version is only Made possible with Pluginlib 1.9 series.
     It's the easiest to use and now the official way of exporting classes. * @param-class_type-the Real class name with namespace qualifier (e.g. animals::lion) * @param-base_class_type -The real base class type from which Class_type inherits/#define Pluginlib_export_class (Class_type, Base_clas S_type) \ Class_loader_register_class (Class_type, Base_class_type);
3.2 class_desc.h

The class_desc.h file is defined as the following, which is primarily used to save the content of plugin edited in the aforementioned XML file.

      Class Classdesc {public:/** * @brief Constructor for a classdesc * @param lookup_name the class * @param derived_class the type of the derived class of the CL Ass * @param base_class The type of the class, corresponds to the type of the base class * @param PA Ckage the package the class lives in * @param description A description for the class * @param libra
           Ry_name the name of the containing library for the class (not a full path!) * @param plugin_manifest_path the path to the plugin manifest file */classdesc (const STD::STRING&AMP ; 
              Lookup_name, const std::string& derived_class, const std::string& base_class, const std::string& Package, Const std::string& Description, const std::string& library_name, const std::string& plugin_manifest 
         _path): Lookup_name_ (Lookup_name),   Derived_class_ (Derived_class), Base_class_ (Base_class), Package_ (package), descript
            Ion_ (description), Library_name_ (Library_name), Resolved_library_path_ ("unresolved"),
          Plugin_manifest_path_ (Plugin_manifest_path) {} std::string lookup_name_;
          Std::string Derived_class_;
          Std::string Base_class_;
          Std::string Package_;
          Std::string Description_;
          Std::string library_name_; Std::string resolved_library_path_;
      This is set by Pluginlib::classloader at the load time std::string plugin_manifest_path_; };
3.2 Class_loader

In addition to the above two parts, the other is a class declaration relating to offences, pluginlib_exceptions.h. Of course, the most important part of this is class loader. Class_loader is the most basic part of Pluginlib. Finishes loading imported C + + classes, Plug-ins, and objects that create these classes dynamically from the runtime at run time. Generally, when the plug-in is created for Non-ros package, the application uses class loader; When importing Plug-ins into Ros package, you should use Pluginlib. Class loader mainly provides two interfaces, namely Class_loader::classloader and Class_loader::multilibraryclassloader. Provides a similar interface, except that the former can bind only a single library, which can be bound to multiple libraries. The basic application examples are as follows:

    #include <class_loader/class_loader.h>//Include header file
    #include "MyBase.h"//Introducing the declared base class

    int main ()
    {
      // Instantiates a Class_loader::classloader object in which the passed-in parameter is the full path of the library to be imported and the name
      class_loader::classloader loader ("libmylibrary.so");
      Gets the class
      std::vector<std::string> classes = loader.getavailableclasses<mybase> () of the interface defined by the base class MyBase.
      for (unsigned int c = 0; c < classes.size (); ++c)
      {
        boost::shared_ptr<mybase> plugin = Loader.createinst Ance<mybase> (Classes[c]);
        Plugin->somemethod ();
        Plugin is a local variable, leaving the curly bracket scope automatically destructors}
    

Points to note:

ClassLoader can query for Plug-ins (or classes) that have a base class, and instantiate them, provided that the Plug-ins are registered and imported in advance, by adding the following macro definition at the end of the implementation file.

    Class_loader_register_class (Derived, Base)

As you can see from the example usage above, the client's code, while not requiring a specific definition of subclasses, inevitably requires the definition of a common base class (that is, MyBase). As you can see in the preceding code, the query for the available plug-ins and the two interfaces that create the instances are template functions. If the base class you have given is not right, you cannot get anything successfully.

Allow a class to be declared multiple times and have different base class declarations

All interfaces for Class_loader::classloader and Class_loader::multilibraryclassloader are thread-safe.

Templates in template functions are statically determined to ensure that the interfaces obtained are of type accuracy. Invalid plug-ins that are loaded into an incompatible interface are not present.

Finally, if you are interested in the specific implementation of class loader friends, you can refer to the following links, Http://wiki.ros.org/class_loader/Design, and with the previous GitHub on the source code to understand.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.