How to Create a Subscriber Node in C++
Let’s look at how to create a subscriber node in ROS.
We will write a subscriber node in C++ that will subscribe to the /message topic so that it can receive the string messages published by the publisher node (simple_publisher_node) we created in the previous section.
Open up a new terminal window.
Move to the src folder of the package: noetic_basics_part_1.
roscd noetic_basics_part_1/src
Let’s create a C++ program named simple_subscriber_node.cpp. The name for this node in ROS will be simple_subscriber_node. The purpose of this node is to subscribe to the topic named /message.
Ok, now type this command to open a brand new C++ file.
gedit simple_subscriber_node.cpp
Type the code below into the file, one line at a time so that you read all the comments and know what is going on. Creating a node in ROS that can subscribe to a topic is something you’ll do again and again as you work with ROS.
/** * A basic program to subscribe to messages that are published on a ROS topic * @author Addison Sears-Collins (https://automaticaddison.com/) * @version 1.0 */ // Include the header file that has declarations for the standard ROS classes #include "ros/ros.h" // Header file for messages of type std_msgs::String // Each type of message you use in a program will have a C++ header file you // need to include. The syntax is #include <package_name/type_name.h> // Here we want to subscribe to messages of type String that are owned by the // std_msgs package (http://wiki.ros.org/std_msgs) #include "std_msgs/String.h" /** * This function is known as a callback function. A program that subscribes to * to a topic (i.e. subscriber node) doesn't know when messages will be * arriving. If you have code that needs to process an incoming message * (e.g. most often sensor data in robotics), you place that code inside a * callback function. A callback function is a function that is called once f * or each message that arrives. * The syntax for a subscriber callback function is: * void function_name(const package_name::type_name &msg) { * [insert your code here] * } * The function below is executed each time a new message arrives. * Topic Name: /message * Message Type: std_msgs/String */ void messageCallback(const std_msgs::String::ConstPtr& msg) { // Display the message that was received to the terminal window ROS_INFO("The message I received was: [%s]", msg->data.c_str()); } /** * Main method */ int main(int argc, char **argv) { // Initialize the node and set the name. // The ros::init() function needs to see argc and argv. // The third argument is the name of the node. ros::init(argc, argv, "simple_subscriber_node"); // Create the main access point for the node // This piece of code enables the node to communicate with the ROS system. ros::NodeHandle n; // This code below is how we subscribe to a topic. // The syntax is: // ros::Subscriber sub = node_handle.subscribe( // topic_name_without_leading_slash, // queue_size, callback_function); // I like to use a large number like 1000 for the queue size. // When new messages arrive on a topic, they line up in a queue until the // callback function is executed by ROS. If the queue is full when a new // message arrives, the oldest unprocessed messages are removed to create // space for the new message. // To prevent overflow of the queue, you'll need to frequently run the // ros::spinOnce or ros::spin function to make sure the callback // function is executed. ros::Subscriber sub = n.subscribe("message", 1000, messageCallback); // This code enters a loop, continually processing messages that arrive // in the queue. If you don't have this code, messages that are waiting in // the queue will never be processed. // Note, if you use ros::spinOnce() instead of ros::spin(), unprocessed // messages will be processed and then the code below this line will // start executing. // With ros::spin(), you want ROS to wait for messages and // process those messages until you either hit Ctrl + C on your keyboard // or close down this node. // Here we use ros::spin() because the node's sole job is to echo (to the // (terminal window) the message that it receives. However, if you have a // node that needs to do something other than just printing // messages to a terminal screen, use // ros::spinOnce(), since that gives you more control when you want the node // to process incoming messages. ros::spin(); // Code was executed successfully return 0; }
In a nutshell, what we did above was:
- Initialize the ROS system
- Subscribe to the message topic (i.e. /message)
- Spin, waiting for new messages to arrive
- When a new message arrives, call the callback function to process it (i.e. messageCallback())
How to Create a Subscriber Node in Python
Go to your package.
roscd noetic_basics_part_1
Go to the scripts folder.
cd scripts
Open a new file.
gedit simple_python_subscriber.py
Type the following code in it.
#!/usr/bin/env python3 import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data) def receive_message(): # In ROS, nodes are uniquely named. If two nodes with the same # name are launched, the previous one is kicked off. The # anonymous=True flag means that rospy will choose a unique # name for our 'simple_python_subscriber' node so that multiple listeners can # run simultaneously. rospy.init_node('simple_python_subscriber', anonymous=True) rospy.Subscriber("message_py", String, callback) # spin() simply keeps python from exiting until this node is stopped rospy.spin() if __name__ == '__main__': receive_message()
Save the file and close it.
You should now have two Python files in your scripts folder of your noetic_basics_part_1 package.