ROS 2 Foundations: Nodes, Frames, and Robot Vision
You can structure a robot as ROS 2 nodes, choose topics against services and actions correctly, and place a camera's observations in the robot's frame tree.
Everything else on this site runs on a microcontroller: one program, one loop, total control. ROS 2 is what you move to when a robot outgrows that—when it has a camera and a planner and a driver stack that all need to run at once, written by different people, restartable independently.
The price is a new set of ideas, and the first is that a ROS 2 robot is not a program but a graph of nodes talking to each other. Most of the early confusion is choosing wrong between the four ways they can talk, so that is where this path starts: topics for streams, services for quick requests, actions for long jobs you might need to cancel.
Then TF2, which is the part that quietly defeats people. A robot is a tree of coordinate frames—the base, the wheels, every sensor—and almost every real bug is a transform that is missing, stale, or attached to the wrong parent. Once frames make sense, a calibrated camera becomes just another node publishing a pose into that tree, which is exactly how vision joins the rest of the robot.
Before you start
You need to be comfortable on a Linux command line and able to read either Python or C++. A Raspberry Pi is the usual target, but everything up to the hardware nodes runs on a laptop. Expect the setup itself to take an evening — ROS 2 is a distributed system, and it behaves like one from the first command.
What you will be able to do
- Choose correctly between a topic, a service, and an action, rather than making everything a topic and discovering the limits later.
- Build a TF2 frame tree that transforms a point from a sensor’s frame into the robot’s, and debug it when a transform is missing.
- Calibrate a camera, obtain a marker’s pose, and understand what the numbers are relative to.
- Convert a
cmd_veltwist into left and right wheel speeds, with saturation that preserves the commanded arc.
Where people get stuck
Nodes cannot see each other. ROS 2 discovery is automatic right up until it is not. Two
machines on different ROS_DOMAIN_ID values are invisible to one another, and Wi-Fi that blocks
multicast breaks discovery entirely while every node reports itself perfectly healthy.
Mismatched QoS. A publisher offering best-effort and a subscriber demanding reliable will never connect, and — unhelpfully — neither logs an error. The topic simply carries nothing. Sensor data is the usual culprit, since sensor drivers commonly default to best-effort.
Forgetting to source the workspace. A new terminal knows nothing about your packages until
it is sourced, so ros2 run reports a package that plainly exists as missing.
A frame with two parents. TF2 is a tree. Publishing the same child frame from two sources gives it two parents, and lookups start failing intermittently depending on timing — one of the harder ROS bugs to see, because it works most of the time.
Empty timestamps and frame IDs. A message whose header has no frame_id cannot be
transformed, and one whose stamp is zero will not line up with anything. Both pass through the
type system without complaint and fail only when something tries to use them together.
What you need
| What | Why this path needs it | Notes |
|---|---|---|
| A Linux command line you are comfortable in | ROS 2 is a Linux-first system and the tooling assumes it | Ubuntu matches the official binaries; anything else means building |
| Python or C++ | Node code is written in one of the two | Python is faster to learn on; C++ is what the performance-critical nodes use |
| A machine with a real screen | Debugging a distributed system over SSH is unnecessarily painful | A laptop is a better learning target than a headless Pi |
| A Raspberry Pi (hardware nodes only) | Only needed once the robot carries its own computer | Pi 4 or better; a Pi Zero will disappoint |
| A camera (vision node only) | The last node of the path | A Pi Camera module or any UVC webcam |
| An evening for setup | Not a joke — this is part of the path | Installation is genuinely the first hard step |
Why the path runs in this order
Communication patterns come first because choosing wrong is expensive later. The four ways ROS 2 nodes talk are not interchangeable, and the standard beginner failure is making everything a topic — then discovering, three hundred lines in, that you needed cancellation and progress feedback and have been reimplementing actions by hand. Ten minutes deciding between topic, service and action saves a rewrite.
TF2 comes second because it is the part that quietly defeats people. Once several nodes publish data about the physical world, every one of them means “3 metres” relative to something different — the camera, the base, the map. TF2 is how those relate, and the majority of real ROS bugs are a transform that is missing, stale, or attached to the wrong parent. Learning it as a subject, before you need it, is far cheaper than meeting it as a mysterious lookup failure.
Camera calibration comes third because it depends entirely on the second. A marker pose is meaningless without knowing what it is relative to. Once frames make sense, a calibrated camera is just another node publishing a pose into the tree, and vision stops being a special case.
The drive node closes the path because it is where ROS meets the hardware everything
else on this site is about. A cmd_vel twist is the standard interface every ROS navigation
component speaks, and turning it into left and right wheel speeds — with saturation that
preserves the commanded arc rather than distorting it — is the bridge between the ROS world
and the motor world.
Checkpoints: how to know a stage landed
| Stage | The check | What a pass looks like |
|---|---|---|
| Installation | ros2 topic list in a fresh terminal |
It works after sourcing, and you know why it fails before |
| Two nodes | Run talker and listener on two machines | Messages arrive — and you can name the domain ID they share |
| Topic vs service | Write one of each for the same data | You can say in one sentence why each pattern fits its job |
| Actions | Start a long action and cancel it midway | It stops cleanly, and you received feedback while it ran |
| QoS | Deliberately mismatch reliability on a topic | It silently carries nothing — now you recognise the symptom |
| TF2 tree | Run ros2 run tf2_tools view_frames |
One tree, one root, no frame with two parents |
| Transform lookup | Transform a point from a sensor frame into the base frame | The number is right, and you can check it against a tape measure |
| Stale transforms | Stop a transform publisher while a lookup runs | You get an extrapolation error — the failure has a name now |
| Camera calibration | Calibrate and check the reprojection error | Well under one pixel; a large value means the capture set was poor |
| Marker pose | Put a marker at a measured distance and read its pose | Matches the tape measure, and you can say which frame it is in |
cmd_vel |
Publish a twist with both linear and angular terms | Both wheels turn correctly, and saturating one scales the other to keep the arc |
Where this path stops
This path builds the foundations underneath a ROS 2 robot — how nodes talk, how frames relate, and how a sensor and a drivetrain join the graph. It deliberately stops before the large subsystems that assume all of it.
It does not cover the Nav2 stack, which is the standard answer to autonomous navigation
in ROS 2 and is a substantial system in its own right — costmaps, behaviour trees,
planners and controllers, all configured rather than written. Nav2 assumes a working TF
tree and a cmd_vel interface, which is exactly what this path gives you.
It does not cover URDF and robot description, the XML format that tells the rest of ROS what shape your robot is. Everything here publishes transforms by hand; URDF is how you stop doing that.
It does not cover simulation in Gazebo, which is how most serious ROS development actually happens, or ros2_control, the standard hardware abstraction layer for real drivetrains.
And it does not cover real-time behaviour. ROS 2 is built on DDS and is far more capable here than ROS 1 was, but a Linux machine running a Python node is not a real-time control system. The tight loops — balancing, current control, anything at a kilohertz — still belong on a microcontroller, with ROS 2 above it. That division is the normal architecture of a serious robot, not a compromise.
Learning roadmap
The path
Follow the nodes in order—each unlocks the next once you have done it. Your progress saves on this device.
0 / 5 done
Common questions
Frequently asked questions
When should a robot move from Arduino to ROS 2?
When one loop stops being enough. A microcontroller running a single program is the right architecture for a line follower, a maze solver, or anything whose whole behaviour fits in one control loop — and it is genuinely better than ROS 2 for those, because it is deterministic and you can hold all of it in your head. ROS 2 earns its complexity when a robot has several things that must run at once at different rates — a camera pipeline, a planner, a driver stack — written at different times, restartable independently. The cost is a distributed system, and it behaves like one from the very first command.
Topic, service, or action — how do I choose in ROS 2?
Match the communication pattern to the job. A topic is a stream with no reply: sensor data, odometry, velocity commands — anything published continuously where the latest value is what matters. A service is a quick request and response, used for things that complete in milliseconds, like querying a parameter or toggling a mode. An action is for long jobs you might want progress from or need to cancel — navigating to a goal, or closing a gripper on an object. The most common early mistake is making everything a topic and then building request-response semantics on top by hand.
Why can my ROS 2 nodes not see each other?
Three causes cover nearly all of it. Different ROS_DOMAIN_ID values make two machines completely invisible to each other while both report themselves perfectly healthy. Wi-Fi networks that block multicast break the discovery protocol entirely, which is why nodes often find each other over Ethernet and not over the office access point. And mismatched QoS — a publisher offering best-effort while a subscriber demands reliable — means the two never connect, and neither logs an error. Check the domain ID first; it costs nothing and is the most common answer.
What is TF2 and why do transforms keep failing?
TF2 maintains the tree of coordinate frames on a robot — base, wheels, and every sensor — so you can ask where a point in the camera's frame sits in the robot's frame. Lookups fail for three recurring reasons. A frame with two parents breaks the tree, and because it only fails depending on which publisher won the timing, it works most of the time. An empty frame_id in a message header means the message cannot be transformed at all. And a zero or stale timestamp means the transform exists but not at the time you asked for. All three pass the type system without complaint.
Do I need a Raspberry Pi to learn ROS 2?
No. Everything except the hardware driver nodes runs perfectly on a laptop, and it is a better place to learn — faster builds, a real screen, and no risk of an SD card corruption setting you back an evening. A Pi becomes necessary when you want the robot to carry its own computer. If you do use one, expect the Pi to be the constraint: a camera pipeline and a planner together will find the limits of a Pi 4 quickly.
Why does ros2 run say my package does not exist when it clearly does?
You have not sourced the workspace in that terminal. Every new terminal starts knowing nothing about your built packages until you source the install setup script, and ROS reports the result as a missing package rather than as an unsourced environment. It is the single most common ROS 2 papercut, it catches everyone repeatedly, and the standard mitigation is to source it from your shell profile — with the caveat that this then hides the problem when you genuinely have two workspaces.