Skip to content

プラグインの作成と使用 (C++)

目標: pluginlibを使用してシンプルなプラグインを作成し、読み込む方法を学習する。

チュートリアルレベル: 初心者

所要時間: 20分

背景

このチュートリアルはhttp://wiki.ros.org/pluginlibWriting and Using a Simple Plugin Tutorialから派生しています。

pluginlibはROSパッケージ内でプラグインを読み込み・アンロードするためのC++ライブラリです。 プラグインはランタイムライブラリ(共有オブジェクト、動的リンクライブラリなど)から読み込まれる動的ロード可能なクラスです。 pluginlibを使用すると、アプリケーションをクラスを含むライブラリに対して明示的にリンクする必要がありません。代わりに、pluginlibはエクスポートされたクラスを含むライブラリを、アプリケーションがライブラリやクラス定義を含むヘッダファイルについて事前に認識することなく、任意の時点で開くことができます。 プラグインは、アプリケーションのソースコードを必要とせずにアプリケーションの動作を拡張・変更するのに便利です。

前提条件

このチュートリアルは基本的なC++の知識と、ROS 2のインストールが正常に完了していることを前提としています。

タスク

このチュートリアルでは、2つの新しいパッケージを作成します。1つはベースクラスを定義し、もう1つはプラグインを提供します。 ベースクラスは汎用的な多角形クラスを定義し、プラグインは特定の形状を定義します。

1 ベースクラスパッケージの作成

ros2_ws/srcフォルダに次のコマンドで新しい空のパッケージを作成します:

bash
ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies pluginlib --node-name area_node polygon_base

お気に入りのエディタを開き、ros2_ws/src/polygon_base/include/polygon_base/regular_polygon.hppを編集して、以下の内容を貼り付けます:

cpp
#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

上記のコードはRegularPolygonという抽象クラスを作成しています。 注意すべき点は、initializeメソッドの存在です。 pluginlibでは、パラメータなしのコンストラクタが必要なので、クラスにパラメータが必要な場合は、initializeメソッドを使用してオブジェクトにパラメータを渡します。

このヘッダを他のクラスで利用可能にする必要があるので、ros2_ws/src/polygon_base/CMakeLists.txtを編集します。 ament_target_dependenciesコマンドの後に以下の行を追加します:

cmake
install(
  DIRECTORY include/
  DESTINATION include
)

そして、ament_packageコマンドの前に以下のコマンドを追加します:

cmake
ament_export_include_directories(
  include
)

後でテストノードを作成するために、このパッケージに戻ります。

2 プラグインパッケージの作成

次に、抽象クラスの仮想でない実装を2つ作成します。 ros2_ws/srcフォルダに以下のコマンドで2番目の空のパッケージを作成します:

bash
ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies polygon_base pluginlib --library-name polygon_plugins polygon_plugins

2.1 プラグインのソースコード

ros2_ws/src/polygon_plugins/src/polygon_plugins.cppを編集し、以下の内容を貼り付けます:

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)

SquareクラスとTriangleクラスの実装は比較的わかりやすいです:辺の長さを保存し、それを使用して面積を計算します。 pluginlib固有の部分は最後の3行で、クラスを実際のプラグインとして登録する魔法のマクロを呼び出しています。 PLUGINLIB_EXPORT_CLASSマクロの引数を説明します:

  1. プラグインクラスの完全修飾型。この場合はpolygon_plugins::Square

  2. ベースクラスの完全修飾型。この場合はpolygon_base::RegularPolygon

2.2 プラグイン宣言XML

上記の手順により、含まれているライブラリが読み込まれると、プラグインのインスタンスを作成できるようになりますが、プラグインローダーはそのライブラリを見つける方法と、そのライブラリ内で何を参照すべきかを知る必要があります。 この目的のために、パッケージマニフェストの特別なエクスポート行と合わせて、プラグインに関するすべての必要な情報をROSツールチェインで利用できるようにするXMLファイルも作成します。

以下のコードでros2_ws/src/polygon_plugins/plugins.xmlを作成します:

xml
<library path="polygon_plugins">
  <class type="polygon_plugins::Square" base_class_type="polygon_base::RegularPolygon">
    <description>This is a square plugin.</description>
  </class>
  <class type="polygon_plugins::Triangle" base_class_type="polygon_base::RegularPolygon">
    <description>This is a triangle plugin.</description>
  </class>
</library>

注意すべき点がいくつかあります:

  1. libraryタグは、エクスポートしたいプラグインを含むライブラリへの相対パスを示しています。 ROS 2では、これは単にライブラリの名前です。 ROS 1ではlibプレフィックスや時にはlib/lib(つまりlib/libpolygon_plugins)が含まれていましたが、ここではより簡単です。

  2. classタグは、ライブラリからエクスポートしたいプラグインを宣言します。 パラメータについて説明します:

    • type: プラグインの完全修飾型。私たちの場合はpolygon_plugins::Square

    • base_class_type: プラグインの完全修飾ベースクラス型。私たちの場合はpolygon_base::RegularPolygon

    • description: プラグインの説明とその機能。

2.3 CMakeプラグイン宣言

最後のステップは、CMakeLists.txtを介してプラグインをエクスポートすることです。 これは、エクスポートがpackage.xmlを介して行われていたROS 1からの変更です。 find_package(pluginlib REQUIRED)の行を読んだ後に、ros2_ws/src/polygon_plugins/CMakeLists.txtに以下の行を追加します:

cmake
pluginlib_export_plugin_description_file(polygon_base plugins.xml)

pluginlib_export_plugin_description_fileコマンドの引数は:

  1. ベースクラスを持つパッケージ、つまりpolygon_base

  2. プラグイン宣言xmlへの相対パス、つまりplugins.xml

3 プラグインの使用

プラグインを使用する時が来ました。 これは任意のパッケージで行うことができますが、ここではベースパッケージで行います。 ros2_ws/src/polygon_base/src/area_node.cppを編集して以下の内容を含めます:

cpp
#include <pluginlib/class_loader.hpp>
#include <polygon_base/regular_polygon.hpp>

int main(int argc, char** argv)
{
  // 未使用パラメータ警告を避けるため
  (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は理解すべき重要なクラスで、class_loader.hppヘッダファイルで定義されています:

  • ベースクラスでテンプレート化されます。つまりpolygon_base::RegularPolygon

  • 最初の引数は、ベースクラスのパッケージ名の文字列。つまりpolygon_base

  • 2番目の引数は、プラグインの完全修飾ベースクラス型の文字列。つまりpolygon_base::RegularPolygon

クラスのインスタンスを作成する方法はいくつかあります。 この例では、共有ポインタを使用しています。 プラグインクラスの完全修飾型(この場合はpolygon_plugins::Square)でcreateSharedInstanceを呼び出すだけです。

重要な注意:このノードが定義されているpolygon_baseパッケージはpolygon_pluginsクラスに依存していません。 プラグインは依存関係を宣言する必要なく動的に読み込まれます。 さらに、ここではハードコードされたプラグイン名でクラスをインスタンス化していますが、パラメータなどを使用して動的に行うこともできます。

4 ビルドと実行

ワークスペースのルートros2_wsに戻り、新しいパッケージをビルドします:

bash
colcon build --packages-select polygon_base polygon_plugins

ros2_wsから、セットアップファイルをソースしてください:

Linux/macOS:

bash
source install/setup.bash

Windows:

bash
call install/setup.bat

ノードを実行します:

bash
ros2 run polygon_base area_node

以下が出力されるはずです:

Triangle area: 43.30
Square area: 100.00

概要

おめでとうございます! 初めてのプラグインを作成して使用しました。

関連リンク

Released under the MIT License.