ORB-SLAM2をdockerで動かしてみる

[CQ出版のオフ会](https://twitter.com/If_CQ/status/1021987438066061313)でSLAMの話でもりあがったので、ORB-SLAM2をdockerで動かせるようにしてみました。

## 動作環境
PC：VirtualBox 5.2.16
ホストOS: Ubuntu 18.04 LTS Bionic Beaver

## 導入方法

■ リポジトリをclone します。

```
git clone https://github.com/ft28/dockernized_orb_slam2.git 
cd dockernized_orb_slam2
git submodule update --init --recursive
```

■ ユーザ設定と、X設定のための環境変数を.bashrc に追加します。

```
# docker 上で現在のユーザーとグループを使うための設定
export USER_ID=`id -u`
export USER_NAME=`id -nu`
export GROUP_ID=`id -g`
export GROUP_NAME=`id -ng`

# docker 上でx-window を使うための設定
export XSOCK=/tmp/.X11-unix
export XAUTH=/tmp/.docker.xauth
touch $XAUTH
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
```

■ 設定を反映します。
```
source ~/.bashrc
```

■ イメージを作成します。
```
docker-compose build orb_slam2_ros
```

■ コンテナを起動します。

```
docker-compose run orb_slam2_ros
```

## コンテナの仕様
ホストの orb_slam_ros_home 以下を docker 環境内のhomeに、ORB_SLAM2 以下を home 直下にマウントしています。

初めて起動するときに、ROS 環境の初期化とORB_SLAM2のコンパイルを行います。 catkin_ws 以下のファイルをすべて削除すると、再度 ROS環境の初期化とORB_SLAM2のコンパイルが実行されます。詳しくは、entrypoint.sh を参照してください。

docker-compose.ymlでは、実験データは、ホストの/opt/data 以下にあると想定しています。他の場所に置く場合は、docker-compose.yml を修正してください。

## サンプルデータ取得

[EuRoC Dataset](https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets) からテスト用にデータをダウンロードします。

```
# ROS実験用
wget -c http://robotics.ethz.ch/~asl-datasets/ijrr_euroc_mav_dataset/machine_hall/MH_01_easy/MH_01_easy.bag

# 非ROS実験用
wget -c http://robotics.ethz.ch/~asl-datasets/ijrr_euroc_mav_dataset/machine_hall/MH_01_easy/MH_01_easy.zip
```

ダウンロードしたデータをホストの/opt/data 以下に保存します。zipファイルは解凍しておきます。

```
ls /opt/data
MH_01_easy.bag  mav0
```

## サンプルデータ実行

コンテナ起動します。

```
docker-compose run orb_slam2_ors
```
### 非ROS実行
ORB-SLAM2では、ROSに依存することなく実行することが可能となっています。今回のDockerfileでは、ROS関連パッケージをインストールしていますが、OpenCVとpangolinだけインストールすればORB-SLAM2を動かすことができます。

cd ORB_SLAM2
./Examples/Stereo/stereo_euroc ./Vocabulary/ORBvoc.txt ./Examples/Stereo/EuRoC.yaml /opt/data/mav0/cam0/data /opt/data/mav0/cam1/data ./Examples/Stereo/EuRoC_TimeStamps/MH01.txt

![screen_shot.png](https://qiita-image-store.s3.amazonaws.com/0/247417/027f0ffb-2119-51e9-de46-6a6d56af296e.png)

### ROS実行
tmux で複数ウィンドウを開いてROSを実行します。xtermを使っても問題ありません。

```
cd ORB_SLAM2
tmux 
# ウィンドウ1でroscore 実行
roscore

# ウィンドウ2でORB_SLAM実行
rosrun ORB_SLAM2 Stereo Vocabulary/ORBvoc.txt Examples/Stereo/EuRoC.yaml true

# ウィンドウ3でbagファイル再生
rosbag play --pause /opt/data/MH_01_easy.bag /cam0/image_raw:=/camera/left/image_raw /cam1/image_raw:=/camera/right/image_raw
```
ROSの場合も同様に、pangolinで作成された2つの画面が起動します。

### web カメラを使ったサンプル
```
cd ORB_SLAM2
tmux 
# ウィンドウ1でroscore 実行
roscore

# ウィンドウ2でORB_SLAM実行
rosrun ORB_SLAM2 Mono Vocabulary/ORBvoc.txt Examples/Stereo/EuRoC.yaml true

# ウィンドウ3でweb カメラ再生
roslaunch ../camera.launch
```

camera.launch は使っているweb カメラによって修正が必要です。また、EuroC.yml については、使っているカメラのキャリブレーション結果と置き換える必要があります。


## 発生した問題
### OpenGL問題
ホストのX環境が nvidia のドライバで動いている場合に、OpenGLを含むアプリケーションを動かすには、別の方法が必要なようです。

### /dev/video0 のパーミッション問題
ホストの /dev/video0 のパーミッションが root:videoなのにdocker上のパーミッションが root:rootになってしまうという問題がありましが、dockerのバージョンを18.06.0-ce にアップデートすることで解決しました。

### docker-compose で複数グループ指定
ユーザをvideo グループに所属させたかったのですが、
```
user: ${USER}:${GROUP}
```
で指定してしまうと、ホスト上のプライマリグループにしか所属させることができませんでした。docker-compose.yml に add_group オプションを追加すると解決したのですが、docker-compose.ymlでversion ３の場合、この記述はエラーになってしまいます。このため、docker-compose.ymlのversionを2にしています。

### guvcview エラー問題
web カメラを docker 上で使えるか確認するためのコマンドとして、guvcview を使っていたのですが、coreを吐いて死んでしまうという問題が置きました。dockerの実行ユーザーをrootにすると動くようになるので、パーミッション関係の問題だと思うのですが、原因はわかりませんでした。

## まとめ
dockerでXアプリケーションも動かせることを知って驚きました。デバイス依存が強い場合に、dockerで仮想化する意義がどのくらいあるか少し疑問はありますが、dockerの活用範囲が広げられそうです。

## 参考リンク
ORB-SLAM2 公式
https://github.com/raulmur/ORB_SLAM2

@nnn112358 さんqiita記事
[ROSのORB_SLAM2を動かしてみた。](https://qiita.com/nnn112358/items/1087d5b0e4df5367f48b)

@Leonardo-mbc さんqiita記事
[Docker + ROS(kinetic)でチュートリアル](https://qiita.com/Leonardo-mbc/items/cfd38a4fae8667593cf1)