MoriKen's Journal

MoriKen's Journal

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

【ROS 2】ament_cmake ユーザーズマニュアル(公式文書和訳)

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

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

www.moriken254.com

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

ament_cmake は ROS 2 の CMake ベースのパッケージビルドシステムです(全部とは言わないまでも、ほとんどの C/C++ プロジェクトで使用されています)。これは CMake を拡張したもので、パッケージ作成時の便利機能が追加されています。 CMake の基本を知っておくと理解が深まります。公式のチュートリアルも参考になります。

基本

基本的な CMake のアウトラインは、コマンドラインでros2 pkg create <パッケージ名>で作成されます。その後、基本的なビルド情報はpackage.xmlCMakeLists.txtの2つのファイルに集約されます。 colcon がパッケージの正しいビルドの順番を検出したり、必要な依存関係を CI にインストールしたり、さらにリリースに関する情報をbloomで提供できるようにするには、package.xmlにすべての依存関係とメタデータを含めておく必要があります。 CMakeLists.txtには、実行可能ファイルとライブラリをビルドおよびパッケージ化するためのコマンドが含まれています。このページでは主としてそのコマンドについて説明します。

ament チュートリアルも参照してください。←リンク切れ

標準プロジェクト概要

ament パッケージのCMakeLists.txtの基本的な概要は以下の通りです。

cmake_minimum_required(VERSION 3.5)
project(my_project)

ament_package()

projectの引数はパッケージ名になり、package.xmlのパッケージ名と同じでなければなりません。

プロジェクトの設定はament_package()によって行われます。この呼び出しはパッケージごとに1回だけ行う必要があります。 ament_package()は、package.xmlをインストールし、そのパッケージをamentインデックスに登録し、そして CMake のための config(そしておそらくtarget)ファイルをインストールします。これにより、find_package で他のパッケージから検出できるようになります。 ament_package()CMakeLists.txt内の全ての情報を集約して処理をする関数なので、"CMakeLists.txt"の最終行に記述する必要があります。ファイルとディレクトリをコピーするinstall関数によってament_package()を再現することもできますが、ament_package()を最後の呼び出すほう楽です。

ament_packageには追加の引数を与えることができます。

  • CONFIG_EXTRAS:パッケージのクライアントに利用できるようにする追加の CMake ファイル(configure_file() によって展開された.cmakeまたは.cmake.inテンプレート)のリスト。これらの引数を使用する場合の例については、リソースの追加を参照してください。テンプレートファイルの使用方法の詳細については、Cmake の公式文書を参照してください。

  • CONFIG_EXTRAS_POSTCONFIG_EXTRASと同じですが、ファイルが追加される順序が異なります。 CONFIG_EXTRASから生成されたファイルがament_export_*用に生成されたファイルの「前」に含まれるのに対して、CONFIG_EXTRAS_POSTから生成されたファイルはその「後」に含まれます。

ament_packageに追加する代わりに、変数${PROJECT_NAME}_CONFIG_EXTRASおよび${PROJECT_NAME}_CONFIG_EXTRAS_POSTに同じ効果を追加することもできます。唯一の違いは、ファイルが追加される順序が次の通りであることです。

  • CONFIG_EXTRASによって追加されたファイル
  • ${PROJECT_NAME} _CONFIG_EXTRASにアペンドして追加されたファイル
  • ${PROJECT_NAME} _CONFIG_EXTRAS_POSTにアペンドして追加されたファイル
  • CONFIG_EXTRAS_POSTによって追加されたファイル

ファイルとヘッダの追加

ビルドする主なターゲットは2つあります。それぞれadd_libraryadd_executableによってビルドされる、ライブラリと実行可能ファイルです。

ヘッダーファイルの分離と C/C++ での実装では、add_library/add_executableの引数として、必ずしも両ファイルを追加する必要はありません。

以下のベストプラクティスが提案されています。

  • ライブラリをビルドする場合は、クライアントに解放する全ヘッダーをパッケージ名に相当する名前のフォルダ配下のincludeサブディレクトリに格納しておきます。一方、エクスポートしてはいけないファイル(.c / .cppとヘッダファイル)は srcフォルダ内にあります。
  • add_libraryまたはadd_executableの呼び出しでは、cpp ファイルのみを明示的に参照する。
  • 下記のコマンドでヘッダを検出させる:
target_include_directories(my_target
  PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)

これにより、ビルド時に${CMAKE_CURRENT_SOURCE_DIR}/includeフォルダ内の全てのファイルがパブリックインターフェースに追加され、インストール時に include フォルダ内の全てのファイル(${CMAKE_INSTALL_DIR}からの相対パス)が追加されます。

原則として、両フォルダともincludeフォルダだった場合や、${CMAKE_CURRENT_SOURCE_DIR}および${CMAKE_INSTALL_DIR}直下のフォルダの場合には、ジェネレータ式を使用する必要はありません。ただし、そのような場合でもジェネレータ式を記述しておくのが一般的です。

リソースの追加

パッケージを新しい依存関係に対してリンクするには2つの方法があります。

最初の、そして推奨される方法は ament マクロ であるament_target_dependenciesを使うことです。例として、my_targetを線形代数ライブラリ Eigen3 に対してリンクさせたいとします。

find_package(Eigen3 REQUIRED)
ament_target_dependencies(my_target Eigen3)

これにより、必要なヘッダとライブラリおよびそれらの依存関係を、プロジェクトが検出できるようになります。また、オーバーレイワークスペースを使用するときに、すべての依存関係のインクルードディレクトリが正しく順序付けされていることも保証されます。

2番目の方法はtarget_link_librariesを使うことです。

現代の CMake で推奨される方法は、ターゲットのみを使用し、それらをエクスポートしてリンクすることです。 CMake ターゲットは C++ と同様に名前空間があります。たとえば、Eigen3の場合は、ターゲットとしてEigen3 :: Eigenをを指定します。

少なくともCrystal Clemmysより前までは、ターゲット名がament_target_dependenciesマクロでサポートされていません。場合によっては、target_link_libaries CMake 関数を使う必要があります。 Eigen3 の例では、呼び出しは次のようになります。

find_package(Eigen3 REQUIRED)
target_link_libraries(my_target Eigen3::Eigen)

これには必要なヘッダ、ライブラリ、およびそれらの依存関係も含まれますが、ament_target_dependenciesとは対照的に、オーバーレイワークスペースを使用するときに依存関係を正しく順序付けできない可能性があります。

注意

明示的に必要とされないライブラリをfind_packageすることは不要ですが、別の依存関係(依存A)を持つ依存関係(依存B)を記述する場合には、依存Aを明示的に記述しなければなりません。 その場合、該当するパッケージに対してバグを報告してください。

ライブラリのビルド

再利用可能なライブラリをビルドするとき、それを利用しやすくするために下流のパッケージ用にいくつかの情報をエクスポートする必要があります。

ament_export_interfaces(export_my_library HAS_LIBRARY_TARGET)
ament_export_dependencies(some_dependency)

install(
  DIRECTORY include/
  DESTINATION include
)

install(my_library
  TARGETS my_library
  EXPORT export_my_library
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

ここでは、includeフォルダーにエクスポートする必要があるヘッダーが含まれているとします。すべてのヘッダを別々のフォルダに入れる必要はないことに注意してください。クライアントが含めるべきヘッダだけを入れてください。

上記のスニペットで起こっていることは以下の通り:

  • ament_export_interfacesマクロは CMake のターゲットをエクスポートします。これは上記ライブラリのクライアントがtarget_link_libraries(client my_library::my_library)構文を使えるようにするために必要です。

  • ament_export_interfacesは、install コマンドにおけるEXPORTという名前の任意のターゲットリストと、環境変数に潜在的な依存ライブラリを登録する追加オプションHAS_LIBRARY_TARGETを取ることができます。

  • ament_export_dependenciesは依存関係を下流のパッケージにエクスポートします。これは、ライブラリのユーザがそれらの依存関係に対してもfind_packageを呼び出さなくてもいいようにするためです。

  • 最初のinstallコマンドは、クライアントに公開するヘッダファイルをインストールするためのものです。

  • 最後の大きなinstallコマンドはライブラリをインストールします。アーカイブとライブラリファイルはlibフォルダにエクスポートされ、ランタイムバイナリはbinフォルダにインストールされ、インストールされたヘッダへのパスもシステムに登録されます。

    • 注意:Windows の dll は(ライブラリではなく)ランタイムとして扱われ、RUNTIME DESTINATIONフォルダにインストールされます。したがって、UNIX ベースのシステムでライブラリを開発する場合でも、RUNTIMEインストールを除外しないことをお勧めします。
  • include directoryに関しては、install コマンドは CMake に情報を追加するだけで、実際には includes フォルダをインストールしません。これは上述のように、install(DIRECTORY <dir> DESTINATION <dest>)によってヘッダをコピーすることによって行われます。

  • install 呼び出しのEXPORT表記にはさらに注意が必要です。これは、my_library ターゲット用の CMake ファイルをインストールします。これはament_export_interfacesの引数とまったく同じ名前で、ライブラリのように名前を付けることもできます。しかし、これよって出力されたライブラリは、 ament_target_dependenciesでインクルードできなくなってしまいます。それとは完全に切り離せるよう、専用のexport_<target>のようなものをエクスポート先に追加することをお勧めします。

  • すべてのインストールパスは、colcon/amentによってすでに正しく設定されているCMAKE_INSTALL_PREFIXからの相対パスです。

使用できる2つの追加機能がありますが、ターゲットベースのインストールには不要です。

ament_export_include_directories(include)
ament_export_libraries(my_library)

最初のマクロは、エクスポートされたインクルードディレクトリをマークします(これは、ターゲットinstallINCLUDES DESTINATIONによって実現されます)。 2番目のマクロは、インストールされているライブラリの場所を示します(これはament_export_interfaceへの呼び出しのHAS_LIBRARY_TARGET引数によって行われます)。

いくつかのマクロは非ターゲットエクスポートに対して異なるタイプの引数を取ることができますが、最近のMakeのための推奨される方法はターゲットを使うことであるので、ここでは カバー しません。これらのオプションのドキュメントはソースコード内に記載してあります。

コンパイラとリンカのオプション

ROS 2 は、少なくともCrystal Clemmysまでは、C++14 および C99 規格に準拠しているコンパイラを対象としています。より新しいバージョンは将来的には見据えていて、こちらで言及されています。したがって、対応する規格に応じた CMake フラグを設定するのが慣例です。

if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

コードをきれいに保つために、コンパイラは不正が疑われるコードに対してワーニングを出すべきであり、修正されるべきです。

少なくとも次の警告レベルをカバーすることをお勧めします。

  • Visual Studioの場合:デフォルトのW1警告は保持されます。
  • GCC と Clang の場合:-Wall -Wextra -Wpedantic が必要で、-Wshadow -Werrorが推奨されます(後者はワーニングをエラーと解釈します)。

現代の CMake はターゲットベースでコンパイラのフラグを追加することをお勧めしますが(例:call)、

target_compile_options(my_target PRIVATE -Wall)

現時点では、ディレクトリレベルの関数add_compile_options(-Wall)を使用して、すべての実行可能ファイルおよびテスト用のターゲットベースのコンパイルオプションで行数が増えてしまい、コードが乱れないようにすることをお勧めします。

Windows におけるライブラリのビルド

Linux、Mac、Windows はすべて公式にサポートされているプラ​​ットフォームなので、効果を最大限得るためには、どのパッケージも Windows 上でビルドできるようにする必要があります。 Windows ライブラリフォーマットはシンボルの可視性を強化します。クライアントから使用されるべきすべてのシンボルはライブラリによって明示的にエクスポートされる必要があります(そしてデータシンボルは暗黙的にインポートされる必要があります)。

Clang と GCC ビルドとの互換性を保つために、GCC ウィキ内に記載されているロジックを使用することをお勧めします。 my_libraryというパッケージに使用するには

  • リンク内のロジックをvisibility_control.hppというヘッダーファイルにコピーします。
  • DLLMY_LIBRARYに置き換えます(例については、rviz_rendering の可視性制御を参照してください)。
  • エクスポートする必要があるすべてのシンボル(つまり、クラスまたは関数)に対してマクロ「MY_LIBRARY_PUBLIC」を使用します。
  • プロジェクトCMakeLists.txtで、以下のコマンドを使用します。
target_compile_definitions(my_library PRIVATE "MY_LIBRARY_BUILDING_LIBRARY")

テストと構文チェック

colcon を使ってライブラリをビルドをすることととテストを分離するために、構文チェックとテストへのすべての呼び出しを条件付きでラップします。

if(BUILD_TESTING)
  find_package(ament_gtest)
  ament_add_gtest(<tests>)
endif()

構文チェック

ament によって提供されるすべての構文チェッカは別々に追加することができますが、それらを組み合わせたコマンドを使用することをお勧めします。

find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

これはpackage.xmlで定義されているように構文チェッカを実行します。 ament_lint_commonパッケージで定義されている一連の構文チェッカを使用することをお勧めします。これにより、以下のすべての構文チェッカ実行されます。

  • 著作権文とライセンスヘッダの存在とその正当性を確認する著作権構文チェッカ
  • cppcheck、論理テストも見つけることができるC++ 構文チェッカ
  • cpplint、C++ スタイルの構文チェッカ(例えばコメントスタイル)
  • C++ スタイル構文チェッカ
  • cmake チェッカ
  • XML uncrustify
  • flake8、python ファイル用のスタイルチェッカ
  • pep257、python docstrings のスタイルチェッカ

ament_uncrustifyには、コマンドラインツールが付属しています。これを呼び出すと、スタイルガイドに従ってコードを自動的に再フォーマットできます。

ament_uncrustify --reformat <path_to_source_folders>

テスト

Ament には GTests の設定を簡単にするための CMake マクロが含まれています。下記のように GTest を追加します。

find_package(ament_gtest)
ament_add_gtest(some_test <test_sources>)

これは他のライブラリ(プロジェクトライブラリなど)に対してリンクできる通常のターゲットです。マクロには追加のパラメータがあります。

  • APPEND_ENV:環境変数を追加します。たとえば、次のように呼び出して ament プレフィックスパスに追加できます。
find_package(ament_gtest REQUIRED)
ament_add_gtest(some_test <test_sources>
  APPEND_ENV PATH=some/addtional/path/for/testing/resources)
  • APPEND_LIBRARY_DIRS:実行時にリンカが検出できるようにライブラリを追加します。Windows ではPATH、Linux ではLD_LIBRARY_PATHのような環境変数を設定することでも実現できますが、この場合は呼び出しプラットフォームごとに異なる設定をしなければなりません。
  • ENV:環境変数を設定します(APPEND_ENVと同じ構文)。
  • TIMEOUT:テストタイムアウトを秒単位で設定します。 GTestsのデフォルトは60秒です。
  • SKIP_TEST:このテストをスキップします(コンソール出力に "passed" と表示されます)。
  • SKIP_LINKING_MAIN_LIBRARIES:GTestにリンクしません。
  • WORKING_DIRECTORY:テスト用の作業ディレクトリを設定します。

それ以外のデフォルトの作業ディレクトリはCMAKE_SOURCE_DIRで、これは最上位のCMakeLists.txtのディレクトリとなります。

同様に、GMock を含む GTest を設定するための CMakeマクロがあります。

find_package(ament_gmock REQUIRED)
ament_add_gmock(some_test <test_sources>)

ament_add_gtestと同様に追加パラメータがあります。

ament の拡張

追加のマクロ/関数を ament_cmake に登録し、それを拡張することができます。

ament への関数/マクロの追加

ament を拡張することはしばしば他のパッケージが使えるようにするための機能を追加したいという意図があると思われます。マクロをクライアントパッケージに提供する最善の方法は、それを ament に登録することです。

これは、${PROJECT_NAME}_CONFIG_EXTRAS変数を追加することによって実行できます。これは、次の関数を呼び出した後、ament_package()を呼ぶことで実現できます。

list(APPEND ${PROJECT_NAME}_CONFIG_EXTRAS
  path/to/file.cmake"
  other/pathto/file.cmake"
)

あるいは、ファイルをament_package()で直接追加することもできます。

ament_package(CONFIG_EXTRAS
  path/to/file.cmake
  other/pathto/file.cmake
)

拡張ポイントへの追加

他のパッケージで使用できる機能を追加する単純な設定ファイル以外にも、ament 自体への拡張も可能です。その拡張機能は拡張ポイントを定義する関数で実行されるスクリプトです。 ament 拡張の最も一般的なユースケースは、おそらく rosidl メッセージジェネレータを登録することです。ジェネレータを書くとき、メッセージ/サービス定義パッケージのためのコードを修正することなしにジェネレータですべてのメッセージとサービスを通常生成するのが望ましいです。これは、ジェネレータをrosidl_generate_interfacesの拡張として登録することで可能です。

例として、下記があります。

ament_register_extension(
  "rosidl_generate_interfaces"
  "rosidl_generator_cpp"
  "rosidl_generator_cpp_generate_interfaces.cmake"

これは、パッケージrosidl_generator_cppのマクロrosidl_generator_cpp_generate_interfaces.cmakeを、拡張ポイントrosidl_generate_interfacesに登録するものです。拡張ポイントが実行されると、ここでスクリプトrosidl_generator_cpp_generate_interfaces.cmakeの実行がトリガーされます。もっと正確に言うと、これは関数rosidl_generate_interfacesが実行されるたびにジェネレータを呼び出します。

ジェネレータ用のrosidl_generate_interfaces以外の最も重要な拡張ポイントはament_packageです。これは単にament_package()呼び出しでスクリプトを実行するだけです。この拡張ポイントは、リソースを登録するときに役立ちます(下記参照)。

ament_register_extensionは、必ず3つの引数をとる関数です。

  • extension_point:拡張ポイントの名前(ほとんどの場合、これはament_packageまたはrosidl_generate_interfacesのいずれかになります)
  • package_name:CMake ファイルを含むパッケージの名前(すなわち、ファイルが書き込まれているプロジェクトのプロジェクト名)
  • cmake_filename:拡張ポイントが実行されたときに実行されるcmakeファイル

注意:ament_packagerosidl_generate_interfacesと同じようにカスタム拡張ポイントを定義することは可能ですが、これはほとんど必要ないでしょう。

拡張ポイントの追加

ごくまれに、ament への新しい拡張ポイントを定義したくなることもあるでしょう。

対応するマクロが呼び出されたときにすべての拡張が実行されるように、拡張ポイントをマクロ内に登録できます。以下のように行います。

  • 登録したい拡張の名前(例えばmy_extension_point)を定義して、ファイルとして保存してください。これは拡張ポイントを使うときにament_register_extensionマクロに渡される名前です。
  • 拡張機能を実行するマクロ/関数では、次のように呼び出します。
ament_execute_extensions(my_extension_point)

ament 拡張は、拡張ポイントの名前を含む変数を定義し、それに実行するマクロを入れることによって機能します。 ament_execute_extensionsを呼び出すと、変数に定義されたスクリプトが次々に実行されます。

リソースを追加する

特にプラグインやプラグインを利用するパッケージを開発するときは、ある ROS パッケージから別の ROS パッケージにリソースを追加することが不可欠です(例:プラグイン)。例としては、pluginlib を使ったツール用のプラグインがあります。

これは、ament インデックス(「リソースインデックス」とも呼ばれる)を使用して実現できます。

ament インデックスの説明

設計の詳細については、こちらを参照してください。

原則として、ament インデックスはパッケージの install/share 内のフォルダに含まれています。さまざまな種類のリソースにちなんだ名称のサブフォルダが含まれています。サブフォルダ内では、上記リソースを提供する各パッケージは「マーカファイル」という名で参照されます。ファイルには、リソースを取得するために必要なあらゆるコンテンツが含まれます。(リソースのインストールディレクトリへの相対パスが例。それは単に空の場合もありますが。)

例として、RViz 用の表示プラグインを提供することを検討してください。pluginlib によって読み込まれるmy_rviz_displaysという名前のプロジェクトで RViz プラグインを提供するときは、pluginlib によってインストールおよび使用されるplugin_description.xmlファイルを提供します。プラグインこれを実現するために、plugin_description.xml は次のようにして resource_index にリソースとして登録されます。

pluginlib_export_plugin_description_file(rviz_common plugins_description.xml)

colcon buildを実行すると、my_rviz_displaysファイルを resource_index のサブフォルダrviz_common__pluginlib__pluginにインストールします。 rviz_common 内の Pluginlib モジュールは、プラグインをエクスポートするパッケージについて、rviz_common__pluginlib__pluginという名称の全フォルダから情報を収集することを認識します。 pluginlib モジュール用のマーカーファイルには、plugins_description.xmlファイルへのインストールフォルダへの相対パス(およびマーカファイル名としてのライブラリの名前)が含まれています。この情報を使って、pluginlib はライブラリをロードし、どのプラグインをplugin_description.xmlファイルからロードするかを知ることができます。

2つ目の例として、RViz プラグインがあなた自身のカスタムメッシュを使うようにする可能性を考えてください。メッシュは起動時にロードされるため、プラグインの所有者はそれを処理する必要はありませんが、これは RViz がメッシュについて知っている必要があることを意味します。これを達成するための関数を RViz は備えています。

register_rviz_ogre_media_exports(DIRECTORIES <my_dirs>)

これは、ディレクトリを agre_media リソースとして ament インデックスに登録します。つまり、関数を呼び出すプロジェクト名のファイルをrviz_ogre_media_exportsというサブフォルダーにインストールします。このファイルには、マクロにリストされているディレクトリへのインストールフォルダへの相対パスが含まれています。RViz の起動時、rviz_ogre_media_exportsという名前のすべてのフォルダを検索し、指定された全フォルダにリソースをロードできるようになりました。これらの検索はament_index_cpp(または Python パッケージの場合はament_index_py)を使って行われます。

以下のセクションでは、カスタマイズしたリソースを ament index に追加し、そうするためのベストプラクティスを提供する方法を探ります。

ament インデックスへの問い合わせ

必要ならば、CMake を介して ament インデックスに対してリソースの問い合わせが可能です。これについては、3 つの機能があります。

  • ament_index_has_resource:以下のパラメータで存在する場合、リソースへのプレフィックスパスを取得します。
  • var:出力パラメータ:リソースが存在しない場合はこの変数にFALSEを、存在する場合はリソースへのプレフィックスパスを設定します。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • resource_name:通常resource_type 型のリソースを追加したパッケージの名前に相当するリソースの名前(例:rviz_default_plugins

  • ament_index_get_resource:特定のリソースの内容、すなわち ament インデックス内のマーカファイルの内容を取得する。

  • var:出力パラメータ:リソースマーカファイルが存在する場合はその内容が代入されます。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • resource_name:通常resource_type型のリソースを追加したパッケージの名前に相当するリソースの名前(例:rviz_default_plugins
  • PREFIX_PATH:検索するプレフィックスパス(通常、デフォルトのament_index_get_prefix_path()で十分です)。

リソースが存在しない場合、ament_index_get_resourceはエラーをスローするので、ament_index_has_resourceを使用して確認する必要があるかもしれません。

  • ament_index_get_resources:インデックスから特定の種類のリソースを登録したすべてのパッケージを取得します。
  • var:出力パラメータ:resource_typeのリソースを登録した全てのパッケージ名のリストが代入されます。
    • resource_type:リソースの種類(例:rviz_common__pluginlib__plugin
  • PREFIX_PATH:検索するプレフィックスパス(通常、デフォルトの`ament_index_get_prefix_path() で十分です)。

ament インデックスへの追加

リソースを定義するには、2 ビットの情報が必要です。

  • ユニークなリソース名
  • マーカファイルのレイアウト。これは何でもかまいませんし、空にすることもできます(例えば ROS 2 パッケージをマークする“package”リソースに当てはまります)。

RVizメッシュリソースの場合、対応する選択肢は次のとおりです。

  • リソースの名前として rviz_ogre_media_exports
  • リソースを含むすべてのフォルダへの相対パスをインストールします。これにより、パッケージ内の対応するリソースを使用するためのロジックを記述できるようになります。

パッケージのリソース登録を容易にするには、さらに、pluginlib関数やrviz_ogre_media_exportsなどのマクロや関数を用意する必要があります。

リソースを登録するには、ament関数ament_index_register_resourceを使用します。これによりマーカファイルが作成され、resource_indexにインストールされます。例として、rviz_ogre_media_exportsの対応する呼び出しは以下のとおりです。

ament_index_register_resource(rviz_ogre_media_exports CONTENT ${OGRE_MEDIA_RESOURCE_FILE})

これにより、${PROJECT_NAME}のような名前のファイルが、変数${OGRE_MEDIA_RESOURCE_FILE}で指定された内容で resource_index の中にあるrviz_ogre_media_exportsフォルダにインストールされます。マクロには色々な便利パラメータが用意されています。

  • 最初の(名前なし)パラメータはリソースの名前です。これは、resource_index 内のフォルダの名前になります。
  • CONTENT:文字列としてのマーカファイルの内容。これは相対パスのリストなどです。CONTENTCONTENT_FILEと一緒に使用することはできません。
  • CONTENT_FILE:マーカファイル作成のために使用されるファイルへのパス。ファイルはプレーンファイルでも、configure_file()で展開されたテンプレートファイルでも、どちらでも構いません。CONTENT_FILECONTENTと一緒に使用することはできません。
  • PACKAGE_NAME:リソースをエクスポートしているパッケージ/ライブラリの名前。マーカファイルの名前になります。デフォルトは${PROJECT_NAME}です。
  • AMENT_INDEX_BINARY_DIR:生成された ament インデックスの基底パス。本当に必要な場合以外は、常にデフォルトの${CMAKE_BINARY_DIR}/ament_cmake_indexを使用してください。
  • SKIP_INSTALL:マーカファイルのインストールをスキップします。

パッケージごとに存在するマーカファイルは1つだけなので、cmake 関数/マクロが同じプロジェクトによって 2 回呼び出されると、通常は問題になります。ただし、大規模プロジェクトでは、リソースを登録する呼び出しを分割するのが最善の方法です。

したがって、register_rviz_ogre_media_exports.cmakeなどのリソースを登録するマクロにいくつかの変数のみを設定させることがベストプラクティスとなります。 ament_index_register_resourceの実際の呼び出しは、ament_packageへのament 拡張内で追加できます。プロジェクトごとにament_packageを呼び出すのは 1 回限りでなければならないため、リソースが登録される場所は常に1つのみとなります。 rviz_ogre_media_exportsの場合、これは次のような方針となります。

  • マクロregister_rviz_ogre_media_exportsはフォルダのリストを受け取り、それらをOGRE_MEDIA_RESOURCE_FILEという変数に追加します。

${OGRE_MEDIA_RESOURCE_FILE}が空でない場合、register_rviz_ogre_media_exports_hookという別のマクロがament_index_register_resourceを呼び出します。

register_rviz_ogre_media_exports_hook.cmake は、呼び出しによって3番目のファイル register_rviz_ogre_media_exports_hook-extras.cmakeament 拡張として登録されます。

ament_register_extension("ament_package" "rviz_rendering"
  "register_rviz_ogre_media_exports_hook.cmake")

register_rviz_ogre_media_exports.cmakeおよびregister_rviz_ogre_media_exports_hook-extra.cmakeは、ament_package()CONFIG_EXTRAとして登録されます。

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【ROS 2】クロスコンパイル(公式文書和訳)

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

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

www.moriken254.com

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

概要

Open Robotics はマルチプラットフォーム用にビルド済みの ROS 2 パッケージを提供していますが、それでも以下のようなさまざまな理由で多くの開発者がクロスコンパイルに頼っている状況です。

  • 開発マシンがターゲットシステムと一致しない。
  • 特定のコアアーキテクチャ向けにビルドを調整する(例:Raspberry Pi 3 用にビルドする場合は -mcpu = cortex-a53 -mfpu = neon-fp-armv8と設定)。
  • Open Robotics がリリースしたビルド済みイメージでサポートされているもの以外の異なるファイルシステムをターゲットにしている。

このドキュメントでは、ROS 2 ソフトウェアスタックをクロスコンパイルする方法の詳細と、Arm コアベースのシステムへのクロスコンパイルの例を紹介します。

  • 注意
    • ビルド中に無効にしなければならない(クロスコンパイルが無効な) ROS 2 パッケージがいくつかあります。 4. ビルドの章を参照してください。

どう動くのか?

単純なソフトウェアのクロスコンパイル(外部ライブラリに依存しないなど)は比較的単純で、ネイティブツールチェーンの代わりにクロスコンパイラツールチェーンを使用するだけで済みます。

このプロセスをより複雑にする要因はいくつかあります。

  • 構築中のソフトウェアは、ターゲットアーキテクチャをサポートしている必要があります。ターゲットアーキテクチャに応じて、アーキテクチャ固有のコードをビルド中に適切に分離して有効にする必要があります。例としてはアセンブリコードがあります。
  • すべての依存関係(ライブラリなど)は、ビルド済みパッケージとして存在するか、またはそれらを使用するターゲットソフトウェアがクロスコンパイルされる前にクロスコンパイルされる必要があります。
  • ビルドツール(colcon など)を使用して(スタンドアロンソフトウェアとは対照的に)ソフトウェアスタックをビルドする場合、開発者がスタック内の各ソフトウェアで使用されている基盤ビルドシステムでクロスコンパイルを有効にできる仕組みが必要です。

ROS 2 のクロスコンパイル

ROS 2 は数多くの依存関係を持つ豊富なソフトウェアスタックですが、主に 2 種類のパッケージを使用しています。

  • クロスコンパイルを必要としない Python ベースのソフトウェア。
  • クロスコンパイルを行うためのメカニズムを提供する CMake ベースのソフトウェア。

さらに、ROS 2 ソフトウェアスタックは、各パッケージ/ライブラリの個々のビルドに使用される CMake インスタンスにパラメータを転送する仕組みをもつ Colcon でビルドされています。

ROS 2 をネイティブでビルドする場合、(ROS 2 ディストリビューションの一部である)パッケージをコンパイルする前にすべての依存関係(例えば Python と他のライブラリ)をダウンロードする必要があります。クロスコンパイルするときも、同様です。最初にターゲットシステムのファイルシステムにすべての依存関係がインストールされている必要があります。

次の章では、ROS 2を クロスコンパイルするための cmake-toolchainsCMAKE_SYSROOT 機能の使用方法について詳しく説明します。

CMake toolchain-file

CMake toolchain-file は、クロスコンパイル用に CMake を設定するための変数を定義するファイルです。基本的なエントリは以下のとおりです。

  • CMAKE_SYSTEM_NAME:ターゲットプラットフォーム。例: linux
  • CMAKE_SYSTEM_PROCESSOR:ターゲットアーキテクチャ。例: aarch64arm
  • CMAKE_SYSROOT:ターゲットファイルシステムへのパス
  • CMAKE_C_COMPILER:Cクロスコンパイラ。例:aarch64-linux-gnu-gcc
  • CMAKE_CXX_COMPILER:C ++クロスコンパイラ。例:aarch64-linux-gnu-g++
  • CMAKE_FIND_ROOT_PATH:ファイルシステムを見つけるためにfind_*コマンドによって使用される代替パス

ROS 2 をクロスコンパイルするときは、次のオプションを設定する必要があります。

  • CMAKE_FIND_ROOT_PATHfind_*コマンドによって使用される代替パス。これを使用して ROS 2 /install フォルダへのパスを指定します。
  • CMAKE_FIND_ROOT_PATH_MODE_*:プログラム、パッケージ、ライブラリ、およびインクルードの検索方法。通常は、NEVER(host-fs を検索)、ONLY(sysroot を検索)、ONLY(sysroot を検索)、およびONLY(sysrootを検索)
  • PYTHON_SOABI:ROS 2 によって生成された Python ライブラリのインデックス名。例: cpython-36m-aarch64-linux-gnu
  • THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE:バイナリはホストシステムで実行できないため、TRY_RUNコマンドの結果を矯正的に 0(成功)にします。

toolchain-file は、-DCMAKE_TOOLCHAIN_FILE=path/to/fileパラメータでパスを指定して CMake に提供されます。これにより、ビルド中のソフトウェアのCMAKE_CROSSCOMPILING変数もtrueに設定されます。

CMAKE_SYSROOT は ROS 2 にとって特に重要です。パッケージには多くの依存関係(python、openssl、opencv、poco、eigen3 など)が必要だからです。すべての依存関係がインストールされているターゲットファイルシステムにCMAKE_SYSROOTを設定すると、CMake はクロスコンパイル中にそれらを見つけることができます。

  • 注意
    • あなたは CMake のページでより多くの情報を見つけることができます。

ROS 2 ソースコードをダウンロードするとき、一般的な toolchain-file はリポジトリ ros2/cross_compile/cmake-toolchains にあり、別々にダウンロードすることができます。それを使用することに関する更なる例は、Arm のクロスコンパイルの章に記してあります。

対象ファイルシステム

前述のように、ROS2はクロスコンパイルするために提供される必要がある異なるライブラリを必要とします。

ファイルシステムを取得する方法はいくつかあります。

  • ビルド済みイメージをダウンロードする。
  • ターゲットへの依存関係のインストールとファイルシステムのエクスポート(sshfs などを使用)をする。
  • qemu + docker(または chroot)を使ってホストマシン上にファイルシステムを生成する。

 

  • 注意
    • Docker + qemuの使い方については、次の Arm のクロスコンパイルの章を参照してください。

ビルドプロセス

ビルドプロセスはネイティブコンパイルと似ています。唯一の違いは、toolchain-file を指定するための Colcon への追加の引数です。

colcon build --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_TOOLCHAIN_FILE="<path_to_toolchain/toolchainfile.cmake>"

toolchain-fileは、クロスコンパイラターゲットファイルシステムの情報を CMake に提供します。 colcon は ROS 2 のすべてのパッケージで与えられたtoolchain-fileで CMake を呼び出します。

Arm のクロスコンパイルの例

ROS 2 のソースコードをダウンロードした後、git clone https://github.com/ros2/cross_compile.git src/ros2/cross_compileでクロスコンパイルアセットをワークスペースに追加できます。これらは、Arm コアのクロスコンパイル方法に関する実用的な例です。

以下のターゲットがサポートされています。

  • Ubuntu-arm64:ARMv8-A ベースのシステムで使用します。
  • Ubuntu-armhf:最新の ARMv7-A ベースのシステムと一緒に使用します。

主な手順は次のとおりです。

  • 開発ツールのインストール
  • ROS2ソースコードのダウンロード
  • ROS2クロスコンパイルアセットのダウンロード
  • sysrootを準備する
  • ROS2ソフトウェアスタックのクロスコンパイル

次のセクションでは、これらの各ステップについて詳しく説明します。簡易設定もありますので、それについては自動クロスコンパイルを見てください。

  • 注意
    • これらのステップは Ubuntu 18.04(Bionic)でテストされました。

1. 開発ツールをインストールする

このステップは、ネイティブに構築する場合と似ていますが、違いもあります。ライブラリとツールの一部は、代わりに sysroot に含まれるため、必要とされないライブラリとツールがあります。以下のパッケージが必要です

sudo apt update && sudo apt install -y \
    cmake \
    git \
    wget \
    python3-pip \
    qemu-user-static \
    g++-aarch64-linux-gnu \
    g++-arm-linux-gnueabihf \
    pkg-config-aarch64-linux-gnu

python3 -m pip install -U \
    vcstool \
    colcon-common-extensions
  • 注意
    • pip を使って vcstool と colcon-common-extensions をインストールできますので、特別な apt リポジトリを追加する必要はありません。

Docker はターゲット環境を構築するために使用されます。インストールについてはDcoker の公式文書に従ってください。

2. ROS 2 のソースコードをダウンロードする

次にワークスペースを作成して ROS 2 ソースコードをダウンロードします。

mkdir -p ~/cc_ws/ros2_ws/src
cd ~/cc_ws/ros2_ws
wget https://raw.githubusercontent.com/ros2/ros2/release-latest/ros2.repos
vcs-import src < ros2.repos
git clone https://github.com/ros2/cross_compile.git src/ros2/cross_compile
cd ..

3. sysroot を準備する

Docker と qemu を使ってすべての ROS 2 依存関係について解決済みの arm Ubuntu イメージをビルドします。qemu-staticバイナリをワークスペースにコピーします。これは、dockerを 使用して ros2 依存関係をターゲットファイルシステムにインストールするために使用されます。

mkdir qemu-user-static
cp /usr/bin/qemu-*-static qemu-user-static

ROS 2 の標準セットアッププロセスが、arm docker 内で実行されます。これは、arm マシンをエミュレートするqemu-staticのおかげで可能となっています。使用されているベースイメージは Docker Hub の Ubuntu Bionic です。

docker build -t arm_ros2:latest -f ros2_ws/src/ros2/cross_compile/sysroot/Dockerfile_ubuntu_arm .
docker run --name arm_sysroot arm_ros2:latest

結果のコンテナをTAR形式にエクスポートして抽出します。

docker container export -o sysroot_docker.tar arm_sysroot
mkdir sysroot_docker
tar -C sysroot_docker -xf sysroot_docker.tar lib usr opt etc
docker rm arm_sysroot

このコンテナは、作成されたファイルシステムを実行してデモコードを実行するための仮想ターゲットとして、後ほど使用できます。

4. ビルド

汎用toolchain-fileで使用される変数を設定します

export TARGET_ARCH=aarch64
export TARGET_TRIPLE=aarch64-linux-gnu
export CC=/usr/bin/$TARGET_TRIPLE-gcc
export CXX=/usr/bin/$TARGET_TRIPLE-g++
export CROSS_COMPILE=/usr/bin/$TARGET_TRIPLE-
export SYSROOT=~/cc_ws/sysroot_docker
export ROS2_INSTALL_PATH=~/cc_ws/ros2_ws/install
export PYTHON_SOABI=cpython-36m-$TARGET_TRIPLE

以下のパッケージは、クロスコンパイル中にエラーを引き起こします(調査中)。今のところ無効にする必要があります。

touch \
    ros2_ws/src/ros2/rviz/COLCON_IGNORE \
    ros2_ws/src/ros-visualization/COLCON_IGNORE

構築済みのPocoには、SYSROOT ではなくホストシステムでlibzlibpcreを検索するという既知の問題があります。現時点での回避策として、両方のライブラリをホストのファイルシステムにリンクしてください。

mkdir -p /usr/lib/$TARGET_TRIPLE
ln -s `pwd`/sysroot_docker/lib/$TARGET_TRIPLE/libz.so.1 /usr/lib/$TARGET_TRIPLE/libz.so
ln -s `pwd`/sysroot_docker/lib/$TARGET_TRIPLE/libpcre.so.3 /usr/lib/$TARGET_TRIPLE/libpcre.so

次に、toolconin-fileを指定して colcon でビルドを開始します。

cd ros2_ws

colcon build --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
        -DCMAKE_TOOLCHAIN_FILE="$(pwd)/src/ros2/cross_compile/cmake-toolchains/generic_linux.cmake" \
        -DSECURITY=ON

完了です!インストールディレクトリとビルドディレクトリには、クロスコンパイルされたファイル(クロスコンパイルアセット)が出力されます。

自動クロスコンパイル

上記のステップはすべて Dockerfile にも含まれており、自動化/CI に使用できます。

まず、dockerfile をダウンロードしてイメージを構築します。

wget https://raw.githubusercontent.com/ros2/cross_compile/master/Dockerfile_cc_for_arm
docker build -t ros2-crosscompiler:latest - < Dockerfile_cc_for_arm

それでは、イメージを実行します。(しばらく時間がかかります。。。)

docker run -it --name ros2_cc \
    -v /var/run/docker.sock:/var/run/docker.sock \
    ros2-crosscompiler:latest
  • メモ
    • -v /var/run/docker.sockにより、DockerをDocker内で使用することができます。

ビルドの結果はros2_wsディレクトリ内になります。このディレクトリは次のコマンドでエクスポートできます。

docker cp ros2_cc:/root/cc_ws/ros2_ws .

ビルド済みの ROS 2 に対するクロスコンパイル

事前に構築された ROS 2 に対して自作パッケージをクロスコンパイルすることも可能です。以下の変更を加えて、ステップは前述の Arm クロスコンパイルの章で示した例と似ています。

ROS 2 スタックをダウンロードする代わりに、自身のワークスペースにパッケージ(この場合は ros2 の例)とクロスコンパイルアセットを追加するだけです。

mkdir -p ~/cc_ws/ros2_ws/src
cd ~/cc_ws/ros2_ws/src
git clone https://github.com/ros2/examples.git
git clone https://github.com/ros2/cross_compile.git
cd ..

3. sysroot を準備するの説明に従って、ファイルシステムを生成してエクスポートします(ただし、提供されている Dockerfile_ubuntu_arm64_prebuilt を使用します)。これらの_prebuilt Dockerfile は、ソースからビルドするのではなく、バイナリパッケージを使用して ROS 2 をインストールします。

インストールディレクトリを指すように環境変数 ROS2_INSTALL_PATH を変更します。

export ROS2_INSTALL_PATH=~/cc_ws/sysroot_docker/opt/ros/crystal

ターゲットファイルシステムのsetup.bashスクリプトを読み込みます。

source $ROS2_INSTALL_PATH/setup.bash

次に、toolconin-fileを指定してColconでビルドを開始します。

colcon build \
    --merge-install \
    --cmake-force-configure \
    --cmake-args \
        -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
        -DCMAKE_TOOLCHAIN_FILE="$(pwd)/src/cross_compile/cmake-toolchains/generic_linux.cmake"

ターゲットで実行

ターゲットにファイルシステムをコピーするか、以前に作成したdockerイメージを使用します。

docker run -it --rm -v `pwd`/ros2_ws:/ros2_ws arm_ros2:latest

環境を取り込みます。

source /ros2_ws/install/local_setup.bash

C++ または python の例をいくつか実行します。

ros2 run demo_nodes_cpp listener &
ros2 run demo_nodes_py talker

翻訳元文書

index.ros.org

関連文書

www.moriken254.com

www.moriken254.com

【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