Intro

In ROS2, communication between different parts of a robot system is done using a Publisher-Subscriber model. Instead of functions calling each other directly, nodes exchange data asynchronously through topics.

A publisher node sends messages to a topic, while a subscriber node listens to that same topic and reacts when new data arrives. This allows components like sensors, controllers, and planners to run independently while still sharing information.

In this example, we build two minimal ROS 2 C++ nodes:

  • A publisher (talker) that periodically sends a string message using a timer
  • A subscriber (listener) that receives and logs those messages

This is one of the most fundamental communication patterns in ROS 2 and forms the basis for more complex robotic systems.

Both nodes are implemented in C++ using rclcpp, demonstrating how ROS 2 integrates real-time communication with modern C++ features like classes, Templates, Smart Pointers, and lambda functions.

Resources

Create Publisher Node

#include <chrono>
#include <memory>
#include <string>
 
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
 
using namespace std::chrono_literals;
 
class MinimalPublisher : public rclcpp::Node
{
    public:
        MinimalPublisher() : Node("minimal_publisher"), count_(0)
        {
            rclcpp::QoS qos(10); // Remember up to 10 messages
            publisher_ = this->create_publisher<std_msgs::msg::String>("topic", qos);
            auto timer_callback = [this](){
                auto message = std_msgs::msg::String();
                message.data = "Hello, world! " + std::to_string(this->count_++);
                RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
                this->publisher_->publish(message);
            };
            timer_ = this->create_wall_timer(500ms, timer_callback);
        }
    
    private:
        rclcpp::TimerBase::SharedPtr timer_;
        rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
        size_t count_;
 
};
 
int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<MinimalPublisher>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
ros2 run my_package talker

Create Subscriber Node

#include <memory>
#include <string>
 
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
 
class MinimalSubscriber : public rclcpp::Node
{
    public:
        MinimalSubscriber() : Node("minimal_subscriber")
        {
            rclcpp::QoS qos(10);
            auto topic_callback = [this](std_msgs::msg::String::UniquePtr msg) {
                RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
            };
 
            subscriber_ = this->create_subscription<std_msgs::msg::String>("topic", qos, topic_callback);
        }
        
 
    private:
        rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
};
 
int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<MinimalSubscriber>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
  • Run the subscriber node
ros2 run my_package listener