※今回の記事はほとんど下の記事の再現です。偉大な先人に感謝。
https://qiita.com/Kotakku/items/cdc3eca89dd8aec4ee86
今回はAndroidとROS2を通信させてスマホコントローラーを作成する。なお,UnityはUbuntu22.04に対応していないため,大人しくWindowsにインストールすること。
それぞれのバージョンは以下の通り。
- Windows11
- Unity 2021.3.25f1
- Ubuntu22.04
- ROS2 Humble
それぞれの導入については簡単なので省略する。
大まかな解説
最近はTelloなどの市販品に限らず,ロボコンなどでもスマホでロボットを操作する事例をよく見かける。遅延はどうなってるの?とか,コントローラーで良くね?とか,色々消極的な意見はあるだろうが,やはりスマホからロボットを操作しているとなんとなくカッコよく見えるのは確かだ。
そんなわけでスマホをコントローラーとして用いようと思った時に,最も簡単と思われるのがUnityとROS2を用いるやり方だ。この方法ではros2-for-unityという,ネイティブでUnityとROS2を通信させるライブラリを利用する。こちらの記事によるとこのライブラリは他のものと比べて高速で通信できるらしい。
ros2-for-unityはRobotec.aiによって開発されたUnityとROS2間の通信を行うライブラリです。
ROS2 C#クライアントライブラリをネイティブライブラリとしてUnity Projectに取り込み、DDSのプロトコルで他のROS2アプリケーションと通信します。
OSはWindowsとUbuntuをサポートしています。
無駄な変換が一切挟まっていないため、これより早いプロセス間通信の方法は存在しないと言えます。
OSはWindowsとUbuntuをサポートしています
という部分を読んで「ならAndroidでは使えないじゃん!」と思った人がいるかもしれないが,Androidに対応するためのパッケージを有志が公開しているので問題ない。
また,Unityを利用する事でUI開発が容易になるなどのメリットもある。便利なツールはどんどん使って開発を効率化していこう。
準備
Unityで開発を始めるための準備を行う。
パッケージのダウンロード
こちらからros2-for-unityのビルド済みソースコードをダウンロードする。
今回の例ではROS2がインストールされていないWindows上での利用が想定されるので,Ros2ForUnity_humble_standalone_windows11.zip
をダウンロードする。
次にこちらからros2-for-unity-android-packageをダウンロードする。これはros2-for-unityをAndroidでも使えるようにしたパッケージだ。
ダウンロードした2つのファイルを適当に解凍しておく。
プロジェクトの作成
Unity Hubを開く。
日本語の方が良いという人は左上の歯車マークをクリックしてAppearance
>Language
>日本語
とする。
右上の新しいプロジェクト
を選択してテンプレートとして2D Mobile
を選択する(恐らく初回はテンプレートのダウンロードが求めらられる)。
プロジェクト名と保存場所を指定し,プロジェクト作成
をクリックする。
なお,今回はプロジェクト名をAndroid Controller
とした。
パッケージの導入
Unityのウィンドウが開く。日本語にしたい人は左上からEdit
>Preferences...
>Languages
>Editor language
>日本語(Experimental)
とすれば良い。
先ほどダウンロードしたパッケージを導入する。
プロジェクトウィンドウ上で右クリックをすると以下の画像のように表示が出てくるので,パッケージをインポート
>カスタムパッケージ...
をクリックする。
先ほど解凍したros2-for-unity-android-package
の中にあるRos2ForUnityAndroid.unitypackage
を選択し,開く。
すると下の画像のようなポップアップが出てくるので,そのままインポート
を選択する。
プロジェクトウィンドウにRos2ForUnity
というフォルダが追加されているのが確認できると思う。
Ros2ForUnity
>Plugins
と開くとAndroid
というフォルダがあるので,その上で右クリックをしてエクスプローラーで表示
をクリックする。
エクスプローラーをもう一つ開いて先ほど解凍したもう一つのフォルダ(Ros2ForUnity)を開く。Ros2ForUnity
>Plugins
と開くと,中にWindows
というフォルダがあるので,それを先ほど開いた別のエクスプローラーのウィンドウにコピペする。
Unityの画面をクリックすると自動的にインポートが行われる。
諸々の設定
次のステップでInput System Package
というパッケージを利用したいのでその設定を行う。
ウィンドウ
>パッケージマネージャー
を開く。
出てきたウィンドウの左上のパッケージ:プロジェクト内
となっている部分をクリックし,Unityレジストリ
に変更する。
右上の検索欄にInput
と入力するとInput System
というパッケージが表示されるので,それをインストールする。
画像のような表示が出てくるのでYes
を選択してUnityを再起動する。
次にInput System Packageをアクティブにする。
左上のメニューから編集
>プロジェクト設定...
と開き,プレイヤー
>Windows, Mac, Linuxの設定
>その他の設定
を開く。
その他の設定
>設定
>アクティブな入力処理*
>入力システムパッケージ(新)
とする。
ポップアップが出てくるのでApply
を選択してUnityを再起動する。
Unityでの作業
Buttonを作成する
コントローラーのボタンの代替となるようなものを作る。
ヒエラルキーウィンドウ上で右クリックし,UI
>ボタン-TextMeshPro
と選択。
このような表示が出てきたらImport TMP Essentials
を選択しておく。
必要であればヒエラルキーウィンドウからButton
>Text(TMP)
の不可視マークを外しておく。
同じくヒエラルキーウィンドウのCanvas
をダブルクリックすると,シーンビューがUIサイズに調整されるので,Buttonのオブジェクトをいい感じのサイズに調整しておく。
ヒエラルキーウィンドウでText(TMP)
を選択すると,右側のインスペクターウィンドウにText(TMP)
の情報が表示される。
TextMeshPro-Text(UI)
>Text Input
にある文字列を変更しておく。
今回はSay Hello
とした。その下のMain Setting
では文字の色やサイズが変更できるので気になる人は触ってみるといいだろう。
次にButton
にアタッチするC#スクリプトを作成する。
プロジェクトウィンドウ上で右クリックし,作成
>C#スクリプト
とする。
今回はファイル名をHelloButton
とした。作成したC#ファイルをダブルクリックすると,VisualStudioが開くのでそこに以下のコードを貼り付ける。
using UnityEngine;
using ROS2;
using UnityEngine.UI;
public class HelloButton : MonoBehaviour
{
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IPublisher<std_msgs.msg.String> chatter_pub;
private int i = 0;
// ボタンの参照
[SerializeField] private Button button;
// シーン読み込み時に呼び出される関数
void Start()
{
TryGetComponent(out ros2Unity);
// onClickに関数を登録
button.onClick.AddListener(OnButton);
}
// フレーム更新時に呼び出される関数
void Update()
{
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
// Nodeの名前を指定する
ros2Node = ros2Unity.CreateNode("ROS2UnityTalkerNode");
//トピックの名前と型を指定する
chatter_pub = ros2Node.CreatePublisher<std_msgs.msg.String>("hello_from_Unity");
}
}
}
// ボタンが押されたときに呼び出される関数
void OnButton()
{
i++;
std_msgs.msg.String msg = new std_msgs.msg.String();
msg.Data = "Hello world from Unity" + i;
chatter_pub.Publish(msg);
}
}
ちなみにUnityではファイル名とclass名が同じでないとエラーが出るのでコピペする際には注意する事。
今作成したHelloButton.csと/Ros2ForUnity/Scripts/ROS2UnityComponent.csをヒエラルキーウィンドウのButton
にドラッグ&ドロップしてアタッチする。
インスペクターウィンドウで画像のように表示されていればアタッチできている。
インスペクターのHello Button(スクリプト)
>ボタン
の⦿をクリックしてButtonオブジェクトを割り当てる。
これをしないとボタンを押しても何も起こらないので注意。
JoyStickを作成する
コントローラーのJoyStickの代替となるようなものを作る。
先ほどと同様にヒエラルキーウィンドウ上で右クリックし,UI
>画像
と選択。Imageでは分かりにくいので名前をJoyStickに変更しておく。
インスペクターウィンドウからImage
>ソース画像
の右側の⦿をクリックし,出てきたポップアップから好きな画像を選ぶ。今回はKnob
という白い円の画像を選んだ。
さらにインスペクターの下部にあるコンポーネントを追加
からOn-Screen Stick
を追加する。
追加されたOn-Screen Stick
の設定を以下の画像のように変更する。
Movement Range
はジョイスティックの動き回る範囲であり,小さすぎると操作しずらいので値を増やしておく。
シーンビューからJoyStickのサイズや位置を調整しておく。
次にJoyStick
にアタッチするC#スクリプトを先ほどと同様の手順で作成する。今回はファイル名をJoyController
とした。
ファイルを開いて以下のコードをコピペする。
using UnityEngine;
using UnityEngine.InputSystem;
using ROS2;
public class JoyController : MonoBehaviour
{
private ROS2UnityComponent ros2Unity;
private ROS2Node ros2Node;
private IPublisher<geometry_msgs.msg.Twist> twist_pub;
// シーン読み込み時に呼び出される関数
void Start()
{
TryGetComponent(out ros2Unity);
}
// フレーム更新時に呼び出される関数
void Update()
{
if (ros2Unity.Ok())
{
if (ros2Node == null)
{
// Nodeの名前を指定する
ros2Node = ros2Unity.CreateNode("UnityJoyNode");
// トピックの名前と型を指定する
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);
}
}
}
turtlesimを動かしたいのでこのようなコードになっている。
先ほどと同様に,今作成したJoyController.csと/Ros2ForUnity/Scripts/ROS2UnityComponent.csをヒエラルキーウィンドウのJoyStick
にドラッグ&ドロップしてアタッチする。
その他必要な作業
- ROS_DOMAIN_IDの設定
これまでの実装では特にROS_DOMAIN_IDを設定してこなかったが,実際に使う場合は必ずIDを設定する必要がある。また,ROS_DOMAIN_IDはROS2UnityComponent.csの実行よりも先に設定される必要があるため,その設定を行う。
※ROS_DOMAIN_IDは例として123
としているが,適宜自分の環境のものに置き換えること。
先ずは,プロジェクトウィンドウ上で右クリックをしてSetID
という名前のC#スクリプトを作成し,そこに以下のコードをコピペする。
using System;
using UnityEngine;
public class SetID : MonoBehaviour
{
void Start()
{
// 現在設定されているIDを調べる
string value = Environment.GetEnvironmentVariable("ROS_DOMAIN_ID");
Debug.Log("current ROS_DOMAIN_ID:" + value + "\n");
// ROS_DOMAIN_IDに123を設定する
Environment.SetEnvironmentVariable("ROS_DOMAIN_ID", "123");
Debug.Log("DOMAIN_ID set OK\n");
// 上手く設定できているか確認する
value = Environment.GetEnvironmentVariable("ROS_DOMAIN_ID");
Debug.Log("current ROS_DOMAIN_ID:" + value + "\n");
}
}
作成したスクリプトを適当な空のオブジェクトに貼り付けて実行されるようにする。
ヒエラルキーウィンドウ上で右クリックをして空のオブジェクトを作成
を選択し,分かりやすくするためオブジェクトにSetID_Object
という名前を付けた。
先ほど作成したSetID.cs
をプロジェクトウィンドウからヒエラルキーウィンドウ上のSetID_Object
へドラッグ&ドロップしてスクリプトをアタッチする。
上手くいっていればSetID_Object
のインスペクターが画像のようになる。
次にメニューから編集
> プロジェクト設定...
>スクリプト実行順序
と開き,右下の+
からSetID
を追加する。
SetID
の右側の欄に-1
と入力し,Apply
をクリックすればSetID
は通常のスクリプトよりも早く実行されるようになる。
- Input System Packageの準備の続き
ヒエラルキーウィンドウからEventSystem
を選択し,インスペクターウィンドウでStandalone Input Module
のReplace with InputSystemUIInputModule
をクリックする。
以上でInput System Package
を使う準備ができた。
ビルド&インストール
ビルドの実行
今作ったゲーム?を実際にアプリとしてビルドしていく。
ファイル
>ビルド設定
を開く。
下の画像のようなウィンドウが出てくるので,プラットフォームにAndoridを選択してプレイヤー設定...
を開く。
※Androidが選択できない人はUnity HubからAndroid Bulid Supportをインストールすること。
解像度と表示
>自動回転を許可する向き
を横向き(右)
と横向き(左)
だけにする。
その他の設定
>設定
>スクリプティングバックエンド
をMonoからIL2CPPに変更し,その下のターゲットアーキテクチャ
のARM64にチェックを入れる。さらにインターネットアクセス
を必須
に変更する。
Project Settingsを閉じてBuild Settingに戻り,ウィンドウ右下のターゲット切り替え
をクリックする。
すると下の画像のような画面になるのでビルド
を選択する。
ファイル名と保存場所を聞かれるので適当に決める。しばらく時間がかかるので気長に待つこと。
Androidへのインストール
先ほど作成したapkファイルをAndroidにインストールする。以下の操作はスマホによって微妙に異なると思われるのであくまでも一例。
Androidの設定アプリを開き,デバイス情報
>ビルド番号
を連打して開発者向けオプションを有効化する。
システム
>開発者オプション
>デバッグ
からUSBデバッグを有効にする。
スマホをPCにUSB接続し,先ほど作成したapkファイルをスマホの適当なフォルダにコピペする。
スマホ側で適当なファイルアプリでapkファイルを探してタップすると画像のような表示が出てくるので,そのままインストールする。いろいろ警告が出てくるかもしれないが全て無視すれば良い。
テスト
スマホ側で先ほどインストールしたアプリを起動する。
Ubuntu22.04を起動し,スマホと同じWiFi(テザリングでも良い)に接続していることを確認してから,ターミナルに以下のコマンドを打ち込む。
$ ros2 topic list
このように表示されていればうまくいっている。
/hello_from_Unity
/parameter_events
/rosout
/turtle1/cmd_vel
次に以下のコマンドをそれぞれ別のターミナルで実行する。
$ ros2 run turtlesim turtlesim_node
$ ros2 topic echo /hello_from_Unity
スマホ側のボタンを押すとメッセージが,joystickを操作すると亀が動くはずだ。
※動画に飛びます
終わり
ROS_DOMAIN_IDの設定にかなり手こずってしまったが,なにはともあれ憧れのスマホコントローラーができた。今回はボタンとjoystickというコントローラーの基礎的な要素のみの実装だったが,これを応用すれば素晴らしいオリジナルUIを作成できるのではないだろうか。
参考文献
入力システムパッケージ (Input System Package) を導入する
ros2-for-unity-android-package
AndroidをROS2ノードにしてロボットのコントローラを作る
ROS2 for Unityで始めるUnityとROS2間の高速データ通信