はじめに
ROS 2 と Unity を使ったデジタルツインの学習として、前回は Publisher / Subscriber を利用したTopic通信を実装しました。
動作確認
実際に Unity と ROS 2 でサービス通信を行った様子です。
今回は一歩進めて、UnityからROS 2のServiceを呼び出す通信を実装します。
構成は以下です。
Windows
┌─────────────────────┐
│ Unity 6 │
│ SimpleClient │
└──────────┬──────────┘
│
│ TCP
▼
WSL2
┌─────────────────────┐
│ ROS-TCP-Endpoint │
└──────────┬──────────┘
│
│ ROS 2
▼
┌─────────────────────┐
│ ROS 2 Jazzy │
│ simple_server │
└─────────────────────┘
実装中には、
Connection to 127.0.0.1:10000 failed
というエラーにも遭遇しました。
この記事では、Service通信の実装方法と、このエラーの原因・解決方法までまとめます。
環境
OS : Windows + WSL2
Ubuntu : Ubuntu 24.04
ROS 2 : Jazzy
Unity : Unity 6.3 LTS
通信 : ROS-TCP-Connector / ROS-TCP-Endpoint
ROS 2 ワークスペース:
~/chapter2_ws
ROS 2 Serviceとは
ROS 2 のTopic通信は、
Publisher
↓
Topic
↓
Subscriber
という形でデータを配信します。
一方、Service通信は、
Client
│
│ Request
▼
Server
│
│ Response
▼
Client
という Request / Response型 の通信です。
今回はUnityから、
r = 2.0
h = 3.0
をROS 2へ送り、ROS 2側で処理した結果をUnityへ返します。
Service定義
今回使用するサービスは、
CalculateCylinderVolume.srv
です。
float64 r
float64 h
---
float64 v
--- より上がRequest、下がResponseです。
Request
├─ r
└─ h
Response
└─ v
ROS 2側で定義を確認します。
cd ~/chapter2_ws
source /opt/ros/jazzy/setup.bash
source install/setup.bash
ros2 interface show chapter2_interfaces/srv/CalculateCylinderVolume
以下のように表示されればOKです。
float64 r
float64 h
---
float64 v
Unityへ .srv を取り込む
Unityの上部メニューから、
Robotics
→ Generate ROS Messages
を開きます。
ROS message path にROS 2ワークスペースの、
chapter2_ws/src
を設定します。
WindowsからWSL2を参照する場合、今回の環境では、
\\wsl.localhost\Ubuntu-24.04\home\tokot\chapter2_ws\src
でした。
重要なのは、
chapter2_interfaces
そのものではなく、
chapter2_ws/src
を指定することです。
正しく読み込めると、
src
└─ chapter2_interfaces
└─ srv
└─ CalculateCylinderVolume.srv
が表示されます。
.srv からC#クラスを生成する
ROS Message Browserで、
CalculateCylinderVolume.srv
を確認し、
Build 1 srv
を実行します。
これにより、
CalculateCylinderVolume.srv
↓
Message Generation
↓
CalculateCylinderVolumeRequest.cs
CalculateCylinderVolumeResponse.cs
というUnity用のC#クラスが自動生成されます。
ROS 2独自のService型をUnityから扱えるようにするための重要な処理です。
Unity側にSimpleClientを作る
Hierarchyで、
Create Empty
を実行し、
SimpleClient
というGameObjectを作成します。
続いて、
Assets/Scripts/SimpleClient.cs
を作成します。
コードは以下です。
using UnityEngine;
using Unity.Robotics.ROSTCPConnector;
using RosMessageTypes.Chapter2Interfaces;
public class SimpleClient : MonoBehaviour
{
private ROSConnection ros;
private const string ServiceName = "calculate_cylinder_volume";
void Start()
{
ros = ROSConnection.GetOrCreateInstance();
ros.RegisterRosService<
CalculateCylinderVolumeRequest,
CalculateCylinderVolumeResponse
>(ServiceName);
CalculateCylinderVolumeRequest request =
new CalculateCylinderVolumeRequest();
request.r = 2.0;
request.h = 3.0;
Debug.Log("Request送信: r = 2.0, h = 3.0");
ros.SendServiceMessage<CalculateCylinderVolumeResponse>(
ServiceName,
request,
CalculateCylinderVolumeCallback
);
}
private void CalculateCylinderVolumeCallback(
CalculateCylinderVolumeResponse response)
{
Debug.Log("Response受信: V = " + response.v);
}
}
作成したスクリプトをHierarchyの、
SimpleClient
へアタッチします。
前回のSimplePublisherをOFFにする
Topic通信の学習で作成していた、
SimplePublisher
が動作していると、
No registered publisher on topic /simple_topic
というエラーが発生する場合がありました。
今回はService通信だけを検証したいため、
SimplePublisher → OFF
SimpleClient → ON
としました。
複数の通信処理を実装している場合、検証対象以外を一時的にOFFにするとトラブルを切り分けやすくなります。
WSL2のIPアドレスを確認する
Ubuntuで以下を実行します。
hostname -I
今回の環境では、
172.27.114.122
でした。
WSL2のIPは再起動などで変化する場合があるため、接続できない場合は再確認します。
ROS-TCP-Endpointを起動する
ターミナル①で、
cd ~/chapter2_ws
source /opt/ros/jazzy/setup.bash
source install/setup.bash
続いて、
ros2 run ros_tcp_endpoint default_server_endpoint \
--ros-args -p ROS_IP:=172.27.114.122
を実行します。
成功すると、
[UnityEndpoint]: Starting server on 172.27.114.122:10000
のように表示されます。
このターミナルは終了せず、そのまま待機させます。
UnityのROS Settingsを設定
Unityで、
Robotics
→ ROS Settings
を開きます。
設定:
Protocol : ROS2
ROS IP Address : 172.27.114.122
ROS Port : 10000
ROS 2側のService Serverを起動
別のWSL2ターミナルを開きます。
cd ~/chapter2_ws
source /opt/ros/jazzy/setup.bash
source install/setup.bash
Service Serverを起動します。
ros2 run chapter2 simple_server
この状態でUnityをPlayします。
起動順は、
① ROS-TCP-Endpoint
↓
② ROS 2 simple_server
↓
③ Unity Play
です。
通信の流れ
今回構築した通信は以下です。
Unity
SimpleClient
│
│ Request
│
│ r = 2.0
│ h = 3.0
▼
ROS-TCP-Connector
│
│ TCP
▼
ROS-TCP-Endpoint
│
│ ROS 2 Service
▼
ROS 2
simple_server
│
│ 計算
▼
Response
│
▼
Unity Console
Unity Consoleに、
Request送信: r = 2.0, h = 3.0
Response受信: V = ...
と表示されればService通信成功です。
ハマったポイント:Connection refused
今回発生したエラーがこちらです。
Connection to 127.0.0.1:10000 failed
System.Net.Sockets.SocketException
一方、ROS-TCP-Endpoint側を見ると、
Starting server on 172.27.114.122:10000
となっていました。
構成を図にすると、
Unity
127.0.0.1:10000
│
│ 接続
▼
×
WSL2
172.27.114.122:10000
│
└─ ROS-TCP-Endpoint
となっており、Unityが間違ったIPへ接続していました。
そこで、
hostname -I
でWSL2のIPを確認し、
Unity
→ Robotics
→ ROS Settings
→ ROS IP Address
を、
172.27.114.122
へ変更しました。
Windows上のUnityとWSL2上のROS 2を接続する場合、IPアドレスの設定は特に注意が必要です。
ハマったポイント:No registered publisher
もう1つ発生したのが、
No registered publisher on topic /simple_topic
というエラーです。
原因はService通信ではなく、以前作成した、
SimplePublisher
でした。
Service通信の検証時は、
SimplePublisher → OFF
SimpleClient → ON
として、処理を切り分けました。
TopicとServiceの使い分け
Topicは、
Publisher
↓
Topic
↓
Subscriber
という通信です。
センサ情報などの継続的なデータ配信に向いています。
LiDAR
Camera
Odometry
Robot State
/cmd_vel
などが代表例です。
一方Serviceは、
Client
↓ Request
Server
↓ Response
Client
という通信です。
「何か処理を依頼し、その結果を返してもらう」という処理に向いています。
ロボット初期化
設定取得
状態取得
計算処理
装置への単発指示
などです。
まとめ
今回、
Unity
↓
ROS-TCP-Connector
↓
TCP/IP
↓
ROS-TCP-Endpoint
↓
ROS 2 Service
という通信構成を実装しました。
Topic通信だけでなくService通信も扱えるようになることで、Unityを単なる3D表示ツールではなく、ROS 2と双方向に処理をやり取りするアプリケーションとして利用できます。
今後、
Unity
+
ROS 2
+
Gazebo
+
AMR
+
Mission Controller
という構成でデジタルツインを作っていく場合にも、今回のService通信は重要な基礎になります。
次は逆方向となる、
ROS 2
│
│ Request
▼
Unity
│
│ Response
▼
ROS 2
ROS 2 → Unity のService通信を実装してみます。
タグ
ROS2
Unity
WSL2
Robotics
DigitalTwin