Amazon Web Services ブログ

AWS RoboMakerでROSアプリケーションのビルドとバンドルをおこなう

12か月前から、クラウドロボティクスサービスであるAWS RoboMakerの開発を開始しました。 私たちに直面していた最大の疑問の1つは、次のとおりでした。サービス上でROSアプリケーションを簡単に実行できるようにするにはどうすればよいのか。 ロボットアプリケーションは、多数の依存関係を持つさまざまなパッケージが大量に混在しています。 シミュレーションも含まれると、その依存関係のリストがさらに増えます。 多くの検討と研究の結果、私たちはappimageflatpaksnapcraftに触発され、ローカルのの開発環境でも私たちのサービスでも使える単一のファイルフォーマットを作成しました。 このフォーマットをbundleと呼びます。

パッケージングアーティファクトをどのような設計するかを決定した後、生成を容易にするためのコマンドラインツールを作成したいと思いました。 ツールが既存のROSエコシステム内に収まることが私たちにとって非常に重要でした。 その結果ROSのビルドツールの中で最新かつ最高のものであるcolconの上に構築することにしました。 ColconはROS1ROS2アプリケーションを構築できます。 またそれは非常に拡張性があり、革新的で重要な機能の多くを提供します。

この記事では、既存のROSビルドツールと、なぜcolconがROS2のビルドツールとして選ばれたのかを説明しています。 Colconは私たちの代わりに多くの大変な作業を肩代わりすることにより、私たちは特定の機能に集中することができます。 さらに、colcon buildcatkin_makeament_makeを利用してビルドしていたパッケージを変更することなく置き換えます。 つまり、colconの上にツールを構築することで、AWS RoboMakerユーザーは単一のツールをインストールしてフルワークフローを実行し、AWS RoboMakerで使用するためのbundleを生成できます。

この記事では、AWS RoboMakerで使用するbundleを作成するための通常のワークフローと、頻繁に発生する問題に対するいくつかの解決策について説明します。 2番目のセクションでは、最新バージョンの「bundle」ファイルフォーマットについて詳しく説明し、colcon bundleを実行してbundleを作成するときに何が起こるかについて概要を説明します。

なぜbundleなのか

ROSシミュレーションワークフローには、ROSおよびその他のアプリケーションのランタイム依存関係がインストールされた環境が必要です。 通常は、さまざまなapt-get installコマンドを実行することによって実現されます。 このインストールは時間がかかり、さらに公開されているパッケージのアップデートはいつでも発生する可能性があるため、同じインストールを確実に再現することはできません。 私たちのサービスでは、シミュレーションを実行するたびにaptを使用して新しい環境をインストールすることの信頼性を心配していたので、さまざまなパッケージ形式を調べました。 ROSアプリケーション全体で最も魅力的で信頼性の高い配布形式はDebianパッケージでした。 それはROSのロボットを動かすのに必要な、boostまたは他の外部ライブラリ、そして実際のアプリケーションコードなど、すべてを含むことができます。

ダウンロード、作成、および更新のための最適化を柔軟に追加できるようにするため、既存のフォーマットを使用しないことにしました。 そこで私たちは「bundle」フォーマットと、それをサポートするための対応するツールを作成しました。 最初のバージョンはうまくいきましたが、以下のようないくつかの制限がありました。

  • ワークスペースの更新のみでbundleを更新するのは、最初のcolcon bundleの呼び出しと同じくらいの時間がかかりました。
  • 更新されたbundleをロボットにデプロイすることは、ダウンロードとbundleコンテンツの抽出の両方において、期待したほど速くはありませんでした。

これらの制限を軽減または削除するための最適化を含むbundle形式の、バージョン2を発表できることに興奮しています。 フォーマットは現在部分的なダウンロードと部分的な抽出をサポートしています。 これには3つの大きな利点があります。

  • 2G / 3Gといった帯域幅に制約のあるリモートデバイスであるロボットのアップデートで、使用される帯域幅を削減することができました
  • 必要なものだけを抽出するようになりました。bundleの大部分が変更されていない場合は、ダウンロードまたは展開に無駄なコストを負う必要はありません。 ローエンドのデバイスでは、これにより大幅な時間の節約になります(TurtleBotsでは、デプロイにかかる時間が60分から1分に短縮されました)
  • 全体のアーカイブは圧縮されていないので、段階的にbundleすることができます。 これにより、ワークスペースを簡単に更新するための再bundle時間が5分以上から数秒に短縮されます

AWS RoboMakerはこの新しいバージョンをサポートしています。 これらの新しい機能を試してみたい場合は、開発環境でsudo pip3 install -U colcon-bundleを実行してください。 このブログの後半では、この新しい形式の実装について詳しく説明します。 質問、アイデア、または問題がある場合は、GitHub issueに追加してください。

colconを使ってAWS RoboMaker用のbundleを作成する

ワークスペースをcolconでbundleするには、2つのステップのみです。

  • ワークスペースを colcon build でビルドする
  • ワークスペースを colcon bundle でbundleを作成する

ビルド

catkin_makeでビルドできるすべてのROSパッケージは、colcon buildでもビルドできます。 何か月もの間ROSパッケージを使って作業してきましたが、colcon buildを使ってパッケージを作成する際に発生する問題はごくわずかでした。 catkin_makeで正常にビルドされるcolcon buildでパッケージをビルドする際に問題が発生した場合はcolcon-ros issuesで報告してください。

注意catkin_makeからcolcon buildへの最も大きな変更は、develディレクトリがなくなったことです。 ワークスペースをローカル環境に追加したい場合は、代わりにsource install/setup.shを実行してください。

bundle

ワークスペースをcolcon buildでビルドしたら、buildinstallディレクトリが作成されます。 bundleを開始するには、colcon bundleを実行してください。 私たちのツールは依存関係を検査し、それら全てをダウンロードし、そしてそれらをビルドされたワークスペースと一緒にまとめてAWS RoboMakerで使用するために圧縮します。 構築されたbundleはbundle/output.tarにあります。

ビルド時の一般的な問題

ROS1開発のための標準のビルド手順はcatkin_makeを使用します。 colconは比較的新しいので、多くのROS1開発者はそれに慣れていないかもしれませんが、catkinビルドと完全に互換性があります。 colcon buildで既存のROSパッケージ(以前はcatkin_makeでビルドされていました)をビルドしている間に、いくつかの小さな問題に遭遇しました。

階層ディレクトリ内のCMAKElists.txtファイル

私たちが遭遇した一般的なビルドの問題は、colconがワークスペース内のすべてのパッケージを正しく列挙できず、colcon bundleを実行した際に正しくないディレクトリ構造となり失敗します。 これは、ネストされたディレクトリ構造でCMakeLists.txtが正しくないディレクトリにあることが理由で発生します。 catkin_makeを使用してディレクトリ内にネストされたパッケージを構築するために必要ですが、colconで問題を引き起こします。 Colconは深い入れ子のフォルダ構造をサポートしているので、パッケージを自動的に見つけます。 以下は、ディレクトリ構造の例です。

src/
├── package_1/
│ ├── package.xml
│ └── CMakeLists.txt (OK)
└── intermediate_directory/
    ├── package_2
    │ ├── package.xml
    │ └── CMakeLists.txt (OK)
    ├── package_3
    │ ├── package.xml
    │ └── CMakeLists.txt (OK)
    └── CMakeLists.txt (REMOVE)

CMAKELists.txt内のインストール情報が不足

私達が遭遇するもう一つの問題はファイルが足りないことです。 catkin_makeを使用するとき、develディレクトリとitssetup.shはすべてのローカルパッケージをROSツールの検索パスに追加します。 しかし、Colconの動作は異なります。指定したすべてのターゲットがinstallディレクトリにインストールされます。つまり、CMakeinstall()ディレクティブはすべて実行されます。 次のようなエラーが発生した場合

[a_launchfile] is neither a launch file in package [a_package] nor is [a_package] a launch file name

または、

[rosrun] Couldn't find executable named my_node below /opt/ros/kinetic/share/my_package

この場合CMakeLists.txtinstall()の呼び出しを追加すると問題が解決する可能性があります。 これを行う方法の優れた例がROS Wikiにあります。

bundle実行時の問題

AWS RoboMakerでアプリケーションのbundleを処理している間に発生した最も頻繁な問題です。

Cannot Locate rosdep Definition for [Package Name]

利用したいパッケージに正しいROSパッケージ名が使用されているかを確認してください。 パッケージの名前はaptではros-kinetic-packagenameですが、package.xmlではpackagenameになります。 rosdistro GitHubレポジトリを検索し、利用したいパッケージ名が存在するかを確認してください。 ない場合は、rosdepに依存関係を追加するためのチュートリアルを参考にしてください。

Missing Dependencies

さまざまなオープンソースのROSライブラリをbundleしているときによく遭遇した問題は、依存関係が欠けていたことです。 多くの場合、ビルド環境には別のパッケージのために既に依存関係がインストールされており、すべてが正常にビルドされますが、パッケージがbundleされた後はその依存関係は失われます。依存関係が欠けているときの一般的なエラーメッセージは、Could not load libxzy.soNo such file or directory some_script.pyCould not load module 'python_dependency'。これを解決するには、それを必要とするパッケージのpackage.xmlに依存関係を追加して、bundleを再実行します。

独自のaptまたはpipリポジトリからアプリケーションで依存関係を使用する場合は、それらのリポジトリをcolcon bundleに含める必要があります。 これを解決するには、次のことを試してください。

  • colcon bundleaptインストーラの--apt-sources-list引数でsources.listを上書きすることができます。 ベースOSおよびROSパッケージの解決エラーを避けるために、現在使用しているすべてのソースを含めることをお勧めします。 GitHubのexamplesources.listを調べてください。
  • pipまたはpip3ソースを上書きするには、--pip-argsまたは--pip3-args引数を使用します。 この引数の後の文字列は、pipに直接渡されます。例:--extra-index-url https://my-custom-pip-repo/index

Build と Bundle の完了

これで、AWS RoboMakerでシミュレートするためにアプリケーションを構築してbundleすることができます!パッケージを最新バージョンにアップグレードするには、開発環境で「sudo pip3 install -U colcon-bundle colcon-ros-bundle」を実行します。 ご質問がある場合や問題が発生した場合は、関連するGitHubリポジトリに投稿してください。お手伝いいたします。 colcon bundleが実行されたときに実際に何が起きているのかに興味があるなら、読んでください!

Bundle Deep Dive

このセクションでは、bundleフォーマットの内部とcolcon bundleの実装について技術的に詳しく説明します。 このセクションの終わりには、colcon bundleが実行されたときに何が起きているのかを理解することができます。 それがいかに簡単であるかを理解した後、あなたが作成したツールをcolconのエコシステムにコントリビュートすることを願っています。

Bundle File Format

「bundle」とは、ROSアプリケーション全体を実行するためにAWS RoboMakerによって使用されるパッケージ形式です。それはロボットに必要なものをすべて含んだもの、または対応するシミュレーションアプリケーションを含みます。 ROSは、依存関係の宣言方法と使用する構築ツールを指定する標準のパッケージ形式をすでに定義しています。 colconを使用して、このツールは情報を収集し、さまざまなパッケージインストーラ(現在はpipapt)を起動して依存関係を解決し、それらの依存関係をローカルステージング領域にインストールします。そして、それらをローカルの構築済みワークスペースと一緒にまとめてAWS RoboMakerで使用できるようにbundleします。

そのワークフローが私たちが「bundle」と呼ぶものを生み出します(私たちはそれを退屈な名前にしています – あなたがこのフォーマットの命名のアイデアをお持ちの場合は、私たちのリポジトリで問題を開いてください)。 bundle形式の仕様は、colcon-bundleリポジトリにあります。 ここで概要を説明します。

この図は私達のbundleフォーマットのV2を示しています。 ここで履歴を見ることができます。 「bundle」ファイルは標準のGNU tarフォーマットに従います。 最低3つのコンポーネントがあります。

  • バージョンファイルは、後方互換性のない変更を含むbundleバージョンを区別するために使用できます
  • メタデータtarballは、少なくともbundle内に含まれるオーバーレイのリストを含み、bundleが関連を持つ可能性のあるその他のメタデータも含みます
  • 少なくとも1つのオーバーレイを含みます。 ROSワークスペースはワークスペース オーバーレイの概念をサポートしています。 この概念を採用して、アプリケーション全体に適用しました。 これにより、AWS RoboMakerは、ベースのOSを変更することなく、Linuxオペレーティングシステム上でROSアプリケーションを実行できます

上記のフォーマットの最新バージョンの利点について概説しました。 ここからはV1とV2の間の主な変更点です。

  • 最終的なbundleファイルのフォーマットを.tar.gzから .tarに変更しました。これによりファイル内のランダムアクセスを許可します。 コンテンツの一部をすでに持っている場合は、変更されたコンテンツのみをダウンロードできるので、これは素晴らしいことです。 個々のオーバーレイを圧縮すると、gzipは複数の小さいファイルほど効率的ではないため、ファイル全体のサイズに影響を与えます。 しかし、これが問題になるとは思いません。なぜならメジャーアップデートのユースケースでは、変更された部分だけをダウンロードできるようになったからです。
  • 単一のオーバーレイから任意の数のオーバーレイに変更し、各オーバーレイをgzipします。これにより、bundle形式の柔軟性が大幅に向上します。 上記のストレートtarへの変更により、各オーバーレイは個別にアクセス可能になりました。 更新ユースケースに合わせて最適化するために、必要なだけオーバーレイを用意する柔軟性があります。 最も詳細なレベルでは、各ライブラリは別々のオーバーレイに存在する可能性があり、1つのライブラリが変更された場合、それがロボットによってダウンロードされる必要がある唯一の変更です(それ以外が最新の場合)。 これにより、リモートデバイスの帯域幅と時間が大幅に節約されます。
  • overlays.jsonをメタデータに追加します。このファイルには、ワークスペースに適用するオーバーレイの順序付きリスト、およびオーバーレイのバージョン間の比較に使用できるハッシュが含まれています。 このメタデータは、任意の数のオーバーレイを持つことを可能にし、環境が正しい順番で構築されることを保証します。ハッシュで特定のオーバーレイが変更されたことをわかるため、インテリジェントなダウンロードと抽出が可能になります。

全体として、私たちはこれらの改良に非常に満足しています。 もし何かアイデアがありましたら、GitHub issueに登録してください。

colcon bundle の実行フロー

このセクションでは、colcon bundleを実行したときに実際に何が起きているのかを説明します。 (これは、私たちのツールをcolconの上に構築する素晴らしい経験でした。あなたが特別なビルドツールを作成しようとしているのなら、colconが基本フレームワークとして素晴らしいので、見ることを強くお勧めします。)

colconエコシステムは、直接の依存関係と、基本としてのcolcon-coreのみに依存する個々のPythonパッケージで構成されています。 colconのほとんどすべての機能は、colcon-coreの基本インフラストラクチャによって定義されたExtensionPointとしてモデル化されています。 colconは、モジュール性と疎結合という目的を達成するためのプラグインメカニズムとして、setuptoolsエントリポイントを利用します。

コマンドラインインターフェースを拡張するために、colconVerbExtensionPointsを介したコマンドの追加をサポートしています。 機能を構築するために、bundle verb(サブコマンド)を作成し、それをsetup.cfgに登録しました。 この素晴らしいツールに統合されたコマンドを取り入れるためにしなければならなかったのはこれですべてです。 (それ以降の作業はすべて、私たちのドメインに固有のものでした。)ROSワークフローの周りにツールを構築することを検討している場合は、colconを使用してください。 十分にお勧めできてませんが、オススメです。

colcon bundleが呼び出されると、一連のアクションが発生し、その一部はcolconのインフラストラクチャから提供され、また一部は私たちが作成したものです。 ここでそれらの概要を説明しますが、すべてがどのように機能するのかをより深く理解するためにコードにdive deepすることをお勧めします。

  1. 私たちのVerbExtensionPointである BundleVerbが呼び出されます。 登録したすべての引数と他の拡張ポイントからの引数を含むコンテキストが渡されます。
  2. 次に、colcon-core機能の一部を呼び出してワークスペースファイルツリーをクロールし、パッケージを識別します。 次に、これは各パッケージのPackageDescriptorを作成してbundleの実行に含めます。 パッケージの識別はPackageIdentificationExtensionPointsを使って行われます。 ROSパッケージを識別するために登録されている拡張子は、colcon-rosで定義されているRosPackageIdentificationです。 ROSパッケージを識別すると、PackageDescriptorのタイプをros.cmake、ros.catkin、またはros.amentに設定します。
  3. すべてのパッケージが識別されると、colconは特定のパッケージタイプに登録されているPackageAugmentationExtensionPointを実行します。 識別とともに、RosPackageIdentificationは、パッケージのpackage.xmlを解析し、パッケージ記述子メタデータにビルド、テスト、および実行時の依存関係を追加する拡張機能も提供します。
  4. 今度はコードに戻り、各PackageDescriptorに添付されている依存関係メタデータを調べて、最後の呼び出し以降に何か変更があったかどうかを判断します。 何も変わっていなければ、それ以上の依存関係分析をスキップして、構築されたワークスペースを既存の依存関係オーバーレイとbundleを続けます。 依存関係が変化したと判断した場合は、引き続き完全な依存関係分析を行います。
  5. 完全な依存関係の分析中に、各PackageDescriptorに対して、対応するbundleタスクを呼び出します。 現在ros.catkinros.cmake、およびpythonパッケージタイプをサポートしています。 このサポートはcolcon-bundle/setup.cfgおよびcolcon-ros-bundle/setup.cfgに登録されています。 対応するbundleタスクを追加することで新しいパッケージタイプをサポートできます。 パッケージタイプごとに、対応するbundleタスクが依存関係を収集してから、依存関係ごとに適切なBundleInstallerExtensionPointを呼び出します。 私たちのROSタスクはrosdepを使ってROS依存関係名からそれにマッチするシステムパッケージ名に変換します。
  6. すべての依存関係がインストーラに登録され、完全推移閉包が決定されると、libc*gazebo*、およびその他のインフラストラクチャによって提供されるパッケージをインストールしたくないので、システムパッケージリストにブラックリストを適用します。 ブラックリストの動作を上書きしたい場合は、--apt-package-blacklist引数を使用して独自のブラックリストを指定できます。
  7. ブラックリストに登録した後、依存関係の登録手順で使用されたすべての登録済みBundleInstallerExtensionPointに対してinstall()を呼び出します。 インストーラは--bundle-base(デフォルト:bundle)ディレクトリ内のディレクトリにすべてをインストールします。 ダウンロードとインストールが完了したら、オーバーレイを有効にするsetup.shにコピーして、次にtargzipでフォルダをdependencies.tar.gzにコピーします。
  8. 依存関係がアーカイブされたら、最新のビルドされたinstallフォルダをアーカイブします。 オーバーレイを有効にするために、setup.shを使用して--install-base(デフォルト:install)ディレクトリ全体を別のオーバーレイにtarおよびgzipします。
  9. 両方のアーカイブが作成されたら、バンドリングプロセス中に生成されたすべてのメタデータを収集し、サイズ、オフセット、およびオーバーレイの順序を記述するメタデータを追加します。 次に、そのメタデータをtarしてmetadata.tar.gzに圧縮します。
  10. 次に、さまざまなアーカイブをまとめて「bundle」を作成し、それをbundle/output.tarのディスクに書き込みます。 これで、bundleはAWS RoboMakerのローカルに使用できるようになりました。

bundleを使ってアプリケーションを実行する

bundle形式は、AWS RoboMakerで使用するのと同じようにローカルで使用できます。 これを行うことはあなたのアプリケーション、あるいは単にデバッグツールとして役に立ちます。近々、私達が作成した、サービス内のcolcon bundleで作成したアーカイブを展開するためのGoLangライブラリを公開します。ここでは、現在どのような手順でcolcon bundleで作成したアーカイブを展開するかを紹介します

  1. アーカイブからversionmetadata.tar.gzを抽出します
  2. 使用する互換モードを確認するためにversionを確認します(このステップ以降、bundleはversion 2であると想定します)
  3. metadata.tar.gzからoverlays.jsonを抽出します
  4. overlays.jsonを解析して、ディスクに抽出するアーカイブのリストを収集します。 overlays.jsonには、オーバーレイのsha256ハッシュが含まれています。 これらのハッシュは、オーバーレイが変更されたため再抽出する必要があるかどうかを判断するために使用されます。 それらはまた、オーバーレイの整合性を検証するためにも使用できます
  5. オーバーレイがディスクに抽出されたら、それらはoverlays.jsonにリストされている順序で環境に反映される必要があります。 環境にオーバーレイを追加するには、次のコマンドを実行します。BUNDLE_CURRENT_PREFIX=<path_to overlay_folder> source <path_to_overlay_folder>/setup.sh ファイルを読み込むときに、そのファイルがどこにあるのかを判断するために、BUNDLE_CURRENT_PREFIXが必要となります
  6. ROSワークスペースとbundle内の依存関係がローカルにインストールされているかのように、実行環境で実行可能な状態に設定されました

まとめ

私達が作成したフォーマットとツールに満足しており、引き続き改善していくことを楽しみにしております。colcon bundleの実装は、より多くのパッケージタイプとインストーラに拡張可能です。colcon上にあなた自身のツールを構築したい場合は、colconのコラボレーションドキュメントをチェックしてください。


原文はこちら

翻訳はSA 市川が担当しました