Why I Use ROS2 for Every Robot Project Now
After years of writing custom middleware, here's why I switched to ROS2 even for small projects — and what I had to let go of to make it work.
I resisted ROS for a long time. The dependency footprint, the catkin build system, the feeling that it was designed for academic robots rather than hardware that actually ships — it seemed like overkill for a four-wheeled ground robot.
Then I spent a month fighting a homegrown pub/sub system trying to get a lidar, an IMU, and a motor controller to talk to each other reliably. After the third timing bug that only appeared at specific message rates, I gave ROS2 a serious look.
That was two years ago. I haven’t written custom inter-process communication for a robot since.
What ROS2 Gets Right
The DDS transport is not ROS1’s XMLRPC. In ROS1, the master node was a single point of failure and the networking was fragile across subnets. ROS2 uses DDS under the hood, which handles discovery, reliability guarantees, and QoS profiles properly. It’s not perfect, but it’s a solved problem.
The tool ecosystem is genuinely good. ros2 bag for recording, rviz2 for visualization, ros2 doctor for diagnosing your setup — these tools save hours. Writing equivalent debugging infrastructure from scratch is not something you want to do when you’re trying to debug a control loop.
The type system forces you to think about interfaces. Defining a custom message type feels like overhead until you’re three months into a project and need to change how two nodes communicate. The interface files become your documentation.
What You Have to Accept
ROS2 has opinions, and some of them are annoying.
The build system (colcon + ament_cmake) is not CMake. It’s CMake with a layer on top that handles workspace-level dependency resolution. Once you internalize that distinction it’s fine, but it trips everyone up early.
The lifecycle management for nodes is verbose. If you want controlled startup and shutdown ordering — which you do on real hardware — you need to use managed lifecycle nodes. The boilerplate is significant compared to just writing a thread with a loop.
Embedded is a different story. ROS2 doesn’t run on bare metal. For microcontrollers you use micro-ROS, which works well but adds its own complexity. My current setup runs full ROS2 on a companion computer and uses micro-ROS for the STM32 motor controllers.
The Actual Decision Criteria
If you’re building a one-off sensor test or a simple controller that won’t grow, custom code is probably faster.
If you’re building something that has:
- More than two computational components
- A need to log and replay sensor data
- Any expectation of adding capabilities later
…then the upfront cost of ROS2 pays back quickly. The architecture forces you to decompose your system into nodes with clean interfaces, which makes it dramatically easier to swap components, add sensors, and debug problems in isolation.
The honest reason I use it for everything now is that even on small projects, I always end up needing more than I planned for. ROS2’s overhead at the start is a fixed cost. The cost of retrofitting a communication layer into tightly-coupled code is not.