MoriKen's Journal

MoriKen's Journal

アラサー社会人博士による徒然日記。技術についてつらつら。だけだとコンテンツが貧弱なので、会社公認で大学院博士課程に進学した経緯や、独学でTOEICを475→910にしたノウハウを共有します。

【ROS 2】bloom で ROS 2 パッケージをリリースする(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/09 現在のものです。

はじめに

このページでは ROS 2 buildfarmでリリースするリポジトリの準備方法を説明します。パッケージを作成した後、これはパブリックに利用可能な Debian パッケージにあなたのパッケージを入れるための次のステップです(すなわち、あなたはaptを通してパッケージをインストールすることができるでしょう)。ここでは、ROS Wiki の Bloom リリースチュートリアルの前に実行する ROS 2 独自の手順を説明します。

必要なツール

  • ROS 2 Bouncy の場合:
    • bloom >= 0.6.6
    • catkin_pkg >= 0.4.5
  • ROS 2 Crystal の場合:
    • bloom >= 0.6.9
    • catkin_pkg >= 0.4.5

最新バージョンの bloom と catkin_pkg があることを確認

上記のバージョン要件を参照してください。

sudo apt install python-catkin-pkg python-bloom

0.6.8より古いバージョンの bloom を使用している場合は、リリースに v3 のインデックスURLを使用する必要があります。

export ROSDISTRO_INDEX_URL='https://raw.githubusercontent.com/ros/rosdistro/master/index.yaml'

ROS 1 の bloom と同一手順

ROS 2 のリリースの手順は、ROS 1 の bloom と同一です。

手順

  • 手順は ROS 1 と同一:
  • ROS 1 パッケージを ROS 2 に移植する場合:
    • 新しいリリースのリポジトリを作成することをお勧めします。

ビルドステータス

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】カスタムインターフェースの定義(msg / srv)(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/08 現在のものです。

既存の「標準」メッセージとサービスの定義を可能な限り再利用することをお勧めしますが、特定のアプリケーション用に独自のカスタムメッセージやサービスを定義する必要がある場合が多くあると思われます。カスタムメッセージまたはサービスを定義するための最初のステップは、ROS インターフェース定義言語を使用して.msgまたは.srvファイルを作成することです。慣例では、.msgファイルはmsgというパッケージサブディレクトリに入れられ、.srvファイルはsrvというパッケージサブディレクトリに入れられます(違う場所も指定はできますが、規則に従うことをお勧めします)。

カスタマイズした.msg.srvファイル、あるいはその両者を作成したら、定義の上にコードジェネレータを走らせるために、該当するパッケージ内のCMakelists.txtファイルにコードを追加する必要があります。本件に関するより詳細なチュートリアルの代わりに、例としてpendulum_msgsパッケージを調べてください。そのパッケージの CMakeLists.txt ファイルで、関連する CMake 呼び出しを確認できます。

これをビルドするには、package.xmlのフォーマットを 3 にする必要があります。これは、member_of_groupコマンドにフォーマット 3 が必要なためです。ROS 2 の create packageは、デフォルトのフォーマット 2 で package.xmlを生成します。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】インターフェースの新機能(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/07 現在のものです。

ROS 2 インターフェース定義言語(IDL)ROS 1 IDL と密接に関連しています。ほとんどの既存のROS 1 .msg および .srv ファイルは、ROS 2 とそのまま使用できます。その既存機能に加えて、ROS 2 IDL には新しい機能が導入されています。

  • 有界配列

    • ROS 1 IDLは無制限配列(例えば int32[] foo)と固定サイズ配列(例えば int32[5] bar)が使えますが、ROS 2 IDL はさらに有界配列(例えば `int32[<=5] bat)が使えます。
    • 常時膨大なメモリ空間を使用することなく、かつ配列のサイズに上限も設けることが重要となるユースケースがあります(例えば、実行中に使用されるすべてのメモリを事前に割り当てる必要があるリアルタイムシステムが好例です)。
  • 有界文字列

    • ROS 1 IDL は無制限文字列(例えば、string foo)を使えますが、ROS 2 IDL はさらに有界文字列(例えば、string≦5 bar)を使えます。
  • デフォルト値

    • ROS 1 IDL は定数フィールド(例えば int32 X=123)を使えますが、ROS 2 IDL はさらにデフォルト値(例えば int32 X 123)の指定ができます。
    • デフォルト値は、メッセージ/サービスオブジェクトを作成するときに使用され、後でフィールドに代入することで上書きできます。
    • 注意:ROS Ardent では、デフォルト値は複合型、文字列配列、またはエンコーディング付き文字列ではサポートされていません。
    • 注:ROS Bouncy では、デフォルト値は複合型またはエンコーディング付きの文字列ではサポートされていません。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】msg および srv インタフェースの紹介(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/08 現在のものです。

※作成中:これは、カスタム ROS I/F を作成および使用するためのチュートリアルのドラフトです。

※免責事項:提供されているコードは説明上の補助的なものであり、古くなっている可能性があり、そのままコンパイルできない場合もあります。ご了承下さい。

  • msg : msg ファイルは ROS メッセージのフィールドを記述する単純なテキストファイルです。種々の開発言語のメッセージのソースコードを生成するのに使われます。

  • srv : srv ファイルはサービスを記述します。リクエストとレスポンスの記述部分によって構成されています。リクエストとレスポンスはメッセージ宣言です。

メッセージは、1 行に 1 つのフィールドタイプと 1 つのフィールド名を持つ単純なテキストファイルです。使用できるフィールドタイプは以下のとおりです。

  • int8、int16、int32、int64(および uint*)
  • float32、float64
  • string
  • 他の msg ファイル
  • 可変長 配列[]、固定長 配列[C]、有界長 配列[<= C]

これは、文字列プリミティブと他の 2 つのメッセージを使用した msg の例です。

string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist

srv ファイルは msg ファイルと似ていますが、リクエストとレスポンスの 2 つの部分があります。 両者は「-」線で区切られています。次が srv ファイルの例です。

float64 A
float64 B
---
float64 sum

上記の例では、A と B がリクエストで、Sum がレスポンスです。

msg ファイルはパッケージの msg ディレクトリに格納され、srv ファイルは srv ディレクトリに格納されます。

これらはほんの一例です。 msgおよびsrvファイルを作成する方法に関する詳細については About ROS Interfaces を参照してください。

msg パッケージを作成する

注:現在 ament_cmake パッケージのみがメッセージを生成できます(ament_python パッケージは不可)。

このチュートリアルでは rosidl_tutorials リポジトリのパッケージを使います。

cd ~/ros2_overlway_ws/src
git clone -b rosidl_tutorials https://github.com/ros2/tutorials.git
cd rosidl_tutorials/rosidl_tutorials_msgs

msg ファイルを作成する

ここでは、個人に関する情報を伝えるためのメッセージを作成します。

msg/Contact.msg を開くと表示されます。

bool FEMALE=true
bool MALE=false

string first_name
string last_name
bool gender
uint8 age
string address

このメッセージは 5 つのフィールドで構成されています。

  • first_name : string 型
  • last_name : string 型
  • gender : bool 型で、MALE でも FEMALE でもかまいません
  • age : uint8 型
  • address : 文字列型

もう 1 つステップがあります。 msg ファイルが C++、Python、その他の言語のソースコードに変換されていることを確認しましょう。

msg ファイルのビルド

package.xmlを開き、次の2行のコメントを外します。

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<exec_depend>rosidl_default_runtime</exec_depend>

注記:ビルド時には "rosidl_default_generators"が必要ですが、実行時には "rosidl_default_runtime"のみが必要です。

CMakeLists.txtを開き、次の行のコメントが解除されていることを確認してください。

msg/srv ファイルからメッセージコードを生成するパッケージを探します。

find_package(rosidl_default_generators REQUIRED)

生成したいメッセージのリストを宣言します。

set(msg_files
  "msg/Contact.msg"
)

手動で .msg ファイルを追加することによって、他の .msg ファイルを追加した後に CMake がいつプロジェクトを再構成する必要があるかを保証できます。

メッセージの生成は次のように記述します。

rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
)

また、必ずメッセージのランタイム依存関係をエクスポートしてください。

ament_export_dependencies(rosidl_default_runtime)

これで、msg 定義からソースファイルを生成する準備が整いました。

srv ファイルを作成する

パッケージに srv 宣言を追加します。

srv/AddTwoFloats.srv ファイルを開き、この srv 宣言を貼り付けます。

float64 a
float64 b
---
float64 sum

srv ファイルを構築する

CMakeLists.txtでサービスを宣言します。

set(srv_files
  "srv/AddTwoFloats.srv")

上述した rosidl_generate_interfaces を修正し、メッセージに加えてサービスも生成できるようにします。

rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
  ${srv_files}
)

カスタムメッセージを使う

他のパッケージから msg/srv を使う

前のセクションで作成した Contact.msg を使用して C++ ノードを書きましょう。

rosidl_tutorials パッケージに移動して、src/publish_contact.cppファイルを開きます。

#include <iostream>
#include <memory>

#include "rclcpp/rclcpp.hpp"

#include "rosidl_tutorials_msgs/msg/contact.hpp"


using namespace std::chrono_literals;

class ContactPublisher : public rclcpp::Node
{
public:
  ContactPublisher()
  : Node("address_book_publisher")
  {
    contact_publisher_ = this->create_publisher<rosidl_tutorials_msgs::msg::Contact>("contact");

    auto publish_msg = [this]() -> void {
        auto msg = std::make_shared<rosidl_tutorials_msgs::msg::Contact>();

        msg->first_name = "John";
        msg->last_name = "Doe";
        msg->age = 30;
        msg->gender = msg->MALE;
        msg->address = "unknown";

        std::cout << "Publishing Contact\nFirst:" << msg->first_name <<
          "  Last:" << msg->last_name << std::endl;

        contact_publisher_->publish(msg);
      };
    timer_ = this->create_wall_timer(1s, publish_msg);
  }

private:
  rclcpp::Publisher<rosidl_tutorials_msgs::msg::Contact>::SharedPtr contact_publisher_;
  rclcpp::timer::TimerBase::SharedPtr timer_;
};


int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);

  auto publisher_node = std::make_shared<ContactPublisher>();

  rclcpp::spin(publisher_node);

  return 0;
}
コードの説明
#include "rosidl_tutorials_msgs/msg/contact.hpp"

ここでは、使用したいメッセージのヘッダーをインクルードします。

ContactPublisher()
: Node("address_book_publisher")
{

ここでノードを定義します

auto publish_msg = [this]() -> void {

定期的にメッセージを送信する publish_msg 関数

auto msg = std::make_shared<rosidl_tutorials_msgs::msg::Contact>();

 msg->first_name = "John";
 msg->last_name = "Doe";
 msg->age = 30;
 msg->gender = msg->MALE;
 msg->address = "unknown";

連絡先メッセージを作成し、そのフィールドに入力します。

std::cout << "Publishing Contact\nFirst:" << msg->first_name <<
  "  Last:" << msg->last_name << std::endl;

contact_publisher_->publish(msg);

最後にパブリッシュします。

timer_ = this->create_wall_timer(1s, publish_msg);

毎秒 publish_msg 関数を呼び出すために、1 秒のタイマを作成します。

さぁ、ビルドしましょう!

このメッセージを使用するには、package.xmlでrosidl_tutorials_msgsへの依存関係を宣言する必要があります。

<build_depend>rosidl_tutorials_msgs</build_depend>

<exec_depend>rosidl_tutorials_msgs</exec_depend>

そしてまたCMakeLists.txtの中で:

find_package(rosidl_tutorials_msgs REQUIRED)

そして最後に、メッセージパッケージを実行可能ファイルのターゲット依存関係として宣言する必要があります。

ament_target_dependencies(publish_contact
  "rclcpp"
  "rosidl_tutorials_msgs"
)

同一パッケージから msg/srv を使う

ほとんどの場合、メッセージはインタフェースパッケージ内で宣言されますが、メッセージを 1 つのパッケージ内で宣言、作成、使用できるのも便利です。

rosidl_tutorials パッケージにメッセージを作成します。 rosidl_tutorials パッケージに msg ディレクトリを作成し、そのディレクトリ内に AddressBook.msg を作成します。その msg に以下の行を記載します:

rosidl_tutorials_msgs/Contact[] address_book

ご覧のとおり、先ほど作成した連絡先メッセージに基づいてメッセージを定義します。

このメッセージを生成するには、package.xmlでこのパッケージへの依存関係を宣言する必要があります。

<build_depend>rosidl_tutorials_msgs</build_depend>

<exec_depend>rosidl_tutorials_msgs</exec_depend>

そしてCMakeLists.txtでは以下の通り:

find_package(rosidl_tutorials_msgs REQUIRED)

set(msg_files
  "msg/AddressBook.msg"
)

rosidl_generate_interfaces(${PROJECT_NAME}
  ${msg_files}
  DEPENDENCIES rosidl_tutorials_msgs
)

これで、このメッセージを使うコードを書き始めることができます。

src / publish_address_book.cpp を開きます。

#include <iostream>
#include <memory>

#include "rclcpp/rclcpp.hpp"

#include "rosidl_tutorials/msg/address_book.hpp"
#include "rosidl_tutorials_msgs/msg/contact.hpp"

using namespace std::chrono_literals;

class AddressBookPublisher : public rclcpp::Node
{
public:
  AddressBookPublisher()
  : Node("address_book_publisher")
  {
    address_book_publisher_ =
      this->create_publisher<rosidl_tutorials::msg::AddressBook>("address_book");

    auto publish_msg = [this]() -> void {
        auto msg = std::make_shared<rosidl_tutorials::msg::AddressBook>();
        {
          rosidl_tutorials_msgs::msg::Contact contact;
          contact.first_name = "John";
          contact.last_name = "Doe";
          contact.age = 30;
          contact.gender = contact.MALE;
          contact.address = "unknown";
          msg->address_book.push_back(contact);
        }
        {
          rosidl_tutorials_msgs::msg::Contact contact;
          contact.first_name = "Jane";
          contact.last_name = "Doe";
          contact.age = 20;
          contact.gender = contact.FEMALE;
          contact.address = "unknown";
          msg->address_book.push_back(contact);
        }

        std::cout << "Publishing address book:" << std::endl;
        for (auto contact : msg->address_book) {
          std::cout << "First:" << contact.first_name << "  Last:" << contact.last_name <<
            std::endl;
        }

        address_book_publisher_->publish(msg);
      };
    timer_ = this->create_wall_timer(1s, publish_msg);
  }

private:
  rclcpp::Publisher<rosidl_tutorials::msg::AddressBook>::SharedPtr address_book_publisher_;
  rclcpp::timer::TimerBase::SharedPtr timer_;
};


int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  auto publisher_node = std::make_shared<AddressBookPublisher>();

  rclcpp::spin(publisher_node);

  return 0;
}
コードの説明
#include "rosidl_tutorials/msg/address_book.hpp"

新しく作成した AddressBook msg のヘッダを含めます。

#include "rosidl_tutorials_msgs/msg/contact.hpp"

カスタムメッセージ kaddress_book に連絡先を追加できるようにするために Contact msg のヘッダーを含めます。

using namespace std::chrono_literals;

class AddressBookPublisher : public rclcpp::Node
{
public:
  AddressBookPublisher()
  : Node("address_book_publisher")
  {
    address_book_publisher_ =
      this->create_publisher<rosidl_tutorials::msg::AddressBook>("address_book");

ノードと AddressBook のパブリッシャを作成します。

auto publish_msg = [this]() -> void {

メッセージを定期的にパブリッシュするためのコールバックを作成します。

auto msg = std::make_shared<rosidl_tutorials::msg::AddressBook>();

AddressBook メッセージインスタンスを作成します。後ほどパブリッシュします。

{
rosidl_tutorials_msgs::msg::Contact contact;
contact.first_name = "John";
contact.last_name = "Doe";
contact.age = 30;
contact.gender = contact.MALE;
contact.address = "unknown";
msg->address_book.push_back(person);
}
{
rosidl_tutorials_msgs::msg::Contact person;
contact.first_name = "Jane";
contact.last_name = "Doe";
contact.age = 20;
contact.gender = contact.FEMALE;
contact.address = "unknown";
msg->address_book.push_back(contact);
}

Contact メッセージを作成と値の入力をし、それらを address_book メッセージに追加します。

std::cout << "Publishing address book:" << std::endl;
for (auto contact : msg->address_book) {
  std::cout << "First:" << contact.first_name << "  Last:" << contact.last_name <<
    std::endl;
}

address_book_publisher_->publish(msg);

そして、定期的にメッセージを送ります。

timer_ = this->create_wall_timer(1s, publish_msg);

毎秒publish_msg関数を呼び出すために 1 秒タイマーを作成します。

それではビルドしましょう!CMakeLists.txtでこのノードの新しいターゲットを作成します。

add_executable(publish_address_book
  src/publish_address_book.cpp
)

ament_target_dependencies(publish_address_book
  "rclcpp"
)

同じパッケージで生成されたメッセージを使用するには、以下のcmake コードを使用する必要があります。

get_default_rmw_implementation(rmw_implementation)
find_package("${rmw_implementation}" REQUIRED)
get_rmw_typesupport(typesupport_impls "${rmw_implementation}" LANGUAGE "cpp")

foreach(typesupport_impl ${typesupport_impls})
  rosidl_target_interfaces(publish_address_book
    ${PROJECT_NAME} ${typesupport_impl}
  )
endforeach()

これは msg/srv から関連する生成コード(C++)を検出し、ターゲットがそれらに対してリンクできるようにします。

使用されているインターフェースが事前にビルドされたパッケージからのものである場合、このステップは不要となります。このCMake コードは、msg/srv がビルドされたパッケージと同一のパッケージ内でインターフェースを使用しようとしているときにのみ必要となります。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】シングルプロセスで複数のノードを構成する(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/07 現在のものです。

ROS 1 - ノード vs. ノードレット

ROS 1 では、ROS ノードとしても ROS ノードレットとしてもコードを書くことができます。 ROS 1 ノードは実行ファイルとしてコンパイルされます。一方、ROS 1 ノードレットは共有ライブラリとしてコンパイルされており、共有ライブラリは実行時にコンテナプロセスによってロードされます。

ROS 2 - 統一されたAPI

ROS 2 では、ノードレットとしてコードを書くことが推奨されます - それを「コンポーネント」と呼びます。これにより、既存のコードに対しても ROS 2 共通のコンセプト(例:ライフサイクル)を容易に追加できます。ノードとノードレッドで API が異なるという最大の欠点は、ROS 2では解決されています - どちらのアプローチでもあっても、ROS 2 なら同じ API を使用できるのです。

ノードのように、「コード内に main を記述する」というスタイルを取ることも可能ではあるのですが、一般的には推奨されません。

プロセスレイアウトを展開時の決定にすることで、ユーザーは次のいずれかを選択できます。

  • 別々のプロセスで複数のノードを実行する。

    • 各ノードのデバッグの容易さや、プロセス/障害を分離できるという利点が活かせる。
  • 単一のプロセスで複数のノードを実行する(プロセス内通信を参照)。

    • 低オーバーヘッドや、その他オプションのよる通信の効率化が可能。

更に、ros2 launchは、特殊な起動アクションを通して、これらのアクションを自動化することもできます。

コンポーネントを書く

コンポーネントは共有ライブラリに組み込まれているだけなので、main 関数はありません(Talker のソースコードを参照)。コンポーネントは通常、rclcpp::Nodeのサブクラスです。スレッドで制御されていないため、コンストラクタ内で長時間実行されているタスクや、ブロッキングタスク(イベント待ちのタスク)も実行するべきではありません。代わりに、タイマを使って定期的に通知を受信することができます。さらに、パブリッシャ、サブスクライバ、サーバ、およびクライアントを作成できます。

このようなクラスをコンポーネントにすることの意義は、そのクラスがパッケージrclcpp_componentsからのマクロを使用して登録される点です(ソースコードの最後の行を参照)。これにより、そのライブラリが実行中のプロセスにロードされているとき、そのコンポーネントが検出可能になります - それは一種のエントリポイントとして機能します。

更に、コンポーネント作成の際には、他のツールから検出できるように、インデックスに登録する必要があります。

add_library(talker_component SHARED
   src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")

注意:component_containerが目的のコンポーネントを見つけることができるようにするには、対応するワークスペースを提供しているシェルから実行または起動する必要があります。

コンポーネントを使う

コンポジションパッケージには、コンポーネントを使用する方法がいくつかあります。 代表的な3つを以下に示します。

  1. プログラムやコマンドラインからの呼び出し

    • コンテナプロセスを起動し、コンテナによって提供された ROS サービス load_node を呼び出します。
    • ROS サービスは渡されたパッケージ名とライブラリ名に合致するコンポーネントをロードし、実行中のプロセス内でそれを開始します。
    • プログラムで ROS サービスを呼び出す代わりに、コマンドラインツールと引数を組み合わせて ROS サービスを呼び出すこともできます。
  2. カスタム実行ファイルからの呼び出し

    • コンパイル時に認識される複数のノードを含むカスタム実行可能ファイルを作成します。この方法では、各コンポーネントにヘッダーファイルが必要です(プログラムが小さければ必ずしも必要ではありません)。
  3. launch による呼び出し

    • launch ファイルを作成し、ros2 launchを使用して、複数のコンポーネントがロードされたコンテナプロセスを作成します。

デモ

デモは、rclcpp_componentsros2component、およびコンポジションパッケージの実行可能ファイルを使用し、以下のコマンドで実行できます。

利用可能なコンポーネントの検出

どのコンポーネントがワークスペースに登録され使用可能になっているかを確認するには、シェルで次のコマンドを実行します。

$ ros2 component types
composition
  composition::Talker
  composition::Listener
  composition::Server
  composition::Client

パブリッシャとサブスクライバで ROS サービス(1.)を使ったランタイムコンポジション

シェルで、コンポーネントコンテナを起動します。

ros2 run rclcpp_components component_container

ros2コマンドラインツールを使ってコンテナが実行されていることを確認します。

$ ros2 component list
/ComponentManager

2番目のシェル(talker のソースコードを参照)このコマンドは、ロードされたコンポーネントの一意の ID とノード名を返します。

$ ros2 component load /ComponentManager composition composition::Talker
1 /talker

これで、このシェルは、コンポーネントがロードされたというメッセージと、メッセージをパブリッシュするためのメッセージを繰り返し表示するはずです。

2番目のシェルの別のコマンド(listener のソースコードを参照)

$ ros2 component load /ComponentManager composition composition::Listener
2 /listener

ros2コマンドラインユーティリティを使用して、コンテナの状態を調べることができます。

$ ros2 component list
/ComponentManager
   1  /talker
   2  /listener

最初のシェルが、各受信メッセージの繰り返し出力するはずです。

サーバとクライアントでROSサービス(1.)を使ったランタイムコンポジション

サーバとクライアントの例も、パブリッシャとサブスクライバと同様です。

最初のシェルでは:

ros2 run rclcpp_components component_container

2番目のシェル(サーバクライアントのソースコードを参照)

ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client

この場合、クライアントはサーバーに要求を送信し、サーバーはその要求を処理して応答を返し、クライアントは受信した応答を出力します。

ROSサービス(2.)を使ったコンパイル時の合成

このデモでは、複数のコンポーネントを実行する単一の実行可能ファイルをコンパイルするのに、同じ共有ライブラリを再利用できることを示します。実行可能ファイルには、上記の4つのコンポーネントすべて(トーカーとリスナー、およびサーバーとクライアント)が含まれます。

シェル呼び出しの場合(ソースコードを参照):

ros2 run composition manual_composition
  • manual_composition.cpp
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <memory>

#include "composition/client_component.hpp"
#include "composition/listener_component.hpp"
#include "composition/talker_component.hpp"
#include "composition/server_component.hpp"
#include "rclcpp/rclcpp.hpp"

int main(int argc, char * argv[])
{
  // Force flush of the stdout buffer.
  setvbuf(stdout, NULL, _IONBF, BUFSIZ);

  // Initialize any global resources needed by the middleware and the client library.
  // This will also parse command line arguments one day (as of Beta 1 they are not used).
  // You must call this before using any other part of the ROS system.
  // This should be called once per process.
  rclcpp::init(argc, argv);

  // Create an executor that will be responsible for execution of callbacks for a set of nodes.
  // With this version, all callbacks will be called from within this thread (the main one).
  rclcpp::executors::SingleThreadedExecutor exec;
  rclcpp::NodeOptions options;

  // Add some nodes to the executor which provide work for the executor during its "spin" function.
  // An example of available work is executing a subscription callback, or a timer callback.
  auto talker = std::make_shared<composition::Talker>(options);
  exec.add_node(talker);
  auto listener = std::make_shared<composition::Listener>(options);
  exec.add_node(listener);
  auto server = std::make_shared<composition::Server>(options);
  exec.add_node(server);
  auto client = std::make_shared<composition::Client>(options);
  exec.add_node(client);

  // spin will block until work comes in, execute work as it becomes available, and keep blocking.
  // It will only be interrupted by Ctrl-C.
  exec.spin();

  rclcpp::shutdown();

  return 0;
}

これにより、サーバとクライアントだけでなく、talker と listener の両方のペアからのメッセージが繰り返し表示されます。注:手動で作成したコンポーネントは、ros2コマンドラインツールの出力には反映されません。

dlopen を使った実行時の合成

このデモは、一般的なコンテナプロセスを作成し、ROS インタフェースを使用せずにロードするライブラリを明示的に渡すことで、1.の代替方法を示します。プロセスは各ライブラリをオープンし、ライブラリのソースコード内の各「rclcpp :: Node」クラスのインスタンスを1つ作成します。

  • Linux シェル呼び出しの場合:
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so

OSX シェル呼び出しの場合:

ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.dylib `ros2 pkg prefix composition`/lib/liblistener_component.dylib

Windows の cmd.exe 呼び出しの場合

ros2 pkg prefix composition

により、コンポジションがインストールされている場所へのパスを取得してから、以下のコマンドを実行します。

ros2 run composition dlopen_composition <path_to_composition_install>\bin\talker_component.dll <path_to_composition_install>\bin\listener_component.dll

これで、シェルは送受信されたメッセージごとに繰り返し出力するはずです。

注:dlopen 構成のコンポーネントは、ros2 コマンドラインツールの出力には反映されません。

launch アクションを使用した合成

コマンドラインツールはコンポーネント設定のデバッグと診断に役立ちますが、コンポーネントのセットを同時に起動するほうがもっと便利です。これを自動化するために、ros2 launchの機能を使うことができます。

ros2 launch composition composition_demo.launch.py

発展ネタ

コンポーネントの基本的な操作について説明したので、もう少し高度なネタについて説明します。

コンポーネントをアンロードする

最初のシェルで、コンポーネントコンテナを起動します。

ros2 run rclcpp_components component_container

ros2コマンドラインツールを使ってコンテナが実行されていることを確認します。

$ ros2 component list
/ComponentManager

2番目のシェル(talker のソースコードを参照)このコマンドは、ロードされたコンポーネントに対するユニークな ID とノード名を返します。

$ ros2 component load /ComponentManager composition composition::Talker
1 /talker
$ ros2 component load /ComponentManager composition composition::Listener
2 /listener

このユニークな ID を使用して、コンポーネントコンテナからノードをアンロードします。

$ ros2 component unload /ComponentManager 1 2
Unloaded component 1 from /ComponentManager container
Unloaded component 2 from /ComponentManager container

最初のシェルで、talker と listener からの繰り返しメッセージが停止したことを確認します。

コンテナ名と名前空間のリマップ

コンポーネントマネージャの名前と名前空間は、標準のコマンドライン引数でリマップできます。

ros2 run rclcpp_components component_container __node:=MyContainer __ns:=/ns

2番目のシェルでは、更新されたコンテナ名を使ってコンポーネントをロードできます。

ros2 component load /ns/MyContainer composition composition::Listener

注:コンテナーの名前空間のリマップは、ロードされたコンポーネントには影響しません。

コンポーネント名と名前空間をリマップ

コンポーネント名と名前空間は load コマンドの引数で調整することができます。

最初のシェルで、コンポーネントコンテナを起動します。

ros2 run rclcpp_components component_container

名前と名前空間を再マッピングする方法の例

ros2 component load /ComponentManager composition composition::Talker --node-name talker2
# Remap namespace
ros2 component load /ComponentManager composition composition::Talker --node-namespace /ns
# Remap both
ros2 component load /ComponentManager composition composition::Talker --node-name talker3 --node-namespace /ns2

対応するエントリがros2 component listで表示されます。

$ ros2 component list
/ComponentManager
   1  /talker2
   2  /ns/talker
   3  /ns2/talker3

注:コンテナーの名前空間のリマップは、ロードされたコンポーネントには影響しません。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】複数の ROS 2 ミドルウェア実装を扱う(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/07 現在のものです。

このページでは、デフォルトの RMW 実装と代替方法の指定方法について説明します。

前提条件

既に ROS 2 に対応する様々な DDS / RTPS ベンダ(本ブログ和訳版)を読んでいることを前提とします。

複数のRMW実装

現在の ROS 2 バイナリリリースでは、いくつかのRMW 実装をそのままサポートしています(執筆時点では Fast RTPS、RTI Connext Pro、および ADLink OpenSplice)。

追加インストールなしで動作するのは Fast RTPS(デフォルト)のみで、唯一バイナリパッケージと一緒に配布されています。

OpenSplice や Connext のような他のものは追加のパッケージをインストールすることで使用可能ですが、既存のパッケージを置き換えたり再構築したりする必要はありません。

また、ソースからビルドした ROS 2 ワークスペースは、同時に複数の RMW 実装をビルド・インストールすることがあります。コアの ROS 2 コードのコンパイル中に、関連する DDS / RTPS 実装が正しくインストールされ、関連する環境変数が構成されていれば、検出された RMW 実装はすべてビルドされます。

例えば、RTI Connext 用の RMW パッケージのコードがワークスペースにある場合、RTI の Connext Pro のインストールも見つかるとビルドされます。多くの場合、さまざまな RMW 実装を使用しているノードが通信できますが、必ずしも全ての組み合わせで通信ができるとは限りません。サポートされているベンダー間通信設定のリストは近日公開予定です。

デフォルトのRMW実装

ROS 2ワークスペースに複数の RMW 実装がある場合、デフォルトの RMW 実装が使用可能であれば Fast RTPS が選択されます。 Fast RTPS RMW 実装がインストールされていない場合は、アルファベット順で最初の RMW 実装 ID を持つ RMW 実装が使用されます。実装識別子は、RMW 実装を提供する ROS パッケージの名前です(例:rmw_fastrtps_cpp)。

rmw_opensplice_cpprmw_connext_cppの両方の ROS パッケージがインストールされている場合は、rmw_connext_cppがデフォルトになります。 rmw_fastrtps_cppがインストールされている場合は、それがデフォルトになります。 ROS 2 の例を実行するときにどのRMW実装を使用するかを指定する方法については、以下を参照してください。

RMW 実装の指定

複数の RMW 実装を使用可能にするには、バイナリと特定の RMW 実装に対する追加の依存関係をインストールするか、ワークスペースに複数の RMW 実装を含むソースから ROS 2 を構築し(デフォルトで含まれる)、それらの依存関係を満たす必要があります。サンプルは、インストール(本ブログ和訳版)を参照してください。

Beta 2 以降では、C++ ノードと Python ノードの両方で環境変数RMW_IMPLEMENTATIONがサポートされています。異なるRMW 実装を選択するには、環境変数RMW_IMPLEMENTATIONを特定の実装識別子に設定します。

RMW 実装として connext を使用した、C++ による talker と、python による listener のデモを実行するには、次のコマンドを実行します。

  • bash
RMW_IMPLEMENTATION=rmw_connext_cpp ros2 run demo_nodes_cpp talker

# Run in another terminal
RMW_IMPLEMENTATION=rmw_connext_cpp ros2 run demo_nodes_py listener
  • Windows cmd.exe
set RMW_IMPLEMENTATION=rmw_connext_cpp
ros2 run demo_nodes_cpp talker

REM run in another terminal
set RMW_IMPLEMENTATION=rmw_connext_cpp
ros2 run demo_nodes_py listener

ワークスペースへの RMW 実装の追加

ROS 2 ワークスペースに Fast RTPS のみがインストールされており、Fast RTPS RMW 実装のみがビルドされた状況を考えてみます。一旦ワークスペースがビルドされてしまうと、他の RMW 実装パッケージ(例えば、rmw_connext_cpp)は、関連するDDS実装のインストールを検出できない場合があります。

そこで、Connext などの追加の DDS 実装をインストールする場合は、Connext RMW 実装のビルド時に発生する Connext インストールのチェックを再トリガする必要があります。

具体的には、ワークスペースのリビルド時に、--cmake-force-configureフラグを指定することで、RMW 実装パッケージが新しくインストールされた DDS 実装用にビルドが走ります。

--cmake-force-configureオプションを使用して追加の RMW 実装でワークスペースを「リビルド」すると、デフォルトの RMW 実装が変更されたことを示すメッセージが表示されることがあります。これを解決するには、RMW_IMPLEMENTATION CMake引数を使用してデフォルトの実装を以前の実装に設定するか、そのメッセージを出すパッケージのビルドフォルダを削除して--start-with <パッケージ名>を使用してビルドを続行します。

トラブルシューティング

特定のRMW実装を確実に利用する

ROS 2 Ardent 以降

RMW_IMPLEMENTATION環境変数が、インストールされていない RMW 実装に設定されている状況を想定します。インストール済みの実装が1つしかない場合には、次のようなエラーメッセージが表示されます。

Expected RMW implementation identifier of 'rmw_connext_cpp' but instead found 'rmw_fastrtps_cpp', exiting with 102.

複数の RMW 実装がインストール済みで、インストールされていないものの使用を要求した場合は、次のように表示されます。

Error getting RMW implementation identifier / RMW implementation not installed (expected identifier of 'rmw_connext_cpp'), exiting with 1.

このような場合は、ご使​​用の ROS 2 インストール環境に、RMW_IMPLEMENTATION環境変数で指定したRMW実装のサポートが含まれていることを再確認してください。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】本文書について(公式文書和訳)

ROS 2 公式文書(英語) 日本語訳シリーズです。

本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。

www.moriken254.com

※2019/05/06 現在のものです。

本文書について

このドキュメント(英語)http://github.com/ros2/ros2_documentation にあります。純粋な ReST マークアップフォーマットで書かれており、ROSIndex ビルド中に Sphinx を使って処理されます。Intersphinx support は、パッケージ固有のエンティティをクロスリンクするために利用できます。

更新要求は、現状プルリクエストを通して処理されています。

翻訳元文書

index.ros.org

関連記事

www.moriken254.com