箱庭でUnityの電車をマイコンシミュレーション環境で動かそう

EV3で実現した電車と信号を、Unityとマイコンシミュレーションを連携させて動かせるようにしました。


# 前提とする環境
現時点では、以下の環境を想定しております。

## Unity

* Unity Hub
  * Unity Hub 3.4.1
* Unity
  * Unity 2021.3.7f1
* Blender
  * Blender v2.9.3以降

なお、Unityおよび Unity Hub, Blender はインストールされていることを前提として解説します。

## Windows 環境

* Windows10 Home, Windows 11
* WSL2/WSLg/Ubuntu20.0.4
* WSL2/Docker Engine
* VSCode
  * マイコンの制御プログラムの編集／ビルド用に使います。

# 事前準備

Windows版では、WSL2を利用しますので、事前にインストールをお願いします。
また、インストール完了後、端末上で以下のコマンドを実行して、ネットワークツール(routeやifconfigコマンド)をインストールしてください。

```
sudo apt install net-tools
```

また、事前にUnityおよび [Blender](https://www.blender.org/) のインストールもお願いします！

さらに、Unityプロジェクトを開く際に Windows の Git がインストールされている必要があります。Windows用のGitがインストールされていない方は、こちらの記事を参考にして、インストールおよび環境変数のセットアップをお願いします。

https://www.hanachiru-blog.com/entry/2021/05/24/120000 

なお、環境変数設定後は、パソコンの再起動が必要となります。



# インストール手順

本環境を利用するための最初の一歩は、[hakoniwa-unity-ev3model](https://github.com/toppers/hakoniwa-unity-ev3model)をクローンすることです。

```
git clone --recursive https://github.com/toppers/hakoniwa-unity-ev3model.git
```

※注意：クローンする場所は、WSL2内のディレクトリではなく、`/mnt/c`の配下に任意のディレクトリを作成して、クローンしてください



## マイコンシミュレーション環境のインストール

マイコンシミュレーション環境はは、docker コンテナ上で実行しますので、以下の手順でイメージを作成します。


* 補足
  * Windows版では、`Docker Desktop for Windows`は利用しません。WSL2上のUbuntu環境に Docker Engine をインストールしたものを利用しますので、事前にご準備ください。インストール手順は、[こちら](https://docs.docker.com/engine/install/ubuntu/)です。


作業するディレクトリは、``hakoniwa-unity-ev3model/hakoniwa-base``直下です。

```
cd hakoniwa-unity-ev3model/hakoniwa-base
```

次に、docker イメージを取得します。

最初に docker を起動します。

```
sudo service docker start
```

そして、マイコン上で動作するプログラムのビルド用の環境の docker イメージを取得しましょう。

```
bash docker/template/make_dockerfile.bash  dev ev3rt
```

```
bash docker/pull-image.bash dev
```

なお、上記コマンドを実行すると、以下のようなエラーが発生する場合があります。

```
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/create?fromImage=toppersjp%2Fhakoniwa-ai-runtime&tag=v1.1.0": dial unix /var/run/docker.sock: connect: permission denied
```

この場合は、`/var/run/docker.sock`にアクセス権限を与えてください。

例：
```
sudo chown tmori /var/run/docker.sock
```

成功するとこうなります。

```
v1.0.0: Pulling from toppersjp/hakoniwa-single_robot-dev
　：
Digest: sha256:67590dbad91aa5c6829aa4d10d78ace140afe7087b15e333421cff13d4983250
Status: Image is up to date for toppersjp/hakoniwa-single_robot-dev:v1.0.0
docker.io/toppersjp/hakoniwa-single_robot-dev:v1.0.0
```


次に、マイコンシミュレーション環境の docker イメージを取得しましょう。

```
bash docker/template/make_dockerfile.bash  runtime single_robot
```

```
bash docker/pull-image.bash runtime
```


成功するとこうなります。

```
v1.0.0: Pulling from toppersjp/hakoniwa-single_robot-runtime
　：
Digest: sha256:9d4cc7156174e78b3592d2bdbe59be359ac39d34121063065c9e002ac02e3a5e
Status: Image is up to date for toppersjp/hakoniwa-single_robot-runtime:v1.0.0
docker.io/toppersjp/hakoniwa-single_robot-runtime:v1.0.0
```

最後に、`hakoniwa-unity-ev3model/hakoniwa-base/workspace/runtime/asset_def.txt` を以下のように編集してください。

変更前：
```
base_practice_1:1
```

変更後：
```
block_signal:1:signal.log
train_slow_stop:1:train.log
```

## Unity環境のセットアップ

こちらの[インストール方法](https://github.com/toppers/hakoniwa-unity-ev3model#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%96%B9%E6%B3%95)を参照ください。

### 電車モデルのシーン表示

Unityエディタ起動後、プロジェクトビューの　`Scenes/TraninModel/Hakoniwa` をダブルクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/6e993d24-bde9-e89c-5381-c1fcd9205872.png)

このまま、`Window/Hakoniwa/Generate` をクリックしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/e2ceca77-f128-77bf-636d-af75763ec2e7.png)


Unity側のセットアップはこれで終わりです。

# マイコンシ制御プログラムの場所とビルド方法

## マイコン制御プログラムの場所

プログラムの場所は以下になります。ロボット毎にディレクトリが切られています。
マイコン用のプログラムなので、C言語で書かれており、EV3RTというプラットフォーム上で制御プログラムは動作する前提となります。

* 電車モデルの制御プログラム
  * hakoniwa-unity-ev3model/hakoniwa-base/workspace/dev/src/train_slow_stop
* 信号モデルの制御プログラム
  * hakoniwa-unity-ev3model/hakoniwa-base/workspace/dev/src/block_signal


## マイコン制御プログラムのビルド方法

### 電車モデルの制御プログラムのビルド方法

```
bash docker/build.bash train_slow_stop
```

### 信号モデルの制御プログラムのビルド方法

```
bash docker/build.bash block_signal
```

# シミュレーション実行方法

まず、以下のコマンドを起動しましょう。

```
bash docker/run.bash runtime
```

成功するとこうなります。

```
INFO: ACTIVATING HAKO-MASTER
INFO: ACTIVATING ASSET-PROXY
OPEN RECIEVER UDP PORT=172.25.195.216:54001
OPEN SENDER UDP PORT=172.25.195.216:54002
delta_msec = 20
max_delay_msec = 100
INFO: shmget() key=255 size=80768 
Server Start: 172.25.195.216:50051
INFO: START block_signal-1
INFO: START train_slow_stop-1
INFO: SIMULATION READY!
```

次に、Unityのシミュレーション開始ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/644eb74a-56a3-1e24-e48a-39849b6c7298.png)

成功すると、こうなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244147/7ea9e931-4dbe-d0b3-f0c9-c8804aa987ba.png)

さらに、先ほど実行したコマンド側はこうなります。

```
subscribe_pdu_channel: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: SubscribePduChannelRequest { asset_name: "UnityAsset", channel_id: 0, pdu_size: 196, listen_udp_ip_port: "172.25.192.1:54003", method_type: "UDP", robo_name: "EV3TrainModel" }, extensions: Extensions }
create_asset_sub_pdu
create_pdu_channel: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: CreatePduChannelRequest { asset_name: "UnityAsset", channel_id: 1, pdu_size: 248, method_type: "UDP", robo_name: "EV3TrainModel" }, extensions: Extensions }
INFO: EV3TrainModel create_lchannel: logical_id=1 real_id=2 size=248
create_asset_pub_pdu: robo_name=EV3TrainModel channel_id=1 real_id=2
create_asset_pub_pdu: channel_ID=1
subscribe_pdu_channel: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: SubscribePduChannelRequest { asset_name: "UnityAsset", channel_id: 0, pdu_size: 196, listen_udp_ip_port: "172.25.192.1:54003", method_type: "UDP", robo_name: "EV3SignalModel" }, extensions: Extensions }
create_asset_sub_pdu
create_pdu_channel: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: CreatePduChannelRequest { asset_name: "UnityAsset", channel_id: 1, pdu_size: 248, method_type: "UDP", robo_name: "EV3SignalModel" }, extensions: Extensions }
INFO: EV3SignalModel create_lchannel: logical_id=1 real_id=3 size=248
create_asset_pub_pdu: robo_name=EV3SignalModel channel_id=1 real_id=3
create_asset_pub_pdu: channel_ID=1
register: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: AssetInfo { name: "UnityAsset" }, extensions: Extensions }
asset_notification_start: Got a request: Request { metadata: MetadataMap { headers: {"te": "trailers", "content-type": "application/grpc", "user-agent": "grpc-csharp/2.37.0-dev grpc-c/15.0.0 (windows; chttp2)", "grpc-accept-encoding": "identity,deflate,gzip", "accept-encoding": "identity,gzip"} }, message: AssetInfo { name: "UnityAsset" }, extensions: Extensions }
```

この状態で、Unityの `START` ボタンをクリックするとシミュレーション開始します。

# デモ

マイコン制御プログラムのデモはこちらです。

https://twitter.com/kanetugu2020/status/1683969231845748737