Note: This tutorial assumes that you have completed the previous tutorials: creating a ROS msg and 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. |
Writing a Simple Publisher and Subscriber (Euslisp)
Description: This tutorial covers how to write a publisher and subscriber node in euslisp.Tutorial Level: BEGINNER
Next Tutorial: Writing a Simple Service and Client (Euslisp)
Contents
シンプルなパブリッシャーとサブスクライバーを書く (Euslisp)
"Node"は ROSのネットワークにつながった実行可能なもののためのROS用の用語です。ここで、我々は、メッセージをずっと発信するパブリッシャーtalkerのnodeを作りましょう。
今までのTutorialsで作ってあるbeginner_tutorialsのフォルダに移動しましょう。
roscd beginner_tutorials
The Code
まず、Euslispのコードを入れておくeuslispフォルダを作りましょう。以下の様に打ってください。
$ mkdir euslisp
その後、euslisp/の中に以下のコードをコピー&ペーストしたtalker.lファイルを生成します。
#!/usr/bin/env roseus ;;; ;;; euslisp version of ros_tutorials/rospy_tutorials/001_talker_listener ;;; (ros::load-ros-manifest "roseus") ;;; (ros::roseus "talker") (ros::advertise "chatter" std_msgs::string 1) (ros::rate 100) (while (ros::ok) (setq msg (instance std_msgs::string :init)) (send msg :data (format nil "hello world ~a" (send (ros::time-now) :sec-nsec))) (ros::ros-info "msg [~A]" (send msg :data)) (ros::publish "chatter" msg) (ros::sleep) ) (ros::roseus "shutdown") (exit)
そしたら、ファイルを忘れずに実行可能にしておきましょう。
$ chmod +x euslisp/talker.l
コードの解説
さて、コードをすこしづつ見ていきたいと思います。
#!/usr/bin/env roseus
どのROSのNodeでもこの宣言が先頭にあります。この初めの行は実行可能な環境が何かを明示してます。ここでは、このスクリプトを実行するのにroseusの環境が必要であることを書いてます。
(ros::load-ros-manifest "roseus")
2行目はroseusのmanifest.xmlを読むようにいい、列挙されたすべての依存ファイルを加えるように言います。ROS Nodeを書く際はroseusをインポートする必要があります。このroseusのインポートをすることでstd_msgs.msgのインポートを行うことができます。std_msgs.msgのインポートは、std_msgs::string のメッセージのタイプ (文字列のみを保持する)を発信者として再利用するためです。
(ros::roseus "talker")
(ros::roseus "talker")はnodeのinitをするとともに、roseusにnode名を伝えるのでとても大切です。この情報をroseusが得るまで、ROSのMasterと通信をはじめることができません。この場合、node名は、talkerになります。
注意:名前は、base nameでなければなりません。つまり、 "/"を含んではいけません。
(ros::advertise "chatter" std_msgs::string 1)
このコードの部分は、ROSの残りのtalkerのインターフェースを定義しています。(ros::advertise "chatter" std_msgs::string 1)は、nodeがchatterというtopicに std_msgs::stringというメッセージのタイプのものを送っていることを宣言します.
(ros::rate 100)
この(ros::rate 100)はループを100hzで回すことを宣言するものです。この表記はよく見ると思います。
(while (ros::ok) (setq msg (instance std_msgs::string :init)) (send msg :data (format nil "hello world ~a" (send (ros::time-now) :sec-nsec))) (ros::ros-info "msg [~A]" (send msg :data)) (ros::publish "chatter" msg) (ros::sleep) )
このループは、roseusのコードでは今後かなりお馴染みになります。ros::okを毎回チェックして、すべき事をこなします。ros::okを確認して、プログラムが止まるべきかを判断します。(例えば、 Ctrl-Cが押されたかなどです). この場合、すべきこととは、initさてsend msg :dataで文字列をセットされたメッセージを (ros::publish "chatter" msg)によってchatterに送りこむことです。このループはtime.sleep()とシミュレーションの時間と同様に動く点を除いて同じような効果をするros::sleepを呼び出します。
このループは、 (ros::ros-info "msg [~A]" (send msg :data))もまた呼び出します。これは3つの役目を果たします。まず、スクリーンにメッセージを書き出し、nodeのログファイルに書き込み、rosoutに吐き出します。rosoutはデバッグするのに使い勝手がよいです。nodeの出力をもつコンソールを探さずに、rqt_consoleを使うだけで、メッセージを見つけ出せます。
(ros::roseus "shutdown") (exit)
ここで、ループを抜けてきたnodeを止める手続きに入ります。
Writing the Subscriber Node
The Code
さて、次に以下のコードをeuslispフォルダの下に新規にlistener.lをつくってコピー&ペーストしてください。
1 #!/usr/bin/env roseus
2 ;;;
3 ;;; euslisp version of ros_tutorials/rospy_tutorials/001_talker_listener
4 ;;;
5
6 (ros::load-ros-manifest "roseus")
7 ;;;
8
9 ;;;
10 ;;;
11 (ros::roseus "listener")
12 ;;(setq sys::*gc-hook* #'(lambda (a b) (format t ";; gc ~A ~A~%" a b)))
13
14 ;; callback function
15 ;(defun string-cb (msg) (print (list 'cb (sys::thread-self) (send msg :data))))
16 ;(ros::subscribe "chatter" std_msgs::string #'string-cb)
17
18 ; lambda function
19 ;(ros::subscribe "chatter" std_msgs::string
20 ; #'(lambda (msg) (ros::ros-info
21 ; (format nil "I heard ~A" (send msg :data)))))
22
23 ;; method call
24 (defclass string-cb-class
25 :super propertied-object
26 :slots ())
27 (defmethod string-cb-class
28 (:init () (ros::subscribe "chatter" std_msgs::string #'send self :string-cb))
29 (:string-cb (msg) (print (list 'cb self (send msg :data)))))
30 (setq m (instance string-cb-class :init))
31
32 (do-until-key
33 (ros::spin-once)
34 ;;(sys::gc)
35 )
36 ;(ros::spin)
実行可能にすることを忘れないでください。
$ chmod +x euslisp/listener.l
コードの解説
listener.lはtalker.lに, メッセージのための新たなコールバック関数を用意することを除いて似ています。
この宣言は、nodeが、chatterというstd_msgs::stringのタイプのtopicから購読することを示します。新しいメッセージがtopicに到着するたびに、callbackが第一引数にメッセージを受け取りながら呼ばれます。
また、rospy.init_node()を呼び出すように変えました。anonymous=Trueのキーワードも加えました。ROSはそれぞれのnodeが固有の名前を持つこと要求します。もし同じ名前のものが、起きたら、古い方を止めてしまいます。これは、正常に動作しないnodeを簡単にネットワークからはじき出すためです。anonymous=True のフラグは、rospy が複数のlistener.pyを簡単に走らせるようにできるために、固有の名前をつけます。
最後の変更は、rospy.spin()nodeが閉じられるまでにnodeが閉じてしまうのを単に防ぐためです。roscppと違って、rospy.spin()は、rospyの場合、コールバックにはそれぞれのスレッドがあるので、購読者のコールバックのために呼んでいるのではありません。
nodeをビルドする。
我々は、CMakeをビルドシステムを採用してます。それは、Pythonのnodeのときでさえも同じように使わなくてはなりません。これは、messagesとserviceのためのpythonのコードが自動生成されることを確認するためです。
我々は、ちょっとした利便性を得られるためMakefile利用しています。
roscreate-pkgは自動的に、Makefileをつくるので、編集する必要はありません。
catkinのワークスペースへ移動して、catkin_makeをしましょう。
$ cd ~/catkin_ws $ catkin_make
nodeを実行する
nodeを実行するには、ROSのコアを起動していなくてはなりません。ROSコアは、ROSのシステムに前準備として必要となるnodeとプログラムの集まりです。ROSのnode同士が通信しあうために、ROSコアを実行しなければなりません。新しいターミナルを開いて、以下の様に打ってください。
$ roscore
roscoreはおそらく以下のような出力をすると思います。
... logging to /u/nkoenig/ros-jaunty/ros/log/d92b213a-90d4-11de-9344-00301b8246bf/roslaunch-ncq-11315.log ... loading XML file [/u/nkoenig/ros-jaunty/ros/tools/roslaunch/roscore.xml] Added core node of type [rosout/rosout] in namespace [/] started roslaunch server http://ncq:60287/ SUMMARY ======== NODES starting new master (master configured for auto start) process[master]: started with pid [11338] ROS_MASTER_URI=http://ncq:11311/ setting /run_id to d92b213a-90d4-11de-9344-00301b8246bf +PARAM [/run_id] by /roslaunch +PARAM [/roslaunch/uris/ncq:60287] by /roslaunch process[rosout-1]: started with pid [11353] started core service [/rosout] +SERVICE [/rosout/get_loggers] /rosout http://ncq:36277/ +SERVICE [/rosout/set_logger_level] /rosout http://ncq:36277/ +SUB [/time] /rosout http://ncq:36277/ +PUB [/rosout_agg] /rosout http://ncq:36277/ +SUB [/rosout] /rosout http://ncq:36277/
さて、これで、talker/listenerを実行する準備ができました.新しいターミナルを開き、 以下を打ってください
$ rosrun beginner_tutorials talker.l
さらに、他のターミナルで以下を打ってください。
$ rosrun beginner_tutorials listener.l
rosrunは単に、簡便なスクリプトにすぎません。あなたは、./talker.lと打つことで実行することもできます.
Talkerは以下のような内容のテキストを出力し始めます。
Registered [/talker-9224-1233892469.83] with master node http://localhost:11311 hello world 1233892469.86 hello world 1233892470.86 hello world 1233892471.86 hello world 1233892472.86 hello world 1233892473.86 ...
そしてlistenerは以下のような出力をしはじめます。
/listener-7457-1233891102.92: I heard hello world 1233892470.86 /listener-7457-1233891102.92: I heard hello world 1233892471.86 /listener-7457-1233891102.92: I heard hello world 1233892472.86 /listener-7457-1233891102.92: I heard hello world 1233892473.86 /listener-7457-1233891102.92: I heard hello world 1233892474.86 ...
これで、あなたは始めてのlistenerのノードを書きましたので、ROSはrostopicと呼ばれるいかなるtopicも聞くものが実行されていることもしておくべきです。もし、rostopic echo topic_nameをしたら、listener.lで書かれたようなことと似た内容を出力します。
$ rostopic echo chatter
rostopic: topic is [/chatter] topic type is [std_msgs/String] --- data: hello world 1241463489.67 --- data: hello world 1241463490.67 ---
おめでとうございます!ROSのEusLispを実行しました。
より詳しいコードをみるには、roseusパッケージを見るか、次のチュートリアルにお進みください.