せまいおうちでもできるお気軽AIカー

# はじめに
たぶんTwitterで見たんだと思いますが、今年の夏頃にM5StickC/Vというものを知りました。

- プログラミング可能
- ネットにつながる
- 画面がついてる
- 電池内蔵
- 安い
- RISC-V
- AI?!

「3000円のこれでKerasで学習したモデルを実行できるらしい」
「カメラが付いてるから画像認識系いろいろできるはず」
「マイクが付いてるからアレクサみたいなのもできるよね？」

面白そうなので「到着するまでに何を作るか考えよう」ってことにしてAliExpressで注文しました。

...

で、いろいろ考えた結果、

「BeetleCってのもあるし、AIといえばとりあえず自動運転だよな」ということで、
M5だらけのちっちゃい自動運転、はじまりーはじまりー。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5StickC+BeetleC+M5StickVで自動運転する｜ノート君｜note（ノート） <a href="https://t.co/mHNUd8a9rt">https://t.co/mHNUd8a9rt</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1162870557513142273?ref_src=twsrc%5Etfw">August 17, 2019</a></blockquote>
https://twitter.com/urekat/status/1162870557513142273



# ハードウエア
まずはこの3つを接続しました。
 - M5StickC
 - M5StickV
 - BeetleC

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">AI自動運転カーのハードウェア部分できた <a href="https://twitter.com/hashtag/M5StickV?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickV</a> <a href="https://twitter.com/hashtag/M5StickC?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickC</a> <a href="https://t.co/pLmzHge7YB">pic.twitter.com/pLmzHge7YB</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1168653990914973697?ref_src=twsrc%5Etfw">September 2, 2019</a></blockquote>

https://twitter.com/urekat/status/1168653990914973697


BeetleCにCを刺し、GroveケーブルでCとVを接続します。
CとVはシリアル通信でアレコレやり取りします。



# ラジコン化

とりあえず、まずはラジコン化します。

## BlynkというiPhoneのアプリでBLE接続してラジコン
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">タイヤ空回りしまくり。AIカーがこの悪条件でスイスイまわれるなら意味あるAIだなと思う。人間には難しいことをやる。 <a href="https://twitter.com/hashtag/BeetleC?src=hash&amp;ref_src=twsrc%5Etfw">#BeetleC</a> <a href="https://twitter.com/hashtag/M5StickC?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickC</a> <a href="https://twitter.com/hashtag/M5StickV?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickV</a> <a href="https://t.co/HaSubTbUAS">pic.twitter.com/HaSubTbUAS</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1169465715318915073?ref_src=twsrc%5Etfw">September 5, 2019</a></blockquote>
https://twitter.com/urekat/status/1169465715318915073


## 微妙なモーター制御に向いてなかったBeetleC
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">BeetleC、ゆっくり動かして、だいぶ制御できるようになりました！<a href="https://twitter.com/hashtag/M5StickC?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickC</a><a href="https://twitter.com/hashtag/BeetleC?src=hash&amp;ref_src=twsrc%5Etfw">#BeetleC</a> <a href="https://t.co/Gemn01DS0g">https://t.co/Gemn01DS0g</a> <a href="https://t.co/DxaydDtXb5">pic.twitter.com/DxaydDtXb5</a></p>&mdash; Nochi (@shikarunochi) <a href="https://twitter.com/shikarunochi/status/1169256028317159425?ref_src=twsrc%5Etfw">September 4, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/shikarunochi/status/1169256028317159425

## WiiリモコンとESP32がつながった
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">BeetleC、なんとか操作できるようになった｜ノート君｜note（ノート） <a href="https://t.co/0kU92E5Y6G">https://t.co/0kU92E5Y6G</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1172336625629548545?ref_src=twsrc%5Etfw">September 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/urekat/status/1172336625629548545

BeetleCは細かいゆっくりとした制御に向いていないことがわかりました。
なんとか操作できるようにはなりましたが、次やるなら別のでやります。

古いBluetoothプロトコルを深追いしすぎました。

https://github.com/takeru/Wiimote

ESP32のArduinoでWiiリモコンとヌンチャクを使えるようになりました。



# トレーニングデータの収集
Wiiリモコンでコースを走らせながら、
「M5StickCから制御データ(アクセル＆ハンドルなど)を取ってきて、M5StickVで撮影した画像をSDカードに保存」
これをずっと繰り返すだけ、かんたん。と思っていたけど、なんやかんや試行錯誤があってずいぶんアレなコードになりました。

数百枚数千枚の画像を制御データと関連付けてなるべく高頻度に記録したいので、

 - 1. 画像をRamdiskにいったん書き出す
 - 2. SDカード上の記録ファイルに
  - 2-1. 制御データ追記
  - 2-2. 画像データ追記

としています。4-5fpsで記録できています。



# コース

![IMG_5481.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16626/e73f7238-a7ea-faa6-d908-9e7f2a036c50.jpeg)


- 最初は道幅5cmぐらいの細いコースでした。
- 細い道を走ってもらってもつまらないことに気がついたので、道幅を15cmぐらいにしました。
- A3のコピー用紙にホワイトボードマーカーで描いています。
- なかなか学習がうまくいかないのでコースの線が見えやすいように太く(2cmぐらいヌリヌリ)しました。
- それでもなかなかうまくいかないので赤いセンターラインを追加しました。



# ハードウエア改善

## タミヤのボールキャスター
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">すごく操作しやすくなりました！ <a href="https://t.co/GYUJIaAD4V">pic.twitter.com/GYUJIaAD4V</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1172562954975240192?ref_src=twsrc%5Etfw">September 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/urekat/status/1172562954975240192
小回りしようとすると後輪が引っかかって回らないので後輪を諦めました。

## ダイソーのスマホ用広角レンズ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/m5stickv?src=hash&amp;ref_src=twsrc%5Etfw">#m5stickv</a> <a href="https://twitter.com/hashtag/m5stickc?src=hash&amp;ref_src=twsrc%5Etfw">#m5stickc</a> <a href="https://twitter.com/hashtag/beetlec?src=hash&amp;ref_src=twsrc%5Etfw">#beetlec</a> とダイソー広角レンズとタミヤのボールキャスターで地面だけちゃんとみてなさい <a href="https://t.co/Q3J94sdMB2">pic.twitter.com/Q3J94sdMB2</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1188010204597587968?ref_src=twsrc%5Etfw">October 26, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/urekat/status/1188010204597587968
M5StickVのカメラは視野が狭すぎだったのですが、これをつけるといいかんじになりました。

## 角度調整スペーサー(発泡スチロール)
もっと地面をしっかり見てもらうために三角に切った発泡スチロールをM5StickCの下にいれてレンズを下に向けています。




# 学習する
ベースにしたのはこれ。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">gheesung版を発展させ、自分で用意したデータセットに対応できるようにしつつGrad-CAMによる可視化を追加した <a href="https://twitter.com/hashtag/M5StickV?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickV</a> 用のサンプルです。説明の不備や実装の間違いなどいろいろあると思います。コメント、リプライなどご都合のよい方法でお知らせいただけると助かります。<a href="https://t.co/A5H9bX7eLH">https://t.co/A5H9bX7eLH</a></p>&mdash; Shigeru Kobayashi (@kotobuki) <a href="https://twitter.com/kotobuki/status/1160353423293706240?ref_src=twsrc%5Etfw">August 11, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
https://twitter.com/kotobuki/status/1160353423293706240

ハンドルの切り具合ごとに画像を9個のカテゴリにわけて学習させました。
1枚の画像から、右に行くのか左に行くのかハンドルの切り具合を判断させます。

これもいろいろ試行錯誤があってアレなコードになっています。
- 左右反転
- データ水増し
- 分類 or 回帰
- ハイパーパラメータ
- DonkeyCar
- ハンドルとアクセルを同時に出力
いろいろやってみました。

![train.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16626/b7830391-f2d7-17f8-0387-aec4a222c53d.png)

ひどい過学習ですが、

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16626/71457c2e-b8d3-8b04-bb48-91c2470cd5f6.png)
![categ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16626/9d85ce92-7ab6-4509-07d2-9db0fe65a2ee.png)

右は右、左は左と判断できてるので、いいか。

![kaiki.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16626/e4aa8687-7338-0fe6-a1cc-69c6f62c8d34.png)

↑ハンドルの値を直接回帰のモデルで学習した結果。これも右は右、左は左。



「画像」を入力すると「ハンドルの切り具合」が出力させるモデルが完成しました。



# kmodelまわりの苦労
M5StickVでの学習〜推論のやりかた情報が少なくて苦労しました。

* ncc(kerasで学習後のtffile->kmodel変換ツール)の謎エラー
* nccのバージョン問題 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://t.co/25aSaTInI8">https://t.co/25aSaTInI8</a><a href="https://twitter.com/hashtag/M5StickV?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickV</a> のkmodel関連コードって古い？<br>最新のnncaseのバグfixや機能追加を利用できないんでは？<br><br>(途中で諦めたのでなんか勘違いしてるかもしれません)</p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1187803051605651456?ref_src=twsrc%5Etfw">October 25, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>




* M5StickVでkmodel読み込み時のエラー表示がおかしい 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://t.co/d8NAHvZNtK">https://t.co/d8NAHvZNtK</a> おかしくないですか？ <a href="https://twitter.com/hashtag/m5stickv?src=hash&amp;ref_src=twsrc%5Etfw">#m5stickv</a></p>&mdash; 佐々木竹充/SASAKI TAKERU (@urekat) <a href="https://twitter.com/urekat/status/1185332270632071168?ref_src=twsrc%5Etfw">October 18, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


使っている人が少ないのでトラブったときにググってもヒットせず、元のソースコードやデータシートまで調べてなんとかするってことがよくあります(Vに限らずCも)。



# 自動運転!!

M5StickVの中のAIが「カメラ画像」から「左右」を判断します。
アクセルを固定の値にして、判断した「左右」をM5StickCに送り、BeetleCを制御するようにしました。


『
自動運転モードにしてコースに置いてみると、、

！！！！

ちょっと左右にフラフラしてるけど、直線コースにそって走ってます！！黒い線に跳ね返されてかくかくしてるのか？
はじめてのカーブ、、も、ぐいっとちゃんと曲がって、かくかくしてるけど！右カーブ、左カーブ、ちゃんとコースをまわっています！！
運がいいと、直線コースをまっすぐ走れることもあるみたいです。かくかくしないでカーブするときもあります。
逆周りも、できてるー！が、あ、コースを外れてしまった、が、反対側のコースから入って周回再開！！
なんか、いきものっぽくてかわいい。なんだこれ、虫か？！
』

それでは、とてもかわいいM5自動運転カーを動画でどうぞ↓↓↓


...
...


という予定でしたが、撮影する直前に片側のモーター(のギア?)が壊れてしまったようです。
あまりにかわいく走りまわるのでぐるぐる無理をさせすぎてしまいました。

タイヤがまわりはしているのですが、音がシャリシャリ鳴っていてトルクが弱いです。ギアが割れてる？
もうまともに走れないので残念ですが動画はありません。。。
見たい人がたくさんいるならBeetleCを追加で買って撮影してもいいけど、、、次はもうすこし良いクルマでやりたいです。



という感じで、M5自動運転カー、無事に完成ということで。お疲れさまでしたー。