Skip to main content

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:

  1. Nodes: The basic computational units that perform processing
  2. Topics: Asynchronous, many-to-many communication channels
  3. 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​

  1. Create a publisher node that publishes sensor data (e.g., temperature readings)
  2. Create a subscriber node that receives and processes the sensor data
  3. Implement a service that performs a calculation based on input parameters
  4. 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: