初めてUnityを触る機会がありました。
そうはならんやろ pic.twitter.com/9T3oBH2AdG
— Ar-Ray (@Ray255Ar) 2022年1月29日
なぜUnityを触りだしたかというと、私が現在開発しているYOLOX-ROSをバーチャル環境上でシミュレーションしたらデモとして最高なんじゃね?と思ったからです。
これまで私は画像認識系のプログラムの動作デモの画像を作るときは、Webカメラからのキャプチャ画像をスクショしていました。
しかしながら、画像だけだと移植元の物体検出プログラムとあんまり変わらないREADMEになっちゃうし、いまいちインパクトに欠けるなと感じていました。
そこで、これからはメタバースの時代ということで最新のプログラムを仮想世界に持っていくことにしました。バーチャル上の映像でもお試しできるなんて新時代ですよね!?
こうしてAr-Rayは個人的未開の地であるシミュレーションの道へと突き進むのであった…
ROSのシミュレーションといえば…?
ROSのシミュレーション環境といったらGazeboだと思っていたのですが、最近だとUnityのほうが人気みたいです。
ということでUnityの環境構築をしてレッツプログラミング!C#?なにそれ…
ここから先は実装までの過程を書くので、プログラムだけ気になる方は以下のGitHubリポジトリをご確認ください。使い方などもそのリポジトリのREADMEに載っています。わからないことがあればTwitterやissue欄で教えてください。
ググりまくる
あいにく私の手元にはUnityの本がなかったので、「ROS2 Unity」とかのキーワードを使って検索しまくりました。
すると最初にROS関連でよくお世話になっている片岡さんの記事を見つけました。どうやらros2-for-unityというプログラムが速くていいらしいです。
シミュレーションつよつよな方のオススメを無視するわけには行かなかったので、私はros2-for-unityのコードを参考に実装していくことにしました。
当然かもしれませんが、都合よくros2-for-unity + 画像処理を使ったプログラムが見つかるわけもなく、この後自分のC#力の低さやUnityのComponentの考え方が全く理解できないことが壁となり苦戦しまくることとなりました…
実装したい内容
ここで、当初私が作りたかった手法を以下の画像に示します。
言葉にしたらとってもシンプル。Unity上のカメラ映像をROS2用の画像データにして公開するだけです。
しかしながら、Unity歴1日の私にはとても苦しいことがありました。それは次の2点です。
- カメラからの画像の取得方法が分からない。(RenderTextureが分かっていないため)
- これまでcv_bridgeを使ってきたためか、どのようにしてカメラ画像を
sensor_msgs/Image
に値を埋め込めばいいのか分からなかった。
他にもデバッグログが出せないとか入出力先の指定方法がわからないなどの実装以前の悩みを抱えながら実装を始めました。
かなり参考になった情報
カメラから画像を取得するときはRenderTextureオブジェクトを使えば良いということが分かってきた頃、「Unity RenderTexture 保存」みたいなワードで探している途中で「な、なるほどーー」と思った記事に遭遇しました。
この記事は、カメラの映像と3D物体をRenderTextureオブジェクトを用いて紐付けるという内容が書かれていました。
記事とこの実装が直接関係しているわけではないのですが、RenderTextureの指定先を自由に変更可能であることと、RenderTextureさえ弄れれば画像を公開可能であるということが分かったのがとても大きかったです。
試行錯誤の末、ようやく形になりました。
やったああああああああ📹📹📹📹📹
— Ar-Ray (@Ray255Ar) 2022年1月30日
半日の沼の末に勝利😇
(YOLOX-ROS + Unity のデモ)#Unity #ROS #YOLOX pic.twitter.com/QMAUX1gXdJ
この時の実装の様子を以下の画像に示します。
なぜわざわざInt16MaltiArrayに直しているのか。これは下手にsensor_msgs/Image
に直接変換してわけわかんないことになるよりも、メッセージが見やすい型で送ったほうが良いだろうと思ったからです。結果、初歩的なエラーをなんとかデバッグコンソールを読みつつ解決することができ、正しく画像の送受信を実現することができました。このときは、「IntMultiArray、何でも入れられるので最強なのでは」と思ってしまいました。
cv_bridgeを実装
当然ながらこの2工程は明らかに無駄だし他の人からすれば何やっているのかさっぱりだと思ったので、省くことにしました。
これまで画像を送受信するときはcv_bridgeを使っていましたが、UnityにはもともとOpenCVがインストールされていないので、自分でcv_bridge風のプログラムを書くことにしました。
cv_bridgeとは?:ROS2の独自型とOpenCVの型を変換するライブラリ。ROSは画像の取扱にOpenCVではなく、独自のメッセージ型を使用する。そのため、OpenCVの型で画像を扱うために型変換を送受信前後に挟む必要がある。
cv_bridgeのコード(noeticブランチ)は以下のURLから確認できます。
特に難しい処理をしているわけでもなかったので、そのまま2重ループを使って実装しました。デフォルトだと上下左右反転された画像が公開されてしまっていたので、その処理がややこしいというか少し面倒でした。
最終的にはInt32MultiArrayを削った以下の図のような感じの実装になりました。
まー順当。
YOLOX-ROSとの連携もしてみました。特に遅延も感じず、いい感じのデモが完成しました!
このプログラムは、README付きでアップしています↓
実装を通して
この実装を通して、同じ1日間、本を読んだり座学を受けるよりも勉強になったのでは?と思えるくらいUnityやC#を扱えるようになりました。手を動かすのが最も効率いいんだなーと改めて感じられるいい機会になったと思います。
これからちまちまと知識を吸収して現実世界とUnityを繋いでデモできる環境を整えていきたいですね。