HORUS/blog

Sep 5, 2026 · shared-memory · middleware · ros2 · robot-architecture

Do You Need Shared Memory for Robot Software?

Most robots do not need shared memory. ROS 2 defaults carry you until several programs on one board move camera-sized data. Here is exactly when that flips.

Most robots do not need shared memory: ROS 2 defaults or a single program carry you until copying between processes is what actually hurts. Copying costs nothing you can feel while messages are small and few. The verdict flips when several programs on one machine pass camera frames or point clouds every cycle, which is where a shared-memory middleware such as HORUS begins to matter. The rest of this post is for someone whose robot already works and has started to stutter, trying to work out whether the messaging layer is to blame.

It started as a robot that worked. One board, a few programs, everything arriving on time, and a demo you were happy to show people. Then you added the camera, and the whole thing developed a personality.

Now the arm overshoots by a hair on some runs and not others. The video preview on your laptop is behind reality, which you could live with, except the obstacle check reads that same stream. A core is pinned near the top of the process list and you cannot say which program is holding it there. Someone suggested turning the image resolution down, that helped straight away, and it felt like a diagnosis when it was really a smaller version of the same symptom.

Meanwhile you have read that the messaging layer copies every message, and that there is a way to avoid the copies, and you are four tabs deep in transport configuration written by people who already know what all of it means. What you want to know is simpler than any of those tabs. Is the plumbing the reason your robot got worse, or have you spent a week blaming the plumbing for something else?

Do you need shared memory for robot software?

Not for most robots, and not until the robot itself starts complaining. Shared memory solves one problem: two programs on the same machine passing data big enough that copying it has become part of your control loop. Camera frames, depth images, point clouds, occupancy grids. If everything your programs send each other would fit in a text message, such as joint angles, a velocity command or a battery reading, then copying costs you nothing you could feel through the wheels.

The mistake is treating shared memory as a quality upgrade, the thing a serious robot has and a toy does not. It is a specific tool for a specific bottleneck, and reaching for it before you have that bottleneck costs you setup time, a smaller pool of people who can help, and a class of bug that only appears when the machine is busy.

So the honest first question is not whether shared memory is better in the abstract. It is whether the data moving between your programs is large enough and frequent enough that the copies are what you are feeling. Often the answer is no, and something else is eating your cycle.

What does shared memory actually mean between two programs?

Shared memory means two programs looking at the same region of the machine's memory rather than each keeping a private copy. The usual arrangement is the opposite of that: one program packs its data into a flat form, hands the bundle to the operating system, and the other program unpacks a copy at the far end. The packing and unpacking is called serialisation, and for a small message nobody notices. For a camera frame arriving many times a second, and arriving again separately at three subscribers, it is work done for no gain, because every party ends up holding an identical picture of the same instant.

The shared-memory version is a whiteboard in a room everyone can walk into. The camera program writes the frame once and anything that needs the frame reads it where it lies. The ring buffer is the usual shape: a fixed row of numbered slots, the writer filling the next one each time round, the oldest quietly overwritten, so nothing grows without limit and no writer waits for a slow reader. If publishers, subscribers and topics are new words to you, what middleware actually does in a robot is the better first read.

What does it look like when copying is what is hurting you?

It looks like a robot that got worse on the day you added the camera and behaved perfectly before. That signature is worth knowing, because it rules a great many things out. The control loop that used to hold its rhythm now misses occasionally, and the misses cluster whenever the vision program is busy rather than scattering randomly across the hour. A core sits pinned and the program pinning it is not doing anything you would describe as thinking. Dropping the image resolution helps immediately, which feels like a fix and is really a measurement.

Then there is the giveaway. The number of programs listening to a stream changes how the robot behaves. Open a viewer on your laptop and the arm becomes less precise; close the viewer and the arm settles. That is not superstition and it is not the arm. It means the cost of delivering data is scaling with the number of parties who want that data, which is the exact shape of a copying problem and nothing else.

If your symptom is missing readings rather than late ones, the reasons sensor data goes missing covers a different family of causes worth eliminating first.

What are your actual options for moving data between programs?

There are seven honest options, and several of them are not new middleware at all. The first is to stop having two programs: merge the vision work and the decision work into one process and pass a reference rather than a message. ROS 2 supports precisely this through composition, where nodes that would each have been a process are loaded into one and the messages between them skip the transport. The second is to keep the processes apart and change the transport underneath them, since several DDS implementations ship a same-machine shared-memory path switched on by configuration rather than by rewriting node code.

The third is a middleware built around shared memory from the beginning, which is where HORUS sits alongside ROS 2 as a different answer to the same question: 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 at all, under Apache-2.0, validated in simulation, and carrying no navigation stack of its own. The remaining options are older and blunter: shared memory you write yourself, a broker such as MQTT, or plain files and pipes.

How do these options compare side by side?

Read the last column first. Most people can strike three or four rows immediately, and the argument is usually between the two that survive.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
Merge into one programRobots whose split was never deliberateYour own code and where the boundaries areTwo programs always start and die together anywayParts must be restarted or crash separately
ROS 2 node compositionTeams already on ROS 2 with a hot pair of nodesROS 2 launch files and node lifecyclesThe heavy stream has a single busy consumerConsumers are written in different languages
ROS 2 on a shared-memory DDSTeams who must keep every existing packageYour DDS vendor's configuration filesProcesses must stay separate and stay ROS 2Nobody on the team will own the tuning
HORUSTeams whose loop is late because of same-machine trafficRust, Python or C++, and how your processes splitSeveral programs on one board pass large dataYou need mapping and navigation packages
Shared memory you writePeople with one narrow, well-understood pathMemory layout, locking, and lifetime rulesA single link matters and nothing else doesMore than one team will touch the code
A broker such as MQTTFleets, dashboards, and links off the robotTopics, brokers, and network failure modesData leaves the robot or crosses buildingsThe link is inside one machine
Files and pipesLogging, replay, and offline toolingOrdinary file handlingNothing is on a deadlineThe data feeds a control loop

Are you one person, a small team, or an integrator on a deadline?

If you are one person, the correct move is almost always to merge programs rather than to introduce a transport you now have to maintain. A single maintainer pays the whole cost of every extra moving part and gets none of the benefit of a division of labour, because there is no labour to divide. Combining two programs into one is unglamorous and it removes the problem completely, which is better than moving the problem somewhere subtler.

A team of three to eight is where separate processes start to earn their keep, because separate processes let people work without stepping on each other. That is also where copying costs stop being theoretical, since each person tends to add another subscriber to the busiest stream. This is the size at which a same-machine shared-memory path is genuinely worth an afternoon of investigation.

If you are an integrator working to somebody else's deadline with somebody else's hardware, be conservative. Change the transport under an existing stack before changing the stack, keep the vendor packages you were given, and do not arrive at an acceptance test with a messaging layer nobody in the building has seen before.

What hardware is the robot running on?

Shared memory only helps when the programs live on the same machine, so start by drawing where your programs actually run. A robot with one compute board doing everything is the case where shared memory can help most, because every message is a same-machine message. A robot split between a board on the chassis and a laptop on the trolley has a network link in the middle, and no shared-memory arrangement touches that link.

The number of cores matters more than people expect. On a board with plenty of cores, a copy-heavy stream can hide, because the copying happens on a core that had nothing better to do. On a small board where the vision program and the control loop are already competing, the same copying is immediately visible in the robot's motion.

There is also the split that removes the question. Many robots put the tight motor loop on a microcontroller and leave the Linux board for perception and planning. When the deadline-sensitive part lives on a separate chip, the pressure on the messaging layer drops, and a copying problem that looked urgent becomes something you can leave alone.

How long before this has to work in front of someone?

With a demo inside two weeks, do not change the messaging layer. That is a decision, not a cop-out. Turn the resolution down, reduce the publishing rate on the stream everyone is subscribed to, close the viewer during the run, and pin the busy programs to different cores. Those changes are reversible in minutes and none of them can break the demo the night before.

With a couple of months, investigate properly and in order. Prove the copies are the problem, try the cheapest fix that could work, then the next cheapest. Most teams discover something surprising in the first week, and the surprise is usually not the transport.

With a horizon of a year or more, decide by where the robot is going rather than by where it is now. A robot that will grow more sensors, more consumers of the same stream, and tighter timing is a robot whose same-machine traffic only gets heavier. That is the case where changing the foundation early is cheaper than changing it later, and why projects stall after the prototype is worth reading before you commit a year to anything.

What does your team already know how to do?

If your team lives in Python and has never debugged a process that shares memory with another process, that gap is the most important input to this decision. Shared memory brings a specific kind of bug: nothing crashes, nothing logs an error, and occasionally a reader sees a slot that has already moved on. Finding that requires people who think in terms of lifetimes and ordering, and a team without that habit will spend longer learning it than the problem is worth.

If your team is comfortable in C++ or Rust, the picture changes. The concepts are familiar and the tooling is not exotic, so the honest cost is configuration and testing rather than education.

Mixed-language teams have the sharpest version of the question. A Python perception script and a C++ controller normally meet at a translation boundary, and every large message crossing that boundary is converted twice. A middleware where both languages read the same buffers removes the conversion rather than speeding it up, and removing work is the only kind of improvement that never has a catch.

What do you give up by moving to shared memory?

You give up the easy answer to "how do I run this on two machines", because shared memory ends at the edge of one computer. Anything crossing to a second board, a laptop, or a server still needs a network transport, so you end up maintaining two ways of moving data rather than one. That is a real cost and it never goes away.

You give up some tooling. The recording, replaying and inspecting tools that grew up around the standard stacks assume messages pass through the standard path, and messages that never leave a shared buffer can be harder to observe with those tools.

You give up a certain amount of forgiveness. A copy is isolation: if a subscriber corrupts its own copy, the corruption stops there. Shared buffers hand several programs a view of the same bytes, which means a mistake in one place can be felt in another, and the discipline that prevents this has to live in your team rather than in the design.

And you give up the crowd. When the question is unusual, fewer people have answered it before you.

When is ROS 2 the better choice?

ROS 2 is the better choice for most robots being built right now, and HORUS is not the answer for a project whose real bottleneck is everything except messaging. If you need mapping, autonomous navigation, arm motion planning, or a way to see what the robot believes about the world, use ROS 2 and use the packages, because those packages are decades of accumulated work that nobody sensible rewrites.

Use ROS 2 when your sensors ship with vendor drivers written for it, when your robot spans more than one machine, when you are hiring and want candidates who know the vocabulary, and when your team is large enough that a shared standard beats any individual technical win. Use ROS 2 when the messages between your programs are small, which is most robots, because then the copying was never costing you anything.

And use ROS 2 when you have not yet proven where your time is going. Changing the foundation on a hunch is how teams spend a quarter and end up with the same stutter, a different set of tools, and less patience left over. The fuller comparison of the two walks through the cases in more detail.

Is the transport always the reason a robot is late?

No, and here is why: in most robots that stutter, the transport is not the largest thing in the loop. Perception is usually the largest thing. A vision model, a filter over a point cloud, or an image conversion nobody meant to leave in the pipeline will each cost far more than the delivery of the message that carried the image. Teams reach for the transport because it is the part they read about most recently, not because the evidence pointed there.

There is a second common cause that has nothing to do with either. The operating system is free to interrupt your control loop whenever it likes, and a program with no scheduling priority will occasionally be set aside while something unimportant runs. That produces exactly the same symptom, an occasional missed cycle, and no transport change fixes it. What real-time actually means for a control loop is the honest treatment of that.

The practical rule is to prove where the time goes before moving anything. Switch off one consumer at a time and watch the robot. Whichever change makes the stutter disappear is your answer, and it is often not the one you expected.

Does shared memory mean nothing is ever copied again?

Partly, but not the way you think: the copies that vanish are the ones between processes on one machine, and every other copy in your system stays exactly where it is. The camera driver still writes the frame into memory. Your image conversion still makes a new buffer. The moment a program hands data to a model, a library or a language runtime with opinions of its own, another copy quietly appears. Removing the transport copy is a genuine improvement and it is not the whole picture.

The word people use for this is zero-copy, and the term promises more than any system delivers, because it describes one link rather than the whole chain. What zero-copy messaging really means is worth reading before you make a decision on the strength of the phrase alone.

There is also a copy you should keep. If two programs need to work on the same data at different rates, one of them holding a private copy is not waste; it is the thing that stops the slower program from seeing data change underneath it. Shared memory gives you the choice, not the obligation.

How do you decide whether this is your problem?

Decide by finding the busiest stream on your robot and counting how many programs read it. Then answer three questions in order. Is the data large, meaning images and clouds rather than numbers and strings? Is the rate high, meaning every cycle rather than every second? Do several programs want the same item at the same time? Three yeses and the copies are worth attacking. Two or fewer and you are about to fix the wrong thing.

If you get three yeses, do the cheap experiments first. Close every viewer and see whether the robot settles. Merge two programs that always run together and see whether the stutter goes. Those experiments take an afternoon and they either prove the case or save you a month.

Only when the cheap experiments confirm the diagnosis is it worth changing the foundation, and at that point the wider question is not shared memory but what you build on. How to choose a robotics framework in 2026 sets out that decision without pretending it is a technical one.

Here is the whole decision in five lines.

The HORUS Fit Framework compresses this onto five axes you can score any option against: ecosystem size, setup effort, team size fit, deployment target, and licence. Here, deployment target does most of the work, since a one-board robot and a two-machine robot have different answers.

When your robot does grow into a same-machine traffic problem, the useful thing is having the option on your shelf rather than starting a search under deadline. Put HORUS on that shelf now: star it so it is in your list when you start building.

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