[Project Bonsai] Bonsaiを活用してみる　


# はじめに
Project Bonsaiについて調べてみましたの6回目です。
過去の内容はこちら。
1回目は[「Bonsaiを触ってみる」](https://qiita.com/masaya3/items/5cca56a773386bb80262)
2回目は[「Bonsaiをいじってみる」](https://qiita.com/masaya3/items/42d3050354c9c8141042)
3回目は[「Bonsaiのシミュレータを触ってみる」](https://qiita.com/masaya3/items/05b1d578d1921f8ae20f)
4回目は[「Bonsaiをローカル環境で使ってみる」](https://qiita.com/masaya3/items/1b7df03eb87747a3a)
5回目は[「Bonsaiのシミュレータを触ってみる【番外編】」](https://qiita.com/masaya3/items/090262c495e58f717f00)

いろいろ触ったり、いじったりしてきましたが、
今回はいままでの内容をもとに実際に動くものを作ってみたいと思います。

# [Bonsai](https://azure.microsoft.com/ja-jp/services/project-bonsai/)って何？
Microsoftが提供している自律システム向け機械教示サービスです。（現在はPublic Preview）
産業制御システムに焦点を当てた汎用ディープ強化学習プラットフォームのようです

# すべてを作ってみる
## 何をするの？
[Moab](https://microsoft.github.io/moab/)のように、何かハードを動かしたかったので、手元にあるtoioを学習結果をもとに動く何かを作ります。

### toioって？
SONYさんから販売されているロボットトイです。
専用のソフトを使って遊ぶことも可能ですし、ビジュアルプログラミングツールや、BLEを使い制御して動かすこともできます。

https://toio.io/

###何を学習させるの？

toioが2台あるので、１台が逃げて、もう１台が追いかける、追いかけっこをさせたいと思います。
追いかけるtoioが、状況を学習し、逃げるtoioを捕まえることを目的とします。

###追いかけっこのルール
####ルール
* 横7枠、縦5枠の枠内を移動するものとします（toioの簡易プレイマットのサイズです）
* 追う駒が動いた後、逃げる駒が移動します。（ターン制にしています）
* 追う駒、逃げる駒それぞれ移動可能な範囲が違います。（追う駒を有利にしてます）
![移動範囲](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/eb2219d0-ce83-1676-94c7-2d7cb06ca51c.png)


* 逃げる駒は、必ず動きます。
* 追う駒は、動かないという選択も可能です。
* 逃げる駒は、逃げれる可能な範囲から、追う駒との距離が一番離れる場所を選択し、複数存在する場合は、ランダムで位置を選びます。
* 逃げる駒の移動終了時に、追う駒の前後左右（斜め以外）の位置に逃げる駒が止まった場合は、捕まえたと判断し終了とします。

## シミュレータを作成する

上記のルールを、シミュレータに実装します。
今風に言うとデジタルツイン！？(Digital Twin)
[「ローカル環境でシミュレータを動かす」](https://qiita.com/masaya3/items/05b1d578d1921f8ae20f#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%A7%E3%82%B7%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99B)で、使用したC#版Cartpoleのシミュレータサンプルを参考にし、Bonsaiへの入出力及び、「追いかけっこ」のルールを実装します。
作成したコードは[こちら](https://github.com/tanaka3/bonsai_sample/tree/main/Simualtor/CatchAndRun)


|クラス名|説明|
|---|---|
|[Action](https://github.com/tanaka3/bonsai_sample/blob/main/Simualtor/CatchAndRun/Action.cs)|Bonsaiからのデータ入力を設定します。<br>今回の目的は、逃げる駒を捕まえることですので、追う駒がどこに移動するか？を入力値とします。<br>また、この値が実機を動かすためのデータとなります。|
|[Config](https://github.com/tanaka3/bonsai_sample/blob/main/Simualtor/CatchAndRun/Config.cs)|学習を行うための環境情報を設置します。<br>駒の初期位置を設定できるように準備していましたが、今回は使用していません。|
|[State](https://github.com/tanaka3/bonsai_sample/blob/main/Simualtor/CatchAndRun/State.cs)|Actionの入力に対するシミュレータ結果情報です。<br>この値がBonsaiへの入力値となります。<br>実機制御側も、State値を算出し、Bonsaiのエンジンにデータを投げる必要があります。|
|[Model](https://github.com/tanaka3/bonsai_sample/blob/main/Simualtor/CatchAndRun/Model.cs)|Actionなどの情報をもとにした結果をStateとして出力します。<br>今回は、実機におけるメインの制御部分の動作と同じ動きになります。|
|[Program](https://github.com/tanaka3/bonsai_sample/blob/main/Simualtor/CatchAndRun/Program.cs)|Bonsaiとのやりとりを行います。<br>基本的にはサンプルほぼそのままですが、Bonsaiとの入力部分を今回の仕様に合うように修正しています。|

#### デバッグしてみる
シミュレータが想定通りに動くかデバッグしてみます。
デバッグ方法は、[「シミュレータをデバッグする」](https://qiita.com/masaya3/items/05b1d578d1921f8ae20f#%E3%82%B7%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF%E3%82%92%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%99%E3%82%8B)を参考にしてください。

入力（`Action`）と、出力（`State`）が、想定している仕様通り動作しているか確認します。
![デバッグ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/56f48058-f69e-a451-5552-a91a182b6fc0.png)

### 教育カリキュラムを作成する
シミュレータが完成したら、教育カリキュラムを作成します。
シミュレータの入出力（`SimState`、`SimAction`）と、[学習の目標](https://qiita.com/masaya3/items/42d3050354c9c8141042#goal%E3%81%AE%E7%9B%AE%E6%A8%99)を設定します。
作成したコードは[こちら](https://github.com/tanaka3/bonsai_sample/blob/main/Inlking/catchandrun)

追う駒の移動は、**前後左右斜め** 、または **移動なし** なので、以下のように固定値を返すように設定しています。

```bash:SimAction
type SimAction{

    # 追う駒の移動方向
    catch_position_x:number<-1, 0, 1>,
    catch_position_y:number<-1, 0, 1>
}
```

今回の目標は、追う駒を、逃げる駒の前後左右に追い詰めることです。
この状態になると、各駒の距離である`distance`が1になりますので、`distance`をチェック対象とします（斜めの場合は1にならない）
1度目でも、1になればOKですので、`reach`(テスト値が規定の範囲内に少なくとも１回収まると、**成功**)とし、`ditance`が1以下を指定します。

```bash:goal
# 駒の距離が1以下になるれば終了
goal (state: SimState){
    reach `catch`:
       state.distance in  Goal.RangeBelow(1)
}
```

### 学習する

準備ができたので、Bonsaiに学習させます。
学習方法は、[「学習させてみる」](https://qiita.com/masaya3/items/5cca56a773386bb80262#%E5%AD%A6%E7%BF%92%E3%81%95%E3%81%9B%E3%81%A6%E3%81%BF%E3%82%8B)を参考にしてください。

##### 恥ずかしながら・・
当初逃げる駒の動作は、2枠分移動させるというルールでシミュレータを実装していました。
しかし、いくら学習させても目標達成率が0%から向上しませんでした。
教育カリキュラムに目標を追加したりといろいろ試したのですが、そもそも逃げる駒が2枠分動くと **目的を達成することができない** との結論に至り、現状の1枠分の移動にしています。。
当たり前のことですが、実現不可な内容は、学習できないという良い経験になりました。。

#### ローカルシミュレータからマネージのシミュレータに変更
Azureのコストを気にして、ローカルのシミュレータで学習を実行していました。
しかし、通信トラブルや、PC不調など24時間以上学習させても、終わる気配がありませんでした。。

結果として、ローカルでの実行を諦め、AzureにシミュレータのDockerイメージをアップし、Bosanaiに登録することで学習を終了させました。

利用方法に関しては、[「ローカルシミュレータを、Azureにアップし、Bonsaiに登録して利用する方法」](https://qiita.com/masaya3/items/090262c495e58f717f00)を参照してください。
これをしないとたぶん学習終わらないです。

ローカル環境からクラウドに移行するとサクッと学習が終わりました。
実行速度はローカルより30倍くらい早かったです。
![学習完了](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/fd05b5b1-ce2a-b59d-9afc-3d337d8c5964.png)


#### 学習結果を出力する

学習結果をローカルで利用するため、Dockerのイメージを出力します。
出力方法に関しては、[「学習されたBonsaiを出力する」](https://qiita.com/masaya3/items/1b7df03eb87747a3a4bc#%E5%AD%A6%E7%BF%92%E3%81%95%E3%82%8C%E3%81%9Fbonsai%E3%82%92%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B)を参照してください。

Bosaniの環境を部屋に転がっていたRasberryPi3B＋でBonsaiの環境を作りたいと思いますので、今回は`Linux Arm32V7`で学習結果を出力します。

![Export](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/7868509c-5c26-4ce4-d730-fe0b897ca940.png)


### RasberryPiにBonsaiのローカル環境作成する
Bonsai環境の構築には、Azure CliとDockerが必要となります。
インストールや設定は、以下のサイトなどを参考にしてください。

* [Raspberry Pi にDockerとDocker-composeを入れる](https://qiita.com/hawk777/items/467ea47377e69089de5b)
* [Install the Azure CLI on Linux](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-linux?pivots=apt)


準備ができれば、[「コンテナをダウンロード＆実行」](https://qiita.com/masaya3/items/1b7df03eb87747a3a4bc#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E5%AE%9F%E8%A1%8C)を参考に、環境を作成します。
![RaspberryPi Bonsai](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/7247d713-a2b5-e8d4-8fdd-0b1b84ddd760.png)
解像度が低くてBonsaiのアスキーアートが崩れてますね。。


### toioの制御とBonsaiへの通信処理を作成する
toioは、こちらも部屋に転がっていたM5Stack Core2から制御します。
toioの制御は、こちらを参考にさせてもらっています。

https://note.com/vhideo/n/nff5e2845beb1


M5Stack Core2は、シミュレータに相当する処理を担います。
追う駒、逃げる駒の位置情報の把握や、逃げる駒の移動先決定などがそれにあたります。
駒の位置情報(`State`)を　RasberryPi上に構築したBonsai環境と通信（HTTP)し、移動先情報(`Action`)を取得します。
取得後の情報をもとに、移動先を算出し、駒を制御（BLE)します。
そのため、コードも、C#で作成したシミュレータのソースコードに近しいものになっています。
作成したソースコードは[こちら](https://github.com/tanaka3/bonsai_sample/tree/main/M5Stack/bonsai_edge)


M5Stackの画面には、WiFiやtoioの接続状況、動作の状況、各種駒の位置を表示しています。
![M5Stack](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/5dce7a08-d299-0d02-a1d4-d7d9be42ddf2.png)


### 動かしてみる！
出来上がったら、動かしてみます。
最終的な構成は以下の通りです。
IoTのエッジ風にするのであれば、M5Stackを経由させず、すべてRaspberryPiで処理することも可能だと思います。
![構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/996dae01-964b-3814-3576-9ffabeca7fd4.png)
実際の環境
![環境](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/43b09f1e-d2d9-3b75-de75-18c0e99c298b.png)

#### 実行時の動き
実際に動かしたのがこちら。
左が逃げる駒、右が追う駒です。
M5Stackの開始ボタンを押すことで、スタートします。
1ターンごとに、マップ上の状況(`State`)をRaspberryPi上に構築されたBonsaiに送信し、追う駒の移動情報(`Action`)を取得しています。
逃げる駒が、上下左右に来た時点で、終了（終了の判断は、M5Stackが実施）
toioを適当な場所に配置しても、逃げる駒を最短距離で追い詰めていますね。面白い。

https://twitter.com/masaya3/status/1402189983184687129?s=20

# まとめ
教師データを一切作成せず、逃げる駒を追う駒の上下左右にするという目標設定で、目的の結果を得ることができました。
駒の移動のルールや、マップの範囲などは`State`の情報から学習していったのでしょうか？？（このあたり詳しい人がいたら聞きたいです）

教師データが不要な分、しっかりとしたシミュレータを用意するのか？が重要になるかと思います。
今回はシンプルためシミュレータの作成は簡単でしたが、IoTなど実際の実環境をシミュレートするのような場合は、既存のシミュレータを使用するほうが良いでしょうね。

成功するかわからないまま学習させてみましたが、学習が完了して、しっかり目的通りの動きになるのは楽しかったです。

産業用のため、なかなか個人で使うことはないと思いますが、もし興味があればぜひ使ってみてください。