ML-Agentsで自動運転シミュレーション

# はじめに
「自動運転シミュレーション」は車を壁や他の車に衝突させないように運転させる学習環境です。
車が壁やほかの車に衝突するとエピソードが完了し、長く運転し続けた方が報酬が多くなるように設定している。

# 使ったもの
- ML-Agents(https://unity3d.com/jp/machine-learning)
- AnacondaNavigator(https://anaconda.org/anaconda/anaconda-navigator)
- 自動運転車の3Dモデル(https://assetstore.unity.com/packages/3d/vehicles/land/car-20128)

# 方法
## 学習実行のコマンド
※[MacでUnity ML-Agentsの環境を構築する(v0.5.0対応)](https://qiita.com/kai_kou/items/6478fa686ce1af5939d8)を参照

AnacondaNavigatorから作成した環境のTermialを起動する。

Terminal上で .../ml-agents-master/ml-agentsまで移動して、
```$mlagents-learn trainer_config.yamlのパス --env=hoge1 --run-id=hoge2 --train```
hoge1はUnityでBuildした実行ファイルの名前。ただし、拡張子(.exe, .app)は抜く。
hoge2は出力される学習モデル(ml-agents-master/ml-agents/models/hoge2-0/hoge1_hoge2.bytes)の名前。

## ソースコードの構成
- CarAcademy.cs:Academy(Academy.csのオーバーライド)
- Brain.cs:Brain(ML-Agents/Scripts/Brain.cs)
- CarAgent.cs:Agent
- RayPerception.cs:Agentの衝突判定(ML-Agents/Examples/SharedAssets/Scripts/RayPerception.cs)

## ソースコードの詳細
```CarAcademy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MLAgents;

public class CarAcademy : Academy
{
	public override void AcademyReset()
	{

	}

	public override void AcademyStep()
	{

	}
}
```

```CarAgent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Sprites;
using MLAgents;

public class CarAgent : Agent {
	// 参照
	private CarRayPerception rayPer;
	private Rigidbody rigidbody;

	// 変数
	private Vector3 initPosition; //初期位置
	private Quaternion initRotation; // 初期向き
	private bool crush; //衝突フラグ

	//初期化時に呼ばれる
	public override void InitializeAgent()
	{
		// 参照
		this.rayPer = GetComponent<CarRayPerception> ();
		this.rigidbody = GetComponent<Rigidbody> ();

		// 変数
		this.initPosition = this.transform.position;
		this.initRotation = this.transform.rotation;
	}

	public override void AgentReset(){
		// 車の初期化
		this.transform.position = this.initPosition;
		this.transform.rotation = this.initRotation;
		rigidbody.velocity = new Vector3 (0, 0, 0);
		rigidbody.angularVelocity = new Vector3 (0, 0, 0);
		this.crush = false;
	}

	//Stateの取得
	public override void CollectObservations(){
		float rayDistance = 50.0f;
		float[] rayAngles = { 0f, 45f, 90f, 135f, 180f, 110f, 70f };
		string[] detectableObjects;
		detectableObjects = new string[] { "car", "wall" };
		AddVectorObs (rayPer.Perceive (rayDistance, rayAngles, detectableObjects, 1f, 0f));
	}

	// フレーム毎に呼ばれる
	public override void AgentAction(float[] vectorAction, string textAction){
		// アクション
		//float handle = Mathf.Clamp (vectorAction [0], -1.0f, 1.0f) * 1.5f; //教材のコードでは上手くいかなかった。
		float handle = Random.Range(-1.0f,1.0f)*3.0f;

		// 車の向きと加速度の指定
		this.gameObject.transform.Rotate (0, handle, 0);
		this.rigidbody.velocity = this.gameObject.transform.rotation * new Vector3 (0, 0, 20); //速度調整できる。

		// 報酬
		AddReward (0.001f);

		// エピソード完了
		if (this.crush)
			Done ();
	}

	// オブジェクト衝突時に呼ばれる
	void OnCollisionEnter(Collision collision){
		this.crush = true;
	}
}
```

## Unityの画面
### Hierarchy
学習環境内で独立して行動する<font color="Red">Agent</font>:jeep
学習環境を管理する<font color="Red">Academy</font>:CarAcademy
<font color="Red">Agent</font>が観測した「状態」に応じて、Agentの「行動」を決定する<font color="Red">Brain</font>:CarBrain, PlayerBrain
![carHierarchy.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/feedd73d-4f90-f744-1ac4-b0de66cdd6fa.png)


### Scene
上から見下ろすと8台のjeepが原点を中心に配置される。
![scene.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/c198c73e-3ded-d4e7-7570-adbe6e9b7ee6.png)


### CarAcademy(Academy)
CarAcademy.csを加える。
![carAcademy.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/3a5deb0f-0586-e512-8a48-0e98e02f9b7e.png)


### CarBrain(Brain)
Brain.csを加える。
![carBrain.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/8a9da1bb-dbd2-ab73-d5b3-f8c10f4810b5.png)


### jeep(Agent)
jeepを8台用意する。その内1台のjeepにCameraを加えて実行時の走行の様子を確認する。
また、CarAgent.csとRayPerception.csを加える。CarAgent.csは3つのパラメータを持ち、jeepが<font color="Red">CarSpeed</font>の速さで<font color="Red">TimeOut</font>秒ごとに最大で<font color="Red">MaxAngle</font>度向きを変える。
![jeep1.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/e9d72533-4eba-c3c2-a144-22a5122da1aa.png)

# 結果
### 100000回学習したもの
![GreenTrue1.gif](https://qiita-image-store.s3.amazonaws.com/0/176349/bdb16421-9448-51bf-e73b-f0ac820e0d71.gif)
### 200000回学習したもの
![GreenTrue2.gif](https://qiita-image-store.s3.amazonaws.com/0/176349/d94971d5-0c2d-0b70-65da-b6834382254b.gif)
### 300000回学習したもの
![GreenTrue3.gif](https://qiita-image-store.s3.amazonaws.com/0/176349/3fe56922-18e1-2775-c39d-0a1747de5963.gif)

学習後、Terminalから下のコマンドを打ち込む。
```$tensorboard --logdir=summaries```

```TensorBoard 1.7.0 at http://WINDOWS-1O4MM0L:6006 (Press CTRL+C to quit)```が表示されたら、URL(http://WINDOWS-1O4MM0L:6006 )からTensorBoardに移動。
TensorBoardを開くと次のように学習状況のグラフが表示される。
![tecsorboard.PNG](https://qiita-image-store.s3.amazonaws.com/0/176349/d1f786d4-d70f-8e53-0dd0-f7e272a096b5.png)
<font color="Magenta">マゼンタ</font>:100000回, <font color="Seagreen">深緑</font>:200000回, <font color="Darkgray">シルバー</font>:300000回学習した結果。

以下がそれぞれのグラフの説明。

- Info/Lesson:レッスンの進捗状況(カリキュラム学習)
- Info/cumulative_reward:平均累積報酬のグラフ(継続して増加し上下の振れ幅が小さいことが期待される。)
- Info/entropy:Actionのランダムな動きの割合(継続的な減少が期待される。)
- Info/episode_length:エピソードの平均長(今回は長いほうが良いので増加が期待される。)
- Info/learning_rate:学習率のグラフ(時間とともに継続して減少する。)
- Info/policy_loss:Actionの方策の変化の割合(学習成功時に減少し継続して減少することが期待される。)
- Info/value_estimate:将来の報酬の予測(学習成功時に増加し継続して増加することが期待される。)
- Info/value_loss:将来の報酬予測と実際の報酬の乖離(報酬は安定したら減少することが期待される。)


# 参考文献
[Unityではじめる機械学習・強化学習 Unity ML-Agents実践ゲームプログラミング](https://www.amazon.co.jp/Unity%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%BB%E5%BC%B7%E5%8C%96%E5%AD%A6%E7%BF%92-Unity-ML-Agents%E5%AE%9F%E8%B7%B5%E3%82%B2%E3%83%BC%E3%83%A0%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E5%B8%83%E7%95%99%E5%B7%9D-%E8%8B%B1%E4%B8%80/dp/4862464181)