2009年12月22日火曜日

パブリッシャー(発行)とサブスクライバー(購読)のサンプルをPythonで書く

今回はトピックを書き込むパブリッシャーと、読み込むサブスクライバーをPythonで書きます。

http://www.ros.org/wiki/ROS/Tutorials/WritingPublisherSubscriber(python)
$ roscd beginner_tutorials
して
$ mkdir nodes
$ cd nodes
して、
以下をtalker.pyという名前で保存してください。







#!/usr/bin/env python
import roslib; roslib.load_manifest('beginner_tutorials')
import rospy
from std_msgs.msg import String
def talker():
    pub = rospy.Publisher('chatter', String)
    rospy.init_node('talker')
    while not rospy.is_shutdown():
        str = "hello world %s"%rospy.get_time()
        rospy.loginfo(str)
        pub.publish(String(str))
        rospy.sleep(1.0)
if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException: pass


そして実行可能にするために
$ chomod +x talker.py
としておきます。

とりあえず中身を読んでみましょう。

import roslib; roslib.load_manifest('beginner_tutorials')
のところで、beginner_tutorials/manifest.xmlを読み込んで、
依存関係を解消します。これをやると、
つぎの
import rospy
from std_msg.msg import String
が可能になります。
beginner_tutorials/manifest.xml
に書きましたね。

std_msgs.msgからStringをインポートしていますが、これは標準メッセージタイプの中のStringという型を使うという宣言です。

pub = rospy.Publisher('chatter', String)
で、chatterという名前でString型のトピックを発行(publish)することを宣言しています。
つぎの
rospy.init_node('talker')
でノードの名前を決めています。これをやるとROS Master (roscore)に接続されます。
※ノードの名前には'/'は使えません。

つぎのwhileの中ですが、ここまでチュートリアルをまじめにやっていればそのまま読めますね。
while not rospy.is_shutdown():
    str = "hello world %s"%rospy.get_time()
    rospy.loginfo(str)
    pub.publish(String(str))
    rospy.sleep(1.0)
rospy.loginfo(str)では 1.コンソールへの書き込み、2.ログファイルへの書き込み、3.rosoutへの書き込みの3つを同時にやります。
rospy.sleep(1.0)は実時間ではなく、シミュレーションでの同期時間としてのスリープです。

メッセージのコンストラクタの引数は.msgファイルに書いた順序で与えます。
省略も可能です。
Stringメッセージの宣言は
ros/std_msgs/msg/String.msg
にありまして、中身は
string data
の一行だけです。

なので、
msg = String()
msg.data = str
や、
String('data'=str)
として初期化、データの代入ができますね。

また、サンプル最後の行の
rospy.ROSInterruptException: pass
でsleep中にC-cが投げられたときの例外をキャッチしています。
これがないとsleep中のC-cが拾えないみたいです。

次はサブスクライバーです。
以下をlistener.pyとして保存しましょう。





#!/usr/bin/env python
import roslib; roslib.load_manifest('beginner_tutorials')
import rospy
from std_msgs.msg import String
def callback(data):
    rospy.loginfo(rospy.get_name()+"I heard %s",data.data)

def listener():
    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("chatter", String, callback)
    rospy.spin()

if __name__ == '__main__':
    listener()


やはり、
chmod +x listener.py
して実行可能にします。

talkerとの大きな違いは以下にありますね。
    rospy.Subscriber("chatter", String, callback)
3つ目の引数にcallbackという関数を入れています。
chatterというトピックが更新されたときに呼んでほしい関数名を指定します。

    rospy.init_node('listener', anonymous=True)
のanonymousキーワードでTrueを指定しています。
通常、ROSネットワークでは同じ名前のノードが接続してきた場合、
古いノードは接続を強制的に切られます。anonymousをTrueにしておくと、
ノード名を自動で変更して複数接続できるようにします。
そのため、このlistener.pyは複数立ち上げてもちゃんと繋がるはずです。
    rospy.spin()
で、プロセス終了までアイドリングさせます。

では早速ためしてみましょう。

$ roscore

別なターミナルで
$ rosrun beginner_tutorials talker.py __name:=talker

もうひとつのターミナルで
$ rosrun beginner_tutorials listener.py __name:=listener

listener.pyの立ち上げで__nameで名前を指定すると、anonymousをつけても古いノードが切断されました。
__nameで指定しなければ複数繋がります。
一方でtalker.pyのほうは名前を指定しなくても複数は立ち上がりません。

$ rxgraph
で構造を見てみました。listener.pyを2つ上げています。



次回はサービスとクライアントです。
とりあえずPythonで次も行きます。

0 件のコメント:

コメントを投稿