Skip to main content

Section 2: Topics - Publish and Subscribe

Estimated Reading Time: 8 minutes Bloom's Level: Understand, Apply Key Learning: One-to-many communication with decoupled nodes


The Problem with Direct Communication

Imagine you have a sensor node and a control node. How should they talk?

❌ Direct Connection (Bad)

Sensor Node → Control Node

Problems:

  • Control node must exist before sensor starts
  • Sensor knows about control (tight coupling)
  • Hard to add a second control node
  • If control crashes, sensor can't publish elsewhere

✅ Topic-Based (Good)

Sensor Node → [Topic: /data] ← Control Node
↗ Logger Node
↗ Visualizer Node

Benefits:

  • Nodes don't know about each other (decoupled)
  • Multiple nodes can listen (one-to-many)
  • Nodes can start/stop independently
  • Easy to add new subscribers

What is a Topic?

A topic is a named channel for publishing and subscribing to messages.

Think of it like a bulletin board:

  • Publisher: Posts a message on the board
  • Subscribers: Read messages from the board
  • Topic Name: The bulletin board label (e.g., /temperature)
  • Message Type: What data is posted (e.g., Float32)

Key characteristics:

  • One-to-many: One publisher, many subscribers
  • Asynchronous: Publisher doesn't wait for subscribers
  • Typed: Every topic has a message type
  • Named: Topics have unique names starting with /

Publishers and Subscribers

Publisher Node

# Creates a publisher
publisher = self.create_publisher(String, '/greeting', 10)

# Publishes messages
msg = String()
msg.data = 'Hello!'
publisher.publish(msg)

Subscriber Node

# Creates a subscriber with callback
subscription = self.create_subscription(
String,
'/greeting',
self.listener_callback,
10
)

# Callback handles incoming messages
def listener_callback(self, msg):
self.get_logger().info(f'Received: {msg.data}')

Code Example 2: Publisher/Subscriber System

Location: code-examples/ros2_packages/pub_sub_py/

Generated by: ros2-code-generator skill

Publisher Node

# File: pub_sub_py/pub_sub_py/publisher.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class PublisherNode(Node):
def __init__(self):
super().__init__('publisher')

# Create a publisher for String messages on /greeting topic
self.publisher = self.create_publisher(String, '/greeting', 10)

# Timer to publish every 1 second
self.timer = self.create_timer(1.0, self.timer_callback)
self.i = 0

def timer_callback(self):
# Create and publish message
msg = String()
msg.data = f'Hello from publisher: #{self.i}'
self.publisher.publish(msg)
self.get_logger().info(f'Publishing: {msg.data}')
self.i += 1

def main(args=None):
rclpy.init(args=args)
node = PublisherNode()
rclpy.spin(node)
rclpy.shutdown()

if __name__ == '__main__':
main()

Subscriber Node

# File: pub_sub_py/pub_sub_py/subscriber.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class SubscriberNode(Node):
def __init__(self):
super().__init__('subscriber')

# Create subscriber that listens to /greeting topic
# Calls listener_callback when messages arrive
self.subscription = self.create_subscription(
String,
'/greeting',
self.listener_callback,
10
)

def listener_callback(self, msg):
# This function runs when a message arrives
self.get_logger().info(f'Received: {msg.data}')

def main(args=None):
rclpy.init(args=args)
node = SubscriberNode()
rclpy.spin(node)
rclpy.shutdown()

if __name__ == '__main__':
main()

Running a Pub/Sub System

Terminal 1: Publisher

ros2 run pub_sub_py publisher

Output:

[INFO] [publisher]: Publishing: Hello from publisher: #0
[INFO] [publisher]: Publishing: Hello from publisher: #1
[INFO] [publisher]: Publishing: Hello from publisher: #2

Terminal 2: Subscriber

ros2 run pub_sub_py subscriber

Output:

[INFO] [subscriber]: Received: Hello from publisher: #0
[INFO] [subscriber]: Received: Hello from publisher: #1
[INFO] [subscriber]: Received: Hello from publisher: #2

Terminal 3: Inspect the Graph

rqt_graph

Visualizes:

[publisher] --[/greeting]--> [subscriber]

Topic Naming Conventions

ROS 2 topics follow a naming pattern:

/namespace/subtopic/specific_name

Examples:
/robot/camera/image (camera images)
/robot/sensors/imu (IMU data)
/robot/navigation/goal (navigation goal)
/system/health/cpu_load (system metrics)

Best Practices:

  • Use lowercase with underscores
  • Start with /
  • Be specific and descriptive
  • Group related topics with namespace

Message Types

Topics carry typed messages. Common types:

from std_msgs.msg import (
String, # Text
Float32, # Single floating point
Int32, # Integer
Bool, # Boolean
Header, # Timestamp + frame info
)

from geometry_msgs.msg import (
Point, # X, Y, Z coordinates
Twist, # Linear and angular velocity
Pose, # Position and orientation
)

from sensor_msgs.msg import (
Image, # Camera images
PointCloud2, # 3D point clouds
LaserScan, # LIDAR data
Imu, # Inertial measurement unit
)

Quality of Service (QoS)

The 10 parameter in create_publisher() and create_subscription() is the queue size (QoS):

# Queue size: 10 messages
self.publisher = self.create_publisher(String, '/topic', 10)

What it means:

  • If subscriber is slow, publisher queues up to 10 messages
  • Older messages are dropped if queue fills
  • Larger queue = more memory, better for slow subscribers
  • Smaller queue = faster, better for real-time systems

Key Takeaways

✅ Topics enable one-to-many communication ✅ Publishers and subscribers are decoupled ✅ Multiple subscribers can listen to one topic ✅ Publishers don't wait for subscribers ✅ Topics are typed and named ✅ QoS settings control message buffering


Try It Yourself

Try modifying the pub/sub example:

  • Change the message text in publisher
  • Create a second subscriber (run it in a third terminal)
  • Use rqt_graph to see multiple subscribers
  • Change topic name (must match in both nodes)
  • Try ros2 topic echo /greeting in another terminal

Next

Now you understand how nodes communicate via topics. The next pattern is services: synchronous request/response communication.

👉 Next: Section 3: Services - Request and Response

Or review:


Section Status: Complete ✅ Skills Used: ros2-code-generator, code-explainer