HORUS/blog

Sep 5, 2026 · robot-software-stack · getting-started · robotics-middleware · beginners

What Is a Robot Software Stack? Layer by Layer

A robot software stack is every layer from firmware up to behaviour, and you write far less of it than you fear. Here is each layer and who supplies it.

A robot software stack is every layer between your idea and the motors, and most layers come from ROS 2, HORUS or a vendor SDK. You write almost none of it, because the layers under your behaviour code — drivers, message passing, the operating system — already exist and are the same on every robot. That flips when your hardware is unusual enough that nobody has written its driver yet. The rest of this post is for someone who has read the word "stack" in ten tutorials and still cannot say which part they are supposed to build.

You bought a small computer, a camera and two motors, and the first tutorial worked. Then you tried to change one thing, and discovered you have no idea what you are standing inside of. There are nodes and topics and packages and launch files and drivers and firmware, and nobody says which of those is a program you run, which is a library you import, and which is just a word people use.

So you ask, and the answers make it worse. One person says to write a node. Another says the node is fine but your workspace is wrong. Someone mentions that your motor board has its own firmware, which you did not know was software at all, and now you are unsure whether the thing that failed was yours.

You start to suspect that experienced people carry a picture in their heads that nobody has drawn for you: where each piece sits and what talks to what. Without it, an error could be coming from anywhere, so you change lines at random and occasionally it works, which is worse than failing, because now you cannot say why.

Do you need to understand every layer of the stack before you build a robot?

No. You need to understand the layer you are writing and the layer directly underneath it, and you need to know the names of the rest so you can tell where a problem lives. A robot has roughly five layers, and a beginner writes in one of them — the top, where the machine decides what to do next. Everything below already exists in some form, written by chip vendors, framework maintainers and whoever made your motor driver board. Trying to learn all five before you build anything is the most common way a first robot never gets built. What you do need is the map. When your arm twitches, the map tells you whether to suspect your own logic, the plumbing under it, the driver, or the controller's firmware. Without the map, every failure feels equally likely to live anywhere, so you change things at random and hope. That is a debugging cost, not a building cost, and it is the real reason to learn the layers early.

What is a robot software stack in plain terms?

A robot software stack is the ordered set of programs standing between a decision and a turning motor, arranged so each one only has to talk to its neighbours. Start at the bottom. Firmware runs on the small chips inside your motor controller and your sensors, and it usually arrived with the hardware. Above that sit drivers, which is the code on your main computer that speaks each device's particular dialect and turns it into numbers your program can read. Above the drivers sits the plumbing that moves those numbers between programs, the part people call middleware; what middleware actually does in a robot is worth ten minutes if that word has stayed fuzzy. Above the plumbing sits the code that keeps time, comparing where the arm is against where it should be and correcting the difference. On top sits behaviour: go to the kitchen, look for the cup, give up after a while. Beside all of it, not on top, sit the tools you use to see what happened — logging, a viewer, a simulator. Every robot has this shape, whether or not anybody named it.

What are the real options for the middle of the stack, and who is each one for?

The middle of the stack is the only part you genuinely choose, and there are about seven common answers. ROS 2 is the default one: a large collection of message definitions, drivers, mapping and motion planning, plus the plumbing connecting them, and it assumes you will learn its workspaces and launch conventions before much moves. HORUS is an open-source real-time middleware for Rust, Python and C++ under Apache-2.0, where the three languages share the same shared-memory ring buffers so that programs on one machine exchange messages without serialising them, which starts to matter once a camera frame has to reach three programs at once. A vendor SDK is the stack that came in the box with a finished arm. And you can skip the middle entirely, writing one program that reads sensors and writes motors, which is the right answer far more often than forum threads admit. Read the table as descriptions of situations rather than as a ranking, and notice how many rows are decided by your hardware rather than your taste.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
One program, no middlewareBeginners with one computer and a few devicesPython or C++ basics and how to read a sensorYou want the robot moving this weekTwo programs already need the same camera frame
ROS 2Builders who need maps, navigation and borrowed driversLinux, workspaces, launch files, package layoutExisting packages are most of your robotYou must show movement before you can learn tooling
HORUSMixed-language builders whose programs share one machineYour message shapes and how your loops are scheduledPython, C++ and Rust parts must pass data on one boxYour robot's value lives in ROS 2 packages
Vendor SDK onlyOwners of a finished arm or mobile baseThe vendor's API in the one documented languageYou want the machine to do its documented jobYou must add hardware the vendor never planned for
Microcontroller firmware onlyBuilders whose robot is a board, motors and a batteryPins, timers and how to flash a chipThere is no computer, so there is no middleYou want cameras, maps or recorded logs
MQTT plus your own glueBuilders with devices spread around a room or a buildingBrokers, topics, and what happens when a message is lostYour machine is more sensor network than control loopA loop has to keep time on every single cycle
Simulator first, hardware laterLearners with no robot on the desk yetA little Python and patience with installersYou want to meet the layers before buying partsYou need to feel real motors and real wiring

What stack fits one person building a robot at home?

The smallest stack that moves the robot, which usually means one program, the vendor's library, and nothing in the middle at all. A solo builder's scarce resource is evenings, and every adopted layer costs evenings twice: once to configure it, and again each time something breaks and you must work out whether the fault is yours or the layer's. With one program you always know. The sensor reading is a variable, the motor command is a function call, and when the wheel does not turn there are only three places to look. That arrangement carries a surprising distance — a driving robot, an arm that picks one object, a camera that triggers a behaviour. The signal that you have outgrown it is specific and easy to recognise: you want two things happening at once that do not run at the same pace, such as a camera model that thinks slowly while the wheels need steering constantly. At that point you are not adding a framework for its own sake, you are adding the one layer that lets two programs share data. Add it then, not before.

What stack fits a Raspberry Pi, a Jetson, or a bare microcontroller?

The board decides most of this for you, which is a relief rather than a limitation. A microcontroller has no operating system, so there is no middle layer to choose: you write firmware, it runs forever in a loop, and the whole stack is two layers deep. A Raspberry Pi runs full Linux and can host anything, but memory and heat are real, so the useful instinct is to keep the layer count low and the number of running programs small. A Jetson exists because of cameras and models, and that changes the calculus: once several programs want the same frames, the plumbing layer stops being optional and starts being the thing that decides whether your behaviour code sees fresh data or stale data. Two boards is a third case, and a heavier one — the moment your stack spans machines, everything that was a function call becomes a message that can arrive late or not at all. Whether your robot should run one computer or several is worth settling before you design around it.

What stack fits a deadline a month away?

Whatever the demo needs and not one layer more, chosen for how quickly you can see the robot move. A month is not enough time to learn a framework and invent your behaviour at the same time, and teams that try usually spend three weeks on tooling and one panicked week on the actual robot. So work backwards from the demo. If it must drive across a room and avoid a chair, you need drivers, something that keeps the wheels honest, and a behaviour, and you can hard-code far more than feels respectable. If it must pick something up, you need the arm vendor's SDK and very little else. The layers you skip under deadline are the ones that pay off later rather than sooner: message plumbing, recorded logs, a simulator, a clean split between perception and control. Skipping them is a debt, not a sin, and it comes due the first week after the demo when somebody asks for a second feature. Note the debt somewhere. Then, if the project continues, pay it before adding the feature rather than after.

What stack fits someone who has never written a driver or a control loop?

A stack where somebody has already written both, so your first weeks are spent on behaviour rather than on the parts that are hardest to debug blind. Drivers and control loops are the two layers that punish inexperience most, because both fail quietly. A driver that reads a sensor slightly wrong gives you numbers that look plausible and are not, and a control loop written by feel oscillates in ways that seem mechanical rather than mathematical. Buying hardware that ships with a supported driver, or picking a framework whose community has already written one for your device, removes both landmines from your first project. What you should write is the top layer, where mistakes are visible: the robot goes the wrong way, and you can see it. Do add one habit early, though, which is logging what your program believed at the moment it decided. It costs an afternoon and it is the difference between debugging a robot and guessing at one, and what a control loop is and why its timing matters makes the second layer far less mysterious when you eventually meet it.

Which layer will you actually end up writing yourself?

Two layers, nearly always: the behaviour on top, and a thin strip of glue where your hardware refuses to match anybody's assumptions. The behaviour layer is the reason your robot is not the same as everybody else's, so it is the layer nobody can hand you. The glue is less glamorous and takes more time than anyone plans for. It is the code that converts the units your motor board reports into the units your control code expects, the code that notices a sensor has stopped answering, the code that decides what a gripper does when a reading goes missing. Every robot grows a pile of that, and beginners consistently underestimate it because tutorials never show it. What you will not write, unless something unusual has happened, is firmware, drivers or the message plumbing — those are borrowed, and borrowing them is normal engineering rather than cheating. If that glue layer later starts hesitating in ways your logic cannot explain, what C++ gives you in robotics that Python cannot is the next question to work through.

What does it look like when the stack is wrong for the robot?

It looks like effort landing somewhere other than the robot. The clearest symptom is a calendar: weeks of work with nothing new that moves, because the time went into build configuration, package layout, version mismatches and an error message about a workspace. A second symptom is that adding one sensor requires touching four places and you can never remember all four. A third is blindness — the robot did something odd, you have no record of what it believed at the time, and your only tool is to run it again and watch harder. There is an opposite failure, and it is quieter. The stack is too thin, so the robot works until two things happen at once. The camera code runs long, the wheels keep their last command a moment too long, and the machine drifts into the table leg. Nothing crashed and no error appeared, so you conclude your logic is wrong and rewrite it, which does not help, because the fault was that two parts of your robot were sharing one thread of attention.

What do you give up by adopting somebody else's stack?

You give up knowing exactly what happens between your code and the motor, and that trade is usually worth making until the day it is not. Borrowed layers come with borrowed assumptions: about how often data arrives, about what happens when a message is dropped, about which thread your callback runs on. Those assumptions are invisible while everything works and become the whole problem when something is late. You also give up some control over your own upgrade schedule, since a framework's release cadence is now partly your release cadence, and a device driver maintained by one volunteer is a dependency with a human bottleneck; how to tell a healthy open-source robotics project from a dead one is a useful habit before you commit. And you give up a certain amount of understanding, which is the cost people notice last. A builder who assembled everything from packages can produce a working robot without being able to explain why it works, and that gap shows up on the first bad day rather than the first good one.

When is ROS 2 the better choice?

ROS 2 is the better choice whenever the code other people have already written is the point of your robot. If you need a mobile base that maps a building and navigates it, ROS 2 hands you that; writing it yourself is a multi-year project and a bad use of your life. If you need an arm to plan a path around an obstacle, the motion planning ecosystem around ROS 2 is where that work lives. If your sensors ship with ROS 2 drivers, if your team already speaks it, if your funder or your reviewers expect it, or if you want new hires who can be useful in their first fortnight, ROS 2 wins on grounds that have nothing to do with the plumbing underneath. It is also the right answer for anyone learning robotics as a career rather than building one specific machine, because the vocabulary transfers to most jobs. HORUS is not the answer in any of those cases, and a middleware choice cannot substitute for an ecosystem you actually need. Pick the plumbing only when the plumbing is your problem.

Is a robot software stack just an operating system with extra steps?

No, and here is why. An operating system decides which program gets the processor next, hands out memory, and owns the files and the network. It has no opinion at all about what your programs say to each other, which is the entire subject of a robot stack. Linux will happily let your camera program and your motor program run side by side and never exchange a word. Deciding that the motor program should receive every frame, or only the newest one, or should be told when frames stop arriving — none of that is the operating system's business. This confusion is helped along by naming: the name ROS contains the words "operating system" and it is not one, which trips up almost everybody at first, and clearing up that name saves a surprising amount of confusion later. The practical version is this. You still choose Linux, still choose whether the kernel is a real-time one, and still choose a stack on top. Those are separate decisions and they fail in separate ways.

Does picking a stack lock you in for the life of the robot?

Partly, but not the way you think. Swapping the plumbing layer itself is a weekend of unpleasant work, not a rewrite, because the code that reads a sensor and the code that decides what to do are mostly untouched by it. What genuinely locks you in is subtler and grows quietly. The first is message shapes: once forty files agree on what a pose or a detection looks like, those definitions are the thing you cannot casually change. The second is tooling habits, because a team that debugs by replaying recorded logs has built its working practices on top of that recording format. The third is the assumptions your own code makes about when things happen — code written expecting data to arrive on a callback behaves differently when it has to ask for data instead. So the practical defence is not choosing the perfect stack up front. It is keeping your behaviour code free of framework details, so the borrowed parts stay in a layer you can lift out.

How do you decide which stack to start on?

Name the one thing you want the robot to do this month, and let the layers follow from that rather than from any comparison you read. If the answer involves a map, existing sensor drivers or a navigating base, you have chosen ROS 2 and the rest is learning it. If the answer is a finished arm doing its documented job, you have chosen the vendor's SDK. If the answer is a chip and two motors, you have chosen firmware and there is no middle to argue about. If the answer is a camera model plus wheels on one small computer, you are choosing the plumbing layer, and the question worth asking there is how much data has to move and how fresh it has to be when it arrives. Ask one tie-breaker when it stays close: is the hard part of my robot the behaviour I have to invent, or the timing I have to hold? Invented behaviour favours the stack you can change fastest. Held timing favours the stack that does the same thing on every cycle.

Decide by situation rather than by reputation:

When the plumbing decision comes round in earnest, weigh it on the five axes of the HORUS Fit Framework — ecosystem size, setup effort, team size fit, deployment target, and licence — and take the option that loses on the fewest, with no scores and no numbers involved. If your project keeps landing on one machine, with a Python layer, a C++ layer and a growing suspicion that the boundary between them is where your evenings go, star HORUS on GitHub so it is in your list when you start building.

Found this useful? Share it:Discuss on HNShare on X