HORUS/blog

Sep 5, 2026 · robotics-middleware · python · ros-2 · comparison

Robotics Middleware for Python-First Developers: 7 Options Compared

For Python-first developers ROS 2 with rclpy is the default answer; a shared-memory middleware only wins when Python must sit beside a compiled control loop.

For a Python-first developer the default is ROS 2 with rclpy; the real alternatives are plain Python, a message library, or a shared-memory middleware. ROS 2 wins on the catalogue and the tutorials, and Python is a first-class citizen there. The verdict flips when Python must hand data to a compiled control loop on one computer, which is the case HORUS is built for. This post is for developers who already write Python well, are choosing what their robot's messaging sits on, and do not want a rewrite in six months.

The prototype was one file and it worked. Then the camera handling moved into a thread, and the loop that drives the wheels started arriving late whenever a frame took longer than usual. So you split it into processes and put a queue between them, and now every object crossing that queue is being packed and unpacked, and large frames make the whole thing lurch. Then the motor vendor ships a driver that only exists as a C++ library, and you write a small bridge — a socket, a JSON blob, a loop — telling yourself it is temporary. Six weeks later the bridge has grown a reconnect path, a version field and a bug you cannot reproduce. Meanwhile you still cannot see what is flowing while the robot runs, and when something goes wrong on the bench there is no recording to look at afterwards, only a print statement scrolling past. None of this is a Python problem. It is the moment where a program becomes a system, and the tools for systems are different from the tools for programs.

Which middleware should a Python-first developer actually choose?

Choose ROS 2 unless you can say precisely which of its parts you will use, in which case choose the smallest thing that covers those parts. That sounds like a dodge and it is the actual decision rule, because the value of a framework to a Python developer is almost entirely the catalogue: drivers for hardware you own, a mapping and navigation stack, a simulator, and a way to record and replay a run. Python bindings are table stakes now — every option on this page has them — so bindings are not what separates the choices. What separates them is what arrives alongside. If your list of things-you-will-not-write-yourself is long and specific, the ecosystem is worth its upkeep, and rclpy makes it reachable from the language you already know. If your list is short and vague, a framework contributes packaging and charges you maintenance, and something smaller leaves you more evenings for the robot. The tie-breaker is not the language. The language question is already settled: Python is fine, and has been for years.

What does robotics middleware do that plain Python does not?

Middleware moves messages between separate parts of a robot and decides what happens when a part is slow, missing or dead. Plain Python can move data between threads and processes, and does it well, but stops short in four places that all show up in the same month. It has no answer for a component written in another language, so a vendor's C++ driver needs a bridge you write. It has no crash boundary that keeps the wheels stopping when the perception process dies. It has no way to look at what is flowing while the robot runs, which is the difference between debugging and guessing. And it has no recording, so the run that failed at the demo is gone. Middleware also carries a cost nobody advertises: another thing to install, another model to learn, another set of versions to keep aligned. The trade is worth making when the four gaps above are real for your robot, and not before. A plain-English tour of what middleware does is worth reading before comparing any of them.

What are the seven options worth comparing?

Seven options cover the field a Python-first developer actually chooses among, and the differences between them are about what comes bundled rather than about Python support. ROS 2 with rclpy is the full framework: message layer, build system, package catalogue and tooling as a single decision. Plain Python — threads, asyncio and multiprocessing queues — is the honest starting point and the one most projects begin with. HORUS is an open-source real-time middleware where Rust, Python and C++ share the same shared-memory ring buffers on one machine, so a message crossing between those languages is not serialised between processes, which suits a robot where Python glue sits beside a compiled control loop. ZeroMQ through pyzmq gives you sockets and delivery patterns while you define every message contract yourself. Zenoh reaches across machines and unreliable links. LCM offers a message format with logging and replay and little else. MQTT through a broker is built for telemetry to dashboards and fleet servers. Most real robots end up combining two of these, and that is not a failure of the choice.

How do the seven options line up side by side?

Read the last two columns first, because the situation where an option is wrong tells you more than the situation where it is right.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
ROS 2 with rclpyPython developers needing drivers, mapping, simulationLinux, the ROS build tool, nodes and topicsYour robot is a shape the catalogue already coversNobody on the team has time for upkeep
Plain Python with threads and queuesEarly prototypes living in one languageYour own concurrency model and its trapsThe whole robot still fits in one programA second language or a crash boundary appears
HORUSOne-machine robots where Python sits beside Rust or C++Python, and life outside the ROS package setSensing, control and actuation share a computerYou need ROS drivers or the navigation stack
ZeroMQ through pyzmqDevelopers content defining their own contractsSockets, message shapes, process supervisionThe parts are few and the message shapes are stableYou would rebuild recording and introspection
Zenoh with its Python APIRobots spread over machines, sites or bad linksNetworking, and a younger ecosystemConnections are unreliable or cross the internetYou wanted a package catalogue included
LCMTeams wanting a message format and logs, nothing moreType definitions and code generationRecording and replay matter more than toolingYou expected drivers or a build system
MQTT through pahoTelemetry to a dashboard, a phone or a fleet serverTopics, retained messages, running a brokerThe traffic is status and human commandsSomething on the robot waits on the answer

Pairing two rows is the common outcome: something local for the robot's own parts, and a broker carrying status out to whoever is watching.

What should I pick if I am the only developer on the project?

Pick whichever option you could reinstall on a blank machine in one evening, and let that test outrank every feature comparison. A solo project rarely fails because the wrong middleware was chosen; it fails because six months later the machine no longer builds and nobody remembers how the working version came to work. Write the setup down as a script before committing to anything. If the script is twenty lines you understand, the choice is fine for one person. If you cannot write it at all, the choice is already bigger than you can carry. The second test is what happens after three months away: a small stack with a handful of ideas is still in your head, and a framework with a hundred is not, so the first week back goes to relearning the tool rather than the robot. Neither test says which option is better in the abstract. Both predict which one you will still be using next year, which is the question that actually decides small projects.

What should I pick if everything runs on one small computer?

Pick something built for one machine, because a networked framework spends most of its complexity on cases that cannot happen to you. If the camera, the logic and the motor control all live on one board, there is no discovery to perform, no network transport to configure, no remote peer to lose. That describes far more robots than people expect: desk arms, camera-to-motor pipelines, balancing machines, competition robots, most research prototypes. The single question worth asking is whether it stays true. A workstation doing perception, or a base station collecting logs, means you want the networked framework now rather than a migration later. If nothing like that is coming, a single-machine layer removes an entire category of problems that otherwise eat weekends. Where this gets concrete for Python is memory: on one machine, a large image or point cloud handed to another process can be shared rather than copied and rebuilt, which is the point of zero-copy messaging and the difference between a pipeline that keeps up and one that falls behind.

What should I pick if I have a demo in a month?

Keep whatever is already running and freeze it, because a month is not enough time to change foundations and still have a robot at the end. Decisions made under a deadline are the ones that go wrong, and middleware has never been the reason a demo succeeded. If nothing is running yet, choose by what you will not write: a demo that needs mapping and navigation takes the ecosystem that has them, rough edges included, and a demo where one arm picks one object takes the smallest thing that moves messages. Then stop evaluating. The hours spent comparing options after the choice is made are hours the robot does not get. What actually decides demos is elsewhere: whether the machine stops safely when something goes wrong, whether you can restart it in ten seconds in front of an audience, and whether a person watching can tell what it is doing. Spend the month there. Pin every version the day the first run works, and refuse every update offered between then and the demo.

What should I pick if I have never shipped C++ or Rust?

Pick anything on the list, because none of them require you to write a compiled language, and the ones that involve compiled code do so through libraries other people maintain. This worry stops more Python developers than it should. A ROS 2 project can be entirely rclpy nodes, with any C++ arriving as a vendor's driver package that you install rather than read. A shared-memory middleware exposes the same buffers to Python that it exposes to Rust and C++, so a Python process reads what a compiled process wrote without either side writing a bridge. Where the compiled languages do become relevant is narrow and predictable: the innermost control loop of a machine that must not miss its beat, and heavy per-sample processing that Python cannot keep up with. Both are usually one component, not the system. The honest framing is whether a robot should use one language or several, and for most Python-first teams the answer is two, arranged so the boundary between them sits in one obvious place.

What do I give up by staying Python-first?

You give up the tightest control loops and some of the community's first-day attention, and depending on the robot, you may give up neither in practice. The loop question is real but narrow: the layer that must respond on a strict beat, every beat, without a pause for garbage collection, is a poor fit for Python, and on most robots that layer is small enough to live in a compiled component or on a microcontroller. The attention question is subtler. Examples, new features and forum answers often appear on the C++ side first, so a Python-first developer occasionally reads a C++ example and translates it. That is a tax, not a wall. What you actively gain is worth naming: changes take minutes rather than a build cycle, the libraries for vision, filtering and machine learning are already there, and the person joining your project next probably knows Python. The deeper treatment of where the line falls is in whether Python is fast enough for robot control, and the answer is more nuanced than either camp says.

When is ROS 2 the better choice?

ROS 2 is the better choice whenever the catalogue is the reason you are choosing at all, and for Python-first developers that is most of the time. A mobile base that maps a room and drives across it should use the mapping and navigation that already exist, because rewriting them is a year of work with no reward at the end. A team whose sensors ship ROS 2 driver packages should take the drivers. Anyone whose employer, lab or collaborator already runs ROS 2 should match them, because the person who unsticks you at midnight matters more than any property of the transport. Coursework, hiring, contractors and published research all point the same way. HORUS is not the answer in those cases, and pretending otherwise would cost you a year. The place ROS 2 stops being the obvious pick is narrow and specific: one computer, more than one language, no meaningful use of the package archive, and a control loop whose timing you are already fighting. If that is not your robot, take the ecosystem.

Is Python simply too slow to sit in a robot's control loop?

No, and here is why. The question conflates two different loops. The outer loop — deciding where to go, what to pick up, when to stop — runs at a human pace and Python handles it without strain. The inner loop, the one holding a motor to a trajectory, is where a pause at the wrong moment shows up as a jerk in the arm, and that loop is often not on the main computer at all; it is on a microcontroller or in a compiled component underneath. Most Python robots that stutter are not stuttering because of the language. They stutter because a large image is being copied between processes, or one blocking call is sitting inside a loop that should never block, or garbage collection fires at an unlucky moment. Those are architecture problems with architecture fixes. The way to tell which problem you have is to move a suspect component and watch what changes; if a rewrite in another language would help, the change will already be visible when you simply move the work off the critical path.

Will choosing Python force a full rewrite in C++ later?

Partly, but not the way you think. Something usually does get rewritten, and it is almost never the whole system — it is one component, chosen because a specific loop needs timing Python cannot promise or because a specific stage cannot keep up with the sensor feeding it. Teams that plan for a total rewrite generally waste the planning, because the rewrite they eventually do is small and targeted and arrives eighteen months after they stopped worrying. What determines how painful that day is has nothing to do with Python: it is whether the boundaries between components are clean, and whether the middleware you chose lets a compiled component slot in beside the Python ones without a translation layer. A stack where both languages read the same buffers turns the rewrite into replacing one process. A stack where they cannot turns it into writing and maintaining a bridge forever. Choose for that day now and it costs nothing; discover it later and it costs a quarter.

What breaks first as a Python robot gets more demanding?

Copying large data between processes breaks first, and it usually gets blamed on the language. The pattern is predictable: a camera stage hands frames to a perception stage, each frame is packed on one side and rebuilt on the other, and as the resolution or the frame rate climbs, the work of moving the data grows until the pipeline is spending its time on transport instead of on vision. Second to break is a blocking call somewhere it should not be — a file write, a network request, a lock — inside a loop that has to keep a schedule. Third is the moment a second language arrives and the queue in your program cannot reach it. Fourth is process supervision: something crashes, nothing restarts it, and the robot keeps rolling with a dead perception stage. Recognising which one you have saves months, because the fixes are unrelated. Dropped sensor data has its own diagnosis path, and it is worth walking before concluding that the language is the problem.

How do I decide which of these I actually need?

Write three lists and let them choose for you. First, everything you will not write yourself: drivers, mapping, navigation, planning, simulation. Second, how many computers run your code when the robot works — today, and a year from now. Third, how many languages the robot will contain. Read them together. A long first list means take the ecosystem regardless of the other two, because rebuilding a catalogue is a project of its own. A short first list plus one computer means take a small local stack and keep the evenings. A short first list plus several computers means you want a networked message layer without the catalogue, which is a narrower field than either extreme. More than one language on a single computer is the case where sharing memory across languages earns its place, and the only case where it does. If the lists disagree, weight the first hardest, because it is the only item here that takes years to replace rather than weeks. The broader ranking is laid out in the middleware options worth knowing.

A short version, by situation:

When you want to compare these against each other rather than against your symptoms, the HORUS Fit Framework lines them up on five things that are not numbers: ecosystem size, setup effort, team size fit, deployment target, and licence. For a Python-first team, ecosystem size and deployment target usually decide it, and licence removes a few options before the comparison starts.

HORUS is open source under Apache-2.0 and the repository is linked below. Star it so it is in your list when you start building.

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