はじめに
今回は、Husarnetを利用して離れた場所にあるROS 2環境を接続し、ROS 2のTopicを使って遠隔からデバイスを制御する要素検証を行います。Husarnetは、インターネット上にある複数のコンピュータを仮想的なネットワークで接続できる仕組みです。
ROS 2ではDDSを利用してノード間の通信を行いますが、通常のLAN内だけではなく、Husarnetを利用することでインターネットを経由したROS 2ノード間の通信を構成できます。
ここではHusarnetの登録方法、利用方法は割愛させて頂きます。既に環境が出来ている前提で進めます。もし必要な場合は以下の公式を参照してください。
https://husarnet.com/
今回の検証では、XServer VPS上のROS 2環境と手元のRaspberry PiをHusarnetで接続します。そして、XServer VPSからROS 2のTopicをPublishし、Raspberry PiでSubscribeします。さらにRaspberry PiからHTTP GETを送信し、同一LAN上にあるM5ATOM Liteの内蔵LEDを点灯させます。
単にROS 2のTopicが届くことを確認するだけではなく、最終的に実際のデバイスが動作するところまでを確認します。
今回の検証構成
今回の構成は次のようになります。

XServer VPSとRaspberry Piの間がHusarnetによる通信になります。
一方、Raspberry PiとM5ATOM Liteの間は通常のLAN内HTTP通信です。
つまりRaspberry Piは、
ROS 2 Topic → HTTP GET
を変換するゲートウェイとして動作します。
今回の要素検証では、この構成によってインターネット上のVPSから手元のLAN内にあるデバイスまで制御できることを確認します。
M5ATOM LiteのHTTP制御
M5ATOM Liteについては、前回の要素検証でWi-Fiに接続し、HTTP GETによって内蔵RGB LEDを制御できるところまで確認しています。※必要な場合は以下を参照して下さい。
この前回の投稿を簡単に説明しますと、
M5ATOM Lite上ではHTTPサーバーを動作させ、次のURLを用意しています。
/red
/yellow
/green
/off
例えば、
http://m5atom.local/red
へHTTP GETを送信するとLEDが赤色になります。
同様に、
http://m5atom.local/green
では緑色、
http://m5atom.local/off
ではLEDを消灯します。
今回はこの仕組みをそのまま利用します。
M5ATOM Lite側にROS 2やHusarnetを導入するのではなく、Raspberry PiでROS 2のTopicを受信し、それをHTTP GETへ変換します。
これによって、ROS 2に対応していないWi-FiデバイスもROS 2システムの先に接続できます。
ROS 2からHTTP GETを送信するノード
ここからは今回の内容です。
Raspberry Pi側には、ROS 2のTopicをSubscribeしてHTTP GETを送信するPythonノードを作成します。
今回は次のTopicを使用します。
/atom_led
メッセージ型は、
std_msgs/msg/String
です。
受信する文字列は次の4種類としました。
red
yellow
green
off
例えば、
red
を受信すると、
http://m5atom.local/red
へHTTP GETを送信します。
ノードの処理は非常に単純です。
/atom_ledをSubscribe
↓
文字列を取得
↓
red / yellow / green / off を判定
↓
対応するURLを生成
↓
HTTP GET
↓
M5ATOM LiteのLEDを制御
今回はROS 2とHTTPの接続確認が目的なので、できるだけ単純な構成としています。
ROS 2パッケージの作成
Raspberry PiのROS 2ワークスペースでPythonパッケージを作成します。
cd ~/ros2_ws/src
ros2 pkg create atom_http_control \
--build-type ament_python \
--dependencies rclpy std_msgs
今回はパッケージ名を、
atom_http_control
としました。
ノード名は、
atom_http_node
としています。
HTTP制御ノード
作成したPythonノードでは、/atom_ledをSubscribeします。
self.subscription = self.create_subscription(
String,
'/atom_led',
self.led_callback,
10
)
Topicを受信すると、受信した文字列からURLを生成します。
command = msg.data.strip().lower()
url = f'{self.base_url}/{command}'
HTTP GETにはPython標準ライブラリのurllib.requestを使用しました。
with urllib.request.urlopen(
url,
timeout=3.0
) as response:
body = (
response.read()
.decode('utf-8')
.strip()
)
今回はHTTP通信のために追加ライブラリを導入せず、Pythonの標準機能だけを使用しています。
以下がプログラム全体です。
#!/usr/bin/env python3
import urllib.request
import urllib.error
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class AtomHttpNode(Node):
def __init__(self):
super().__init__('atom_http_node')
self.declare_parameter(
'base_url',
'http://m5atom.local'
)
self.base_url = (
self.get_parameter('base_url')
.get_parameter_value()
.string_value
)
self.subscription = self.create_subscription(
String,
'/atom_led',
self.led_callback,
10
)
self.get_logger().info('ATOM HTTP control node started')
self.get_logger().info(f'base_url = {self.base_url}')
self.get_logger().info('Waiting for /atom_led ...')
def led_callback(self, msg):
command = msg.data.strip().lower()
valid_commands = [
'red',
'yellow',
'green',
'off'
]
if command not in valid_commands:
self.get_logger().warning(
f'Unknown command: {command}'
)
return
url = f'{self.base_url}/{command}'
self.get_logger().info(
f'HTTP GET: {url}'
)
try:
with urllib.request.urlopen(
url,
timeout=3.0
) as response:
body = (
response.read()
.decode('utf-8')
.strip()
)
self.get_logger().info(
f'HTTP response: {body}'
)
except urllib.error.URLError as e:
self.get_logger().error(
f'HTTP request failed: {e}'
)
except Exception as e:
self.get_logger().error(
f'Unexpected error: {e}'
)
def main(args=None):
rclpy.init(args=args)
node = AtomHttpNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
まずRaspberry Pi内で動作確認
いきなりXServer VPSから操作するのではなく、最初にRaspberry Pi内だけで動作確認を行いました。
まずHTTP制御ノードを起動します。
ros2 run atom_http_control atom_http_node
ノードが起動すると、
ATOM HTTP control node started
base_url = http://m5atom.local
Waiting for /atom_led ...
と表示され、/atom_ledの受信待ちになります。以下がそのスクリーンショットです。

別のターミナルから次のTopicをPublishします。
ros2 topic pub --once \
/atom_led \
std_msgs/msg/String \
"{data: 'red'}"
するとatom_http_nodeがredを受信し、
http://m5atom.local/red
へHTTP GETを送信します。以下がそのスクリーンショットです。

M5ATOM Liteの内蔵LEDが赤色に点灯することを確認しました。

同様に、
ros2 topic pub --once \
/atom_led \
std_msgs/msg/String \
"{data: 'yellow'}"
ros2 topic pub --once \
/atom_led \
std_msgs/msg/String \
"{data: 'green'}"
ros2 topic pub --once \
/atom_led \
std_msgs/msg/String \
"{data: 'off'}"
を実行し、黄色、緑色、消灯についても正常に制御できることを確認しました。
これによって、
ROS 2 Topic
↓
Raspberry Pi
↓
HTTP GET
↓
M5ATOM Lite
の部分が正常に動作していることを確認できました。
XServer VPSからHusarnet経由で制御
ローカルでの動作確認ができたので、次にPublishするROS 2環境をXServer VPSへ移します。
XServer VPSとRaspberry Piは、あらかじめHusarnetを利用してROS 2 Humble同士で通信できる状態にしています。
Raspberry Pi側では先ほどと同じatom_http_nodeを起動した状態にします。
そしてXServer VPS側から、
ros2 topic pub --once \
/atom_led \
std_msgs/msg/String \
"{data: 'red'}"
を実行します。
XServer VPSからPublishされた/atom_ledはHusarnetを経由してRaspberry Piへ到達します。
Raspberry Pi上のatom_http_nodeがTopicをSubscribeすると、M5ATOM LiteへHTTP GETを送信します。
結果として、XServer VPSからの操作によってM5ATOM LiteのLEDを点灯させることができました。黄色、緑色、消灯についても同様に動作することを確認しました。
以下はgreenのスクリーンショットです。

今回確認できたこと
今回の要素検証では、次の通信経路が実際に動作することを確認しました。
XServer VPS
ROS 2 Humble
│
│ /atom_led
▼
Husarnet
│
▼
Raspberry Pi 4
ROS 2 Humble
│
│ atom_http_node
▼
HTTP GET
│
▼
M5ATOM Lite
│
▼
RGB LED
まずRaspberry Pi内でROS 2 TopicからHTTP GETへの変換を確認し、その後XServer VPSから同じTopicをPublishすることで、Husarnetを経由した場合にも同じ動作ができることを確認しました。
これによって、Husarnetを利用してインターネット上のROS 2環境と手元のROS 2環境を接続し、さらにその先にあるLAN内のデバイスまで制御できることが確認できました。
今回は動作が分かりやすいようにM5ATOM LiteのLEDを使用しましたが、Raspberry Pi側でHTTP通信を行う構成自体は、他のWi-Fiデバイスや制御システムにも応用できます。
また、最終的な制御対象をROS 2対応機器だけに限定せず、
ROS 2
↓
HTTP
↓
既存のネットワーク機器
という構成を取れることも今回の検証で確認できました。
まとめ
今回は、Husarnetを利用してXServer VPSとRaspberry PiのROS 2 Humble環境を接続し、ROS 2 Topicをインターネット越しに送信する要素検証を行いました。
さらにRaspberry Pi上でROS 2 TopicをHTTP GETへ変換するノードを作成し、同一LAN上のM5ATOM Liteを制御しました。
XServer VPSからROS 2 TopicをPublishすることで、手元にあるM5ATOM LiteのLEDを赤、黄、緑に点灯させ、消灯させるところまで確認できました。
今回の検証によって、
インターネット → Husarnet → ROS 2 → Raspberry Pi → HTTP → Wi-Fiデバイス
という一連の通信経路が実際に利用できることを確認できました。
今後はこの構成をベースとして、LED制御だけではなく、ロボットの状態取得や遠隔操作などへの応用も検討していきます。



