AndroidをROS2ノードにしてロボットのコントローラを作る

# GUI付きのロボットのコントローラ
ROSで動くロボットを作ったときに下記のようなコントローラ作りたくなることがあると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/2e5cca6a-b648-b14c-1359-e10f377bd0b3.png)
↑Boston DynamicsのSpot Tablet

https://twitter.com/WatakoLab/status/1628699233162514432?s=20

今回はROS2で使えるAndroid用のコントローラアプリをUnityで作る方法を紹介します

# これまでのROS+Android
## Android端末でROSを動かす
こちらはAndroid端末にターミナルアプリを入れてUbuntuを動かす方法です
UbuntuPCで作業する時と同様にターミナルにコマンドを打ち込んでノードを起動したりrvizを起動したりします
- [Android携帯でROSを動かすだって！？](https://qiita.com/MeRT/items/c4d27db1aa82925988e9) (ROS1)
- [Android端末でROS1/ROS2を動かす（Rvizもあるよ！）](https://qiita.com/MAEHARA_Keisuke/items/4422ae78fb154fc2a851) (ROS1/ROS2)

## Android端末をROSノード化する
こちらはAndroidアプリを使ったり作ったりしてROSと通信するための方法です
ロボットのコントローラを作るのはこっちのほうが近い

- [ROS TeleopでAndroid-PC間を無線通信してみよう](https://qiita.com/kazuyamashi/items/0e813d0e31182e7c6b53) (ROS1)
    Playストアで公開されてるROS Teleopを使う
- [AndroidでROSアプリ開発](https://qiita.com/goodclues/items/40f7bf52fb74fa13190c) (ROS1)
    rcljavaを用いてAndroid Studioでアプリを作成する
- [ROS講座124 Unityアプリを整える・androidで使う](https://qiita.com/srs/items/e92f726c84de9b7ca6e6)
    RosSharpを用いてUnityでアプリを作成する
- Unity Robotics Hubを使用する方法 (ROS1/ROS2)
    具体的に挙げられる記事を見つけることができませんでしたがUnityが公式対応したUnity Robotics Hubを用いると自分の環境ではAndroid端末でもROS2と(おそらくROS1でも)通信することができました。
    ROS側でROS TCP EndpointというUnity側と通信するためのノードを実行する必要があります。
    (Unity Robotics Hub参考記事) [Unity Robotics Hubの使い方](https://note.com/npaka/n/n6cd4d79f22c2)
    
# ROS2+Unityの選択肢

Android上で動かすことを考えなかったときにros2-for-unityというUnity用のパッケージが存在します。

https://github.com/RobotecAI/ros2-for-unity

これは先に紹介したUnity Robotics Hubと比べてUbuntu・Android端末間で余計なデータ変換が入らず高パフォーマンスで通信が可能です。
詳しくは下記の記事で紹介されています。

https://zenn.dev/hakuturu583/articles/ros2_for_unity

> ros2-for-unityはRobotec.aiによって開発されたUnityとROS2間の通信を行うライブラリです。
ROS2 C#クライアントライブラリをネイティブライブラリとしてUnity Projectに取り込み、DDSのプロトコルで他のROS2アプリケーションと通信します。
OSはWindowsとUbuntuをサポートしています。
無駄な変換が一切挟まっていないため、これより早いプロセス間通信の方法は存在しないと言えます。

ros2-for-unityはかなり整備されていてhumbleでもしっかり動くため、これがそのままAndroid端末でも動けば嬉しいなと思って動くようにしました。

https://twitter.com/Kotakku_07/status/1635654952042528768?s=20

動作確認がhumbleでしかできていませんがリポジトリを以下に公開しました  
Releasesからunitypackageをダウンロードできます

https://github.com/Kotakku/ros2-for-unity

# ジョイスティックのアプリを作ってTurtlesimを動かす例
1. Unityでプロジェクトを作成する
    Input Systemを使用するので導入します
    (見づらいのでMain Camera -> Camera -> Clear Flagsを```Solid Color```にしました)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/9f346046-57ef-3a86-cc01-74aea4d47629.png)

1. https://github.com/Kotakku/ros2-for-unity-android-package からunitypackageをimportする
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/4912f862-d997-c55b-f093-cf83cb8c9d44.png)

1. ジョイスティックになるImageを追加してOn-Screen Stickを追加する
    ```Source Image```は```Knob```にしました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/d7452db2-ae8a-f0bb-ad4c-9f56bdba8dc5.png)

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/f2aa5874-c793-3cc0-976b-cdcdbcbee725.png)
    サイズ、```Movement Range```、```Control Path```は適当に設定してください
    今回は```Control Path```を```Left Stick[Gamepad]```に設定しました

1. スクリプトを書く
    ```C#
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.InputSystem;
    using ROS2;
    
    public class TwistPub : MonoBehaviour
    {
        private ROS2UnityComponent ros2Unity;
        private ROS2Node ros2Node;
        private IPublisher<geometry_msgs.msg.Twist> twist_pub;
    
        // Start is called before the first frame update
        void Start()
        {
    #if UNITY_ANDROID && !UNITY_EDITOR
            // ROS_DOMAIN_IDを使う場合はこの行で設定する
            Environment.SetEnvironmentVariable("ROS_DOMAIN_ID", "1");
    #endif
            TryGetComponent(out ros2Unity);
        }
    
        // Update is called once per frame
        void Update()
        {
            if (ros2Unity.Ok())
            {
                if (ros2Node == null)
                {
                    ros2Node = ros2Unity.CreateNode("ROS2UnityTalkerNode");
                    twist_pub = ros2Node.CreatePublisher<geometry_msgs.msg.Twist>("/turtle1/cmd_vel");
                }
    
                geometry_msgs.msg.Twist msg = new geometry_msgs.msg.Twist();
                var current = Gamepad.current;
                var input_vec = current.leftStick.ReadValue();
                msg.Linear.X = input_vec.x;
                msg.Linear.Y = input_vec.y;
                twist_pub.Publish(msg);
            }
        }
    }
    ```

1. ROS2UnityComponent.csと作ったスクリプトを貼り付ける
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/85ea2237-61c8-c43f-f4b5-f0679bff3067.png)

    ここまで来るとUnity editer上で動作確認ができます。
    Turtlesimを起動します
    ```
    ros2 run turtlesim turtlesim_node
    ```
    Unity editerで実行してジョイスティックを動かしたら亀が移動することを確認します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/503857d4-871e-5a99-c7c1-a8c793b65fe9.png)

1. ビルド用の設定をする
    PlatformをAndroidに変更します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/a5a77aba-0c79-11bd-0d34-8131462a4725.png)
    
    ```Player Settings...```から```Scripting Backend```を```IL2CPP```に変更します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/f5978385-f0b8-e92c-0b00-2684d2754689.png)

    ```Target Architectures```の```ARM64```にチェックを入れます
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/2777376d-9d8d-2bdc-19aa-9f1ae882ae82.png)

    ```Internet Access```を```Require```に変更します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/24966492-6b65-5f0f-b0aa-8f2f226a8cbd.png)


1. ビルドして実行する
    Androidタブレットのジョイスティックを動かしたら亀も動きます
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207366/c42a867b-8c91-6569-eea0-012c743c4f95.png)

これでROS2用のAndroidアプリを作ることができました