|Note: This tutorial assumes that you have completed the previous tutorials: creating a ROS msg and srv.|
|It is appreciated that problems/questions regarding this tutorial are asked on answers.ros.org. Don't forget to include in your question the link to this page, versions of your OS & ROS, and also add appropriate tags.|
Writing a Simple Publisher and Subscriber (Python)Description: This tutorial covers how to write a publisher and subscriber node in python.
Tutorial Level: BEGINNER
Next Tutorial: Examining the simple publisher and subscriber
Writing the Publisher Node
"Node" is the ROS term for an executable that is connected to the ROS network. Here we'll create the publisher ("talker") node which will continually broadcast a message.
Change directory into the beginner_tutorials package, you created in the earlier tutorial, creating a package:
$ roscd beginner_tutorials
First lets create a 'scripts' folder to store our Python scripts in:
$ mkdir scripts
Create the scripts/talker.py file within the beginner_tutorials package and paste the following inside it:
1 #!/usr/bin/env python 2 import roslib; roslib.load_manifest('beginner_tutorials') 3 import rospy 4 from std_msgs.msg import String 5 6 7 def talker(): 8 pub = rospy.Publisher('chatter', String) 9 rospy.init_node('talker') 10 while not rospy.is_shutdown(): 11 str = "hello world %s" % rospy.get_time() 12 rospy.loginfo(str) 13 pub.publish(String(str)) 14 rospy.sleep(1.0) 15 16 17 if __name__ == '__main__': 18 try: 19 talker() 20 except rospy.ROSInterruptException: 21 pass
1 #!/usr/bin/env python 2 import rospy 3 from std_msgs.msg import String 4 5 6 def talker(): 7 pub = rospy.Publisher('chatter', String) 8 rospy.init_node('talker') 9 while not rospy.is_shutdown(): 10 str = "hello world %s" % rospy.get_time() 11 rospy.loginfo(str) 12 pub.publish(String(str)) 13 rospy.sleep(1.0) 14 15 16 if __name__ == '__main__': 17 try: 18 talker() 19 except rospy.ROSInterruptException: 20 pass
Don't forget to make the node executable:
$ chmod +x scripts/talker.py
The Code Explained
Now, let's break the code down.
Every ROS Node will have this declaration at the top. The first line makes sure your script is executable and the second (as explained in the PythonPath tutorial) tells roslib to read in your manifest.xml file and add all the dependencies listed there to your PYTHONPATH. This will be important for the next section of code.
1 #!/usr/bin/env python
Every Python ROS Node will have this declaration at the top. The first line makes sure your script is executed as a Python script.
You need to import rospy if you are writing a ROS Node. The std_msgs.msg import is so that we can reuse the std_msgs/String message type (a simple string container) for publishing.
This section of code defines the talker's interface to the rest of ROS. pub = rospy.Publisher("chatter", String) declares that your node is publishing to the chatter topic using the message type String. String here is actually the class std_msgs.msg.String.
The next line, rospy.init_node(NAME), is very important as it tells rospy the name of your node -- until rospy has this information, it cannot start communicating with the ROS Master. In this case, your node will take on the name talker. NOTE: the name must be a base name, i.e. it cannot contain any slashes "/".
This loop is a fairly standard rospy construct: checking the rospy.is_shutdown() flag and then doing work. You have to check is_shutdown() to check if your program should exit (e.g. if there is a Ctrl-C or otherwise). In this case, the "work" is a call to pub.publish(String(str)) that publishes to our chatter topic using a newly created String message. The loop calls rospy.sleep(), which is similar to time.sleep() except that it works with simulated time as well (see Clock).
This loop also calls rospy.loginfo(str), which performs triple-duty: the messages get printed to screen, it gets written to the Node's log file, and it gets written to rosout. rosout is a handy for debugging: you can pull up messages using rqt_console instead of having to find the console window with your Node's output.
std_msgs.msg.String is a very simple message type, so you may be wondering what it looks like to publish more complicated types. The general rule of thumb is that constructor args are in the same order as in the .msg file. You can also pass in no arguments and initialize the fields directly, e.g.
msg = String() msg.data = str
or you can initialize some of the fields and leave the rest with default values:
You may be wondering about the last little bit:
In addition to the standard Python __main__ check, this catches a rospy.ROSInterruptException exception, which can be thrown by rospy.sleep() and rospy.Rate.sleep() methods when Ctrl-C is pressed or your Node is otherwise shutdown. The reason this exception is raised is so that you don't accidentally continue executing code after the sleep().
Now we need to write a node to receive the messages.
Writing the Subscriber Node
Create the scripts/listener.py file within the beginner_tutorials package and paste the following inside it:
1 #!/usr/bin/env python 2 import roslib; roslib.load_manifest('beginner_tutorials') 3 import rospy 4 from std_msgs.msg import String 5 6 7 def callback(data): 8 rospy.loginfo(rospy.get_name() + ": I heard %s" % data.data) 9 10 11 def listener(): 12 rospy.init_node('listener', anonymous=True) 13 rospy.Subscriber("chatter", String, callback) 14 rospy.spin() 15 16 17 if __name__ == '__main__': 18 listener()
1 #!/usr/bin/env python 2 import rospy 3 from std_msgs.msg import String 4 5 6 def callback(data): 7 rospy.loginfo(rospy.get_name() + ": I heard %s" % data.data) 8 9 10 def listener(): 11 rospy.init_node('listener', anonymous=True) 12 rospy.Subscriber("chatter", String, callback) 13 rospy.spin() 14 15 16 if __name__ == '__main__': 17 listener()
Don't forget to make the node executable:
$ chmod +x scripts/listener.py
The Code Explained
The code for listener.py is similar to talker.py, except we've introduced a new callback-based mechanism for subscribing to messages.
This declares that your node subscribes to the chatter topic which is of type std_msgs.msgs.String. When new messages are received, callback is invoked with the message as the first argument.
We also changed up the call to rospy.init_node() somewhat. We've added the anonymous=True keyword argument. ROS requires that each node have a unique name. If a node with the same name comes up, it bumps the previous one. This is so that malfunctioning nodes can easily be kicked off the network. The anonymous=True flag tells rospy to generate a unique name for the node so that you can have multiple listener.py nodes run easily.
The final addition, rospy.spin() simply keeps your node from exiting until the node has been shutdown. Unlike roscpp, rospy.spin() does not affect the subscriber callback functions, as those have their own threads.
Building your nodes
We use CMake as our build system and, yes, you have to use it even for Python nodes. This is to make sure that the autogenerated Python code for messages and services is created.
We also use a Makefile for a bit of convenience. roscreate-pkg automatically created a Makefile, so you don't have to edit it.
Now run make:
Go to your catkin workspace and run catkin_make:
$ cd ~/catkin_ws $ catkin_make
Now that you have written a simple publisher and subscriber, let's examine the simple publisher and subscriber.