Topics
Topics are the most common communication mechanism in ROS 2, providing a publish-subscribe model for asynchronous data streaming. They are ideal for broadcasting continuous data streams, such as sensor readings, robot odometry, video feeds, or status updates.
Publish-Subscribe Model
- Publisher: A node that sends messages to a specific topic. It doesn't know (or care) which nodes are receiving the data.
- Subscriber: A node that receives messages from a specific topic. It doesn't know (or care) which nodes are sending the data.
- Topic Name: A unique string identifier for the communication channel (e.g.,
/camera/image_raw,/odom,/robot_status). - Message Type: Every topic has a defined message type (e.g.,
sensor_msgs/msg/Image,nav_msgs/msg/Odometry,std_msgs/msg/String). This ensures that publishers and subscribers agree on the data structure.
Creating a Publisher and Subscriber (Python Example)
Let's expand on our my_python_pkg from the Nodes section to create a publisher and subscriber.
First, define a custom message type (e.g., Num.msg) in a new package if you don't want to use standard messages. For simplicity, we'll use std_msgs/msg/String.
Publisher Node (talker_publisher.py)
Create ~/ros2_ws/src/my_python_pkg/my_python_pkg/talker_publisher.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class TalkerPublisher(Node):
def __init__(self):
super().__init__('talker_publisher')
self.publisher_ = self.create_publisher(String, 'chatter', 10)
self.timer = self.create_timer(0.5, self.timer_callback)
self.i = 0
self.get_logger().info('Talker Publisher Node started.')
def timer_callback(self):
msg = String()
msg.data = f'Hello ROS 2: {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)
talker_publisher = TalkerPublisher()
rclpy.spin(talker_publisher)
talker_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Subscriber Node (listener_subscriber.py)
Create ~/ros2_ws/src/my_python_pkg/my_python_pkg/listener_subscriber.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class ListenerSubscriber(Node):
def __init__(self):
super().__init__('listener_subscriber')
self.subscription = self.create_subscription(
String,
'chatter',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
self.get_logger().info('Listener Subscriber Node started.')
def listener_callback(self, msg):
self.get_logger().info(f'I heard: "{msg.data}"')
def main(args=None):
rclpy.init(args=args)
listener_subscriber = ListenerSubscriber()
rclpy.spin(listener_subscriber)
listener_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Update setup.py
Add the new executables to ~/ros2_ws/src/my_python_pkg/setup.py under console_scripts:
entry_points={
'console_scripts': [
'talker = my_python_pkg.talker_node:main', # Existing
'talker_publisher = my_python_pkg.talker_publisher:main',
'listener_subscriber = my_python_pkg.listener_subscriber:main',
],
},
Building and Running
- Build the workspace:
cd ~/ros2_ws
colcon build --packages-select my_python_pkg
source install/setup.bash - Run the publisher in one terminal:
ros2 run my_python_pkg talker_publisher - Run the subscriber in another terminal:
You should see the subscriber node printing the messages published by the talker node.
ros2 run my_python_pkg listener_subscriber
ros2 topic Commands
ros2 topic list: Lists all active topics.ros2 topic info <topic_name>: Displays the message type, publishers, and subscribers for a topic.ros2 topic echo <topic_name>: Prints messages being published on a topic to the console.ros2 topic pub <topic_name> <message_type> <args>: Publishes data to a topic from the command line.
Topics are fundamental for building distributed and dynamic robotic systems, allowing different components to share information efficiently.