In this tutorial, we’ll create a simple ROS 2 node in C++ that subscribes to a topic and listens for messages published on it. If you haven’t checked out the simple publisher tutorial, I recommend starting there first.
In ROS (Robot Operating System), software is organized into small, independent processes called nodes.
Nodes communicate by publishing and subscribing to topics — named channels that carry specific types of data.
For example:
A camera node might publish images on /camera/image_raw
A motor controller might subscribe to /cmd_vel
to receive movement commands.
Nodes and topics decouple your software, so different parts of your robot can evolve independently and work together via message passing.
If you haven’t created a ROS2 workspace yet, follow this clear tutorial first:
How to Create a ROS2 Workspace
Once your workspace is ready and sourced, continue from here.
If you haven’t already, create a new ROS 2 package:
ros2 pkg create --build-type ament_cmake cpp_simple_subscriber --dependencies rclcpp std_msgs
This will generate a new package with the necessary CMakeLists.txt
and package.xml
files.
Let’s create a new C++ file inside src/
:
cd ~/ros2_cpp_ws/src/tutorial_publisher/src
touch simple_subscriber.cpp
Open it in your text editor, and paste the following code:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
class SimpleSubscriber : public rclcpp::Node
{
public:
SimpleSubscriber() : Node("simple_subscriber")
{
subscription_ = this->create_subscription(
"topic", 10, std::bind(&SimpleSubscriber::topic_callback, this, _1));
}
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
rclcpp::Subscription::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared());
rclcpp::shutdown();
return 0;
}
Let’s carefully go through this code line by line.
Include Required Headers
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
We bring in ROS 2 C++ APIs, the standard string message type, and chrono utilities for timing.
Create a Node Class
class SimpleSubscriber : public rclcpp::Node
We define a class SimpleSubscriber
that inherits from rclcpp::Node
.
Define the Subscription
subscription_ = this->create_subscription(
"topic", 10, std::bind(&SimpleSubscriber::topic_callback, this, _1));
Inside the constructor, we create a subscription to a topic named "topic"
.
The queue size is set to 10.
std::bind
is used to associate the topic_callback
method with the subscription’s callback.
Implement the Callback
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
This function will be called each time a message is received on the subscribed topic. It simply logs the message content.
Main Function
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared());
rclcpp::shutdown();
The main
function initializes ROS 2, spins the node (which keeps it alive to listen for messages), and then shuts down cleanly when done.
Open CMakeLists.txt
and add the subscriber source file:
Add executable and link libraries:
add_executable(simple_subscriber src/simple_subscriber.cpp)
ament_target_dependencies(simple_subscriber rclcpp std_msgs)
install(TARGETS
simple_subscriber
DESTINATION lib/${PROJECT_NAME}
)
This tells CMake how to build your program and link it with ROS libraries.
From your workspace root:
cd ~/ros2_cpp_ws
colcon build --packages-select cpp_simple_subscriber
source install/setup.bash
In a new terminal:
source install/setup.bash
ros2 run tutorial_publisher simple_cpp_publisher
In another terminal (after sourcing the workspace), run the ROS 2 CLI to publish a test message:
ros2 topic pub /topic std_msgs/msg/String "{data: 'Hello from ROS 2!'}"
You should see the subscriber log the received message.