July 23, 2026
hands-on-with-ros-2-nodes-topics-and-services

The development of advanced robotics has long been a complex endeavor, often hampered by the lack of standardized communication protocols between disparate hardware and software components. Imagine a team of engineers tasked with building a sophisticated humanoid robot: one group designs the intricate movements of the arms and hands, another focuses on locomotion and balance, while a third handles sensor integration and data processing. Without a unified framework, each team would traditionally develop bespoke messaging schemes, leading to a fragmented, inefficient, and time-consuming integration process. This foundational challenge, akin to "reinventing the wheel" for every new robotic project, significantly slowed innovation and consumed valuable development resources.

Hands On with ROS 2: Nodes, Topics, and Services

The Genesis of a Standard: From Stanford to Willow Garage

Before the advent of the Robot Operating System (ROS), roboticists globally grappled with these exact issues. The early 2000s saw a proliferation of custom, often proprietary, solutions for inter-component communication, making collaboration difficult and code reuse nearly impossible across different robotic platforms. This fragmentation meant that a significant portion of project time was dedicated not to advancing robotic capabilities, but to merely establishing basic operational infrastructure.

A pivotal shift began in 2006, when two Ph.D. students at Stanford University’s Salisbury Robotics Lab, Eric Berger and Keenan Wyrobek, recognized the urgent need for standardization. Their vision led to the creation of the Robot Operating System (ROS), an initiative aimed at providing a common language and framework for robotic components to interact. Their groundbreaking work soon attracted the attention of Scott Hassan, founder of the influential Willow Garage incubator. Hassan invited Berger and Wyrobek to continue their research and development within Willow Garage’s highly collaborative environment.

Hands On with ROS 2: Nodes, Topics, and Services

Over the subsequent three years, this dedicated team at Willow Garage meticulously refined ROS. Their efforts culminated in the development of the PR2 robot, a sophisticated research platform that became the primary testbed for ROS. The PR2 (Personal Robot 2), an evolution of Stanford’s PR1, was designed to navigate human environments and interact with objects, pushing the boundaries of autonomous manipulation. As the PR2 advanced, so too did ROS, solidifying its role as the underlying software framework that enabled the robot’s complex functionalities. This period marked the establishment of ROS 1 as an open-source robotics middleware framework and a comprehensive collection of libraries, fundamentally transforming how robotic systems were designed and integrated.

ROS 1 to ROS 2: An Evolution Driven by Modern Robotics Needs

It is crucial to understand that ROS is not a traditional "operating system" like Windows, macOS, or Linux. It does not directly manage hardware resources or contain a kernel for process and memory allocation. Instead, ROS operates as a layer of middleware, typically built atop a conventional operating system (most commonly Linux, particularly Ubuntu). Its primary function is to facilitate multiprocessing communication and provide a rich collection of computational libraries, such as the Transform Library 2 (TF2) for managing coordinate frame transformations, crucial for spatial reasoning in robotics.

Hands On with ROS 2: Nodes, Topics, and Services

While ROS 1 revolutionized the field, its architecture, designed for a different era of robotics, eventually revealed certain technical limitations, particularly in its underlying messaging layers. These limitations became more pronounced as robotics evolved, demanding features like real-time performance, enhanced security, multi-robot coordination, and robust support for embedded systems. In response to these growing needs, the ROS team embarked on a significant architectural overhaul, leading to the inception of ROS 2 in 2014.

ROS 2 was engineered from the ground up to address the shortcomings of its predecessor, offering improved real-time capabilities, a more secure communication framework, and native support for distributed, multi-robot systems. This foresight ensured that ROS would remain at the forefront of robotic development for years to come. The transition culminated on May 31, 2025, when ROS 1 officially reached its end-of-life status, ceasing to receive updates or support. ROS 2 has now fully superseded it as the standard for modern robotics development.

Hands On with ROS 2: Nodes, Topics, and Services

The ROS ecosystem, much like Linux, follows a distribution model. Approximately once a year, the ROS team releases a new distribution – a versioned set of ROS packages – each given a distinct, alliterative name featuring a turtle and progressing through the alphabet. For instance, the latest release in May 2025 was named Kilted Kaiju. However, for long-term stability and support, developers often opt for distributions with Long-Term Support (LTS). Jazzy Jalisco, for example, offers support until 2029, making it a reliable choice for ongoing projects. Each ROS distribution is carefully pinned to specific versions of underlying operating systems (e.g., Jazzy Jalisco officially supports Ubuntu 24.04 and Windows 10 with Visual Studio 2019) to guarantee compatibility and optimal performance of its myriad libraries.

Understanding ROS 2: Nodes, Topics, and Services

ROS’s scalability is its defining feature. While it might be overkill for simple, single-purpose robots (like basic vacuum cleaners or maze solvers), it becomes indispensable for projects involving multiple, complex components that require intricate coordination. The framework’s widespread adoption across academia for cutting-edge research and its increasing integration into commercial products – including Amazon’s warehouse robots, Avidbots’ commercial-grade cleaning machines, and Omron’s TM manipulator arms – attests to its robustness and utility.

Hands On with ROS 2: Nodes, Topics, and Services

At the heart of ROS 2’s architecture are nodes, which are independent processes responsible for specific tasks. These could range from reading sensor data and executing complex algorithms to controlling motors and actuators. Each node operates in its own runtime environment, communicating with other nodes through a set of standardized methods. This modular approach allows for concurrent development, easier debugging, and enhanced fault isolation within large robotic systems.

Topics: The Publish/Subscribe Model

The primary communication mechanism in ROS 2 is the topic, which operates on a publish/subscribe messaging model. In this model, a node designated as a publisher sends data to a named topic. The ROS middleware then efficiently delivers this message to any node that has subscribed to that particular topic. This asynchronous, one-to-many communication is ideal for continuous data streams, such as real-time sensor readings (e.g., camera feeds, lidar scans, IMU data) or actuator commands (e.g., motor speeds, joint positions). A single topic can have multiple publishers and multiple subscribers, creating a flexible and scalable data pipeline.

Hands On with ROS 2: Nodes, Topics, and Services

Nodes can be written in various programming languages. Out of the box, ROS 2 provides robust support for Python and C++, allowing developers to choose the language best suited for their specific task. C++ is often preferred for low-level drivers and performance-critical processes, leveraging its speed and direct memory access. Python, with its faster development cycles and extensive libraries (e.g., OpenCV for vision processing, PyTorch/TensorFlow for machine learning), is excellent for prototyping, high-level control, and complex data analysis. The beauty of ROS lies in its language agnosticism, enabling seamless communication between nodes written in different languages.

In the object-oriented paradigm embraced by ROS, individual nodes are typically implemented as subclasses of the Node class provided by the ROS Client Library (RCL). This inheritance grants custom nodes access to core ROS functionalities, including the creation of publishers and subscribers. A single custom node can host multiple publishers and subscribers, managing various data flows simultaneously.

Hands On with ROS 2: Nodes, Topics, and Services

Services: The Client/Server Request/Response Model

While topics excel at broadcasting continuous data, they are not suitable for scenarios requiring a direct, synchronous interaction where one node requests an action or data from another and expects an immediate response. For these use cases, ROS 2 employs services, which follow a client/server model.

In a service interaction, one node acts as a server, patiently awaiting incoming requests on a specific service interface. Another node, acting as a client, sends a request to that server and then pauses, waiting for a response. This synchronous, one-to-one communication pattern is perfect for triggering specific actions, querying parameters, or requesting a one-time update. For instance, a high-level navigation node might send a request to a motion control node to "move forward by 1 meter," expecting a confirmation once the action is complete. The server processes the request, computes a response, and sends it back to the client, which can then proceed with its next task.

Hands On with ROS 2: Nodes, Topics, and Services

The client-server model is critical for ensuring reliable command execution and for querying specific pieces of information without needing to continuously monitor a topic. The response mechanism, often handled through "futures" in asynchronous programming, allows the client to efficiently manage its state while awaiting the server’s reply.

Practical Application: Setting Up a ROS 2 Environment with Docker

To demonstrate these core communication principles, setting up a ROS 2 environment is essential. While a real robot would typically run Ubuntu on dedicated hardware like a small laptop or a single-board computer (e.g., Raspberry Pi), a Docker image offers an excellent, cross-platform solution for development and learning. Docker Desktop provides a virtualized environment that encapsulates all necessary dependencies, ensuring consistent behavior across macOS, Windows, and various Linux distributions. This approach circumvents potential compatibility issues that can arise from diverse host operating systems and library versions.

Hands On with ROS 2: Nodes, Topics, and Services

To begin, Docker Desktop must be installed on the host computer (available from docker.com). Once Docker is operational, the next step involves obtaining the ROS Docker image and an accompanying example repository. This typically involves cloning or downloading a GitHub repository (e.g., introduction-to-ros) that contains the necessary Dockerfile and example code.

Within the host’s command line terminal, navigating to the repository’s root directory allows for the Docker image to be built using docker build -t env-ros2 .. This command initiates a process that can take some time, as it constructs a comprehensive environment including a full instance of Ubuntu 24.04 with a graphical interface, specifically configured for ROS 2 Jazzy Jalisco.

Hands On with ROS 2: Nodes, Topics, and Services

Upon successful build, the Docker image can be run with specific parameters to map ports and mount local directories, allowing persistent storage for development files and access to a web-based graphical desktop environment (KasmVNC, accessible via https://localhost:3000). This setup provides a complete, isolated Ubuntu desktop environment within the browser, pre-configured with ROS 2 development tools like VS Code.

Implementing Communication: Topics in Action (Publishers and Subscribers)

With the Dockerized ROS 2 environment ready, developers can proceed to create and test their first nodes. The VS Code instance, preconfigured within the Docker container, provides an integrated development experience. The workspace/ directory, mounted from the host, ensures that all code changes are saved persistently.

Hands On with ROS 2: Nodes, Topics, and Services

A ROS package serves as the fundamental unit of code organization. Using ros2 pkg create --build-type ament_python my_first_pkg within the src/ directory of the workspace creates a new package tailored for Python development. This command generates a directory structure that includes boilerplate files necessary for ROS to understand how to build and install the package.

Creating a Publisher Node

A Python file, my_publisher.py, is created within my_first_pkg/my_first_pkg/. This file defines a MinimalPublisher class that inherits from rclpy.node.Node. Inside this class, self.create_publisher() is used to instantiate a publisher object, specifying the message type (String from std_msgs.msg), the topic name (my_topic), and a quality-of-service (QoS) profile (e.g., queue size 10). A timer is then set up using self.create_timer() to trigger a _timer_callback() method at a fixed interval (e.g., every 0.5 seconds). This callback constructs a "Hello world: [count]" string, logs it, and publishes it to my_topic. The main() function initializes rclpy, creates and spin()s the MinimalPublisher node, and handles graceful shutdown.

Hands On with ROS 2: Nodes, Topics, and Services

Creating a Subscriber Node

Similarly, my_subscriber.py defines a MinimalSubscriber class. This class uses self.create_subscription() to create a subscriber object, listening to my_topic with the same message type and QoS profile. A _listener_callback() method is attached, which is automatically invoked whenever a message arrives on my_topic. This callback simply logs the received message to the console. The main() function structure mirrors that of the publisher, initializing rclpy, spinning the MinimalSubscriber node, and managing its lifecycle.

Building and Running Topic Nodes

Before building, the package.xml file within my_first_pkg/ must be updated to declare rclpy as a build and runtime dependency. Additionally, setup.py needs to specify the entry points for the new my_publisher and my_subscriber executables within the console_scripts section. Once these configuration files are updated, the package is built using colcon build --packages-select my_first_pkg from the workspace root.

Hands On with ROS 2: Nodes, Topics, and Services

To observe the topic communication, three terminal windows are opened within the Docker environment. In the first, the publisher node is run: source install/setup.bash followed by ros2 run my_first_pkg my_publisher. In the second, the subscriber node is run: source install/setup.bash followed by ros2 run my_first_pkg my_subscriber. The output in both terminals will show the "Hello world" messages, demonstrating successful inter-node communication. The third terminal is used to launch rqt_graph, a powerful visualization tool that displays the nodes and the topic connecting them, providing a clear graphical representation of the communication flow.

Implementing Communication: Services in Action (Clients and Servers)

The client/server model for services also requires the creation of specific nodes.

Hands On with ROS 2: Nodes, Topics, and Services

Creating a Service Server Node

A new Python file, my_server.py, defines a MinimalServer node. This node uses self.create_service() to instantiate a service named add_ints, employing the AddTwoInts interface (a standard ROS 2 interface for adding two integers, imported from example_interfaces.srv). A _server_callback() method is assigned to handle incoming requests. When a request containing two integers (req.a and req.b) arrives, the callback sums them, stores the result in the response object (res.sum), logs the operation, and returns the response.

Creating a Service Client Node

The my_client.py file defines a MinimalClient node. Here, self.create_client() is used to create a client object for the add_ints service, again specifying the AddTwoInts interface. A timer is set to trigger a callback every 2 seconds, which constructs a request with two random integers. This request is then sent to the server using self.client.call_async(), which returns a future object. A callback is attached to this future, executed once the server’s response is received, at which point the client logs the sum to the console. The future concept is crucial for asynchronous operations, allowing the client node to remain responsive while awaiting a response.

Hands On with ROS 2: Nodes, Topics, and Services

Building and Running Service Nodes

Similar to topic nodes, setup.py must be updated to include my_client and my_server as entry points. After this, the package is rebuilt with colcon build --packages-select my_first_pkg.

To test the services, two terminal windows are opened. In the first, the server node is launched: source install/setup.bash followed by ros2 run my_first_pkg my_server. In the second, the client node is launched: source install/setup.bash followed by ros2 run my_first_pkg my_client. The server terminal will display messages indicating received requests and sent responses, while the client terminal will show the calculated sums, confirming the successful request-response cycle. While rqt_graph can visualize the client and server nodes, it typically does not draw explicit lines for service interactions, as they represent a direct request-response rather than a continuous data stream like topics.

Hands On with ROS 2: Nodes, Topics, and Services

The Broader Impact and Future of ROS

This hands-on exploration of ROS 2’s fundamental communication mechanisms – topics and services – underscores its power as a "mighty middleware." It demonstrates how ROS provides an elegant, standardized solution to the complex problem of inter-component communication in robotics. While the initial setup and understanding might seem to involve overhead, this investment pays dividends in larger, more intricate robotic projects, where it saves hundreds of hours of development time and mitigates integration frustrations.

ROS has democratized robotics development, allowing engineers and researchers to focus on innovative algorithms and hardware designs rather than re-engineering basic communication layers. Its open-source nature fosters a vibrant global community, contributing to a vast ecosystem of tools, libraries, and packages that extend far beyond simple messaging. These include sophisticated navigation stacks (e.g., for autonomous movement), manipulation frameworks (e.g., MoveIt for robotic arm control), simulation environments (e.g., Gazebo for virtual robot testing), and visualization tools (e.g., Rviz for 3D data display).

Hands On with ROS 2: Nodes, Topics, and Services

The transition to ROS 2 has further solidified its position, addressing the demands of modern robotics for real-time performance, security, and scalability in multi-robot and embedded contexts. As robotics continues its rapid expansion into diverse sectors – from industrial automation and logistics to healthcare and personal assistance – ROS 2 will remain a critical enabling technology. Its standardized approach fosters interoperability, accelerates research-to-product cycles, and facilitates the creation of increasingly complex, intelligent, and autonomous systems that are shaping our future. This foundational understanding of nodes, topics, and services is merely the beginning of leveraging ROS’s full potential to build the next generation of robots.