Chapter 2: ROS 2 Architecture and Core Concepts
Type: Theory-to-Practice
Lessons: 8 (5 theory + 3 code)
Duration: 12-16 hours
Chapter Overview
This chapter introduces ROS 2 (Robot Operating System 2) as the middleware framework that enables distributed robotic systems. You'll learn the architecture, core concepts, and build your first ROS 2 nodes.
By the end of this chapter, you will:
- Understand ROS 2 as middleware (not an operating system)
- Know why DDS replaced ROS 1's architecture
- Create and run ROS 2 nodes
- Implement publish-subscribe communication
- Configure QoS (Quality of Service) policies
- Create launch files for multi-node systems
ROS 2 as Middleware
What is Middleware?
Middleware is software that provides services and capabilities beyond what the operating system offers. ROS 2 is middleware specifically designed for robotics.
ROS 2 is NOT:
- An operating system (runs on Linux, Windows, macOS)
- A programming language (works with Python, C++, Java, etc.)
- A robot (it's software that runs on robots)
ROS 2 IS:
- A communication framework
- A set of tools and libraries
- A standard way to build robot software
Why ROS 2?
Problems ROS 2 Solves:
- Distributed Systems: Robots have many components (sensors, actuators, AI) that need to communicate
- Modularity: Break complex systems into manageable nodes
- Reusability: Share code and packages across projects
- Standardization: Common interfaces and message types
DDS: The Foundation of ROS 2
Why DDS Replaced ROS 1
ROS 1 Limitations:
- Single master node (single point of failure)
- No real-time guarantees
- Limited security features
- Difficult to use across networks
DDS (Data Distribution Service) Benefits:
- Decentralized: No single master node
- Real-Time: QoS policies for deterministic communication
- Security: Built-in authentication and encryption
- Network Transparency: Works seamlessly across networks
- Industry Standard: Used in aerospace, automotive, medical devices
DDS Architecture
DDS provides a publish-subscribe model:
Publisher Node Subscriber Node
│ │
│ (DDS Layer) │
│ │
└─────────┬─────────────┘
│
DDS Domain
- No central broker: Nodes discover each other automatically
- Type-safe: Message types are enforced
- QoS Policies: Control reliability, durability, deadline
Core Concepts
Nodes
Nodes are independent processes that perform specific tasks.
Characteristics:
- Single-purpose (e.g., "camera driver", "path planner")
- Communicate via topics, services, actions
- Can run on same machine or distributed across network
Example Nodes:
camera_node: Publishes camera imagesnavigation_node: Plans pathsmotor_controller_node: Controls motors
Topics
Topics are named communication channels for streaming data.
Publish-Subscribe Pattern:
- Publisher: Sends messages to a topic
- Subscriber: Receives messages from a topic
- Many-to-Many: Multiple publishers/subscribers per topic
Example Topics:
/camera/image_raw: Camera images/cmd_vel: Velocity commands/odom: Odometry data
Services
Services provide request-response communication.
Characteristics:
- Synchronous: Client waits for response
- One-to-One: One server, one client per request
- Use Cases: Calibration, configuration, one-time actions
Example Services:
/calibrate_camera: Trigger camera calibration/get_robot_state: Query current robot state
Actions
Actions combine topics and services for long-running tasks.
Characteristics:
- Asynchronous: Client doesn't block
- Feedback: Continuous updates during execution
- Cancellable: Can abort long-running tasks
- Use Cases: Navigation, manipulation sequences
Example Actions:
/navigate_to_pose: Navigate to a goal/pick_object: Execute pick-and-place sequence
Your First ROS 2 Node
Minimal "Hello World" Node
Create hello_ros2.py:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
class HelloNode(Node):
def __init__(self):
super().__init__('hello_node')
self.get_logger().info('Hello, ROS 2!')
def main(args=None):
rclpy.init(args=args)
node = HelloNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Key Components:
rclpy.init(): Initialize ROS 2Node: Base class for all nodesget_logger(): For logging messagesrclpy.spin(): Keep node aliverclpy.shutdown(): Clean shutdown
Running the Node
# Make executable
chmod +x hello_ros2.py
# Run
python3 hello_ros2.py
Expected Output:
[INFO] [hello_node]: Hello, ROS 2!
Publish-Subscribe Example
Publisher Node
Create talker.py:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class TalkerNode(Node):
def __init__(self):
super().__init__('talker')
self.publisher_ = self.create_publisher(String, 'chatter', 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 = f'Hello 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)
node = TalkerNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Subscriber Node
Create listener.py:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class ListenerNode(Node):
def __init__(self):
super().__init__('listener')
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)
node = ListenerNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Testing
Terminal 1 (Publisher):
python3 talker.py
Terminal 2 (Subscriber):
python3 listener.py
Terminal 3 (Monitor):
ros2 topic echo /chatter
Quality of Service (QoS)
QoS policies control how ROS 2 handles message delivery.
Common QoS Profiles
Reliable (default):
- Messages guaranteed to be delivered
- Use for: Commands, critical data
Best Effort:
- Messages may be dropped
- Use for: High-frequency sensor data (cameras, LiDAR)
Example:
from rclpy.qos import QoSProfile, ReliabilityPolicy
# Reliable QoS
qos_reliable = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
depth=10
)
# Best Effort QoS (for high-frequency data)
qos_best_effort = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
depth=10
)
self.publisher_ = self.create_publisher(
Image, 'camera/image_raw', qos_best_effort
)
Launch Files
Launch files start multiple nodes and configure the system.
Basic Launch File
Create my_launch_file.launch.py:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_package',
executable='talker',
name='talker_node',
),
Node(
package='my_package',
executable='listener',
name='listener_node',
),
])
Running Launch Files
ros2 launch my_package my_launch_file.launch.py
Chapter Projects
Project 1: Minimal ROS 2 Node
- Create a node that logs a message every second
- Verify it runs and logs correctly
Project 2: QoS-Configured Sensor Publisher
- Create a publisher for simulated sensor data (temperature, distance)
- Configure QoS for best-effort delivery
- Publish at high frequency (10+ Hz)
Project 3: Multi-Node System
- Create publisher, subscriber, and processor nodes
- Use a launch file to start all nodes
- Verify communication between nodes
Chapter Summary
Key Takeaways:
-
ROS 2 is middleware for building distributed robot systems
-
DDS architecture provides decentralized, real-time communication
-
Core concepts: Nodes, Topics, Services, Actions
-
QoS policies control message delivery reliability
-
Launch files orchestrate multi-node systems