Imagine you're about to build a robot called ARCHO: a small robot that needs to move between the tall shelves of a warehouse, find its way using a laser sensor, and stop if a human steps in front of it. The first temptation is to open a code file and start writing: read from the camera, process it, send it to the motors. After a few hours you find everything stuck in a thousand-line file, unable to test any part on its own, and if one line of code breaks, the entire robot freezes. This is exactly the path most ROS 2 tutorials take: they jump straight to terminal commands β install this, run that β and you learn to type commands without knowing why.
This book, and this chapter in particular, takes a different path. Before we write even a single line of code for ARCHO, we sit down and sketch its map: what pieces we need, what each one does, and how they talk to each other. This is exactly what a real robotics engineer does before writing the first line of code. The good news is that the people who built ROS 2 solved this exact problem years ago, and almost the entire system β from a simple vacuum robot to an industrial robotic arm β is built on just a handful of concepts:
If you learn the five concepts Node, Topic, Message, Service, and Action well, you've understood about 70% of ROS 2. The rest of the book's chapters are just details and tools built around this same core.
By the end of this chapter, you'll know these seven foundational concepts and be able to answer these four questions for any Node in any project:
The learning map for this chapter looks like this β each box builds on the one before it:
Throughout this book, we'll apply every concept to one consistent project: ARCHO, a simple mobile robot that will move around a large warehouse, sense its surroundings with LiDAR, and find its own path. Each chapter completes another piece of this robot. In this chapter, we'll only design ARCHO's communication skeleton; we won't write any code yet.
Let's go back to ARCHO and that thousand-line file. Suppose you've been working on it for three weeks: reading the camera, processing images, computing the path, controlling the motors β all in one program, in one process. As long as the project is small, everything goes fine. But one morning, the image-processing part crashes and the entire robot freezes β even the motors stop responding, because everything is stuck in one program. Worse, when you just want to test the path-planning algorithm, you have to compile and run the entire giant program just to see whether one line of computation works correctly. This is the wall that almost everyone who starts without ROS eventually hits.
ROS solves this problem with a concept called a Node. Each Node is a small, independent program with exactly one clear responsibility. One only reads from the camera, one only computes the path, one only sends commands to the motors.
A Node is an independent process in ROS 2 that performs one specific task and communicates with other Nodes through Topics, Services, Actions, or Parameters in order to do so.
Picture a restaurant. The chef, the waiter, the cashier, and the dishwasher are each independent people; none of them does another's job, but they coordinate to keep the restaurant running. If the dishwasher doesn't show up today, the restaurant can still serve food β the dishes just pile up. In exactly the same way, if a robot's camera Node goes down, the other Nodes (say, the motor or the LiDAR) are still alive.
In a real mobile robot, dozens of Nodes are typically running simultaneously:
| Node | Responsibility |
|---|---|
camera_node | Reads images from the camera and publishes them |
lidar_node | Reads laser range-finder data |
slam_node | Builds a map of the environment from sensor data |
navigation_node | Computes the path to the target |
motor_controller_node | Converts velocity commands into motor signals |
Many newcomers think they should write all of the robot's logic in one big Node to make it "simpler." The result is the opposite: debugging becomes hard, testing each part separately becomes impossible, and if one part crashes, the whole robot goes down. Golden rule: one Node, one responsibility.
From a software-architecture point of view, a Node in ROS 2 plays exactly the same role as a microservice in web-service architecture: a small, independent, replaceable unit with a clear communication contract with the rest of the system. This resemblance is no accident β both were designed to solve the same problem: reducing coupling between the parts of a large system.
If you wanted to design a simple vacuum-cleaning robot, what Nodes would you probably need? Name at least four and write each one's responsibility in a single sentence.
Okay, now we've split ARCHO into several independent Nodes β one for the camera, one for image processing, one for the motors. But a new problem has appeared: these Nodes are isolated in their own worlds. The camera Node captures a fresh image every moment, but if there's no way to get it to the image-processing Node, that image just sits in the camera's memory and is useless. We need a way for these independent islands to talk to each other. The first and most common communication method in ROS 2 is the Topic.
A Topic is a named channel for transmitting data. One Node can publish information on a Topic, and any other Node can subscribe to that same Topic and receive the information. Every packet of data sent on a Topic is called a Message.
In a restaurant, the chef displays every dish that's ready on a monitor. The chef doesn't know who's watching β maybe the waiter, maybe the manager, maybe no one. They just keep publishing the status continuously. This is exactly how a Publisher behaves on a Topic.
A few important points about Topics to keep in mind from the start:
/image is always sensor_msgs/Image.
In the ARCHO robot, the LiDAR Node publishes distance data on a Topic called /scan using the message
type sensor_msgs/LaserScan. At the same time, both the mapping (SLAM) Node and the obstacle-detection
Node subscribe to this same shared Topic β without the LiDAR ever knowing how many listeners there are.
Many newcomers think all communication between Nodes should go through Topics. A Topic is only suited to a continuous stream of data. If you want to ask a Node something and wait for a specific answer, a Topic is not the right tool β as we'll see in the next section.
Suppose you've mounted an IMU (an acceleration and orientation sensor) on the robot that produces new data every 100 milliseconds. Is this a good fit for a Topic or not? Write your reasoning in two lines.
Now that ARCHO is broadcasting camera data over a Topic, something interesting happens: the development team, excited about this new tool, starts trying to solve everything with Topics β even saving the map, even reading the battery level. The result? Countless channels constantly sending useless messages. To understand why this is a mistake, let's step back into a restaurant. If you want to see the kitchen's status live, that's exactly like a Topic β information is continuously broadcast. But if you want to say "one pizza, please," something different happens: you send a specific request and wait for a specific answer. That's no longer a Topic; it's a Request/Response pattern. ROS built Service for exactly this purpose.
A Service is a two-way communication in which one Node (the Client) sends a Request, and another Node (the Server) processes it and returns a specific Response.
| Criterion | A Topic is a good fit if... | A Service is a good fit if... |
|---|---|---|
| Data type | It's constantly changing (camera, LiDAR, IMU) | It's only read or executed when needed |
| Timing pattern | A continuous, repeating stream | A one-time, request-driven event |
| Example | Instantaneous speed, camera image, Odometry | Saving a map, resetting an Encoder, reading battery percentage |
| Expecting a reply | The Publisher doesn't wait for a reply | The Client waits for a specific Response |
Does the robot need to publish its battery percentage to everyone every second? No β that's wasteful and hogs
network bandwidth. Instead, whenever needed (say, when a UI is opened), a Get Battery Status request
is sent, and the Server returns a single response of 78%.
In the ARCHO project, examples of real Services would include:
reset_encoder β zero out the wheel counterscalibrate_imu β calibrate the inertial sensorsave_map β save the built map to diskget_battery_status β read the current battery percentageWhen we reach the hands-on chapters, these commands will be your everyday tools:
ros2 service list
ros2 service type /clear
ros2 interface show std_srvs/srv/Empty
ros2 service call /clear std_srvs/srv/Empty "{}"
The output of std_srvs/srv/Empty is just a line of ---, because this Service type carries
no data in its Request or Response; calling it simply triggers an action, exactly like pressing a button. By
contrast, example_interfaces/srv/AddTwoInts takes two integers (a and b) and
returns their sum (sum).
Suppose you tell the robot "go to room number 5." Is this a Topic? Is it a Service? If the answer to both is no, think about why β this question leads us directly into the next section.
One day, a member of the ARCHO team asks a simple question that leaves everyone quiet for a few minutes: "If we want to tell the robot to go to shelf number 5, which tool should we use?" Everyone thinks the answer is a Service β one request, one response. But looking more closely, they realize the command "go to shelf 5" is neither a Topic nor a Service. Why? Because this task might take several minutes: the movement has to start, progress has to be reported, an obstacle might turn up along the way, it might need to be cancelled, and at the end we need to know whether it succeeded or not. A Service only gives one short reply and doesn't wait around for a long task to finish. For this class of tasks, ROS 2 introduces a third concept called Action.
When you request a ride from a taxi app, you don't just get one reply. First they say a driver accepted, then they say the driver is 3 kilometers away, then they say the driver has arrived, then the trip begins, and finally the trip ends. This is exactly what Action provides in ROS 2: one goal, several progress updates, and one final result.
An Action is a long-running operation whose progress can be observed while it runs, which can be cancelled if needed, and which returns a final result at the end. Every Action consists of three parts: a Goal, Feedback (progress reports), and a Result (final outcome).
Figure 1.3 β The full cycle of an Action: from Goal to Result, with a few Feedback updates in between.
| Action Part | Role | Example |
|---|---|---|
| Goal | The objective to be accomplished | target_x: 5.2, target_y: 8.1 |
| Feedback | Status report during execution | distance_remaining: 2.3 |
| Result | Final outcome once finished | success: true |
Suppose a robot arm has to tighten a screw; this could take 20 seconds. If we used a Service, we'd get only a
request and a reply, with no visibility into what's happening in between. With Action, we track the steps live:
Approaching β Aligning β Tightening β Finished. If the object falls mid-way, the Result comes back
as Failed β not an ambiguous silence.
One of the most well-known real-world Actions in ROS 2 is NavigateToPose in the Nav2 package. Almost
every Nav2-based mobile robot uses this exact Action to go to a target point: the Client says "go here," and the
Server reports the stages Planningβ¦ β Movingβ¦ β Avoiding Obstacleβ¦ β Arrived.
An important capability that Action has and Topic and Service don't is the ability to
Cancel. If the robot is moving and a human suddenly steps in front of it, a Cancel Goal
can be sent at that instant and the robot stops. Many early designs forget this capability and later run into
trouble when trying to add an "emergency stop button."
Picture a smart coffee maker. Explain what its behavior would look like if built with a Topic, what it would look like if built with a Service, and why Action is the best choice. Write at least one limitation of each approach.
A mental image to keep with you forever: a Topic is like a radio, a Service is like a phone call with the bank, and an Action is like ordering a ride-hailing taxi.
| Criterion | Topic | Service | Action |
|---|---|---|---|
| Pattern | Publish / Subscribe | Request / Response | Goal / Feedback / Result |
| Duration | Continuous | Instant and short | Long-running |
| Progress reporting | None (it's a stream itself) | None | Yes |
| Cancellable | No | No | Yes |
| Example | Camera, LiDAR, IMU | Reset, Save, Calibrate | Navigate, Pick Object |
| Best suited for | Sensors and data streams | Instant operations and settings | Long-running robotic tasks |
When designing a new Node's architecture, ask yourself: "Is this data constantly changing, or does it only happen based on a specific trigger?" If it's continuous β Topic. If it's instant and short β Service. If it's long-running and needs interim reporting β Action. The wrong choice usually either floods the network with useless messages (Topic instead of Service) or leaves the UI "locked with no feedback" (Service instead of Action).
On a professional camera, there are settings like ISO, shutter speed, and aperture. To change the ISO you don't need to reprogram the camera's firmware; you just change one setting. Parameter in ROS 2 plays exactly this role.
The ARCHO team now knows when to use a Topic and when to use a Service and an Action. But a smaller, more annoying
problem still remains. One engineer hardcoded the robot's default speed directly into the code:
robot_speed = 0.5;. A month later, while testing the robot in a narrower corridor, they want to change
this value to 0.7. Without a Parameter, you'd have to open the file, edit the code, rebuild, and rerun β for one
small change, the entire development cycle repeats. With a Parameter, the code stays fixed and only the setting
value changes.
A Parameter is an adjustable value that controls a Node's behavior without changing the code. Parameters are typically kept in YAML files so a piece of software can be deployed across several different robots without rewriting code.
# camera_params.yaml
camera_node:
ros__parameters:
fps: 30
width: 1280
height: 720
Suppose you install the same motion software on three robots: a small robot with max_speed = 0.4, a
medium robot with max_speed = 0.8, and a large robot with max_speed = 1.5. The code is
identical; only the Parameter differs. In the ARCHO project, the Node called motor_controller_node
has parameters like wheel_radius, wheel_base, and max_velocity. If the
wheel diameter changes, we only change the value of wheel_radius; the control algorithm stays
untouched.
A simple analogy: a car's instantaneous speed (80 km/h) constantly changes β that's like a Topic. But setting Cruise Control to 100 km/h might stay fixed for hours β that's like a Parameter. A Topic carries live, flowing data; a Parameter holds relatively fixed settings.
ros2 param list
ros2 param get /camera_node fps
ros2 param set /camera_node fps 60
For the LiDAR Node on the ARCHO robot, propose three Parameters that would make sense (for example, detection range, scan rate). For each one, explain why this value should be a Parameter rather than a fixed part of the code.
Every morning when you start work, you open several programs: a browser, email, a messaging tool, a code editor.
If you open them one by one every day, you'll be exhausted after a week. Instead, you build a script
(start_work.sh) that launches them all together. A Launch File does exactly this
for ROS 2.
By now ARCHO has seven or eight independent Nodes, each with its own Topics, Services, Actions, and Parameters. On
test day, an engineer has to run each one manually in a separate terminal window: first the camera, then the LiDAR,
then the IMU, then SLAM, then navigation, then the motor controller, then RViz. If the launch order is wrong or one
is forgotten, the robot won't work correctly. Now imagine this number is a hundred and fifty Nodes instead of
seven β this is exactly what happens on a real industrial robot. A real robot typically needs these Nodes running
simultaneously: camera_node, lidar_node, imu_node, slam_node,
navigation_node, motor_controller, and rviz. Manually running each one with
ros2 run is practically impossible for an industrial robot with hundreds of Nodes.
A Launch File is a Python file that specifies which Nodes run, with what Parameters, in what order, and under what conditions. Launch isn't just a sequential-execution script; it's an engine with conditional logic.
A Launch file can do the following:
Suppose the project has both a Simulation mode and a real-robot execution mode. With one simple condition in the
Launch File, if simulation = true, Gazebo, RViz, and a simulated controller run; if
simulation = false, the real camera, real LiDAR, and real motor driver run instead β same project, no
code changes.
# robot.launch.py (just for familiarity; full details in the hands-on chapters)
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(package="camera_driver", executable="camera_node"),
Node(package="lidar_driver", executable="lidar_node"),
Node(package="rviz2", executable="rviz2"),
])
And running all of this with a single command:
ros2 launch my_robot robot.launch.py
When you open almost any professional ROS 2 project, this same pattern repeats:
my_robot/
βββ launch/
β βββ robot.launch.py
β βββ simulation.launch.py
β βββ navigation.launch.py
βββ config/
β βββ controller.yaml
β βββ nav2.yaml
β βββ camera.yaml
βββ urdf/
βββ rviz/
βββ worlds/
βββ src/
βββ package.xml
Take this with you from this chapter: ROS isn't just programming. ROS is a combination of software architecture (Node, Topic, Service, Action), communication (message passing between processes), deployment (Launch File), and configuration (Parameter and YAML). Someone who only writes code but doesn't know these four layers will never fully understand a real robotic system.
For the ARCHO robot, write a list of Nodes that should run together in a single Launch File (at least five Nodes), and specify which ones should run only in Simulation mode and which ones should run only on real hardware.
Now that we've seen each piece individually, let's put them all together. This is the diagram you should keep in mind for the rest of the book β the backbone of ROS 2:
For every new Node you build in the ARCHO project or any other project, ask yourself these four questions; if you can answer them, you've truly understood that Node's architecture:
Congratulations β you now know the seven main pillars of ROS 2 architecture: Node as the independent execution unit, Topic and Message for a continuous data stream, Service for instant request/response, Action for long-running, cancellable operations, Parameter for tuning behavior without changing code, and Launch File for starting the whole system with a single command. So far we've only learned the city map; we haven't entered the city yet.
ARCHO Project In this chapter, we only designed the robot's communication skeleton on paper: which Node talks to which Node via a Topic, which tasks should be Services, and which should be Actions. We haven't written any code yet, and no Node has run on a system.
From Chapter 2 onward, we enter the hands-on world: building a Workspace from scratch, examining its structure, creating our first Package, writing our first real Node in Python, running it, and watching our first Publisher and Subscriber with our own eyes. From that point on, every concept you learn will immediately run on your own system β that's where ROS 2 turns from a set of concepts into a real tool for building robots.