ROS# を UWP 対応させた話
2018/12/28 追記
私のリポジトリは現在メンテナンスしていません。
ROS# を UWP で使いたい場合は、公式の README にも記載されている以下のリポジトリを使ってみてください。
はじめに
こんにちは、たるこすです。
シーメンスが公開している ROS# という OSS を UWP 対応させました。
オリジナルのリポジトリはこちら github.com
UWP 対応させたものはこちらです。 github.com
ROS# とは?
ROS# は Unity アプリから ROS のトピックをやりとりするためのライブラリです。
URDF のロボットモデルから Unity の GameObject を作成するツールも入っています。
ROS のトピックをやりとりする仕組みですが、Unity 上で ROS が 動くというわけではなく、rosbridge を利用しています。
rosbridge というのは、ROS を利用できない環境 (Webページ, Windows など)から ROS を使えるようにするため、JSON 形式で データを中継してくれる仕組みです。通信方式は Websocket, TCP, UDP がサポートされていて、ROS# では Websocket を使っています。
UWP 対応するためにやったこと
UWP とは Universal Windows Platform の略称で、Windows 10 で動作するアプリ形式です。通常の PC であれば従来の .exe のアプリケーションも動作しますが、HoloLens では UWP アプリしか動作しません。そのため、HoloLens で ROS# を動かすには UWP で動作するよう修正する必要がありました。
Unity Editor 上や .exe 形式にビルドして実行した場合と UWP でビルドして実行した場合では実行環境が違い、一部 API が UWP で使えません。また、プラグイン(DLL) も UnityEditor 用と UWP 用にそれぞれビルドしたものを使う必要があります。
Editor 用プラグインは Assets > RosSharp > Plugins に、UWP 用プラグインは Assets > RosSharp > Plugins > WSA に配置しています。
ROS# で使われていたプラグイン
https://github.com/siemens/ros-sharp/tree/master/Unity3D/Assets/RosSharp/Plugins
ROS# では、以下の DLL が Unity のプロジェクトに入っていました。
- MathNet.Numerics.dll
- Newtonsoft.Json.dll
- RosBridgeClient.dll
- System.Threading.dll
- UrdfImporter.dll
- websocket-sharp.dll
これらの UWP 用のものを用意する必要がありましたが、UrdfImporter.dll については一旦あきらめました。 UrdfImporter は URDF 形式のロボットのモデルファイルを読んで Unity のオブジェクトとしてインポートする機能ですが、動的な読み込みを諦めれば Unity Editor 上でのみ動作すれば事足りるためです。さらに、MathNet.Numerics.dll とSystem.Threading.dll は UrdfImporter.dll で利用しているものだったため、これらも必要なくなりました。
Newtonsoft.Json.dll については、UWP で動作する dll も配布されているので、そちらをインポートしました。
websocket-sharp.dll は UWP 版が無いのですが、UWP の MessageWebSocket を使って websocket-sharp とインタフェースを合わせたクラスのコードが公開されており、そちらを利用しました。 github.com
さて、残るは RosBridgeClient.dll です。
UWP 用の RosBridgeClient.dll を作る
RosBridgeClient.dll を作成するプロジェクトは ROS# のリポジトリに入っています。このプロジェクトをもとに、以下の前本さんのブログを参考にして UnityEditor 用の DLL を作るプロジェクトと UWP 用の DLL を作るプロジェクトを作成しました。 satoshi-maemoto.hatenablog.com
ソースコード: https://github.com/tarukosu/ros-sharp/tree/master/RosBridgeClient
ソースコードは共有にしていて、UnityEditor 用と UWP 用でコードを書き分けたい場合は以下のようにしています。
#if NETXF_CORE // UWP 用コード #else // Unity Editor 用コード #endif
RosBridgeClient では、URDF を読むところで Thread を使っていて UWP ではコンパイルできないため、以下のようにして UWP ではコードを無効にしています。
Task を使えば同等のことは実現できるのですが、未実装です。
private void receiveRobotDescription(ServiceReceiver serviceReciever, object serviceResponse) { #if NETFX_CORE //TODO imprementation #else string robotDescription = formatTextFileContents(((ParamValueString)serviceResponse).value); Thread importResourceFilesThread = new Thread(() => importResourceFiles(robotDescription)); importResourceFilesThread.Start(); Thread writeTextFileThread = new Thread(() => writeTextFile((string)serviceReciever.HandlerParameter, robotDescription)); writeTextFileThread.Start(); Status["robotDescriptionReceived"].Set(); #endif }
Unity のスクリプトを UWP 対応させる
Unity の C# スクリプトでも同様に、UWP で利用できない API を使っている部分を UWP でも動作するように修正しました。
ClockTimePublisher というスクリプトで Thread を使っていたので、以下のように UWP では Task を使うように書き換えています。
public class ClockTimePublisher : Publisher { ... #if NETFX_CORE private Task task; private CancellationTokenSource tokenSource; #else private Thread clockTimeIterate; #endif protected override void Start() { base.Start(); #if NETFX_CORE tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; task = new Task(async () => { while (true) { if (token.IsCancellationRequested) { break; } StartPublication(EventArgs.Empty); await Task.Delay(timestep); } }, token); task.Start(); #else clockTimeIterate = new Thread(ClockTimeIterate); clockTimeIterate.Start(); #endif } ...
おわりに
以上の変更を行うことで、ROS# を UWP でビルドできるようになりました。
次のブログ記事では、この ROS# を使って HoloLens アプリを作成した話を書く予定です。