LoginSignup
30
26

More than 5 years have passed since last update.

ROSを独自のプロトコルへブリッジする

Last updated at Posted at 2017-01-14

2019/02/11 追記

rosbridge と検索して出てくる日本語情報のトップにいるようなので、ちょっと宣伝をば。

私が所属している aptpod という会社は、WebSocketを用いたPubSubブローカーを自社開発している会社でありまして(もうちょっと詳しい説明はアドベントカレンダーに書きました)、下記の記事は、本当は自社PubSubブローカーへのつなぎ込み(記事では Socket.io へのつなぎ込みになっていますが)のために調べた内容でした。

最近ではロボットのROSデータをリモートで可視化したい、というお客様もちらほらで始めているようですので、もし(ROSに限らず)大容量データの伝送・可視化でお困りのことがあれば、ぜひ 弊社 までご相談ください。

はじめに

ROSメッセージを他のプロトコルへブリッジングするRosbridgeというROSモジュールがあります。パッケージに同梱されているrosbridge_serverには、UDP, TCP, WebSocketをトランスポートプロトコルとして使用するサーバープログラムがあり、特にWebSocketに関しては、jsライブラリであるroslibjsを利用することにより、とても簡単にブラウザをROSに参加させることができます。

rosbridge_suite - ROS Wiki

今回は、UDP, TCP, WebSocketといったパッケージ同梱のプロトコル以外のプロトコル(今回はSocket.io)へrosbridgeをブリッジングするため、Rosbridgeプロトコル(rosbridge_library)を直接利用する方法を考えてみます。Rosbridgeプロトコルを使用することで、pubsidh/subscribe/service...などオペレーション種別にいちいち自前実装しなくて良くなったり、いちいち各メッセージのモジュールをimportしなくて良くなるので、とても簡単にブリッジができます。

ROSに関する日本語資料がまだまだ少ない状況なので、誰かの助けになれば幸いです。

想定

  • 別のSocket.ioサーバーが立っているものとする
  • 上記サーバーに接続し、サーバーから送られてくるメッセージをROSへブリッジする
  • 受信したROSメッセージを上記サーバーにブリッジする

実装

概要

  • RosbridgeProtocolをインスタンス化する
  • 外部プロトコルの受信時コールバック関数内でRosbridgeProtocol.incomingを実行する
  • RosbridgeProtocol.outgoing内で外部プロトコルの送信関数を実行する

ソースコード

mybridge.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import json
import rospy
import socketIO_client
from rosbridge_library.rosbridge_protocol import RosbridgeProtocol

# ############################
# arguments
# ############################
host = sys.argv[1]
port = int(sys.argv[2])


# ############################
# init RosBridgeProtocol
# ############################
rospy.init_node('mybridge')
client_id_seed = 0;
protocol = RosbridgeProtocol(client_id_seed)


# ############################
# init Socket.io
# ############################
socketIO = socketIO_client.SocketIO(host, port)

def on_connect(*args):
    rospy.loginfo('socket.io connected.')

def on_disconnect(*args):
    rospy.loginfo('socket.io disconnected.')

socketIO.on('connect',    on_connect)
socketIO.on('disconnect', on_disconnect)


# ############################
# on websocket message
# ############################
def on_message(*args):
    message = json.loads(args[0])
    protocol.incoming(message)

socketIO.on('ws-receive', on_message)


# ############################
# on ros message
# ############################
def outgoing_func(message):
    msg = json.loads(message)
    socketIO.emit('ws-send', json.dumps(msg))

protocol.outgoing = outgoing_func


# ############################
# start Socket.io
# ############################
socketIO.wait()

実行

$ python mybridge.py 192.168.0.1 80

コード解説

RosbridgeProtocolの初期化

rospy.init_node('mybridge')
client_id_seed = 0;
protocol = RosbridgeProtocol(client_id_seed)
  • 特に呼び出しませんが、rospy.init_nodeは実行していないとエラーになります。
  • client_id_seedはよく分かっていませんが、0で良さそうです(同梱のサーバープログラムでも0を使用している)

SocketIO-Clientの初期化

socketIO = socketIO_client.SocketIO(host, port)

def on_connect(*args):
    rospy.loginfo('socket.io connected.')

def on_disconnect(*args):
    rospy.loginfo('socket.io disconnected.')

socketIO.on('connect', on_connect)
socketIO.on('disconnect', on_disconnect)

SocketIO-Clientを初期化しています。特に特別なことはしていません。

SocketIO-Clientの受信時コールバック内でincomingを実行

def on_message(*args):
    message = json.loads(args[0])
    protocol.incoming(message)

socketIO.on('ws-receive', on_message)
  • SocketIO-Clientで'ws-receive'イベントを受信した際のコールバックon_message内にて、incomingを呼び出します。
  • incomingは、ROSBRIDGE_PROTOCOL.mdに従うmessageを渡せば、よしなにROSメッセージへ変換して送出してくれます。

outgoing内でSocketIO-Clientの送信関数を実行

def outgoing_func(message):
    msg = json.loads(message)
    socketIO.emit('ws-send', json.dumps(obj))

protocol.outgoing = outgoing_func
  • RosbridgeProtocolのoutgoingをオーバーライドします。
  • outgoingは、ROSメッセージのを受信するたびに呼び出される関数です。
  • messageROSBRIDGE_PROTOCOL.mdに従うJSON文字列になります。
  • SocketIO-Clientから、'ws-send'イベントとして送信します。
socketIO.wait()

SocketIO-Clientの受信を待ちます。RosbridgeProtocolの方は、waitingのための関数実行は特に必要ありません。

Rosbridgeプロトコル

基本的にはROSBRIDGE_PROTOCOL.mdに沿ったメッセージを送信してやればOKです。下記に例を示しますが、詳細なオプションについては、リンク元を参照してください。

publish

publishしたい場合は、下記のようなmessageをサーバー側から送り込みます。下記のサンプルは、geometry_msgs/Twist型のトピック/cmd_velの例です。

{
  "op": "publish",
  "topic": "/cmd_vel",
  "msg": {
    "linear"  : { "x" : 0, "y" : 0, "z" : 0 },
    "angular" : { "x" : 0, "y" : 0, "z" : 0 },
  }, 
}

subscribe

subscribeしたい場合は、下記のようなmessageをサーバー側から送り込むことで、メッセージのブリッジが開始されます。下記はトピック/odomの例です。トピック名だけでOKです。

{
  "op": "subscribe",
  "topic": "/odom",
}

一度subscribeしたメッセージをunsubscribeする場合は、下記のようにします。

{
  "op": "unsubscribe",
  "topic": "/odom",
}
30
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
26