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.