ROS 2 Communication Mechanisms
Learning Objectives
By the end of this chapter, you will be able to:
- Understand and utilize ROS 2 communication mechanisms (Nodes, Topics, Services)
- Explain the differences between ROS 1 and ROS 2
- Implement basic communication patterns in ROS 2
Introduction
Robot Operating System 2 (ROS 2) is a flexible framework for writing robot software. It is a collection of tools, libraries, and conventions that aim to simplify the task of creating complex and robust robot behavior across a wide variety of robot platforms.
ROS 2 addresses many of the limitations of ROS 1, including:
- Improved real-time support
- Better security features
- Multi-robot systems support
- Cross-platform compatibility (Linux, Windows, macOS)
Core Communication Paradigms
ROS 2 provides three primary communication mechanisms:
- Nodes: The basic computational units that perform processing
- Topics: Asynchronous, many-to-many communication channels
- Services: Synchronous, request-response communication
Nodes
Nodes are the fundamental building blocks of a ROS 2 system. They are processes that perform computation and communicate with other nodes. In ROS 2, nodes are organized within a namespace and have unique names within that namespace.
Creating a Node in Python
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
More Complex Node Example with Parameters
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
from rcl_interfaces.msg import ParameterDescriptor
class ComplexNode(Node):
def __init__(self):
super().__init__('complex_node')
# Declare parameters with descriptions
self.declare_parameter('robot_name', 'default_robot',
ParameterDescriptor(description='Name of the robot'))
self.declare_parameter('publish_rate', 1.0,
ParameterDescriptor(description='Publish rate in Hz'))
# Get parameter values
self.robot_name = self.get_parameter('robot_name').value
self.publish_rate = self.get_parameter('publish_rate').value
# Create publisher
self.publisher_ = self.create_publisher(String, 'robot_status', 10)
# Create timer based on parameter
self.timer = self.create_timer(1.0/self.publish_rate, self.timer_callback)
self.get_logger().info(f'Node initialized for robot: {self.robot_name} at {self.publish_rate} Hz')
def timer_callback(self):
msg = String()
msg.data = f'{self.robot_name} is operating normally at {self.get_clock().now().nanoseconds * 1e-9:.2f}s'
self.publisher_.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = ComplexNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
node.get_logger().info('Node interrupted by user')
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Topics
Topics provide asynchronous, unidirectional communication between nodes. Publishers send messages to topics, and subscribers receive messages from topics. This enables a many-to-many communication pattern.
Publisher Example
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class PublisherExample(Node):
def __init__(self):
super().__init__('publisher_example')
self.publisher_ = self.create_publisher(String, 'chatter', 10)
timer_period = 0.5 # Publish every 0.5 seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = f'Hello ROS 2 World: {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)
publisher_example = PublisherExample()
rclpy.spin(publisher_example)
publisher_example.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Subscriber Example
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class SubscriberExample(Node):
def __init__(self):
super().__init__('subscriber_example')
self.subscription = self.create_subscription(
String,
'chatter',
self.listener_callback,
10)
self.subscription # Prevent unused variable warning
def listener_callback(self, msg):
self.get_logger().info(f'I heard: "{msg.data}"')
def main(args=None):
rclpy.init(args=args)
subscriber_example = SubscriberExample()
rclpy.spin(subscriber_example)
subscriber_example.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Services
Services provide synchronous, request-response communication between nodes. A service client sends a request to a service server, which processes the request and sends back a response.
Service Server Example
from add_two_ints_srv.srv import AddTwoInts
import rclpy
from rclpy.node import Node
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info(f'Returning {response.sum}')
return response
def main(args=None):
rclpy.init(args=args)
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
if __name__ == '__main__':
main()
Quality of Service (QoS) Settings
ROS 2 provides Quality of Service (QoS) settings that allow you to specify different policies for message delivery:
- Reliability: Best effort or reliable delivery
- Durability: Volatile or transient local
- History: Keep all or keep last N messages
- Depth: Size of the history queue
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy
# Create a QoS profile
qos_profile = QoSProfile(
depth=10,
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST
)
ROS 2 Architecture Diagrams
Basic Node-Topic-Service Architecture
graph TB
subgraph "ROS 2 System"
A[Node A] -->|"Publishes to"| T1[(Topic)]
B[Node B] -->|"Subscribes to"| T1
C[Node C] -->|"Provides Service"| S1{{Service}}
D[Node D] -->|"Calls Service"| S1
end
style A fill:#4CAF50,stroke:#388E3C,color:#fff
style B fill:#2196F3,stroke:#0D47A1,color:#fff
style C fill:#FF9800,stroke:#E65100,color:#fff
style D fill:#9C27B0,stroke:#4A148C,color:#fff
style T1 fill:#FFEB3B,stroke:#F57F17,color:#000
style S1 fill:#FFEB3B,stroke:#F57F17,color:#000
Publisher-Subscriber Pattern
graph LR
subgraph "Publisher Node"
P1[Publisher]
N1[Node 1]
end
subgraph "ROS 2 Middleware"
DDS[(DDS/RMW)]
end
subgraph "Subscriber Node"
N2[Node 2]
S1[Subscriber 1]
S2[Subscriber 2]
end
P1 -->|"Message"| DDS
DDS -->|"Message"| S1
DDS -->|"Message"| S2
N1 -.-> P1
N2 -.-> S1
N2 -.-> S2
style P1 fill:#4CAF50,stroke:#388E3C,color:#fff
style S1 fill:#2196F3,stroke:#0D47A1,color:#fff
style S2 fill:#2196F3,stroke:#0D47A1,color:#fff
style DDS fill:#FFEB3B,stroke:#F57F17,color:#000
Service-Client Pattern
sequenceDiagram
participant Client as Service Client
participant Middleware as ROS 2 Middleware
participant Server as Service Server
Client->>Middleware: Request
Middleware->>Server: Forward Request
Server->>Server: Process Request
Server->>Middleware: Response
Middleware->>Client: Return Response
Complex Multi-Node System
graph TB
subgraph "Robot Controller"
RC[Robot Controller Node]
end
subgraph "Sensor Processing"
SP[Sensor Processing Node]
LID[LiDAR Driver]
CAM[Camera Driver]
end
subgraph "Navigation"
LOCAL[Localization]
MAP[Mapping]
NAV[Navigation]
end
subgraph "Actuator Control"
ARM[Arm Controller]
BASE[Base Controller]
end
RC -->|"cmd_vel"| BASE
RC -->|"arm_cmd"| ARM
LID -->|"scan"| SP
CAM -->|"image"| SP
SP -->|"processed_data"| LOCAL
SP -->|"processed_data"| MAP
LOCAL -->|"pose"| NAV
MAP -->|"map"| NAV
NAV -->|"cmd_vel"| RC
style RC fill:#E91E63,stroke:#880E4F,color:#fff
style SP fill:#00BCD4,stroke:#006064,color:#fff
style LID fill:#FF5722,stroke:#BF360C,color:#fff
style CAM fill:#FF5722,stroke:#BF360C,color:#fff
style LOCAL fill:#9C27B0,stroke:#4A148C,color:#fff
style MAP fill:#9C27B0,stroke:#4A148C,color:#fff
style NAV fill:#9C27B0,stroke:#4A148C,color:#fff
style ARM fill:#4CAF50,stroke:#388E3C,color:#fff
style BASE fill:#4CAF50,stroke:#388E3C,color:#fff
Exercises
- Create a publisher node that publishes sensor data (e.g., temperature readings)
- Create a subscriber node that receives and processes the sensor data
- Implement a service that performs a calculation based on input parameters
- Experiment with different QoS settings and observe the behavior
Summary
ROS 2 communication mechanisms provide a robust foundation for developing distributed robotic systems. Nodes, topics, and services enable flexible communication patterns that can be combined to create complex robotic behaviors. Understanding these mechanisms is crucial for developing effective robotic applications.
Cross-References
This module provides the foundational ROS 2 concepts that are essential for:
- Digital Twin simulations - where you'll simulate ROS 2 nodes in Gazebo
- Isaac Sim/ROS integration - where ROS 2 connects with NVIDIA Isaac Sim
- VLA systems - where ROS 2 nodes may be controlled by voice commands