ROS の wildcard Subscriber / Publisher を作った

  • 3
    Like
  • 0
    Comment

MQTT で subscribe 時にワイルドカードを使えるのを見て、ROS でも使えたらいいのに!と思ったので、
トピック名をワイルドカード指定して subscribe / publish できるユーティリティを作りました。

ros_wild の紹介

概要

ROS で使えるワイルドカード Publisher, Subscriber ユーティリティです。

https://github.com/yuma-m/ros_wild から clone して使えます。

$ git clone https://github.com/yuma-m/ros_wild.git /path/to/your/catkin_ws/src/ros_wild
$ cd /path/to/your/catkin_ws && catkin_make

使い方(コマンドライン)

ros_wild echo

$ rosrun ros_wild echo "トピック名の正規表現" のように使います。

例えば、全トピックの内容を echo してみると、以下のような感じで、トピック名、メッセージの型とメッセージの中身が表示されます。

$ rosrun ros_wild echo ".+"
---
topic: /ultrasound_sensor
type: sensor_msgs/Range

header: 
  seq: 1
  stamp: 
    secs: 1478776655
    nsecs:    143582
  frame_id: ultrasound_sensor
radiation_type: 0
field_of_view: 0.569999992847
min_range: 0.20000000298
max_range: 3.0
range: 0.600000023842
---
topic: /rosout
type: rosgraph_msgs/Log

header: 
  seq: 1
  stamp: 
    secs: 1479382593
    nsecs: 476881255
  frame_id: ''
level: 4
name: /foo_pkg/bar_node
msg: this is example message
file: /home/foo/catkin_ws/src/foo_pkg/src/bar_node.cpp
function: main
line: 170
topics: ['/rosout', '/foo_pkg/example']

僕は同一種類の複数のセンサー値を確認する時によく利用します。
トピック毎にターミナルを開いていると、それだけで画面がいっぱいになってしまうので。

使い方 (Python)

利用例 (Subscriber)

/bool/.+ のようなトピックにまとめて subscribe して、 callback メソッドを設定する場合は、以下のような感じで書きます。

from ros_wild import Subscriber

def callback_bool(msg):
    print(msg.data)

sub = Subscriber(r"/bool/.+", callback=callback_bool)
print("Subscribed topics are {}".format(sub.subscribed_topics))

実行結果

Subscribed topics are ['/bool/01', '/bool/02', '/bool/03']
True
True
False
...

rospy.Subscriber との違い

rospy.Subscriber との違いは、2つ目の引数である data_class が不要なことです。

  • rospy の場合
    • rospy.Subscriber("/foo/bar", std_msgs.msg.String, callback_func, callback_args="hoge", ...)
  • ros_wild の場合
    • ros_wild.Subscriber("/foo/bar", callback_func, callback_args="hoge", ...)

つまり、メッセージ型を指定せずにトピックに subscribe することが出来ます。

これは便利な半面、思わぬバグや例外を招く危険性があり、使い方には注意が必要です。
そこで register_callback メソッドを利用して以下のように書いてあげることで、より安全な実装が可能です。

利用例 (register_callback)

トピック名の正規表現のみを指定して Subscriber を作成し、 後から data_class の種類毎に異なるコールバックを登録することが出来ます。
これにより、トピックのメッセージ型が想定した型と異なる場合のエラーを防ぐことができます。

from ros_wild import Subscriber
from std_msgs.msg import Bool, String

def callback_bool(msg):
    print("bool is {}".format(msg.data))

def callback_string(msg):
    print("string is {}".format(msg.data))

sub = Subscriber(r".+")
print("Subscribed topics are {}".format(sub.subscribed_topics))
sub.register_callback(Bool, callback_bool)
sub.register_callback(String, callback_string)
print("Topics with callback are {}".format(sub.topics_with_callback))

実行結果

Subscribed topics are ['/bool/01', '/rosout', 'rosout_agg', '/string/01']
Topics with callaback are ['/bool/01', '/strings/01']
bool is True
string is foo bar
...

利用例 (Publisher)

使い方は自分でもよく分かりませんが、一応 Publisher も作りました。
複数のロボットに同一のコマンドを送りたいとか、rviz で全 Range センサーに円錐を表示したいとか、そんな用途に使う...のかなぁ。

import rospy
from ros_wild import Publisher
from geometry_msgs.msg import Range

# publish to all Range topics
rospy.init_node("test")
pub = Publisher("/range/.+", queue_size=1)
pub.publish(Range(field_of_view=0.314, range=1.0))

今後の課題

パッケージに登録

まだ反映はされていませんが、ROS のパッケージリポジトリに登録しました。
最初はパッケージの登録の仕方が全然分からなかったのですが、bloom のチュートリアルに丁寧に記述されています。

基本的には上記チュートリアルの Releasing a package for the first time に則って、コマンドを実行していくだけで、releaseリポジトリへのコミットやrosdistroへのプルリクエストの作成を自動で行ってくれます。
ビルド自体はプルリクのマージから24-48時間以内に行われるのですが、debian パッケージとして apt でインストールできるようになるまでは数週間かかるようです。

機能拡張

今後の課題として以下の機能追加を考えています。
プルリク大歓迎です、お待ちしています!

  • Subsciber, Publisher 作成時のメッセージ型による絞り込み
  • C++ 対応
  • (機能拡張じゃないけど) ros_wild じゃなくて roswild に名前を変えたほうが良いかも...