HORUS/blog

Sep 5, 2026 · python · robot-control · real-time · choosing-tools

Is Python Fast Enough for Robot Control?

Yes for most of a robot, no for the loop that must never hesitate. Keep Python for the deciding parts and hand the rhythm to a compiled process.

Yes for most of a robot, and no for the innermost control loop, where compiled C++ or Rust code is still the honest answer. Python is rarely late because of arithmetic; it is late because of pauses — tidying memory, or waiting on a camera. The verdict flips when the loop that must not hesitate is Python itself; the repair is a compiled neighbour, which HORUS and ROS 2 both support. The rest of this post is for someone who knows Python, is building a first or second robot, and wants to know which parts to move and when.

On the bench it works. The script reads the encoder, works out the correction, sends it to the motor driver, and the wheel does what you asked. Then you add the camera, and the wheel starts twitching — not always, not badly, but you can see it. You add a print statement to find out why and the twitch changes, which is its own kind of bad news. Someone on a forum tells you Python is too slow and you should rewrite in C++, which sounds both plausible and enormous.

What makes it worse is that everything you can measure looks fine. The loop keeps up on average. The board is not out of memory and the processor is not pinned. Yet every so often the arm nudges past where it was supposed to stop, or the robot leans a fraction further than last time, and the fault is never in the same place twice. You cannot tell whether you have hit a real limit of the language, or whether you have written the program in a shape that would stutter in any language at all.

Is Python fast enough for robot control?

Yes for most of a robot, and no for the part that must never hesitate. Most of a robot is not a tight loop: deciding where to go next, talking to an operator, saving logs, running a model at the pace the camera produces frames. Python is a good home for all of that, and moving it to a compiled language buys you very little apart from slower edits. The exception is the loop that reads a position and commands a motor over and over, on a fixed rhythm, where being late once is a visible fault rather than a slower average. Python can usually do that arithmetic. What Python cannot promise is doing it on time every single time, because the interpreter occasionally stops to tidy memory, and because a Python program that also waits on a file or a socket drags the loop along with it. So the useful question is not whether the language is quick. It is which part of your robot is judged by its worst moment, and that part is normally small.

What does fast enough actually mean for a robot?

Fast enough means the command reaches the motor before the motor needed it, and it means that on the worst cycle, not the typical one. A robot is a machine that acts on a description of the past: the sensor tells you where the wheel was, and by the time your correction arrives the wheel has moved on. A small, consistent delay is fine, because a controller can be tuned around a delay it can count on. A delay that is usually small and occasionally large is what wrecks things, because the tuning that suits the usual case overshoots in the unusual one. That is why builders talk about deadlines rather than speed. The arm stops before it hits the table only if the check that says stop happens on schedule, every time, including the moment the log file is being flushed to disk. When people say a robot feels smooth or feels nervous, they are describing the spread between the best cycle and the worst one, and Python's weakness lives in that spread rather than in raw arithmetic.

What are your real options for running control code on a robot?

There are seven honest options and only one of them is rewriting everything. You can keep the whole robot in one Python program and accept its worst moments, which is right for plenty of machines. You can keep Python for the deciding parts and hand the rhythm to a compiled program sitting next to it on the same board, which is what HORUS exists for — 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 — and it is also what ROS 2 supports through its Python and C++ client libraries with a far larger ecosystem attached. You can speed up one slow routine with a compiled extension. You can rewrite the machine in C++ or Rust. You can push the loop down onto a microcontroller and leave the computer to think. Or you can stay inside the SDK that came with your arm. Read the table as a description of a situation, not a ranking.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
Everything in one Python programFirst robots and bench experimentsPython and your sensor's libraryNothing on the machine is judged by its worst momentA motor loop must hold rhythm while a camera runs
Python on ROS 2Builders who need borrowed drivers and navigationLinux, workspaces, launch files, message packagesThe ecosystem is the reason you are thereThe loop that must not hesitate stays in Python
HORUSMixed-language builders on a single machineYour message shapes and how your loops are scheduledPython decides and a compiled process holds rhythmBorrowed packages are the value and would need porting
Python plus a compiled extensionTeams with one slow routine, not one late loopBuilding native modules and the packaging around themOne function is the whole problemThe problem is timing rather than a single function
Whole robot in C++ or RustTeams shipping a product on fixed hardwareA compiled language and its build toolingThe machine must behave the same on its worst dayYou are still discovering what the robot should do
Control loop on a microcontrollerAnyone with a motor that must never waitEmbedded C or an embedded Rust toolchainThe rhythm matters more than the thinking above itThe loop needs a map, a camera or a model
Vendor SDK from PythonBuyers of a complete arm or mobile baseThe vendor's API and its supported languageThe machine should do its documented jobYou must mix in hardware the vendor never planned for

What should you use if Python is the only language you know?

Stay in Python and structure the robot so that the move out, if it ever comes, is a small one. A builder who knows one language well will get further in a month than the same builder fumbling through a second language they half know, and the things that sink first robots — wrong signs, wrong units, a sensor mounted backwards, a gripper that reports closed when it is holding nothing — are found by trying, not by compiling. So write it in Python, and spend your discipline on shape rather than syntax: keep the code that touches hardware apart from the code that decides, put the control maths in plain functions that take numbers and return numbers, and keep anything that waits — disk, network, camera — out of the loop that must keep time. Do that and the day you need a compiled loop, you are moving thirty lines into a new program, not untangling a robot. Skip it and every option above becomes a rewrite, which is how a stutter turns into a stalled project.

What should you run on a Raspberry Pi or similar onboard computer?

Run the deciding parts in Python and give the timing-critical loop either its own compiled program or its own small board. A single-board computer is a real computer with a real operating system, which means your program is one of many things competing for attention, and the operating system is entitled to look away at the wrong moment. That is survivable for a robot that navigates a room and unacceptable for one that balances. The practical pattern is layered: a microcontroller or motor controller holds the fastest rhythm, a compiled program on the board handles anything the microcontroller cannot, and Python sits above making decisions at human pace. Each layer talks to the one below it in messages rather than function calls, so a slow layer never drags a fast one. The other thing a small board punishes is process count — every program brings its own start-up cost, and a robot that takes a long time to become responsive is a robot you will test less often, which is one of the quieter reasons projects stall after the prototype.

What if the robot has to work in a month?

Write it in Python, all of it, and move only the part that visibly fails. A month is enough to make one impressive thing work badly, which is usually the point of a deadline, and it is not enough to learn a compiled language, its build system and its packaging while also discovering that your encoder counts backwards. Most demos are not limited by the control loop at all; they are limited by how many times you can change your mind before the date. Python maximises that number. If the demo genuinely needs a rhythm Python cannot hold — a balancing machine, a fast gripper, a motor that must not be commanded late — then buy that rhythm rather than building it: a motor controller with the loop already inside, a microcontroller running a fixed behaviour, or a compiled program you keep deliberately small. The failure mode to avoid on a deadline is starting a rewrite you cannot finish, and arriving at the date with a robot that is half in each language and working in neither.

What if you have never written C++ or Rust?

Then do not start with the control loop, because the control loop is the worst possible first project in a new language. The loop is the part with the tightest constraints, the least tolerance for a mistake, and the least helpful feedback when something is wrong: a robot that hesitates does not tell you which line hesitated. If you are going to learn a compiled language for robotics, learn it on something forgiving first — a program that reads a sensor and prints values, a tool that replays a recorded log, a small service that does one job with no deadline. Then move the loop, once the language has stopped surprising you. There is also an honest middle path many teams take and few blog posts mention: leave the loop on hardware that already runs compiled code, meaning the motor controller or a microcontroller, and never write C++ on the main computer at all. That is not a compromise so much as the standard architecture, and it lets a Python-only team ship a machine that holds its rhythm.

What does a robot look like when its loop is late?

A late loop looks like a machine that is fine until it is briefly, unpredictably not. The arm tracks smoothly and then twitches once on the way to the same position it reached cleanly a minute ago. The balancing robot holds still for a while and then leans, corrects too hard, and settles. The wheels hum at a steady pitch and the pitch wavers when the camera process wakes up. Nothing crashes. No error is logged, because from the software's point of view nothing went wrong: every instruction executed correctly, just later than the physical world required. That is what makes it maddening to debug — the tools you would reach for all report health. Two other tells are worth knowing. First, the fault moves when you add logging, because logging changes the timing you are trying to observe. Second, it gets worse as you add features that have nothing to do with motion, like a web interface or a recorder, which is the clearest sign that the problem is program shape rather than arithmetic.

What do people try first, and why does it stop working?

Most people reach for threads first, and threads work well right up to the point where two of them want the processor at the same time. In Python, only one thread runs Python code at a time, so a vision thread that wakes up to process a frame makes the control thread wait its turn — and the control thread is the one with the deadline. The usual next move is to make the slow work asynchronous, which helps if the slow work was waiting on something and does nothing at all if the slow work was computing. After that comes a queue between the two halves, which is a genuine improvement and quietly raises the question nobody wanted: what should the motor do with a camera result describing where the robot used to be? Eventually people arrive at separate programs, because separate programs cannot steal each other's turn. That is the same conclusion that middleware encodes, and it is why the fix is architectural rather than linguistic — the same shape that makes a node worth writing in a few lines is the shape that keeps a loop on time.

What do you give up by moving the loop out of Python?

You give up the speed of changing your mind, which is the thing that got you this far. A Python loop can be edited and rerun in seconds; a compiled loop needs a build, and on a small board that build is slow enough to break your concentration. You also give up a single place to look: now there are two programs, they start separately, they can disagree about what is running, and a bug can live in the seam between them rather than in either one. Debugging gets harder because the interesting moment is now split across two processes. Deployment gets harder because you are shipping a binary that must match the board it runs on. And you take on a message boundary, which means deciding what the loop should do when the decision above it is late or missing — a question a single program never had to answer out loud. Those costs are real. They are worth paying only for the parts that are genuinely judged by their worst moment, which is why the smaller you keep that part, the better this trade goes.

When is ROS 2 the better choice?

ROS 2 is the better choice whenever the code you would rather not write already exists as a ROS 2 package. A robot that must map a building and navigate to a goal is a ROS 2 project, because mapping and navigation represent years of work you cannot reproduce on the side. If your camera, lidar or arm ships with a ROS 2 driver and nothing else, that settles it too — porting a driver to save messaging overhead is a bad trade for almost everyone. ROS 2 also wins when your system spans more than one computer, when new team members are expected to read the robot on their first day, and when a research group already shares the vocabulary. In all of those cases HORUS is not the answer, and choosing it means rebuilding plumbing you could have inherited. The Python question is genuinely separate from the framework question: ROS 2 has an official Python library, and a ROS 2 robot with a late loop has exactly the same problem for exactly the same reasons.

Is Python the reason your robot stutters?

No, and here is why: the stutter is almost always the shape of the program, and the language is what makes that shape visible sooner. Any language will hesitate if you put a task that waits — reading a file, fetching a frame, writing a log — in the same execution path as a task that must keep time. C++ has no protection against that mistake; it simply has a wider margin, so the same design survives a bit longer before it shows. Python's pauses and its one-thread-at-a-time rule remove the margin and expose the design flaw early, which is annoying and also useful. The way to test this is cheap: strip the loop down to reading the sensor and writing the command, with nothing else in the program, and see whether the twitch survives. If it disappears, you have a structure problem that a rewrite would have hidden rather than fixed. If it persists, you have a genuine language limit, and the fix is a compiled loop or a microcontroller.

Will rewriting the robot in C++ stop the stutter?

Partly, but not the way you think. Rewriting the timing-critical loop in a compiled language does help, because it removes the interpreter's pauses and lets the loop keep running while other work happens. Rewriting the whole robot in C++ mostly buys you a slower project and a similar stutter, because the parts you rewrote — the mission logic, the web interface, the logger — were never the problem, and the loop is still sharing a machine with all of them. Worse, a big rewrite tends to happen at the exact moment a team is least able to afford it: after the prototype works and before anyone knows which behaviours are final. The version that pays is small and surgical. Take the loop out, keep it to reading a sensor, computing a command, writing an output, and leave everything else where it is. Then the deciding part stays quick to edit, the loop stays quick to reason about, and you have not spent a quarter translating code that was already fine.

How do you decide which parts stay in Python?

Ask which parts of your robot would produce a visible fault if they ran late once, and move only those. That is a short list on most machines, often one loop, and everything not on it belongs in the language you can edit fastest. Ask a second question to break ties: does this part wait on anything outside itself? Waiting and timekeeping do not belong in the same program, whatever the language, and separating them fixes more stutters than any rewrite. A third check is about your own capacity: if the whole team is Python and the deadline is close, buying the rhythm — motor controller, microcontroller, a board that runs the loop for you — beats learning a compiled language under pressure. If you are already fluent in a compiled language, the calculation changes and it is worth reading whether to write robot software in Rust before deciding where the line falls.

Decide by situation rather than by preference:

When two options stay close, weigh them on the five axes of the HORUS Fit Framework — ecosystem size, setup effort, team size fit, deployment target, and licence — and take the one that loses on the fewest. No scores and no numbers: five honest questions about your situation rather than about the software. If your robot keeps landing on one machine with Python above and something compiled below, star HORUS on GitHub so it is in your list when you start building.

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