Module 1: ROS 2 Fundamentals
Welcome to the core of modern robotics development! This chapter introduces you to the Robot Operating System (ROS) 2, the foundational software framework that acts as the nervous system for a vast array of robotic applications. By mastering ROS 2, you'll gain the ability to create complex, modular, and scalable robotics systems.
1. Learning Objectives
Upon completing this chapter, you will be able to:
- Explain the core concepts of ROS 2: nodes, topics, services, actions, and parameters.
- Understand the ROS 2 architecture and communication patterns.
- Create, build, and run a simple ROS 2 package and node using Python.
- Use ROS 2 command-line tools to inspect and debug a running system.
- Explain the importance of launch files for managing complex systems.
2. Introduction to ROS 2
What is ROS 2?
ROS 2 is an open-source, flexible framework for writing robot software. It's not an operating system in the traditional sense (like Windows or Linux), but a set of software libraries and tools that help you build robot applications. Think of it as a middleware that simplifies the task of creating complex and robust robot behavior across a wide variety of robotic platforms.
ROS 2 handles low-level tasks like hardware abstraction, device drivers, and message-passing between processes, allowing you to focus on the high-level logic of your robot.
Why ROS 2?
- Modularity: ROS 2 encourages a modular design, where complex systems are broken down into smaller, reusable programs called nodes.
- Scalability: It is designed for systems of all sizes, from a single-board computer to a large network of robots.
- Real-time Capabilities: ROS 2 provides support for real-time control, which is critical for many robotics applications.
- Cross-platform: It supports Linux, macOS, and Windows.
- Strong Community: A large and active community contributes to a rich ecosystem of packages and tools.
3. Core Concepts of ROS 2
At the heart of ROS 2 is a graph of processes (nodes) that communicate with each other. This communication is what allows a robot's various components—sensors, actuators, and decision-making algorithms—to work together.
Nodes
A node is the fundamental processing unit in ROS 2. Each node should be responsible for a single, well-defined task, such as controlling a wheel motor, reading a laser scanner, or planning a path. A complete robotic system is typically composed of many nodes working in concert.
Communication Patterns
ROS 2 provides four main communication patterns:
a. Topics (Publish/Subscribe)
Topics are used for asynchronous, one-to-many communication. A node can publish messages to a topic, and any number of other nodes can subscribe to that topic to receive the messages. This is the most common communication pattern in ROS 2, used for streaming data like sensor readings or robot state.
How it works:
- A Publisher node sends a message of a specific type to a named topic.
- The ROS 2 middleware delivers the message to all Subscriber nodes that are listening to that topic.
- The publisher doesn't know (or care) who is receiving the messages.
graph TD
A[Publisher Node<br/>(e.g., /camera_driver)] -- Publishes Image Message --> B(Topic<br/>/image_raw);
B -- Message Delivered --> C[Subscriber Node<br/>(e.g., /image_processor)];
B -- Message Delivered --> D[Subscriber Node<br/>(e.g., /image_recorder)];
Figure 1: The Publish/Subscribe Model. A single publisher sends data to multiple subscribers via a topic.
b. Services (Request/Reply)
Services are used for synchronous, one-to-one communication. A Service Client sends a request to a Service Server, which processes the request and sends back a response. This pattern is used for tasks that have a clear start and end, like "capture an image" or "compute a path".
How it works:
- A Client node sends a request message to a named service.
- A Server node receives the request, performs a task, and sends a response message back to the client.
- The client waits (blocks) until it receives the response.
graph TD
A[Service Client<br/>(e.g., /path_requester)] -- 1. Sends Request (Start/Goal) --> B(Service<br/>/compute_path);
B -- 2. Processed by --> C[Service Server<br/>(e.g., /path_planner)];
C -- 3. Sends Response (Path) --> B;
B -- 4. Response forwarded to --> A;
Figure 2: The Request/Reply Model. A client requests an action and waits for a single server to respond.
c. Actions
Actions are used for asynchronous, one-to-one communication for long-running tasks. They are similar to services, but they provide feedback during execution and are cancellable. This is perfect for tasks like "navigate to a goal," which might take a long time to complete.
How it works:
- An Action Client sends a goal to an Action Server.
- The Action Server starts executing the task and sends periodic feedback to the client (e.g., distance to goal).
- When the task is finished, the server sends a final result.
d. Parameters
Parameters allow you to configure nodes at runtime without changing the code. Each node can have a set of parameters (e.g., wheel_radius, camera_resolution) that can be get or set from the command line or other nodes.
4. Hands-On: Your First ROS 2 Package
Let's put these concepts into practice by creating a simple publisher/subscriber system.
Step 1: Create a ROS 2 Workspace
A workspace is a directory where you store your ROS 2 packages.
- Linux/macOS
- Windows
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
mkdir ros2_ws\src
cd ros2_ws
Step 2: Create a ROS 2 Package
Now, let's create a new package named my_first_package inside the src directory.
- Linux/macOS
- Windows
cd src
ros2 pkg create --build-type ament_python my_first_package
cd src
ros2 pkg create --build-type ament_python my_first_package
This creates a new directory with all the necessary files for a Python-based ROS 2 package.
Step 3: Write the Publisher Node
Create a new file named publisher_node.py inside my_first_package/my_first_package/.
# my_first_package/my_first_package/publisher_node.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MyPublisher(Node):
def __init__(self):
super().__init__('my_publisher')
self.publisher_ = self.create_publisher(String, 'chatter', 10)
self.timer = self.create_timer(0.5, 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)
my_publisher = MyPublisher()
rclpy.spin(my_publisher)
my_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Step 4: Write the Subscriber Node
Create another file named subscriber_node.py in the same directory.
# my_first_package/my_first_package/subscriber_node.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MySubscriber(Node):
def __init__(self):
super().__init__('my_subscriber')
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)
my_subscriber = MySubscriber()
rclpy.spin(my_subscriber)
my_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Step 5: Add Entry Points
To make these nodes executable, you need to add them to setup.py.
# my_first_package/setup.py
# ... (other parts of the file)
entry_points={
'console_scripts': [
'my_publisher = my_first_package.publisher_node:main',
'my_subscriber = my_first_package.subscriber_node:main',
],
},
# ...
Step 6: Build and Run
Navigate back to the root of your workspace (ros2_ws) and build your package.
- Linux/macOS
- Windows
cd ~/ros2_ws
colcon build
source install/setup.bash
cd ..\..
colcon build
call install/setup.bat
Now, run the publisher in one terminal:
ros2 run my_first_package my_publisher
And the subscriber in another terminal:
ros2 run my_first_package my_subscriber
You should see the publisher sending messages and the subscriber receiving them!
5. ROS 2 Command-Line Tools
ROS 2 provides a powerful set of command-line tools to inspect and interact with your system.
ros2 node list: See all running nodes.ros2 topic list: List all active topics.ros2 topic echo /chatter: See the messages being published on the/chattertopic.ros2 run <package_name> <executable_name>: Execute a node.
6. Launch Files
Starting each node in a separate terminal is tedious. Launch files allow you to start and configure a whole system of nodes with a single command.
Create a file named my_launch.py in a new launch directory inside your package.
# my_first_package/launch/my_launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_first_package',
executable='my_publisher',
name='my_publisher'
),
Node(
package='my_first_package',
executable='my_subscriber',
name='my_subscriber'
),
])
You'll also need to tell the build system about the launch file by adding this to setup.py:
# In setup.py
import os
from glob import glob
# ...
data_files=[
# ...
(os.path.join('share', package_name, 'launch'), glob('launch/*.py'))
],
# ...
After rebuilding with colcon build, you can run your entire system with:
ros2 launch my_first_package my_launch.py
7. Summary & Key Takeaways
- ROS 2 is a modular framework for building robotic systems.
- Nodes are the fundamental building blocks, each with a single purpose.
- Topics, Services, and Actions are the primary communication patterns.
- Command-line tools like
ros2 runandros2 topic echoare essential for debugging. - Launch files simplify the process of running complex systems.
8. Chapter Quiz
-
What is the primary communication pattern for streaming data like sensor readings? a) Services b) Topics c) Actions d) Parameters
-
Which command would you use to see the data being published on a topic named
/scan? a)ros2 node listb)ros2 topic info /scanc)ros2 topic echo /scand)ros2 run /scan -
What is the main advantage of using Actions over Services? a) They are synchronous. b) They can't be cancelled. c) They provide feedback during execution. d) They are faster.
-
What is the purpose of a
colcon buildcommand? a) To create a new package. b) To compile and install all packages in a workspace. c) To run a single node. d) To clean the workspace. -
A ROS 2 node is typically responsible for: a) Running the entire robotic system. b) A single, well-defined task. c) Only handling hardware drivers. d) Storing configuration data.
(Answers: 1-b, 2-c, 3-c, 4-b, 5-b)