Page

User

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 (C++)

Description: This tutorial covers how to write a publisher and subscriber node in C++.

27#include "ros/ros.h" 28#include "std_msgs/String.h" 29 30#include <sstream> 31 32/** 33 * This tutorial demonstrates simple sending of messages over the ROS system. 34 */ 35intmain(intargc, char **argv) 36{ 37/** 38 * The ros::init() function needs to see argc and argv so that it can perform 39 * any ROS arguments and name remapping that were provided at the command line. For programmatic 40 * remappings you can use a different version of init() which takes remappings 41 * directly, but for most command-line programs, passing argc and argv is the easiest 42 * way to do it. The third argument to init() is the name of the node. 43 * 44 * You must call one of the versions of ros::init() before using any other 45 * part of the ROS system. 46 */ 47ros::init(argc, argv, "talker"); 48 49/** 50 * NodeHandle is the main access point to communications with the ROS system. 51 * The first NodeHandle constructed will fully initialize this node, and the last 52 * NodeHandle destructed will close down the node. 53 */ 54ros::NodeHandlen; 55 56/** 57 * The advertise() function is how you tell ROS that you want to 58 * publish on a given topic name. This invokes a call to the ROS 59 * master node, which keeps a registry of who is publishing and who 60 * is subscribing. After this advertise() call is made, the master 61 * node will notify anyone who is trying to subscribe to this topic name, 62 * and they will in turn negotiate a peer-to-peer connection with this 63 * node. advertise() returns a Publisher object which allows you to 64 * publish messages on that topic through a call to publish(). Once 65 * all copies of the returned Publisher object are destroyed, the topic 66 * will be automatically unadvertised. 67 * 68 * The second parameter to advertise() is the size of the message queue 69 * used for publishing messages. If messages are published more quickly 70 * than we can send them, the number here specifies how many messages to 71 * buffer up before throwing some away. 72 */ 73ros::Publisherchatter_pub = n.advertise<std_msgs::String>("chatter", 1000); 74 75ros::Rateloop_rate(10); 76 77/** 78 * A count of how many messages we have sent. This is used to create 79 * a unique string for each message. 80 */ 81intcount = 0; 82while (ros::ok()) 83 { 84/** 85 * This is a message object. You stuff it with data, and then publish it. 86 */ 87std_msgs::Stringmsg; 88 89std::stringstreamss; 90ss << "hello world " << count; 91msg.data = ss.str(); 92 93ROS_INFO("%s", msg.data.c_str()); 94 95/** 96 * The publish() function is how you send messages. The parameter 97 * is the message object. The type of this object must agree with the type 98 * given as a template parameter to the advertise<>() call, as was done 99 * in the constructor above. 100 */ 101chatter_pub.publish(msg); 102 103ros::spinOnce(); 104 105loop_rate.sleep(); 106 ++count; 107 } 108 109 110return0; 111}

This includes the std_msgs/String message, which resides in the std_msgs package. This is a header generated automatically from the String.msg file in that package. For more information on message definitions, see the msg page.

Initialize ROS. This allows ROS to do name remapping through the command line -- not important for now. This is also where we specify the name of our node. Node names must be unique in a running system.

Tell the master that we are going to be publishing a message of type std_msgs/String on the topic chatter. This lets the master tell any nodes listening on chatter that we are going to publish data on that topic. The second argument is the size of our publishing queue. In this case if we are publishing too quickly it will buffer up a maximum of 1000 messages before beginning to throw away old ones.

NodeHandle::advertise() returns a ros::Publisher object, which serves two purposes: 1) it contains a publish() method that lets you publish messages onto the topic it was created with, and 2) when it goes out of scope, it will automatically unadvertise.

A ros::Rate object allows you to specify a frequency that you would like to loop at. It will keep track of how long it has been since the last call to Rate::sleep(), and sleep for the correct amount of time.

We broadcast a message on ROS using a message-adapted class, generally generated from a msg file. More complicated datatypes are possible, but for now we're going to use the standard String message, which has one member: "data".

Calling ros::spinOnce() here is not necessary for this simple program, because we are not receiving any callbacks. However, if you were to add a subscription into this application, and did not have ros::spinOnce() here, your callbacks would never get called. So, add it for good measure.

The Code

28#include "ros/ros.h" 29#include "std_msgs/String.h" 30 31/** 32 * This tutorial demonstrates simple receipt of messages over the ROS system. 33 */ 34voidchatterCallback(conststd_msgs::String::ConstPtr& msg) 35{ 36ROS_INFO("I heard: [%s]", msg->data.c_str()); 37} 38 39intmain(intargc, char **argv) 40{ 41/** 42 * The ros::init() function needs to see argc and argv so that it can perform 43 * any ROS arguments and name remapping that were provided at the command line. For programmatic 44 * remappings you can use a different version of init() which takes remappings 45 * directly, but for most command-line programs, passing argc and argv is the easiest 46 * way to do it. The third argument to init() is the name of the node. 47 * 48 * You must call one of the versions of ros::init() before using any other 49 * part of the ROS system. 50 */ 51ros::init(argc, argv, "listener"); 52 53/** 54 * NodeHandle is the main access point to communications with the ROS system. 55 * The first NodeHandle constructed will fully initialize this node, and the last 56 * NodeHandle destructed will close down the node. 57 */ 58ros::NodeHandlen; 59 60/** 61 * The subscribe() call is how you tell ROS that you want to receive messages 62 * on a given topic. This invokes a call to the ROS 63 * master node, which keeps a registry of who is publishing and who 64 * is subscribing. Messages are passed to a callback function, here 65 * called chatterCallback. subscribe() returns a Subscriber object that you 66 * must hold on to until you want to unsubscribe. When all copies of the Subscriber 67 * object go out of scope, this callback will automatically be unsubscribed from 68 * this topic. 69 * 70 * The second parameter to the subscribe() function is the size of the message 71 * queue. If messages are arriving faster than they are being processed, this 72 * is the number of messages that will be buffered up before beginning to throw 73 * away the oldest ones. 74 */ 75ros::Subscribersub = n.subscribe("chatter", 1000, chatterCallback); 76 77/** 78 * ros::spin() will enter a loop, pumping callbacks. With this version, all 79 * callbacks will be called from within this thread (the main one). ros::spin() 80 * will exit when Ctrl-C is pressed, or the node is shutdown by the master. 81 */ 82ros::spin(); 83 84return0; 85}

The Code Explained

Now, let's break it down piece by piece, ignoring some pieces that have already been explained above.

This is the callback function that will get called when a new message has arrived on the chatter topic. The message is passed in a boost shared_ptr, which means you can store it off if you want, without worrying about it getting deleted underneath you, and without copying the underlying data.

Subscribe to the chatter topic with the master. ROS will call the chatterCallback() function whenever a new message arrives. The 2nd argument is the queue size, in case we are not able to process messages fast enough. In this case, if the queue reaches 1000 messages, we will start throwing away old messages as new ones arrive.

NodeHandle::subscribe() returns a ros::Subscriber object, that you must hold on to until you want to unsubscribe. When the Subscriber object is destructed, it will automatically unsubscribe from the chatter topic.

There are versions of the NodeHandle::subscribe() function which allow you to specify a class member function, or even anything callable by a Boost.Function object. The roscpp overview contains more information.

ros::spin() enters a loop, calling message callbacks as fast as possible. Don't worry though, if there's nothing for it to do it won't use much CPU. ros::spin() will exit once ros::ok() returns false, which means ros::shutdown() has been called, either by the default Ctrl-C handler, the master telling us to shutdown, or it being called manually.

There are other ways of pumping callbacks, but we won't worry about those here. The roscpp_tutorials package has some demo applications which demonstrate this. The roscpp overview also contains more information.

Again, here's a condensed version of what's going on:

Initialize the ROS system

Subscribe to the chatter topic

Spin, waiting for messages to arrive

When a message arrives, the chatterCallback() function is called

Building your nodes

roscreate-pkg will create a default Makefile and CMakeLists.txt for your package.

This will create two executables, talker and listener, which by default will go into package directory of your devel space, located by default at ~/catkin_ws/devel/lib/<package name>.

Note that you have to add dependencies for the executable targets to message generation targets:

add_dependencies(talker beginner_tutorials_generate_messages_cpp)

This makes sure message headers of this package are generated before being used. If you use messages from other packages inside your catkin workspace, you need to add dependencies to their respective generation targets as well, because catkin builds all projects in parallel. As of *Groovy* you can use the following variable to depend on all necessary targets:

add_dependencies(talker ${catkin_EXPORTED_TARGETS})

You can invoke executables directly or you can use rosrun to invoke them. They are not placed in '<prefix>/bin' because that would pollute the PATH when installing your package to the system. If you wish for your executable to be on the PATH at installation time, you can setup an install target, see: catkin/CMakeLists.txt