RaspberryPi3とZumoとROSで半永久自走式充放電ロボを作成したい_007日目_SLAM_MONO-VO(単眼カメラ視差推定)

#◆ 前回記事
[RaspberryPi3とZumoとROSで半永久自走式充放電ロボを作成したい_006日目_Arduino+RaspberryPi+Bluetooth+シリアル通信+ROS連携](https://qiita.com/PINTO/items/ec0c6f891f28ec856cbe)　の続き

#◆ はじめに
前回はBluetoothによるロボット遠隔操作を試行した。
今回からLiDARやカメラを使ったSLAMに挑戦したい。
Qiitaやブログに関連記事を投稿してくださっている方は沢山いらっしゃるので読みあさってみたが・・・原理を細かく理解しようとした時点で負けが確定するシロモノだ。
言うなれば、何も考えずただボタンを押すだけの今時仕様から、一夜にしてサボテンやピエロを直視で目押ししてDDTしなければ確実に負けるほどの難易度に急上昇した、と言えば、自分が今置かれている状況がご理解頂けるだろうか。
※上記一文が意味不明な方々は華麗にスルーしてあげてください。

いっそのこと割りきって仕組みのことは一切考えない。
しかし・・・ワクワクしすぎて目移りする。
この際、片っ端からためしてやるぜ！ ちくしょう！

#◆ 参考にさせていただいた記事、謝辞
**nnn112358さん　Qiita**

- [ROSのLidarSLAMまとめ](https://qiita.com/nnn112358/items/814c0fb0d2075eb71da0)

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

**Crafty_as_a_Foxさん　Qiita**

- [ROSとGazeboによる自律移動シミュレーション 〜SLAMアルゴリズムの比較〜](https://qiita.com/Crafty_as_a_Fox/items/cc3a1e768c390fdc96c9)

**NegativeMindさん　NegativeMindException**

- [OpenCVでカメラ画像から自己位置認識 (Visual Odometry)](https://blog.negativemind.com/2016/02/19/monocular-visual-odometry-using-opencv/)


#◆ SLAMとは
これっぽっちも理解していないので、あちこちの情報を読みながら、試しつつ、迷子になりながら頭のなかを整理していく。
そもそも、

- 地図生成
- 自己位置推定
- 航路決定？

はそれぞれ別のルーチンで行う。
自分が今回やりたいことに対し、地図生成を自律航行ロボにリアルタイムでやらせること自体に意義は無く、出鼻から大きく勘違いしていたことが分かった。
当たり前の話だが、限られたリソースを有効活用するなら、地図を作ること、と、地図内を航行させること、は分けて考えるべきだと考えた。
探索エリアが決まっている場合は、あらかじめ探索エリア内のマップを生成しておくほうが良いような気がする。
未知のエリアを航行させるわけではないので良しとする。

ということで、第一段階は地図を作ること、に専念することとする。
で、調べた目ぼしい地図生成アルゴリズムは下記。

|今回|他記事|アルゴリズム|特徴|
|:-:|:--|:---|:---|
||**[008日目](https://qiita.com/PINTO/items/9aa737c284dc4e8212f1)**|**[Gmapping](https://github.com/ros-perception/slam_gmapping/tree/hydro-devel/gmapping)**|一番有名らしい、ROS対応、loop closure有り|
||**[009日目](https://qiita.com/PINTO/items/4845c438cac05eda4d1e)**|**[Google Cartographer](https://github.com/googlecartographer/cartographer)**|精度が高そう、**[ROS対応](https://github.com/googlecartographer/cartographer_ros)**、loop closure有り|
||**[010日目](https://qiita.com/PINTO/items/b3eb7205baf9c55de81b)**|**[Hector SLAM](https://github.com/tu-darmstadt-ros-pkg/hector_slam)**|精度がいまいちっぽい、loop closure無し|
||**[011日目](https://qiita.com/PINTO/items/7939b1e53e23a9458c1d)**|**[Karto SLAM](https://www.kartorobotics.com/)**|単眼カメラでも動く、**[ROS対応](https://github.com/ros-perception/slam_karto)**、Visual-SLAM|
||**[012日目](https://qiita.com/PINTO/items/5ffdb0484b07d78ab38f)**|**[ORB-SLAM2](https://github.com/raulmur/ORB_SLAM2)**|単眼カメラでも動く、Visual-SLAM||
|★||**[mono-vo](https://github.com/PINTO0309/mono-vo)**|単眼カメラでも動く、Visual-SLAM|
||**[013日目](https://qiita.com/PINTO/items/8989f45ad0908184e6e5)**|**[VINS-Mono](https://github.com/PINTO0309/VINS-Mono)**|単眼カメラでも動く、ROS対応、Visual-SLAM、ドローン|
|ー|ー|**[CNN-SLAM](https://github.com/Lishunkai/CNN-SLAM)**|単眼カメラでも動く、3D、アイデアのメモ情報しか見当たらなかったため検証対象外とする|
|ー|ー|**[RGBD-SLAM-V2](http://wiki.ros.org/rgbdslam)**|RGB-Dカメラ専用、検証対象外とする|

#◆ アルゴリズムの選定に悩む

将来構想上での要件は、

- 無いものネダリなのは百も承知だが、RaspberryPiごときでも処理可能なほど軽量なアルゴリズムがあればうれしい
- 長時間の自律走行に耐えるためには電力効率を極限まで上げなくてはならない
- **[格安LiDAR (RPLidar A1M8)](https://www.robotshop.com/jp/ja/rplidar-a1m8-360-degree-laser-scanner-development-kit.html)** は所持しているが他の要件を満たすためなら他の機材使用にシフトすることも厭わない
- 正確性はある程度捨てても構わないが迷子になることは避けたい
- 認識スピードはある程度欲しいがこだわらない

分けわかんないのはこの際しようがない。
とにかく、アルゴリズムごとに手当り次第、

    1. とりあえず動かす
    2. 自力で地図・オドメトリを生成してみる
    3. 自作のデータで動かす

を繰り返してみる。
要件への適合性や使い勝手、パフォーマンスの結果を見てどれを選択するのかはあとから判断する。

#◆ mono-vo (OpenCV3.0ベースの単眼視差推定)
下記の情報を参照した。
実行の仕方が分からない〜！と、大騒ぎしている輩が多い。
Gitのチュートリアルが極めて不親切なので致し方ないが、C++のソース読みなよ。と思った。
結果的にはC++をまともに読んだことも書いたこともないトーシローでもだいたい勘で動かせた。

- **[Visual Odmetry from scratch - A tutorial for beginners](http://avisingh599.github.io/vision/visual-odometry-full/)**
- **[Monocular Visual Odometry using OpenCV](http://avisingh599.github.io/vision/monocular-vo/)**
- **[OpenCV3.0ベースの単眼視覚測定 mono-vo](https://github.com/PINTO0309/mono-vo)**
- **[KITTIデータを扱うためのPythonツール](https://github.com/PINTO0309/pykitti)**

#◆ 実行環境構築
まずは味見がてら、Ubuntu16.04 上で実行環境を構築して動かしてみたい。
OpenCVはpipコマンドかソースビルドかdebパッケージであらかじめ導入済みの前提とする。
物珍しさで試してみたくなった方のために、サンプルデータのファイルサイズが22GBもあって、ダウンロードに４時間、解凍に１時間も要することを先にお伝えしておく。

```shell
$ cd ~
$ git clone https://github.com/PINTO0309/mono-vo.git
$ wget http://kitti.is.tue.mpg.de/kitti/data_odometry_gray.zip #<---データセット(グレースケール) 22GBもあるので空き容量注意!!ダウンロードだけで４時間。ZIP解凍に１時間。
$ wget http://kitti.is.tue.mpg.de/kitti/data_odometry_calib.zip #<---キャリブレーションファイル 600KB
$ wget http://kitti.is.tue.mpg.de/kitti/data_odometry_poses.zip #<---グラウンドトゥルースファイル 1.3MB
$ unzip data_odometry_gray.zip;unzip data_odometry_calib.zip;unzip data_odometry_poses.zip
$ rm data_odometry_gray.zip;rm data_odometry_calib.zip;rm data_odometry_poses.zip
```

homeフォルダ直下に下記のような階層が出来上がる。
![0015_SLAM_MONO-VO.png](https://qiita-image-store.s3.amazonaws.com/0/194769/b0359855-d981-5e20-7de0-e43b8364d942.png)

可視化用C++プログラムをフォルダ階層に合わせて手直しする。

```shell
$ nano mono-vo/src/visodo.cpp
```

ファイルパス４行を自分の環境に適合するように修正する。

```c++:visodo.cpp
//ifstream myfile ("/home/avisingh/Datasets/KITTI_VO/00.txt");
ifstream myfile ("/home/<username>/dataset/poses/00.txt");
```

```c++:visodo.cpp
//sprintf(filename1, "/home/avisingh/Datasets/KITTI_VO/00/image_2/%06d.png", 0);
//sprintf(filename2, "/home/avisingh/Datasets/KITTI_VO/00/image_2/%06d.png", 1);
sprintf(filename1, "/home/<username>/dataset/sequences/00/image_0/%06d.png", 0);
sprintf(filename2, "/home/<username>/dataset/sequences/00/image_0/%06d.png", 1);
```

```c++:visodo.cpp
//sprintf(filename, "/home/avisingh/Datasets/KITTI_VO/00/image_2/%06d.png", numFrame);
sprintf(filename, "/home/<username>/dataset/sequences/00/image_0/%06d.png", numFrame);
```

プログラムをビルドする。

```shell
$ mkdir build
$ cd build
$ cmake ..
$ make
```

可視化プログラムを実行。

```shell
$ ./vo
```
![ezgif.com-optimize.gif](https://qiita-image-store.s3.amazonaws.com/0/194769/29982e4d-65bc-a5f7-043f-c0ffce50a2ff.gif)

![ezgif.com-optimize (1).gif](https://qiita-image-store.s3.amazonaws.com/0/194769/431b4923-b920-c0a1-4223-524380e5218a.gif)

うぉっ、すごい！！
動いた。
しかし、単眼カメラでこんなことができるとは！！

が、何かが違う。これじゃない。
野生の直感がそう叫んでいる。。。

ということで、自前のデータセット生成は先送りして次に行ってしまおうと思う。

#◆ 【参考】ビジュアルオドメトリ／SLAM開発キット
http://kitti.is.tue.mpg.de/kitti/devkit_odometry.zip

```shell
$ cd ~/mono-vo
$ wget http://kitti.is.tue.mpg.de/kitti/devkit_odometry.zip
$ unzip devkit_odometry.zip
$ rm devkit_odometry.zip
$ cd devkit/cpp
$ g++ -O3 -DNDEBUG -o evaluate_odometry evaluate_odometry.cpp matrix.cpp
```

#◆ 本日のまとめ

- 文系に数式は無理！ 吐き気がする！ 見た瞬間に 「はわわぁ〜・・・」 ってなる。
- 単眼カメラでは限界がある気がする。やっぱり LiDAR や IMU と組み合わせかな。
- LiDAR / IMUとの合成版もサンプルが準備されているようだ。
- でもやっぱり単眼カメラでもココまでできるのはビックリした。将来的な可能性を感じる。
- DeepLearningと組み合わさるともっといいものが登場したりするのかな。

#◆ 次回予告
Gmapping にトライしてみようと思う。
なぜ最初に基本っぽいところから攻めなかったのか？ と聞かれても返す言葉も無し。。。
オッサンの奮闘は更に続く。。。
FF7の **[「更に闘う者達」](https://www.youtube.com/watch?v=SNAM3I1k1c8)** がずっと頭の中でリフレインしている。

#◆ 次回記事
[RaspberryPi3とZumoとROSで半永久自走式充放電ロボを作成したい\_008日目\_SLAM_GMapping_LiDAR(A1M8)](https://qiita.com/PINTO/items/9aa737c284dc4e8212f1)　へ続く