The development of sophisticated humanoid robots, capable of intricate tasks like object manipulation, precise locomotion, and human interaction, presents a formidable engineering challenge, particularly in harmonizing the communication between a multitude of complex subsystems. Imagine being part of a team tasked with developing the arms and hands for a large humanoid robot. Your responsibilities would encompass everything from executing specific movements and grasping diverse objects to performing delicate handshakes. Crucially, you wouldn’t directly manage sensor inputs, as that falls under another team’s purview. Furthermore, seamless coordination with the locomotion team would be essential, as arm movements significantly impact the robot’s balance. Such an undertaking necessitates an incredibly robust and standardized communication framework to prevent development bottlenecks and ensure system integrity.
The Genesis of Standardized Robotics Communication
For decades leading up to the late 2000s, roboticists grappled with the fundamental problem of inter-component communication. Each new robot project often required engineers to design unique messaging schemes and underlying software frameworks from the ground up. This constant "reinvention of the wheel" became a significant drain on resources, frequently consuming more development time than the actual physical construction and functional programming of the robots themselves. The lack of a universal standard stifled collaboration, hindered code reusability, and created steep learning curves for new engineers entering the field.

This pervasive challenge caught the attention of Eric Berger and Keenan Wyrobek, two visionary Ph.D. students at Stanford University’s Salisbury Robotics Lab. In 2006, they embarked on a mission to solve this problem by creating the Robot Operating System (ROS), aiming to standardize communication among various robotic components. Their groundbreaking work soon garnered the interest of Scott Hassan, the founder of the influential Willow Garage incubator. Hassan, recognizing the immense potential of ROS, invited Berger and Wyrobek to continue their development efforts within Willow Garage’s innovative program.
Over the subsequent three years, the team at Willow Garage meticulously refined ROS, transforming it into the foundational software framework for their ambitious PR2 robot. The PR2, a successor to the earlier PR1 prototype developed at Stanford, was an advanced research platform designed to navigate human environments and interact with objects, becoming an iconic symbol of the open-source robotics movement. The PR2 project not only served as a critical testbed for ROS but also demonstrated the power of a standardized middleware in accelerating complex robotics research and development.
Understanding ROS: Middleware, Not a True Operating System
It is crucial to clarify that ROS, despite its name, is not a traditional "operating system" like Windows, macOS, or Linux. It does not directly control hardware or possess a kernel for managing processes and memory allocation. Instead, ROS is an open-source robotics middleware framework, built upon an existing operating system (most commonly Linux, particularly Ubuntu). Its primary function is to facilitate multiprocessing communication between various software components, or "nodes," within a robotic system. Beyond communication, ROS also provides a comprehensive collection of computational libraries, such as the Transform Library 2 (TF2) for handling intricate coordinate frame transformations, which are vital for robot navigation and manipulation.

The scalability offered by ROS is its defining characteristic. It is not designed for simple, single-purpose robots, such as basic vacuum cleaners or maze-solving bots, where the overhead introduced by a complex middleware might be unnecessary. Rather, ROS truly shines in environments where multiple, sophisticated components must operate in concert, exchanging vast amounts of data reliably and efficiently. In such scenarios, adopting ROS can save hundreds, if not thousands, of hours of development time and mitigate significant frustration.
The Evolution to ROS 2: Addressing New Frontiers
While ROS 1 revolutionized robotics development, it did possess certain technical limitations in its underlying messaging layers, particularly concerning real-time performance, security, and native support for multi-robot systems and embedded platforms. Recognizing these challenges and the evolving demands of modern robotics, the ROS team initiated the development of ROS 2 in 2014. This new iteration was engineered from the ground up to address the shortcomings of its predecessor, offering enhanced real-time capabilities, improved security features, and greater flexibility for deployment across a wider range of hardware and operating systems.
The transition from ROS 1 to ROS 2 marked a significant milestone, with ROS 1 officially reaching its end-of-life (EOL) status on May 31, 2025. This means that ROS 1 no longer receives updates or support, firmly establishing ROS 2 as the sole supported and actively developed version. The development of ROS 2 follows a robust release cycle, with new distributions — versioned sets of ROS packages — released approximately once a year. Each release is given a whimsical, alliterative name, often featuring a turtle and progressing through the alphabet, a nod to the project’s playful spirit. For instance, the latest release, Kilted Kaiju, debuted in May 2025. For stability and long-term projects, developers often opt for Long-Term Support (LTS) distributions, such as Jazzy Jalisco, which provides support until 2029. Jazzy Jalisco officially supports Ubuntu 24.04 and Windows 10 (with Visual Studio 2019), ensuring broad compatibility for developers.

ROS 2 in Commercial and Industrial Applications
The impact of ROS 2 extends far beyond academic research labs. It has been widely adopted by industry leaders for the development and deployment of real, commercial robots across various sectors. Notable examples include:
- Amazon Robotics: Leveraging ROS for the sophisticated navigation and coordination of its vast fleet of warehouse robots, optimizing logistics and fulfillment operations.
- Avidbots: Utilizing ROS in its commercial-grade autonomous floor-cleaning robots, which operate in large public and industrial spaces.
- Omron: Integrating ROS into its TM manipulator arms, enhancing their flexibility and ease of programming for industrial automation tasks.
The open-source nature of ROS, coupled with its robust capabilities, has fostered a vibrant ecosystem of developers and companies, leading to accelerated innovation and a democratized approach to robotics development. This widespread adoption underscores ROS 2’s role as a critical enabler for the next generation of autonomous systems.
Practical Engagement: Installing and Configuring ROS 2 with Docker

For those eager to dive into ROS 2, building a large, complex helper bot, enhancing robotic programming skills, or simply exploring the hype, a hands-on approach is invaluable. While real-world robot deployment often involves installing Ubuntu on a dedicated small laptop or single-board computer like a Raspberry Pi, for educational and development purposes, a pre-made Docker image offers a convenient and consistent environment. Docker isolates dependencies and ensures that the specific versions of libraries required for a particular ROS distribution are correctly configured, making it an ideal choice for cross-platform development on macOS, Windows, and most Linux distributions.
To begin, ensure Docker Desktop is installed on your host computer by downloading it from docker.com and following the default installation steps. Next, acquire the ROS Docker image and example repository by downloading and unzipping the introduction-to-ros GitHub repository.
Open a command line terminal (e.g., zsh, bash, PowerShell), navigate to the project directory, and build the Docker image:
cd introduction-to-ros/
docker build -t env-ros2 .
This process may take some time as it constructs a full instance of Ubuntu 24.04 with a graphical interface. Once complete, run the image using the appropriate command for your operating system:

For macOS or Linux:
docker run --rm -it -e PUID=$(id -u) -e PGID=$(id -g) -p 22002:22 -p 3000:3000 -v "$PWD/workspace:/config/workspace" env-ros2
For Windows (PowerShell):
docker run --rm -it -e PUID=$(wsl id -u) -e PGID=$(wsl id -g) -p 22002:22 -p 3000:3000 -v "$PWDworkspace:/config/workspace" env-ros2
Upon successful execution, you will see the Xvnc KasmVNC welcome message in your terminal. Ignore any keysym, mieq warnings, or xkbcomp error messages. Open a web browser on your host computer and navigate to https://localhost:3000. This will present you with a full Ubuntu desktop environment within your browser, powered by the env-ros2 Docker image, which is based on the XFCE Ubuntu webtop image maintained by the LinuxServer.io group.
Fundamentals of ROS 2 Communication: Topics (Publish and Subscribe)

At the core of ROS 2’s communication architecture are nodes, which are independent processes responsible for specific tasks within the robotic system. These tasks can range from reading sensor data and executing complex algorithms to driving motors. Each node runs in its own runtime environment and communicates with other nodes using a few fundamental mechanisms.
The first and most common communication method is the topic, which operates on a publish/subscribe messaging model. In this asynchronous model, a publisher node sends data (messages) to a named topic. The underlying ROS system then efficiently delivers this message to any subscriber nodes that are listening on that particular topic. This decoupled architecture allows for highly modular and distributed systems, where nodes don’t need direct knowledge of each other, only the topics they interact with.
ROS 2 nodes can be written in various supported programming languages. Out-of-the-box, it supports Python and C++, but community-supported client libraries extend this to Ada, C, Java, .NET (e.g., C#), Node.js (JavaScript), Rust, and Flutter (Dart). A key advantage of ROS is its language agnosticism, allowing nodes written in different languages to communicate seamlessly. Generally, C++ is favored for low-level drivers and performance-critical processes, while Python offers faster development cycles and is excellent for prototyping, complex vision processing (e.g., OpenCV), and machine learning frameworks (e.g., PyTorch, TensorFlow).
ROS heavily leverages object-oriented programming principles. Individual nodes are typically implemented as subclasses of the Node class, inheriting its properties and methods. Publishers and subscribers are instantiated as objects within these nodes, allowing a single custom node to manage multiple publishers and subscribers simultaneously.

For our practical example, we will create a simple subscriber node that listens on a topic named my_topic and prints any received messages to the console. Concurrently, we will develop a publisher node that transmits a "Hello world" string, appended with a counter value, to the same topic twice per second.
Developing Your First ROS 2 Package
To begin coding, double-click the VS Code ROS2 icon on the left of the desktop within the Docker container. This opens an instance of VS Code, preconfigured with essential extensions and the ROS 2 development environment. Open a terminal pane in VS Code by clicking View -> Terminal.
Navigate into the src/ directory within the workspace/ folder. The Docker image mounts the host computer’s workspace/ directory to /config/workspace/ inside the container, ensuring that any changes made to files in workspace/ are persistent and saved on your host.

In ROS 2, a workspace is the root directory for storing and building ROS packages. The workspace/ folder serves as our ROS 2 workspace. A package is the fundamental unit of code organization in ROS 2, containing one or more nodes (parts of your robot application). When nodes within a package are built, their required libraries, artifacts, and executables are placed in the install/ directory of the workspace. Intermediate build files and logs are stored in the build/ and log/ directories, respectively.
From the /config/workspace/src directory, create a new package:
cd /config/workspace/src
ros2 pkg create --build-type ament_python my_first_pkg
This command creates a directory named my_first_pkg/ within workspace/src/. You may need to click the Refresh Explorer button in VS Code to see the new folder. The my_first_pkg/ folder contains a basic template for creating Python nodes, indicated by the ament_python build type.
Implementing Publisher and Subscriber Nodes

Now, let’s create the Python code for our publisher and subscriber.
Create a new file named my_publisher.py in my_first_pkg/my_first_pkg/:
code my_first_pkg/my_first_pkg/my_publisher.py
Copy the publisher Python code from bit.ly/41TlYuj into this file. The code defines a MinimalPublisher class, inheriting from the ROS Node class. It initializes a publisher object using self.create_publisher() to send String messages on my_topic. A timer is set to call _timer_callback() every 0.5 seconds, which constructs and publishes the "Hello world" message with an incrementing counter. The main() function initializes rclpy, creates and spins the MinimalPublisher node, and handles graceful shutdown. Save this file.
Next, create my_subscriber.py in the same directory:

code my_first_pkg/my_first_pkg/my_subscriber.py
Copy the subscriber Python code from bit.ly/3JqL0ut into this file. The MinimalSubscriber class creates a subscription object using self.create_subscription() to listen to my_topic. The _listener_callback() method is invoked whenever a message arrives, printing its content to the console. Similar to the publisher, main() handles rclpy initialization, node creation, spinning, and cleanup. Save this file.
Building and Running Your ROS 2 Package
Before building, we need to configure our package’s metadata and dependencies. Open my_first_pkg/package.xml. This package manifest lists metadata and dependencies. Add rclpy as a dependency just after the <license> tag:
<license>TODO: License declaration</license>
<depend>rclpy</depend>
<test_depend>ament_copyright</test_depend>
This informs ROS about the rclpy package required by our nodes. Save package.xml.

Next, open my_first_pkg/setup.py. This file tells the ROS build system how to locate and install our executables. Add the following to the console_scripts key within the entry_points dictionary:
entry_points=
'console_scripts': [
"my_publisher = my_first_pkg.my_publisher:main",
"my_subscriber = my_first_pkg.my_subscriber:main",
],
,
Save setup.py. Now, navigate back to your workspace directory in the terminal and build your package using colcon build:
cd /config/workspace/
colcon build --packages-select my_first_pkg
Colcon is the primary build tool for ROS 2, managing the compilation and installation of packages within a workspace. The package should build without errors.
To test your code, open three separate terminal windows within the Docker container (Applications menu -> Terminal Emulator).

In the first terminal, source the workspace environment and run the publisher node:
cd /config/workspace/
source install/setup.bash
ros2 run my_first_pkg my_publisher
In the second terminal, source the environment again and run the subscriber node:
cd /config/workspace/
source install/setup.bash
ros2 run my_first_pkg my_subscriber
You will observe "Hello world: [counter]" messages appearing in both terminals, confirming successful communication. In the third terminal, launch rqt_graph, a graphical interface for visualizing ROS 2 applications:
rqt_graph
rqt_graph will display a visual representation of your my_publisher and my_subscriber nodes connected by my_topic, illustrating the flow of data. Remember to click the Refresh button if the nodes don’t appear immediately. To stop the nodes, press Ctrl+C in each terminal or close the windows.

Services: Request-Response Communication
While the publish/subscribe model is excellent for broadcasting continuous data streams like sensor readings, it is less suitable for scenarios requiring a direct, one-time request and response between nodes. For these instances, ROS 2 provides services, which adhere to a client/server communication model.
In a service interaction, one node acts as a server, patiently waiting to receive incoming requests. Another node, the client, sends a specific request to that server and then synchronously waits for a response. This pattern is ideal for tasks such as setting a parameter, triggering a discrete action (e.g., "move robot forward by X distance"), or obtaining a one-time update of information. For example, a high-level computation node might request a motion planning node to calculate and execute a specific movement, receiving confirmation upon completion.
Implementing Server and Client Nodes for Services

Let’s create nodes that utilize the service mechanism.
In my_first_pkg/my_first_pkg/, create my_server.py:
cd /config/workspace/src/
code my_first_pkg/my_first_pkg/my_server.py
Copy the server Python code from bit.ly/4fSWUcT into this file. The MinimalServer class creates a service named add_ints using self.create_service(). It specifies AddTwoInts as the service interface, imported from example_interfaces.srv, which defines the structure of requests and responses (two integers a and b for the request, and their sum sum for the response). The _server_callback() method processes incoming requests, adds req.a and req.b, stores the result in res.sum, and returns the response. Save this file.
Next, create my_client.py in the same directory:

code my_first_pkg/my_first_pkg/my_client.py
Copy the client Python code from bit.ly/47bipmK into this file. The client node instantiates a client object for the add_ints service with the AddTwoInts interface. A timer triggers a callback every 2 seconds to construct a request with two random integers (between 0 and 10). The send_request_async() method sends the request, returning a future object. A callback is attached to this future to process the server’s response when it arrives, printing the calculated sum. Save this file.
Updating the Build System and Running Services
As before, update my_first_pkg/setup.py to include our new server and client nodes in the console_scripts entry points:
entry_points=
'console_scripts': [
"my_publisher = my_first_pkg.my_publisher:main",
"my_subscriber = my_first_pkg.my_subscriber:main",
"my_client = my_first_pkg.my_client:main",
"my_server = my_first_pkg.my_server:main",
],
,
Save setup.py and rebuild your package:

cd /config/workspace/
colcon build --packages-select my_first_pkg
With the package rebuilt, open three new terminal windows.
In the first terminal, source the workspace and run the server node:
cd /config/workspace/
source install/setup.bash
ros2 run my_first_pkg my_server
In the second terminal, source the workspace and run the client node:
cd /config/workspace/
source install/setup.bash
ros2 run my_first_pkg my_client
You will observe the client sending requests with two random integers, and the server receiving these, performing the addition, and sending back the sum, which the client then prints. While rqt_graph can be run in the third terminal, it will display the nodes but not the service interfaces or connecting lines, as services represent direct client-server interactions rather than broadcast topics.

The Broader Impact and Future of ROS 2
This exploration of nodes, topics, and services merely scratches the surface of ROS 2’s capabilities, yet it demonstrates the fundamental principles that underpin its robust architecture. While the initial setup and understanding of these concepts might seem complex, they form the essential building blocks for scaling sophisticated robotics projects. ROS 2, at its core, is a powerful middleware messaging layer designed to solve the critical challenge of inter-component communication in complex robotic systems, offering significant advantages over bespoke solutions.
Beyond these core communication mechanisms, ROS 2 ships with an extensive suite of libraries and tools that further streamline robotics development:
- TF2 (Transform Library 2): Essential for managing coordinate frames, allowing robots to understand their position and orientation relative to their environment and various components.
- Actions: A higher-level communication pattern built on topics and services, designed for long-running, goal-oriented tasks that require feedback and preemption (e.g., "drive to location X").
- Parameters: A system for dynamically configuring node settings at runtime, enhancing flexibility.
- Gazebo: A powerful 3D robot simulator that allows developers to test algorithms and robot designs in a virtual environment before deployment on physical hardware.
- RViz: A 3D visualization tool that provides a graphical representation of sensor data, robot models, and planning outputs, crucial for debugging and understanding robot behavior.
- Diagnostic Tools: A range of utilities for monitoring the health and performance of individual nodes and the entire ROS system.
The continuous development of ROS 2, championed by organizations like Open Robotics, focuses on further enhancing real-time performance, strengthening security protocols, and expanding its applicability to mission-critical systems and highly constrained embedded environments. Its commitment to open-source principles fosters a collaborative global community, driving rapid innovation and ensuring that the platform remains at the forefront of robotic technology.

In conclusion, ROS 2 stands as a foundational technology that democratizes and accelerates the development of complex robotics. By providing a standardized, flexible, and extensible framework for inter-component communication, it empowers engineers to focus on innovative robot functionalities rather than reinventing communication infrastructure. From industrial automation and logistics to advanced research and autonomous vehicles, ROS 2 is increasingly becoming the lingua franca of robotics, shaping the future of how intelligent machines interact with their world.
This article appeared in Make: Volume 95.