COMMUNICATION IN ROS (ROBOT OS)
The Robot Operating System (ROS) is not an actual operating system, but a framework and set of tools that provide functionality of an operating system on a heterogeneous computer cluster. Its usefulness is not limited to robots, but the majority of tools provided are focused on working with peripheral hardware.
To know more about ROS and Installation process — Introduction to ROS
Now I am going to tell how can communication be done between nodes in ROS with the help of a simple Publisher and Subscriber.
First of all, before going to actual code, here is a simple model to understand communication system.
Suppose there is a man (named-manager) who manage a distribution system in which if a person (Mr. X) wants to give something he should tell manager he wants to give something in an open account (named it A) and if a man (Mr. Y) wants to take that items he should first tell manger that every thing that is coming in open account A provide me so whenever items will come in account A manager provide to Mr. Y.
The diagram below is graphical view of this model —
In this diagram, Mr. X gives food in account A and Mr. Y takes.
Now let’s relate this with ROS —
managers — master node
Mr. X — publisher
Mr. Y — subscriber
open account A — Topic
food — message type
Now, it’s time to go at actual code, i am going write publisher and subscriber in c++. There are two node one is talker and other is listener,talker will send a message at chatter topic and listener will get this message by subscribing chatter topic.
First of all goto your workspace/src directory and create two file named talker.cpp and listener.cpp.
1. Publisher
Put this code in talker.cpp —
Now, let’s break the code down.
#include "ros/ros.h"
ros/ros.h is a convenienc include that includes all the headers necessary to use the most common public pieces of the ROS system.
#include "std_msgs/String.h"
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.
ros::init(argc, argv, "talker");
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.
The name used here must be a base name, ie. it cannot have a / in it.
ros::NodeHandle n;
Create a handle to this process’ node. The first NodeHandle created will actually do the initialization of the node, and the last one destructed will cleanup any resources the node was using.
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
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.
ros::Rate loop_rate(10);
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.
In this case we tell it we want to run at 10Hz.
while(ros::ok()){
......
By default roscpp will install a SIGINT handler which provides Ctrl-C handling which will cause `ros::ok()` to return false if that happens.
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
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”.
chatter_pub.publish(msg);
Now we actually broadcast the message to anyone who is connected.
ROS_INFO("%s", msg.data.c_str());
ROS_INFO and friends are our replacement for printf/cout.
ros::spinOnce();
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.
loop_rate.sleep();
Now we use the ros::Rate object to sleep for the time remaining to let us hit our 10Hz publish rate.
2. subscriber
Now, let’s write subscriber node,put this code in listener.cpp —
Now, let’s break it down piece by piece, ignoring some pieces that have already been explained above.
void chatterCallback(const std_msgs::String::ConstPtr& msg){
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
This is the callback function that will get called when a new message has arrived on the chatter topic.
ros::Subscriber sub = n.subscribe("chatter", 1000,
chatterCallback);
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.
ros::spin();
`ros::spin()` enters a loop, calling message callbacks as fast as possible.
Note —
Here number of listener to topic chatter is one, but number of subscriber of a topic can be more than one and all subscriber will get same message.
3. Building Nodes
Now it’s time to build our program —
add these lines to your CMakeLists.txt
To know basics of CMake, I would suggest Derek Molloy’s blog on CMake.
Now, let’s run our program
Make sure that roscore is up and running:
$ roscore
Make your program
# In your catkin workspace
$ cd ~/catkin_ws
$ catkin_make
$ source ./devel/setup.bash
Now, run talker node
$ rosrun talker_listener talker
You will see something similar to:
[INFO] [WallTime: 1314931831.774057] hello world 1314931831.77
[INFO] [WallTime: 1314931832.775497] hello world 1314931832.77
[INFO] [WallTime: 1314931833.778937] hello world 1314931833.78
[INFO] [WallTime: 1314931834.782059] hello world 1314931834.78
The publisher node is up and running. Now we need a subscriber to receive messages from the publisher.
$ rosrun talker_listener listener
You will see something similar to:
[INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26
[INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26
[INFO] [WallTime: 1314931971.266348] /listener_17657_1314931968795I heard hello world 1314931971.26
[INFO] [WallTime: 1314931972.270429] /listener_17657_1314931968795I heard hello world 1314931972.27
Here we can see listener is receiving hello world mgs.
If you enjoyed this post, like and share this story. Thank you!
— Shubham Maddhashiya