たるこすの日記

たるこすの日記

リアルからバーチャルへ、バーチャルからリアルへ

ROS# を UWP 対応させた話

はじめに

こんにちは、たるこすです。
シーメンスが公開している 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 アプリを作成した話を書く予定です。