JetsonのROS用のクロスコンパイル環境を構築する（CUDA対応版）

## 更新情報
2022/1/1 : CUDAに対応しました。動作確認はできていないですが、Ar-Rayさんのdarknet_rosがコンパイルできました。（ https://github.com/Ar-Ray-code/darknet_ros_fp16.git ）

## はじめに

今回は前回の記事の応用で、QEMUを使わない純粋なROSのクロスコンパイル環境を構築することを目指します。基本的には、今回の方法でかなりのパッケージでクロスコンパイルができると思います。
今回の方法は、Raspberry Pi用などで公式で書かれているROSのベースパッケージのクロスコンパイルとは異なり、ユーザーが自前のROSパッケージをあたかもx86_64のPCで開発しているかのようにJetson用のaarch64のクロスコンパイルを行うことを目指しています。

https://qiita.com/tatsuyai713/items/6894f737d4c5b722650b


### 環境の準備

環境の構築は前回の記事と途中まではほとんど同じです。
前回も書きましたが、純粋なクロスコンパイルを行う場合においても基本的に実機のrootfsがないとコンパイルすることはできません。
ですので、ROSをクロスコンパイルする際には、rootfs内に必要なパッケージをインストールしておく必要があるため、ここまではほとんど前回の手順と変わりません。

まず、Hostの環境でJetpackの中身をコピーしておきます。
SDカードのイメージをダブルクリックしてマウントしてコピーをしていきます。

```console
mkdir ~/jetpack
cd <SDカードイメージのマウント先>
sudo cp -raf ./* ~/jetpack/
```

SDイメージのマウントなどを自動化したい人は、以下のようにするとマウントできます。

```console
mkdir ~/sd
sudo mount -t ext4 -o loop,offset=<offset*512> sd-blob.img ~/sd/
```

オフセットを調べるには以下のコマンドでSDのイメージのパーティションのStartのセクタを調べて、512をかけたものをoffsetに設定します。

```sh
fdisk -l sd-blob.img

Device          Start      End  Sectors  Size Type
sd-blob.img1  1187840 35266559 34078720 16.3G Linux filesystem
sd-blob.img2     2048   133119   131072   64M Linux filesystem
......
```

続いて、Dockerの環境でクロスコンパイル環境を構築するため、前回も使用した以下のコンテナ作成と使用をすることができるスクリプト等の一式を使用します。

https://github.com/tatsuyai713/jetson_cross_docker.git


```console
cd ~
git clone https://github.com/tatsuyai713/jetson_cross_docker.git
cd jetson_cross_docker
```

コンテナのセットアップを行います。

```console
./launch_container.sh setup
```

セットアップが完了すると、自動でコンテナ内に入っている状態となります。
コンテナ内であるかどうかはBashで表示されているhostnameがhostname-Dockerとなっていることから判断できます。

exitで出た後に２回目に入る時は

```console
./launch_container.sh
```

コミットする時は

```console
./launch_container.sh commit
```

でコミットできます。
また、このコンテナでは、HOSTのHomeディレクトリをhost_homeというフォルダの以下にマウントします。

### コンテナ内での環境構築1

それでは、コンテナ内で以下の作業を継続していきます。
HomeフォルダにQEMU関連のセットアップのスクリプトがあるので実行します。

```console
./configure.sh
```

続いて、JetPackの中身の環境を構築するために、chrootを用いて、設定していきます。
まずは、展開したフォルダに移動し、必要なフォルダをマウントします。

```console
cd ~/host_home/jetpack/
sudo cp /usr/bin/qemu-aarch64-static usr/bin
sudo cp -b /etc/resolv.conf etc
sudo mount -o bind /dev dev
sudo mount -o bind /proc proc
sudo mount -o bind /sys sys
```

続いて、chrootでJetPack内に入ります。

```console
sudo LC_ALL=C chroot .
```

ここでは、あたかもJetPackの中にいるように操作することができますが、rootでしか操作できません。それによる警告等表示されますが、無視して問題ありません。

まずは、aptが正しく実行できるように、以下のファイルを編集し、<SOC>となっているところをt194に変更します。

```console
vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list 
```

```console
deb https://repo.download.nvidia.com/jetson/t194 r32.6 main
```

それでは、ここからはROSの環境のセットアップになります。
開発しているパッケージに依存関係のあるROSのパッケージなどはこのタイミングでインストールしておきましょう。また、この作業は後でもう一度行うこともできますので、新しいパッケージを作成する際に、ros-melodic-xxxというパッケージが必要になった場合は、またrootfsでchrootを使ってインストールしましょう。ここでは、ros-melodic-desktop-fullだけをインストールしておきます。

```console
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt install -y curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo apt update
sudo apt install -y ros-melodic-desktop-full
sudo apt install -y python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential python-catkin-tools 
source /opt/ros/melodic/setup.bash
sudo rosdep init
rosdep update
```

これでrootfsの準備は完了です。exitで抜けて以下のコマンドを実行しておきましょう。

```console
sudo umount sys proc dev 
sudo rm usr/bin/qemu-aarch64-static 
sudo mv etc/resolv.conf~ etc/resolv.conf
```

### コンテナ内での環境構築2

まだもう少しコンテナ内の準備が必要なので、環境構築を続けていきます。

まず、先ほどのDockerコンテナ作成用のgitリポジトリにクロスコンパイル用のcmakeの設定ファイルが入っていますので、コピーしておきます。

```console
cp ~/host_home/jetson_cross_docker/Toolchain-L4T.cmake ~/
```

続いて、クロスコンパイル用のgccを以下から取得し、展開しておきます。

```console
cd ~
wget http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/aarch64-linux-gnu/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
tar xf gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
mv gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu l4t-gcc
```

今回こちらを使用すると#include_nextでコンパイルエラーが発生することがあるので、以下のコマンドで修正をしておきます（これでよいかはよくわからないですが）

```console
sed -i -e 's/include_next/include/' ~/l4t-gcc/aarch64-linux-gnu/include/c++/7.3.1/cmath
sed -i -e 's/include_next/include/' ~/l4t-gcc/aarch64-linux-gnu/include/c++/7.3.1/cstdlib
sed -i -e 's/include_next/include/' ~/l4t-gcc/aarch64-linux-gnu/include/c++/7.3.1/bits/std_abs.h
```

続いて、コンテナ環境にcatkin関連のパッケージをインストールしていきます。この際にrosのパッケージは特にインストールする必要はありません。（この作業はいらないようにするかもしれません）

```console
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo apt update
sudo apt install -y python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential python-catkin-tools python-vcstool cmake python-empy
```

ここまでの作業が完了しましたら、ROS用のクロスコンパイル環境にしていくための作業を進めていきます。作業内容から分かると思いますが、このコンテナ内で以下の設定を行うと、今後このコンテナではaarch64でのコンパイルしかできなくなるので、ご注意ください。

まず、JetPack内のシンボリックリンクが切れてしまっているので、シンボリックリンクを作り直します。今回はlibrtのみのシンボリックリンクの修正のみになっていますが、他のlibファイルでも起こる可能性が高いですので、No such fileが出た場合はこのシンボリックリンクの修正を行うようにしてください。

```console
cd ~/jetpack/etc/alternatives
ls -al ./ | awk '{print "sudo ln -nfs ~/host_home/jetpack" $11 " " $9}' | xargs -i bash -c  'eval {}'
cd ~/jetpack/usr/lib/aarch64-linux-gnu
ls -al | grep alternatives | awk '{print "sudo ln -nfs ~/host_home/jetpack" $11 " " $9}' | xargs -i bash -c  'eval {}'
```


```console
sudo rm ~/host_home/jetpack/usr/lib/aarch64-linux-gnu/librt.so
sudo ln -s ~/host_home/jetpack/lib/aarch64-linux-gnu/librt.so.1 ~/host_home/jetpack/usr/lib/aarch64-linux-gnu/librt.so
```

続いて、JetPack内のaarch64のlibファイルをコンテナ内で呼べるようにシンボリックリンクを作成します。

```console
sudo ln -s ~/host_home/jetpack/usr/lib/aarch64-linux-gnu /usr/lib/aarch64-linux-gnu
sudo ln -s ~/host_home/jetpack/lib/aarch64-linux-gnu /lib/aarch64-linux-gnu
sudo ln -s ~/host_home/jetpack/lib/ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1
```

ライブラリによっては、/usr/libに直接配置されてしまうものもあるため、soファイルがないというエラーが出た場合は以下のように手動でシンボリックを張っておきます。

```console
sudo ln -s ~/host_home/jetpack/usr/lib/libPocoFoundation.so /usr/lib/libPocoFoundation.so
```

続いて、/opt/ros以下についてもJetPack内のものを参照するように、以下の通りシンボリックリンクを張ります。

```console
sudo mv /opt/ros /opt/ros_x86_64
sudo ln -s ~/host_home/jetpack/opt/ros /opt/ros
```


### 自前開発のROSパッケージをクロスコンパイルする

それではいつも通り、以下のようにワークスペースを作成しsrcフォルダ以下に自前のパッケージを配置します。
今回は、私の作ったROSでSocketCANを使うものを例に挙げて進めます。

```console
mkdir -p ./catkin_ws/src
cd catkin_ws/src
source /opt/ros/melodic/setup.bash
catkin_init_workspace
git clone https://github.com/tatsuyai713/ros_socket_can_driver.git
cd ..
```

続いて、ビルド用のスクリプトを作成します。

```console
vi ./build.sh
```

```console
#!/bin/bash
PWD=$(pwd)
export LANG=C
source /opt/ros/melodic/setup.bash

catkin build \
--cmake-args \
-DCMAKE_TOOLCHAIN_FILE=${PWD}/../Toolchain-L4T.cmake \
-DCMAKE_CROSS_COMPILE_PREFIX=${PWD}
```

実行権限を与えて、

```console
chmod +x ./build.sh
./build.sh
```

これで、クロスコンパイル完了です。あとはこのフォルダごと丸々tarballでかためてscpで実機にデプロイするなり煮るなり焼くなりしていただければと思います。

また、catkin_makeな方は以下のコマンドでもビルドできますので、こちらでも良いです。

```console
source /opt/ros/melodic/setup.bash
export CMAKE_CROSS_COMPILE_PREFIX=$PWD
catkin_make DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-L4T.cmake
```

## まとめ
ということで、今回はJetson用のROSパッケージの作成を純粋なクロスコンパイル環境で実現する方法について書きました。では、また。