tshell_blog

ソフトウェアと車輪がついた乗り物のはなし

Raspberry Pi 3 + Ubuntu 20.04 + ROS2 foxy向けクロスコンパイル環境をDockerで構築する

f:id:tshell:20210308220454g:plain

tshell.hatenablog.com

前回の記事では動作確認用のROS2ノードをPythonで書いていましたが,実はrt-net / raspimouse2のソースをRaspberry Pi 3でビルドしようとするとraspimouseパッケージのビルドが25%で止まってしまっていたからでした。

別の環境ならうまく行くかも,ということでRaspberry Pi のROS2で動作するノードをC++で記述してx86 PCでビルドする環境を作ります。

qemu-user-staticでARM用のバイナリをx86命令へ変換しながら実行する仕組みを利用して,Raspberry Pi用のUbunru 20.04イメージをx86 PCで動作させます。その上にROS2をインストールして,あたかもPC上でRaspberry Pi用のROS2が動いているかのように見せかけます。環境はDockerで構築します。

手順

以下の手順でクロスコンパイル環境を構築します。

  1. DockerでARM用Ubuntu 20.04を動かす
  2. イメージをtarに固めてdocker importする
  3. インポートしたDockerイメージをもとにROS2環境を構築する

DockerでARM用Ubuntu 20.04を動かす

Raspberry PiUbuntu 20.04 LTSをインストールするときに使用したイメージファイルをホストPCでマウントし,qemu-arm-staticを/usr/binへコピーします。

まずはイメージファイルからループバックデバイスを作成します。

$ sudo losetup -fP ubuntu-20.04.2-preinstalled-server-arm64+raspi.img
$ dmesg | grep loop
[    0.113010] Calibrating delay loop (skipped), value calculated using timer frequency.. 5799.77 BogoMIPS (lpj=11599544)
[    0.631321] loop: module loaded
[ 1644.919981] EXT4-fs (loop20): mounted filesystem with ordered data mode. Opts: (null)
[ 2827.012874] EXT4-fs (loop20): mounted filesystem with ordered data mode. Opts: (null)
[14031.188080]  loop20: p1 p2

/dev/loop20に作成されたようです。

$ sudo fdisk -l /dev/loop20
ディスク /dev/loop20: 3.4 GiB, 3259499520 バイト, 6366210 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0x4ec8ea53

デバイス      起動 開始位置 最後から  セクタ サイズ Id タイプ
/dev/loop20p1 *        2048   526335  524288   256M  c W95 FAT32 (LBA)
/dev/loop20p2        526336  6366175 5839840   2.8G 83 Linux

loopデバイスが作成されており,ubuntu 20.04のイメージに含まれるLinuxパーティション/dev/loop20p2になっています。

ローカル環境に余計なものを入れたくないのでDockerコンテナを立ち上げ,適当なところ(ここでは/mnt)にマウントして作業します。

$ docker run --rm -it --privileged -v $(pwd):/home --name ubuntu ubuntu:20.04
# mount /dev/loop20p2 /mnt

作業用のコンテナ内でqemu-user-staticをインストールします。

# apt update
# apt install -y qemu-user-static

qemu-user-staticでインストールされたコンテナ内の/usr/bin/qemu-arm-staticをイメージファイルの/usr/binにコピーします。

# cp /usr/bin/qemu-arm-static /mnt/usr/bin

修正したイメージファイルをtarに固めます。

# cd /mnt
# tar cf /home/raspi-ubuntu-20.04.tar .

コンテナを終了し,作成したtarファイルのオーナーを変更しておきます。

$ sudo chown ユーザー名:ユーザー名 raspi-ubuntu-20.04.tar

tarファイルをDockerにインポートします。

$ docker import raspi-ubuntu-20.04.tar raspi-ubuntu:20.04

インポートしたイメージを起動します。

$ docker run -it --name raspi-ubuntu raspi-ubuntu:20.04 /bin/bash
root@143d831747f9:/# arch
aarch64

アーキテクチャはaarch64になっており,ARM用のUbuntu 20.04イメージが実行されています。

standard_init_linux.go:211: exec user process caused "exec format error"というエラーが出たら以下を実行します。

$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

ROS2のインストール

raspi-ubuntu:20.04イメージが作成できたら以下のようなDockerfileを作成してビルドします。

FROM raspi-ubuntu:20.04

RUN apt update && apt install -y curl gnupg2 lsb-release
RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add -
RUN sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'

RUN apt update && \
    apt install -y ros-foxy-ros-base  python3-colcon-common-extensions python3-pip && \
    pip3 install -U argcomplete
$ docker build --rm -t raspi-ubuntu-ros2:foxy .

ビルドが完了したらコンテナを起動してみます。

$ docker run --rm -it raspi-ubuntu-ros2:foxy /bin/bash

ROS2のexampleを動かしてみて,ちゃんとROS2がインストールされていることを確認します。

raspimouse2のビルド

前回はできなかったraspimouse2のビルドをしてみます。
ホストPCでrt-net / raspimouse2のソースをcloneします。

$ mkdir -p ros2_ws/src
$ cd ros2_ws/src
$ git clone https://github.com/rt-net/raspimouse2.git

cloneしたソースをマウントしてコンテナを起動し,colcon buildを実行します。

$ docker run --rm -it -v $(pwd)/ros2_ws:/home raspi-ubuntu-ros2:foxy /bin/bash
# source /opt/ros/foxy/setup.bash
# cd /home
# colcon build
Starting >>> raspimouse_msgs
Finished <<< raspimouse_msgs [12.2s]                     
Starting >>> raspimouse
[Processing: raspimouse]                             
[Processing: raspimouse]                                      
[Processing: raspimouse]                                       
Finished <<< raspimouse [1min 54s]                             

Summary: 2 packages finished [2min 8s]

正常にビルドできました。

動作確認

rt-net / raspimouse2のREADMEのQuick Startにしたがって動作確認します。

Raspberry Pi MouseにSSH接続し,以下のコマンドを実行します。

$ source ~/ros2_ws/install/setup.bash
$ ros2 run raspimouse raspimouse

別のターミナルでRaspberry Pi MouseにSSH接続し,以下のコマンドを実行します。

$ source /opt/ros/foxy/setup.bash
$ ros2 lifecycle set raspimouse activate

activateすると冒頭のgifのように光センサのLEDが点滅し始めます。
raspimouse2のソースをx86 PCでビルドして,Raspberry Pi Mouseを動かすことができました。