(訳注:最新の情報は原文を参照してください.)
Note: This tutorial assumes that you have completed the previous tutorials: ROSのmsgやsrvなどを作る. |
Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags. |
シンプルな配信者(Publisher)と購読者(Subscriber)を書く(Python)
Description: このチュートリアルでは, 配信者(Publisher)と購読者(Subscriber)のPythonでの書き方について学びますTutorial Level: BEGINNER
Next Tutorial: シンプルなPublisherとSubscriberを実行してみる
配信者ノードを書く
"Node" は ROS のネットワークにつながった実行可能なものを表す ROS 用語です。ここでは、メッセージをずっと発信する 配信者(Publisher)"talker" のノードを作りましょう。
creating a packageで作ったbeginner_tutorialsのディレクトリに移動しましょう:
$ roscd beginner_tutorials
コード
まず、Pythonのコードを入れておくscriptsフォルダを作りましょう:
$ mkdir scripts $ cd scripts
そしてスクリプト例のtalker.pyを先ほど作ったscriptsディレクトリにダウンロードし、ファイルを実行可能にしておきます:
$ wget https://raw.github.com/ros/ros_tutorials/indigo-devel/rospy_tutorials/001_talker_listener/talker.py $ chmod +x talker.py
$ rosed beginner_tutorials talker.py でファイルを確認するか、または以下を見てください。
1 #!/usr/bin/env python
2 # license removed for brevity
3 import rospy
4 from std_msgs.msg import String
5
6 def talker():
7 pub = rospy.Publisher('chatter', String, queue_size=10)
8 rospy.init_node('talker', anonymous=True)
9 r = rospy.Rate(10) # 10hz
10 while not rospy.is_shutdown():
11 str = "hello world %s"%rospy.get_time()
12 rospy.loginfo(str)
13 pub.publish(str)
14 r.sleep()
15
16 if __name__ == '__main__':
17 try:
18 talker()
19 except rospy.ROSInterruptException: pass
コード解説
では、コードを少しづつ見ていきましょう。
1 #!/usr/bin/env python
すべてのPython ROS Nodeは、この宣言が先頭にあります。この1行によって、スクリプトファイルがPython スクリプトとして確実に実行されます。
ROS Nodeを書く際はrospyをインポートする必要があります。std_msgs.msgのインポートは、std_msgs/String のメッセージのタイプ (文字列のみを保持する)を配信に再利用するためです。
このコードの部分は、ROSの残りのtalkerのインターフェースを定義しています。pub = rospy.Publisher("chatter", String)は、ノードがchatterというトピックに Stringというメッセージのタイプで送っていることを宣言しています。 ここで、Stringは実際はstd_msgs.msg.Stringをさします。 New in ROS hydro では、購読者が十分な速度でメッセージを受け取らないでいる場合、引数queue_sizeはメッセージ・キューの量を制限します。ROSの古いディストリビューションでは、この引数は単純に省略します。
次の行では、rospy.init_node(NAME)はrospyにノード名を通知するのでとても大切です。rospyがこの情報を得ない限り、ROSのMasterと通信を始めることができません。この場合、ノード名はtalkerになります。NOTE:名前は、base nameでなければなりません。つまり、 "/"を含んではいけません。
9 r = rospy.Rate(10) # 10hz
この行はRate オブジェクト rを生成しています。Rateのメソッドsleep()助けによって、要求された速度でループする便利な方法が提供されます。その引数‘10’によって、1秒あたり10回ループが回ると推定できるでしょう。(処理時間が1秒の1/10番目を越えていない限り!)
このループは、rospyのかなり標準的な構成です。rospy.is_shutdown()を毎回チェックして、すべき処理を実行します。is_shutdown()を確認して、プログラムが止まるべきかを判断します。(例えば、 Ctrl-Cが押された等です).。この場合、すべき処理とは、新しく作られたメッセージをpub.publish(String(str))によってchatterトピックに配信することです。このループはr.sleep()を呼びだし、ループを通して要求された速度を維持するのに十分な時間スリープします。
(rospy.sleep()も見つけるかもしれません。シミュレーションされた時間で同じように動く以外は、time.sleep() と似ています。(参照:Clock))
このループは、rospy.loginfo(str)も呼び出します。これは3つの役目を果たします。まず、スクリーンにメッセージを書き出し、ノードのログファイルに書き込み、rosoutに吐き出します。rosoutはデバッグするのに使い勝手がよいです。ノードの出力のコンソールを探さなくても、rqt_consoleを使えばメッセージを引き出せます。
std_msgs.msg.String はとてもシンプルなメッセージのタイプなので、より複雑なメッセージタイプがどのようなものか気になるかもしれません。一般的なルールの要約としては、"コンストラクタの引数は、.msgファイルの中と同じ順番にすること"となっています。何の引数も与えずに、領域を直接初期化することもできます。例えば以下のように:
msg = String() msg.data = str
または、領域の一部を初期化して、残りはデフォルト値とすることができます。
String(data=str)
最後の数行が気になっているかもしれませんね:
標準のPythonの __main__ チェックに加えて、これはrospy.ROSInterruptExceptionの例外を捕捉します。Ctrl-Cが押されるか、ノードが違った形で終了される時に、rospy.sleep() と rospy.Rate.sleep()のメソッドによって例外が投げられます。この例外が引き起こされるのは、sleep()の後で間違ってコードを実行し続けないようにするためです。
さて、次はメッセージを受けとるノードを書く必要があります。
購読者のノードを書く
コード
listener.pyファイルをscriptsディレクトリにダウンロードしてください:
$ roscd beginner_tutorials/scripts/ $ wget https://raw.github.com/ros/ros_tutorials/indigo-devel/rospy_tutorials/001_talker_listener/listener.py
このファイルは以下のような内容です:
1 #!/usr/bin/env python
2 import rospy
3 from std_msgs.msg import String
4
5 def callback(data):
6 rospy.loginfo(rospy.get_caller_id()+"I heard %s",data.data)
7
8 def listener():
9
10 # in ROS, nodes are unique named. If two nodes with the same
11 # node are launched, the previous one is kicked off. The
12 # anonymous=True flag means that rospy will choose a unique
13 # name for our 'listener' node so that multiple listeners can
14 # run simultaenously.
15 rospy.init_node('listener', anonymous=True)
16
17 rospy.Subscriber("chatter", String, callback)
18
19 # spin() simply keeps python from exiting until this node is stopped
20 rospy.spin()
21
22 if __name__ == '__main__':
23 listener()
ノードを実行可能にすることを忘れないように:
$ chmod +x listener.py
コード解説
listener.pyのコードは、talker.pyに似ています。差分は、メッセージを購読するための新たなコールバックベースの仕組みを導入していることです。
ここは、ノードが、std_msgs.msgs.Stringのタイプのchatter}}トピック}から購読することを宣言しています。新しいメッセージがトピックに到着するたびに、メッセージを第一引数として{{{callbackが起動されます。
また、rospy.init_node()の呼び出しを多少変更しています。anonymous=Trueのキーワード引数を追加しています。ROSはそれぞれのノードが固有の名前を持つこと要求します。もし同じ名前のノードが立ち上がると、古い方のノードを止めてしまいます。これで、誤動作しているノードを簡単にネットワークからはじき出すことができます。anonymous=True のフラグによって、rospy がノードが固有の名前を生成するので、複数のlistener.pyノードを容易に実行することができます。
最後の変更点は、ノードが終了するまでの間、rospy.spin()が単純に自ノード終了させないようにしています。roscppと違って、コールバックにはそれぞれのスレッドがあるので、rospy.spin()は購読者コールバック関数に影響しません。
ノードをビルドする
CMakeをビルドシステムとして採用しているので、Pythonノードの場合でも同じようにCMakeを使わなければなりません。これは、メッセージとサービスのための自動生成されるPythonコードを確実に作るためです。
我々は、ちょっとした利便性を得られるためMakefile利用しています。 roscreate-pkgは自動的に、Makefileをつくるので、編集する必要はありません。
ではmakeしてみましょう。
$ make
catkinのワークスペースへ移動して、catkin_makeしましょう:
$ cd ~/catkin_ws $ catkin_make
これでシンプルな配信者と購読者を書くことが出来ました。次はシンプルなPublisherとSubscriberを実行してみるに進みましょう。