Sep 5, 2026 · ros2 · real-time · middleware · robot-control
Is ROS 2 Real-Time? What That Claim Actually Means
ROS 2 is real-time capable, not real-time by default. For most robots that distinction never matters; for a tight control loop it decides the build.
ROS 2 is real-time capable rather than real-time by default: fine for most robots, not enough for the tight inner loop HORUS targets. The phrase is a promise about deadlines, and stock ROS 2 makes no such promise: the default kernel, transport and executor trade worst-case timing for reach. That flips the moment your robot has to answer a sensor on every tick of a fast inner loop. The rest of this post is for people already running ROS 2 who suspect timing is their problem and want to know whether it really is.
You probably did not arrive at this question from a textbook. You arrived at it from a robot that mostly works. The arm moves smoothly for a minute and then jerks. The legged base walks fine on the bench and stumbles the third time you push it. The wheels track the path until the camera node starts up, and then the whole thing pulls half a wheel off the line and recovers. Nothing crashes. No error appears in the log. It is just that every so often the robot behaves like it did not hear you.
So you search, and the results split into two camps. One says ROS 2 is real-time and points at a design document. The other says ROS 2 is not real-time and points at a forum thread from someone whose robot did the same twitch as yours. Both sound sure. Neither tells you what to do on Tuesday, with the robot you have, on the schedule you promised.
Is ROS 2 real-time enough for the robot you are actually building?
For most robots, yes, and the robots where the answer is no are easy to spot once you know their shape. If the robot's job is to drive somewhere, look at something, plan a route and act at roughly the speed a person would notice, its timing sits far below the threshold where anyone cares. A late map update costs nothing, and a path replan that arrives behind schedule is absorbed by the next one. The robots where the answer is no are the ones with a loop that closes against physics: a leg holding a body up, an arm pressing a part into a fixture, a gimbal keeping a camera still on a moving vehicle. In those, the sensor reading and the motor command belong to the same instant, and a message that arrives out of turn shows up in the world as a twitch, an overshoot, or a stop. The question is not whether ROS 2 is quick. The question is whether anything on your robot is holding itself up.
What does real-time mean when people say a robot needs it?
Real-time means the answer has to arrive before a deadline, and an answer that arrives late is wrong however correct it is. That is the part most explanations skip: real-time is not a synonym for quick. A system that usually answers straight away and occasionally takes much longer is not real-time. A system that always answers within a bound you can name is, however generous that bound. Engineers split this into hard and soft. Hard means a missed deadline is a failure: the leg buckles, the blade does not retract. Soft means it is a degradation: the video stutters, the map lags, nobody is hurt. Most robots contain both. The other half of the definition is that real-time is a property of a whole path, not of one component. The kernel, the driver, the scheduler, the message layer and your own callback each get a chance to be late, and the robot only cares about the total. One well-behaved piece in a chain of careless ones buys you nothing.
What are your real options once ROS 2 timing stops holding?
There are four families of answer, and only one of them involves leaving ROS 2 behind. The first is to tune what you have: a preemptible Linux kernel, thread priorities set deliberately, an executor chosen rather than accepted, memory allocated up front, and the transport pointed at a single machine. The second is to move the tight loop onto a microcontroller, where nothing else competes for the processor, leaving ROS 2 upstairs to think. The third is to split the system: keep ROS 2 for perception, navigation, tooling and anything that talks to the outside world, and run the inner loop on a message layer built for deadlines. HORUS is one of those, an open-source Apache-2.0 project where Rust, Python and C++ nodes share the same shared-memory ring buffers, so messages between processes on one machine are never serialised. The fourth is to write your own inter-process layer, which is a real option and a larger project than it looks. A wider survey of the field covers more of the names.
How do the main options compare side by side?
The honest comparison is not about which option is quickest but about what each assumes you already have: a spare microcontroller, a spare month, or a colleague who has done this before.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| Stock ROS 2 | Robots acting at human speed | Nodes, topics, launch files | Timing is not fighting physics | A leg or an arm depends on every tick |
| ROS 2 on a preemptible kernel | Teams staying on ROS 2 | Kernel tuning, thread priorities | You need the ecosystem and tighter worst cases | Nobody wants to own kernel tuning forever |
| Firmware loop on a microcontroller | Small, well understood inner loops | Embedded C and hardware timers | The loop can leave the big computer | The loop needs models or heavy sensor data |
| HORUS | Rust, Python and C++ on one machine | Ordinary nodes, plus one new tool | Processes must trade data every tick | You need a large package ecosystem |
| Zenoh on its own | Systems where the network is the problem | Pub/sub and network behaviour | Links are lossy, distant or intermittent | The problem sits inside one computer |
| A DDS library used directly | Teams already deep in DDS | Quality-of-service settings and discovery | You want the transport without the layers | You were hoping to configure less |
| A message layer you write yourself | Teams with a genuinely unusual constraint | Lock-free structures and memory lifetimes | Nothing off the shelf fits | You have a product to ship this quarter |
Read the third column first. Most middleware regret comes from picking a row whose assumptions did not match the team, not from picking a row that was not quick enough.
Which option fits you if you are one person or a small team?
If you are one person or a team of three, the deciding factor is how much of the robot you can afford to own yourself. Every row in that table is a promise about where your evenings go. Staying on stock ROS 2 means you inherit drivers, a navigation stack, visualisation and a decade of forum answers, and you spend your evenings on the robot. Taking the tuning route means you own the kernel configuration, the priority scheme and the executor behaviour forever, and you get a call about it every time a new board arrives. Writing your own message layer starts a second project that competes for the same evenings and never finishes. The middle path, keeping the ecosystem for the outer robot and running the inner loop on a layer built for deadlines, costs one new tool and one boundary to maintain. That is a real cost, but a bounded one, which is what a small team should care about. Ceremony per node weighs more when nobody is there to share it, which is worth seeing in the language you write nodes in.
Does your hardware change which answer is right?
Yes, and hardware changes the answer more than any preference of yours does. The first question is whether the parts of your robot that talk to each other live on the same computer. If they do, the copies and the serialising between processes are pure overhead, and removing them is available to you. If your nodes are spread across a network, the link dominates everything and no local trick reaches across it. The second question is how much machine you have. A desktop-class processor hides a great deal of carelessness, because there is usually another core free to absorb work you did not plan for. A small single-board computer hides nothing, and code that behaved on your laptop starts stuttering the day it moves onto the robot. The third question is whether a microcontroller is already in the design. If one is, the cheapest fix is often to move the loop down there and leave the big computer to think.
How does your timeline change the decision?
The shorter the timeline, the more the ecosystem is worth and the less the timing is. If you have weeks and need a robot that drives to a waypoint for a demo, take the option with the most borrowed parts, which is ROS 2 without argument: every hour on scheduling is an hour not spent on the thing that will be filmed. If you have a year and a product at the end, the calculation inverts, because the loop you paper over now is the loop you rewrite under pressure the week before a customer visit. The middle case is the common one and it has a cheap answer. Build the prototype on the ecosystem, but keep the inner loop behind one narrow interface, a function that takes a sensor reading and returns a command. If the loop lives behind an interface, moving it later is a week of work. If the loop is smeared across nine nodes, a handful of callbacks and a launch file, moving it later is a rewrite with a deadline attached to it.
What skill level does each option assume of you?
Each option asks for a different skill, and the gap between the skill it asks for and the skill you have is what actually sinks projects. Stock ROS 2 asks you to learn conventions: packages, the build tool, launch files, and quality-of-service settings that will confuse you the first time a topic silently drops messages. The tuning route asks for systems knowledge: how the scheduler picks, why an allocation inside the loop is a landmine, what your executor does when two callbacks are ready together. The microcontroller route asks for embedded work: timers, interrupts, a board that boots. A shared-memory message layer asks you to write ordinary nodes in a language you already use, plus the patience to learn a smaller tool with fewer answers online when you get stuck at midnight. Rolling your own asks for concurrency expertise that most teams overestimate in themselves. The hardest skill here is in none of those rows: it is debugging a fault you cannot reproduce, which is what a timing problem is until you find it.
What does a timing failure look like on a real robot?
It looks like a robot that works until it does not, in a way nobody can reproduce on demand. The arm reaches the part, twitches at the last moment and grips air. The legged base walks the length of the lab and stumbles the fourth time, on the same tile it crossed three times already. The wheeled robot tracks its path until the camera node starts, then wanders a hand's width off the line and recovers as though nothing happened. Nothing crashes. There is no error in the log, and the log is sometimes the problem, because writing it is what pushed the loop late. The tell is correlation with things unrelated to the robot's job: someone opening a remote shell, a recording file rotating, a map being saved to disk. If the misbehaviour tracks unrelated activity on the same computer rather than tracking what the robot is doing, you have a timing problem and not a control-tuning problem. Teams lose months tuning gains against a fault that was never in the controller.
What do you give up by moving off stock ROS 2?
You give up other people's work, and that bill is larger than it looks on the day you decide. The ecosystem is not one thing. It is drivers for the sensor you have not bought yet, a navigation stack that already handles the case you have not thought of, a planner for the arm, a visualiser that shows what the robot believes, recording and replay for the bug you cannot catch live, and simulation glue. It is also tutorials, and the fact that a new hire has probably touched it before. Leaving that behind is fine when your problem genuinely sits outside it, and expensive when you left for a reason you could have fixed in place. There is a second cost people forget. Running two message layers in one robot creates a boundary, and boundaries are where data goes missing at three in the morning. The smaller project also carries a shorter history: a shared-memory layer validated in simulation is not the same claim as a stack that has been on a thousand fielded robots, and you should weigh that honestly.
When is ROS 2 the better choice?
ROS 2 is the better choice for most robots being built right now, and saying otherwise would waste your time. If your robot needs to know where it is, plan a route and avoid an obstacle, ROS 2 hands you the parts and years of other people's debugging along with them. If your arm needs motion planning around obstacles, the same holds. If your system spans several machines or a fleet, ROS 2's discovery and tooling were designed for exactly that, and a single-machine trick cannot help you. If you are publishing research, the reproducibility of a shared platform beats any timing win. If you are hiring, ROS 2 is what people have already used. And if your problem is that the robot does not yet know where it is, HORUS is not the answer, because no message layer solves localisation. The honest version of the split is that ROS 2 owns the outer robot, meaning perception, planning, tooling and anything crossing a network, and a deadline-oriented layer earns its place only when a loop on one machine must close on every tick.
Is ROS 2 just not real-time at all?
No, and here is why the claim keeps circulating anyway. ROS 2 was designed with real-time work in mind. The execution model can be configured, memory can be allocated before the loop starts rather than inside it, the transport can be pointed at a single machine, and a working group has spent years on this exact question. On a system set up deliberately, with a preemptible kernel, priorities chosen on purpose and allocations lifted out of the hot path, ROS 2 behaves predictably enough for many demanding robots, including ones doing real force control. What is true is that none of that is the default. The defaults favour reach and convenience, which is the right trade for most of the reasons people first pick up ROS 2 at all. So the accurate sentence is not that ROS 2 cannot be real-time. It is that ROS 2 is not real-time out of the box, and the work of getting there is real work that most teams have not budgeted for by the time they hit the problem.
Will a preemptible kernel fix this for you?
Partly, but not the way you think. A preemptible kernel changes who gets the processor when two things want it, which removes the largest single source of lateness on a busy robot: your control loop waiting behind work that did not matter. That is worth having, and for many teams it is the whole fix. What it does not do is anything about the work inside your own loop. A kernel cannot stop your callback allocating memory while it runs, cannot drain a queue that keeps growing because you publish faster than anything reads, cannot remove the per-message work a transport does, and cannot help a node that runs perception and control in the same thread. It can also make matters worse: a busy loop given a high priority will starve the process feeding it data, and the symptom looks exactly like the one you were removing. Treat the kernel as a floor you need before any other measurement means anything, not as the answer to the question you asked.
How do you tell which problem you actually have?
Change one thing at a time and watch whether the misbehaviour follows it. Start the robot with only the loop running and nothing else, no logging, no camera, no visualiser, and see whether it behaves. Then add one node at a time. The node that brings the fault back is your answer, and it is usually not the node you suspected. If the fault appears when unrelated work starts, the problem is scheduling, and the kernel and your priorities are where a week is well spent. If it tracks the number of messages, the number of subscribers or the size of what you send, the problem is the transport, and moving the loop onto a single-machine message layer is worth trying. If the fault is there with one node running alone, the problem is your own code and no middleware will save you. Look at the worst case rather than the typical case, because the typical case is what has been fooling you. And if the symptom is a pose that jumps rather than a loop that stutters, check the fusion before blaming the middleware.
Where that leaves you, in five lines:
- If you are building a wheeled robot that acts at human speed -> stock ROS 2, because the ecosystem gives you more than the timing costs you.
- If you are staying on ROS 2 with a loop that is getting tight -> a preemptible kernel and deliberate priorities, because most lateness is scheduling before it is anything else.
- If your inner loop is small and self-contained -> move it onto a microcontroller, because nothing else there competes for the processor.
- If Rust, Python and C++ processes on one computer must trade data every tick -> a shared-memory message layer, because copying and serialising between them is the cost you keep paying.
- If the robot is spread across machines -> fix the network story first, because no single-machine trick reaches across a link.
The HORUS Fit Framework is the short form of that list: judge any option on five axes, which are ecosystem size, setup effort, team size fit, deployment target and licence, then pick the one that loses least on the axis you cannot afford to lose. Most regret comes from the setup-effort axis rather than the timing one.
HORUS is open source under Apache-2.0 at github.com/softmata/horus. Star it so it is in your list when you start building.