Sep 5, 2026 · robotics-basics · middleware · getting-started · ros2
What Runs on a Robot Besides Your Own Code?
A robot runs an operating system, drivers, a data layer and a launcher besides your code. ROS 2 supplies most of them; HORUS supplies only the data layer.
Besides your code, a robot runs an operating system, drivers, a data layer and a launcher; ROS 2 supplies most of those, HORUS only one. Most of those layers arrive with the machine or the framework, so the real question is which ones you chose on purpose and which ones you inherited without noticing. That changes the moment a layer starts causing the misbehaviour you are chasing. The rest of this post is for someone building a first robot who keeps meeting words like node, driver and daemon and wants to know what each one is for.
You wrote a program that reads a sensor and turns a motor, and on your laptop it does exactly what you expected. You copy it to the robot and it behaves differently: slower, or fine for a minute and then not. Along the way you were told to install a distribution, source a setup file, add your user to a group so the serial port would open, and run something called a daemon that you did not write and cannot see. A tutorial mentions nodes, a forum post mentions the kernel, the vendor's guide mentions firmware, and you cannot tell whether those are three names for one thing or three separate things you now depend on. When something goes wrong you do not know which of them to blame, so you change your own code, because your own code is the only part you understand. Sometimes that works, which is worse, because it teaches you the wrong lesson. What you want is a map: what is actually running on this machine, which parts you chose, and which parts chose themselves.
Do I actually need to know what runs underneath my own robot code?
You need a rough map of the layers underneath, not expertise in any of them, and the map takes an afternoon to draw. The reason is not curiosity. It is that many problems that look like bugs in your control code are not in your control code at all, and without the map you cannot tell them apart. A motor that jerks might be a mistake in your maths, or it might be a background program that woke up and took the processor for a moment. A sensor that reports nothing might be a wrong topic name, or a driver that never claimed the device because your user lacks permission to open the port. You will spend days on the wrong layer if you cannot name the layers. What you do not need is to read kernel source or tune a scheduler. Know what each layer is responsible for and how to check whether it is doing its job. Depth comes later, and only for the one layer that turns out to be your problem.
What are the layers that sit under a robot program?
Under your program sit four layers you can name in one breath: the operating system, which decides which code gets the processor and owns the hardware; the drivers, which turn a device's raw signals into numbers; the data layer, which carries messages between the separate parts of your robot; and the supervisor, which starts everything in the right order and restarts what dies. Around those sit a few smaller things that surprise people. There is a language runtime, so a Python part carries an interpreter and a memory manager that occasionally pauses to tidy up. There is a clock, and on a robot with more than one computer, something keeping those clocks agreeing. There is logging, which quietly writes to storage and is a common source of pauses nobody expected. And on most robots there is firmware on a microcontroller doing the fast work that never stops, such as counting encoder ticks or cutting power when a limit switch trips. Your code sits on top of all of that and usually gets blamed for all of it.
What are the actual options for the layers I choose myself?
You choose three of them: what supervises your processes, what carries data between them, and what your drivers are written against. The plain option is nothing at all, one program on stock Linux, which is the right answer far more often than beginners are told. The common option is ROS 2, which supplies the supervisor, the data layer and a large collection of drivers as a single package, and which is what most of robotics assumes you are using. A vendor stack is the option when you bought a complete robot: the manufacturer supplies all three layers and expects you not to look inside. A message broker such as MQTT is the option when your robot is really several devices reporting to a server. A shared-memory middleware such as HORUS, open source under Apache-2.0 like ROS 2, replaces only the data layer for processes on a single machine and leaves the rest to you. And underneath any of them you may choose a real-time operating system rather than stock Linux, which is a separate decision covered in do you need a real-time operating system.
| Option | Who it is for | What it assumes you know | When to pick it | When not to |
|---|---|---|---|---|
| One program on stock Linux | First robots, one sensor, one motor | Basic Python or C++ | Everything can run at one rate | Parts must run at different rates |
| Microcontroller firmware only | Rovers, simple arms, no camera | Wiring and a vendor toolchain | The whole job fits on the board | You need vision or planning |
| ROS 2 | Almost everyone starting out | Command line, build tools, patience | You want drivers and tools ready | The whole robot is one program |
| Vendor robot stack | Teams who bought a finished machine | The vendor's own idioms | You ship that hardware and no other | You expect to swap hardware later |
| Broker such as MQTT | Fleets of devices reporting to a server | Networking and brokers | Parts sit across a network | All parts share one board |
| HORUS | Teams whose parts share one machine | Rust, Python or C++, and processes | Local handoffs must land inside a cycle | You need a full driver ecosystem |
| Real-time operating system | Machines that must never pause | Scheduling and priorities | A missed deadline is a safety event | A missed deadline is an annoyance |
What happens between switching on the robot and my code running?
Six things happen before your first line runs, and each one can fail in a way that looks like your bug. The board's firmware wakes the hardware and finds something to boot. The kernel loads and takes ownership of the processor and memory. Drivers attach to devices as the kernel discovers them, which is why a camera plugged in after boot sometimes appears under a different name. A service manager starts background programs: networking, time keeping, logging, and whatever the vendor added. Only then does your launcher start the parts of your robot, in whatever order it was told, which is rarely the order you assumed. Finally your own code runs and immediately tries to talk to a device or another part that may not exist yet. That last gap is the single most common beginner failure: the part that publishes started after the part that subscribes, so the first messages went nowhere and the robot sat still. Knowing the sequence turns that from a mystery into a thing you check.
What does it look like when a layer underneath is the real problem?
It looks like a fault that moves when you change something unrelated to it. The signs are consistent: the robot is fine until you open a viewer or start recording, then it hitches. It runs well for a while and then stumbles once, and never at the same place. It works when you launch the parts by hand in separate terminals and fails when the launcher starts them together. It works on your laptop, and on the robot the same code arrives late. A sensor drops out only when the wireless link is busy. Every one of those points below your code. Faults in your own logic are usually reproducible and usually wrong in the same direction every time; faults in the layers underneath are usually about timing and usually intermittent. If your symptom is specifically that readings go missing under load, that pattern has its own causes and its own fixes, described in why a robot drops sensor data. The rule of thumb: reproducible means look up, intermittent means look down.
Which layers matter if I am a student or a hobbyist working alone?
Two of them matter to you and the rest can wait: drivers and the launcher. Drivers matter because the fastest way to lose a month alone is to buy a sensor nobody else uses and discover you must write its driver yourself, from a datasheet, in a language you are still learning. Check for an existing driver before you buy the part, every time. The launcher matters because starting five programs by hand in five terminals in the right order is fine on day one and unbearable by day ten, and because many of your early failures will be start-order problems that a launcher makes visible. The operating system does not matter to you yet; stock Linux on a common board is correct and you should not tune anything. The data layer does not matter to you yet either, because whatever the framework gives you will carry a hobby robot's traffic without complaint. Learn those two, ignore the rest until something forces your hand, and resist the urge to build a machine you can describe rather than one that moves.
What runs underneath on a small single-board computer compared with a full desktop?
The same layers run on both, but a small board has no spare room to hide the cost of any of them. On a desktop, a background service that wakes up and does a burst of work is invisible, because there are cores to spare and your control loop keeps its own. On a single-board computer the same burst lands on the processor your control loop is using, and your loop arrives late. That is why code that behaves on a laptop stumbles on the robot with no change to the code at all. Small boards also make the copying of messages between processes visible in a way desktops do not, because memory bandwidth and cache are the scarce goods. Practically: on a small board, turn off the background services you do not need, do not log at full detail to the same storage the system boots from, and be suspicious of anything that touches the network or the disk inside a control loop. On a bigger machine, spend your attention on tooling instead, because the machine will absorb your mistakes.
Does a deadline next month change which layers I should learn?
A deadline next month means you learn drivers and nothing else. When time is short, the failures that will actually stop you are physical and specific: the motor controller expects a different wiring order, the camera needs a permission you do not have, the arm's SDK only builds against one version of a library. None of those are architecture problems, and none of them get better by understanding the scheduler. Copy the working example that matches your exact hardware, keep everything in one program if you possibly can, and accept that what you build will be thrown away. For a project measured in months rather than weeks, the launcher and the data layer become worth real thought, because by then you have several parts and start-order and message-drop problems arrive on schedule. For a project measured in years, or one somebody else will maintain, the layers underneath deserve deliberate choices, including the licence of every piece you depend on, which is the sort of question that is cheap to answer early and expensive to answer late.
What if I have only ever written single-file Python scripts?
Then keep writing single-file Python, and let something else own every layer underneath until your robot forces the issue. A first robot written as one script that reads a sensor, decides, and drives a motor is not a beginner's compromise; it is the correct design for that robot, and it removes every category of problem this post describes. The moment to change is specific and you will recognise it: one part of the robot needs to run at its own pace, and forcing it into the same loop makes everything else wait for the slowest step. That is when you split into separate parts and inherit a data layer, a launcher, and the question of what happens when one part dies. Do not go there early to look professional. When you do go there, take the framework that has already made those decisions rather than writing your own, because the interesting bugs in hand-rolled message passing show up once a week under load and take years of experience to find. Ceremony you can skip is shown in a robot node in eight lines of Python.
What do I give up by letting one framework supply every layer?
You give up the ability to see through the stack, and you pay for it during debugging. When one framework supplies the supervisor, the data layer and the drivers, a failure anywhere in it presents to you as the same shrug: something did not appear. You cannot easily tell whether a message was never published, was published to a name nothing listens to, was dropped by a policy you did not set, or arrived while the receiving part was still starting. You also inherit the framework's build system, its directory conventions, its release cadence and its opinion about how your project should be laid out, none of which you chose and all of which shape your repository. And you take on its assumptions about your machine, which are usually generous, because frameworks are developed on desktops. None of that is an argument against frameworks; the alternative is writing four layers yourself and giving up drivers, which is a far worse trade for almost everyone. It is an argument for knowing the names of the layers so the shrug becomes a question you can ask.
When is ROS 2 the better choice?
ROS 2 is the better choice for most robots, and especially for anyone still learning what the layers are. If you need a lidar driver, a mapping stack, a simulator someone has already wired up, or a viewer that shows what the robot believes it sees, ROS 2 hands those over on day one and no alternative is close. If your robot spans two computers joined by a network, ROS 2 is built for that and shared memory is not, because shared memory does not leave the machine it lives on. If you want to hire people or ask a question and get an answer the same day, ROS 2 is the common vocabulary. HORUS is not the answer in any of those cases and does not claim to be, because a shared-memory data layer for one machine is a narrow tool rather than a substitute for a framework with a decade of drivers behind it. The honest position: start with ROS 2, learn what each layer does while it carries you, and only replace a layer once you can name what it is costing you.
Is everything running under my code just bloat I could strip out?
No, and here is why: nearly all of it is doing work you would otherwise have to do yourself, badly. The background services look like waste until you remove them and find that your clock drifts, your logs disappear on reboot, your wireless does not come back after a dropout, and the camera no longer gets a device name. The framework layers look like waste until you write your own and meet the problems they solve: what happens when a reader is slower than a writer, what a reader sees while a writer is halfway through, how a part that starts late finds the others, what cleans up when a process dies holding something. There is real waste, and it is worth removing, but it is specific rather than general: a desktop environment on a headless robot, a package manager updating itself over the wireless link, a logging setting that writes everything at full detail to a memory card. Strip those with confidence. Stripping the layers you have not yet understood usually produces a robot that fails in a new way you cannot name.
Does the operating system decide whether my robot misses its deadlines?
Partly, but not the way you think. A stock operating system will occasionally let something else run while your control loop is waiting, and yes, a real-time kernel narrows that window. But most missed deadlines on a first robot have nothing to do with the kernel. They come from your own code doing something slow inside a loop that should have stayed quick: reading a file, waiting on a network reply, allocating memory in a hurry, or logging at full detail. They come from a part that produces data slower than the part consuming it expects, so the consumer works from stale readings. They come from a lock held across an operation that touches storage. Swapping the kernel does not fix any of those and does add its own configuration burden. The right order is to find where your time is actually going, remove the slow work from the fast path, and only then ask whether the operating system is the remaining obstacle. The question is worth asking eventually, and do you need a real-time operating system covers what an honest answer looks like.
How do I work out which layer my problem is in?
Ask whether the fault is reproducible, then follow the answer down or up. A fault that happens every run at the same point is in your code, and reading about schedulers will not help. A fault that comes and goes, or that changes when you start an unrelated program, sits underneath, and there is a cheap sequence for narrowing it. Run the parts by hand instead of through the launcher; if the problem disappears, it is start order. Watch whether the fault follows load; if it appears when the machine is busy, it is scheduling or a slow operation on the fast path. Check whether the data even arrives; if a value never appears at all, it is naming, permissions or a driver, not timing. Remove the network from the picture and see if the symptom stays. Each takes minutes and eliminates a whole layer. The mistake to avoid is changing two things at once, because then you learn nothing and still have the fault. If the handoff between local parts is where the time goes, the mixed-language version of that question is worth reading too: one language or several in a robot.
Where that leaves you, in rough order of how common the situation is:
- If your robot is one program on one board -> stock Linux and nothing else, because you have no handoff to manage.
- If you are starting your first multi-part robot -> ROS 2, because the drivers and the launcher are what you cannot write yourself.
- If your parts sit on separate computers -> ROS 2 or a broker, because shared memory does not cross machines.
- If processes on one machine must trade data inside a control cycle -> a shared-memory data layer, because copying is what you are deleting.
- If a missed deadline would hurt somebody -> a real-time operating system, because that layer decides who waits.
When you compare candidates for any layer, the HORUS Fit Framework keeps the comparison honest along five axes with no numbers in them: ecosystem size, setup effort, team size fit, deployment target, and licence. A layer that wins four and loses the one that matters to you is still wrong.
If the local handoff is the layer you recognise as your problem, put HORUS on your shortlist before you need it: star it so it is in your list when you start building, rather than half-remembered on the day you are already late.