Sep 5, 2026 · robotics-stack · team-workflow · robotics-middleware · choosing-tools
8 Signs Your Robotics Stack Is Holding You Back
Most of these eight signs point at a design problem, not a tooling one. Only two justify replacing your middleware, and here is how to tell them apart.
Most of these eight signs mean your timing is wrong, not your tooling, and only two justify leaving ROS 2 for HORUS. The signs that matter are the ones about data crossing a language or process boundary; the rest are architecture debt that follows you to any stack. The verdict flips when your team burns more weeks on plumbing than on behaviour. The rest of this post is for a small team that has shipped something working, feels the stack fighting back, and wants to know which fight is worth having.
The robot works. That is the confusing part. It picks the thing up, it drives the route, and the video you posted got a good response. But the last four weeks produced nothing you can show anybody. One week went into a race that only appears when the camera is warm. One went into making two programs agree about which frame a detection belonged to. One went into a build that broke for a reason nobody has written down. One went into a new sensor that should have been an afternoon.
Meanwhile the list of things the robot cannot do yet has not moved. Every new feature seems to cost more than the last one, and the cost is never in the feature. It is in the wiring around it. Someone says the word rewrite in a meeting and the room goes quiet, because nobody can tell whether the pain is the tooling, the design, or simply what this stage feels like. That uncertainty is worse than the bugs. You can fix a bug.
How can you tell if your robotics stack is holding you back?
You can tell by where your week went: if most of it went into moving data around rather than into what the robot does, the stack is a suspect. Eight signs show up again and again. One, adding a new sensor takes longer than the sensor took to arrive. Two, someone maintains a document describing the order programs must be started in. Three, the arm hesitates whenever the vision code is busy and nobody can say why. Four, a Python part and a C++ part exchange data through a file, a socket somebody wrote, or a copy everyone agrees is temporary. Five, the log shows a position the robot could not physically have reached. Six, a new hire needs a fortnight before they can change anything safely. Seven, you added a sleep somewhere and it helped. Eight, the demo works and the same code fails on the third run in the lab. Each sign has a different cause, and only some of them are about the software underneath you.
What is a robotics stack, in plain terms?
A robotics stack is the set of choices about how the parts of your robot find each other, hand over data and start up, plus everything you inherited along with those choices. At the bottom sits the transport: the thing that carries a message from the program that owns the camera to the program that owns the wheels. Above that sit the conventions: how messages are described, how programs are named, how they are launched, what happens when one is missing. Above that sit the tools you did not write, such as drivers, mapping, planners, a visualiser and a recorder. People say stack when they mean any of those three layers, which is why stack arguments go badly. A team complaining that their stack is slow usually means one program is late. A team complaining that their stack is heavy usually means the vocabulary is large. A team complaining that their stack is fragile usually means start-up order matters and nothing enforces it. Naming the layer you are unhappy with turns an unwinnable argument into an ordinary engineering decision.
What are your real options when the stack is the problem?
There are six honest options and only one of them is a rewrite. You can stay where you are and fix the design, which is right more often than the internet admits, since most timing pain comes from what lives in which process rather than from the transport underneath. You can keep ROS 2 for the ecosystem and pull the timing-critical parts into a single process so nothing crosses a boundary on the hot path. You can move the message layer to a smaller middleware such as HORUS, an open-source real-time robotics middleware for Rust, Python and C++ where all three languages share the same shared-memory ring buffers, so messages between processes on one machine are not serialised, and keep the larger ecosystem at the edges. You can write your own transport, which starts well and ages badly. You can collapse into a vendor SDK and one program, which suits a robot with a fixed job. A general message broker sits at the edge of the list: right for telemetry, wrong for a control loop. Read the table as descriptions of teams, not as a ranking.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| Keep ROS 2, fix the design | Teams whose pain is process boundaries chosen casually | Where your deadlines live and which program is late | The signs point at layout rather than tooling | Language boundaries are the recurring cost |
| ROS 2 with a merged hot path | Teams who need the packages and a steady loop | Your own control code well enough to move it | Borrowed navigation or drivers carry the product | The timing-critical part is itself borrowed code |
| HORUS | Mixed-language teams on one onboard computer | Your message shapes and how your loops are scheduled | Python, C++ and Rust parts share data on one machine | Borrowed packages are the value and would need porting |
| A hand-written transport | Teams with an unusual constraint and time to serve it | Queues, back pressure, and what stale data should do | Nothing off the shelf matches your hardware | You have a product deadline |
| Vendor SDK and one program | Teams whose robot does one documented job | The vendor's API and its supported language | The machine's shipped behaviour is the product | You must mix in hardware the vendor never planned for |
| General message broker | Teams reusing web or fleet infrastructure | Brokers, topics, keeping a service alive | The robot mostly reports and receives commands | A control loop depends on message ordering |
What should a two-person team do about it?
A two-person team should fix the design before changing the tooling, because a migration costs weeks you do not have and design changes cost days. With two people everything is a bottleneck at once: whoever is deep in the perception code is also the only person who understands the start-up sequence. So the cheapest wins are the ones that reduce how much has to be held in one head. Merge the two programs that must never disagree. Delete the process that exists because a tutorial had one. Write down what should happen when a message is late, then make that behaviour explicit in code instead of accidental. If, after all that, the remaining pain is still that a Python part and a C++ part must hand each other data on the same machine every cycle, you have found a genuine tooling problem worth solving properly. The tell is repetition. A design problem hurts once and stays fixed. A tooling problem comes back in the same shape every time you add a part, and two people cannot afford to solve the same thing three times.
What if the whole robot runs on one small computer?
On a single small onboard computer the stack matters more than it does on a workstation, because everything competes for the same handful of cores. The symptom is characteristic: a program you believed was independent turns out not to be, and the moment perception gets busy the control side misses its rhythm, which the arm expresses as a hesitation visible from across the room. On a big machine there was slack to hide it. On the robot there is none. Two moves help more than switching anything. First, be honest about how many separate programs you actually need, because each one costs memory, costs start-up time, and gives the scheduler another thing to juggle. Second, keep the parts that must hold a steady rhythm out of the same process as the parts that wait on cameras, disks or networks. If both are done and your remaining cost is data being copied and re-encoded as it crosses between languages on that one board, that is the narrow case where the transport itself is the right thing to change.
What if you have a demo in six weeks?
With a demo in six weeks, change nothing structural and spend the time on the demo. A migration is not a six-week job even when it is a two-week job, because the two weeks are followed by a month of small surprises: the recording tool that no longer records, the parameter with a different default, the start-up race that only appears on the robot. Demos are won by narrowing scope, not by improving foundations. Pick the one behaviour the audience will remember and make it work on the third attempt as dependably as on the first. Everything else is set dressing. What you should do during those six weeks is write down every occasion the stack cost you an hour, along with what you were trying to do at the time. That list is the only honest input to the decision you will make afterwards, and it cannot be reconstructed later. Teams that skip the list end up arguing from feelings, and the loudest feeling is always about the most recent bug rather than the most expensive one.
What if nobody on the team has replaced a middleware before?
If nobody has done this before, assume the work is double the estimate and structure it so a failed attempt costs a week rather than a quarter. The part people underestimate is never the message passing; it is everything that grew around it. Launch scripts, recorded logs in a format only the old tools read, the parameter file somebody edits directly on the robot, the test that starts six programs in order. The safe approach is to move one edge of the system first, a single sensor path from driver to consumer, and run it beside the existing one until you trust it. That gives you a measured answer about effort instead of a guess, and it is reversible. It also teaches the team the new failure modes while the robot still works. The unsafe approach is the branch that migrates everything and is nearly done for two months. When the skill is new, the most valuable thing you can buy is the ability to stop halfway without losing anything, and how other teams sequence a switch is worth reading before you start.
What do teams try first, and why does it stop working?
Most teams first add a queue, a thread and a sleep, and the arrangement works for a surprisingly long time. The queue absorbs the burst, the thread keeps the slow work off the fast path, and the sleep hides a race nobody has time to find. What eventually breaks it is not volume; it is the question none of those three answers. What should the control side do with data that is no longer true? A picture describing where the robot used to be is worse than no picture at all, and a queue that faithfully delivers it is faithfully making things worse. The second attempt usually adds timestamps and starts discarding old messages, which is the correct instinct and also the beginning of writing a middleware by accident. Six months later there is a private transport with no documentation and one person who understands it. None of this is foolish, and plenty of working robots were built exactly this way. The problem is that the result cannot be handed to a new hire, and it becomes the first suspect in every bug forever.
What changes as the robot gets more demanding?
As a robot gets more demanding, the cost moves from writing behaviour to keeping behaviour honest under time pressure. Early on, every question is whether the code does the right thing at all. Later, the code does the right thing and the robot still fails, because the right thing arrived late, or arrived twice, or arrived describing a world that had already moved. That shift is why projects outgrow their first framework rather than simply choosing badly at the start. Three pressures tend to arrive together. The robot runs for hours instead of minutes, so slow leaks and drift become visible. More than one person changes it, so undocumented start-up rules become outages. And the number of parts grows past the point where anyone holds the whole picture, so interfaces between parts have to become real rather than assumed. A stack that was fine for a demonstration is not necessarily wrong; it was answering a different question. The useful move is to notice the question has changed rather than to relitigate the original choice.
What do you give up by moving off the stack you have?
You give up the accumulated knowledge of how your current setup fails, which is worth far more than most teams price it at. Your existing arrangement has a year of scar tissue in it: the flag somebody added after a bad afternoon, the launch order that avoids a race, the workaround for the driver that hangs on a cold boot. None of it is written down and all of it is protection. A migration throws it away and you rediscover the same failures under new names. You also give up tools that quietly did work for you, such as the recorder, the visualiser and the plotting script somebody wrote one weekend, and you give up the ability to search the internet for your error message. Most of all you may give up other people's packages, which for many robots are the reason the project is viable. Weigh all of that against what you actually gain, which is usually one specific thing: a boundary that stops costing you. If you cannot name that boundary in a single sentence, the migration is a mood rather than a plan.
When is ROS 2 the better choice?
ROS 2 is the better choice whenever the value of your robot lives in software you did not write. If mapping and navigation are the product, the packages are the product, and rebuilding them is a multi-year detour nobody is funding. If your sensor's only usable driver ships as a ROS 2 package, that settles the question before any other argument begins. If your system spans more than one computer, a robot and a workstation or a fleet with an operator, ROS 2 was designed for that and is well travelled there. If you hire regularly, shared vocabulary beats a better transport, because an engineer who knows the ecosystem is useful within a week and stays useless for a month on a private architecture. And if your team's real pain is that the design put the wrong things in the wrong processes, changing the middleware moves the pain rather than removing it. HORUS is not the answer in any of those cases, and picking it there trades a working ecosystem for plumbing you would have to rebuild yourself.
Would a faster onboard computer fix this?
No, and here is why: the eight signs describe coordination, and coordination does not get cheaper when the processor gets quicker. A stronger board absorbs the symptom for a while, the hesitation gets shorter and the dropped frames get rarer, and that is genuinely useful when you are close to shipping. But the shape of the problem is untouched. If your control loop can be delayed by a program that decided to write a large file, a quicker disk makes the delay smaller and never makes it impossible. If two programs disagree about which frame a detection belonged to, they disagree exactly as much on better silicon. This matters because hardware upgrades are the easiest thing to approve and the hardest thing to undo: once the robot needs the bigger board it needs it forever, and the bill of materials carries the design mistake into every unit you build. Fix the coordination first, then decide what hardware the fixed system deserves. Doing it the other way round buys silence rather than an answer.
Are these eight signs just what building a robot feels like?
Partly, but not the way you think. Robotics genuinely is harder than the software most people did before, and some friction belongs to the field rather than to your choices: hardware lies, sensors drift, the floor is not flat, and the real world does not replay on demand. Teams who expect a robot project to feel like a web project spend a year being disappointed by physics. But there is a clean line between friction that teaches you something and friction that does not. Debugging why the gripper closes early teaches you about your robot. Debugging why the parameter file on the robot differs from the one in the repository teaches you nothing you will ever reuse. Sort the last month of problems into those two piles. If the teaching pile is bigger, your stack is fine and you are simply doing a hard thing well. If the other pile is bigger and growing, that is not the field being hard, that is your foundation charging rent. All eight signs sit in the second pile, which is why they are worth listing separately.
How do you decide whether to change anything?
Decide by naming the single boundary that keeps costing you, then check whether changing the stack removes it or merely moves it. Walk through the last month of lost hours and label each one design, tooling, hardware, or the field being hard. Design problems stay fixed once fixed. Tooling problems return in the same shape every time you add a part. Hardware problems move when the hardware moves. Field problems teach you something about your robot that you keep. If the tooling pile is small, you have a design job and it is cheaper than you fear. If the tooling pile is dominated by data crossing between languages or processes on one machine, you have a transport job worth doing properly. If the tooling pile is dominated by missing drivers, planners or visualisation, you have an ecosystem job, and the answer is to move towards a larger ecosystem rather than away from one. Most teams find all four piles occupied and one clearly the largest. That largest pile is your actual project, whatever the meeting was about.
Decide by situation rather than by preference:
- If your lost hours are mostly design -> keep your stack, because a migration carries the same mistakes to a new home.
- If a Python part and a C++ part copy data on one machine every cycle -> a shared-memory transport, because that boundary is the recurring bill.
- If you depend on borrowed navigation, planning or drivers -> stay on ROS 2, because the packages are the product.
- If you have a demo in six weeks -> change nothing, because a half-finished migration is the worst place to be on stage.
- If nobody has done a migration before -> move one sensor path first, because a reversible experiment beats a confident plan.
When the choice is close, weigh it on the five axes of the HORUS Fit Framework: ecosystem size, setup effort, team size fit, deployment target, and licence. Take the option that loses on the fewest. No scores, just five honest questions about your situation rather than about the software. If your answers keep landing on one machine, more than one language, and behaviour only your team understands, star HORUS on GitHub so it is in your list when you start building, and read what to run a robot that carries a model on board next.