ROS講座123 UnityでROSと通信する

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 18.04 |
| ROS | Melodic |
| Gazebo | 9.0.0 |
| python | 2.7.17 |

Unityの動作環境は以下です。

| 項目 | 値 |
|:-:|:-:|
| ホストマシン | Windows10 |
| Unity | 2019.4 |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
これまでQtやブラウザでUIを作ってきましたが、今回はスマホで操作することを目標にしてUnityで作ってみます。
Unityはゲーム作成の統合開発環境で、ゲームで必要な3DCGや物理演算などの処理がを簡単に扱うことが出来ます。UnityはWindows/Mac/CentOSをサポートしていますが、今回はWindows上で開発します。
Unity本体だけでも基本的なゲームを作成することが出来ますが、アセットという追加のソフトウェアパッケージで処理や画像などの素材を追加することが出来ます。アセットはUnityのアセットストアで有償/無償で配布しています。無償の物でもクオリティーが高く便利なものがたくさんあります。今回はROSと通信ができる「ROS#」というアセットを使用します。
またUnityはマルチプラットフォームで実行できることが特徴です。Windowsの統合開発環境上でその場でプレビューすることができて、もちろんそれをWindowsアプリとしてリリースすることもがきます。それ以外にもAndroidやiPoneアプリとしてリリースすることもできます（ただしiPnone用のアプリ作成はMac上でのみ可能）。
Unityでは基本的にC#で開発を行います。UnityではC#プログラムを「スクリプト」と呼びます。
Unity自体に関する説明は長くなってしまうので、入門書にありそうな部分は説明を省きます。

## ROS#について
UnityとROSの間を通信するためのUnityのアセットとしてROS#があります。これはC#で書かれたRosBridgeのクライアントでUnityでです。このアセットはUnityのアセットストアで配布しているものではなくgithubのページからダウンロードしてUnityでインポートします。
ROS#はrosbridge_serverがjson形式にしたrostopicをwebsocket通信でやり取りをすることでUnityとROSを接続します。

# インストール（on Windows）

## Unity本体のインストール
[Unityのダウンロードページ](https://unity3d.com/jp/get-unity/download)からダウンロードします。UnityHubはUnityのバージョンなどの管理ツールで最初は「UnityHub」をダウンロードします。「UnityHub」から「Unity本体」をダウンロードします。最新のROS#の推奨バージョンの2019.4を入れます。
後々必要なのでUnity本体のダウンロードの時に「Android Build Support」にチェックを入れてください。

## プロジェクトの作成
UnityHubの「新規作成」から新しいプロジェクトを作成します。名前は何でも構いません。テンプレートは「3D」を選びます。
![unity_basic_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/9501bfd7-094d-c93b-7d8d-a4293f734bd8.png)


## ROS#アセットのプロジェクトへのインストール
1. [github上のリリースページ](https://github.com/siemens/ros-sharp/releases/tag/v1.7)から「RosSharp.unitypackage」をダウンロードしてローカルに保存します。
2. メニューバーの「Assets」->「Import Package」->「Custom Package...」を選んで出てくるウィンドウで「RosSharp.unitypackage」を選択します。
![unity_basic_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/f78fe147-46b8-6461-8e52-9fe937c0906c.png)

3. 出てくるウィンドウですべての項目にチェックが入っていることを確認して「import」 を押す。
![unity_basic_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/075f02b7-8f1f-f96b-517e-03105f28aa08.png)


# Unity上の操作（on Windows）
今回はROSからのimageのsubscribeとjoyのpublishを目標にします。

## RosConnectorの設置
* 「Hierarchy」ウィンドウの「＋マーク」->「Create Empty」で空のGameObjectを作成して、RosConnectorと名前を変えます。
![unity_basic_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/0c67d6d7-f5e3-77cd-49b4-426f406939aa.png)

* 「Project」ウィンドウのAssets/RosSharp/Scripts/RosBridgeClient/RosCommuncation/RosConnector.csをRosConnectorにアタッチします。
    * このスクリプトの「Ros Bridge Server Url」フィールドの値を{ROSを実行するPCのIPアドレス}:{Rosbridgeのポート}にします。
![unity_basic_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/6f868356-dc9c-ce55-9e24-0b5ba1d636e8.png)


## Imageのsubscribe
* まず画像を表示するためのplaneを追加します。「Hierarchy」ウィンドウの「＋マーク」->「3D　Object」->「Plane」を選択します
![unity_basic_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/63a2b52a-654d-565b-5130-9d107b9542d6.png)

* 位置（0,1,0）と姿勢（90,0,180）を調節します
![unity_basic_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/933ca50a-f382-38b9-ef80-767083f09bb0.png)

* 「Project」ウィンドウのAssets/RosSharp/Scripts/RosBridgeClient/RosCommuncation/ImageSubscriber.csをRosConnectorにアタッチします。
    * 「Topic」を「/head_camera/image_raw/compressed」と指定します。
    * 「Mesh Renderer」の横の三角形のマークをクリックして出てくる画面で先ほど作成したplaneを選択します。
![unity_basic_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/e86703e4-ebfe-9234-9ff5-841ea525fa71.png)

## Joyのpublish
* 「Project」ウィンドウのAssets/RosSharp/Scripts/RosBridgeClient/RosCommuncation/JoyPublisher.csをRosConnectorにアタッチします。
    * 「Topic」を/unity/joyとします。
* 「Project」ウィンドウのAssets/RosSharp/Scripts/RosBridgeClient/MessageHandling/JoyAxisReader.csをRosConnectorにアタッチします。
    * 「Name」を「Horizontal」とします。
* もう1つ「Project」ウィンドウのAssets/RosSharp/Scripts/RosBridgeClient/MessageHandling/JoyAxisReader.csをRosConnectorにアタッチします。
    * 「Name」を「Vertical」とします。
![unity_basic_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/197b3439-8d99-5a29-1035-2b5a461780cc.png)


# 実行
## ネットワーク構成
ROSを実行するUbuntuPCとUnityを実行するWindowsPCが同一セグメント上にいるとします。
以下UbuntuPCのIPアドレスを「192.168.2.105」とします

## ROSの実行（on Ubuntu）
シミュレーションを起動します。

```sh:ターミナル1(gazeboの起動)
roslaunch sim3_lecture base_world.launch 
```

```sh:ターミナル1(rosbridgeの起動)
roslaunch rosbridge_server rosbridge_websocket.launch
```

以下のようにgazwboが立ち上がります。
![unity_basic_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/4783adf0-4a5e-6461-ac82-d8dcc4f14fbf.png)

## Unityの実行（on Windows）
UnityEditorのplay(上部中央の三角形のボタン)を押します。

![unity_basic_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254442/967e6549-0d6a-ec14-6b9a-1991c64f8d62.png)

設置したPlaneにカメラの画像が写っています。またROSで`rostopic echo /unity/joy`とすると、Unity側画面でのキーボードの上下左右ボタンの入力が反映されます。

# コメント
このサンプルではpublish側でで不具合があるために動かない可能性があります。これの場合は以下の修正が必要です。

```c#:Assets/RosSharp/Scripts/RosBridgeClient/RosCommuncation/JoyPublisher.csの修正
protected override void Start()
{
    System.Threading.Thread.Sleep(3000); // 追加
    base.Start();
    InitializeGameObject();
    InitializeMessage();
}
```



# 参考
* [（書籍）Unity 3D/2Dゲーム開発実践入門 Unity 2019対応版](https://www.amazon.co.jp/Unity-3D-2D%E3%82%B2%E3%83%BC%E3%83%A0%E9%96%8B%E7%99%BA%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-2019%E5%AF%BE%E5%BF%9C%E7%89%88/dp/480261165X)
    * C#スクリプトの内容は軽く、Unity特有の内容について詳しく説明しているので1冊でUnityの基本的な開発が大体理解できます。
* [ROS#リポジトリ](https://github.com/siemens/ros-sharp)
* [ROS#使い方](https://kato-robotics.hatenablog.com/entry/2018/10/30/170446)

# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)