MoriKen's Journal

MoriKen's Journal

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

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

Sponsored Link

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