HORUS/blog

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

How Do the Parts of a Robot Talk to Each Other?

Robot parts pass named messages between small programs. On one computer that is shared memory, and a network only enters once two machines are involved.

The parts of a robot talk by passing named messages that other parts subscribe to, using a middleware, plain network sockets, or direct function calls. Most of that traffic is one program handing data to another on the same computer, so the practical choice is between ROS 2, a shared-memory middleware such as HORUS, and no middleware at all. The answer changes the moment the pieces sit on separate machines. The rest of this post is for someone building a first robot who has two scripts that need to share data and no idea what belongs between them.

You have a script that reads the camera and prints what it sees. You have another script that drives the motors. Both work. Neither knows the other exists, and every way you can think of to connect them feels wrong. You try writing the camera result to a file and reading the file from the motor script, which works for about a minute and then produces a number from three seconds ago at exactly the wrong time. You try pasting both scripts into one file, and now the wheels hesitate every time the camera takes a picture.

The tutorials do not help, because they start two steps ahead. They say publish to a topic and subscribe to it as though those were things you already do, and the example has a workspace and a build step before anything moves. Nobody explains the part you actually want explained: when the camera program has a number, where does the number physically go, and how does the motor program get it without the two programs having to know anything about each other. That question has a clear answer, and it is smaller than the tooling around it suggests.

How should the parts of your robot talk to each other?

Start with everything in one program, and split into separate programs the moment two parts need different rhythms. A robot that reads a sensor, decides something and moves a motor can live in a single loop where the parts talk by calling each other, and that is not a beginner compromise — it is the correct design for that robot. The split becomes necessary when one job must keep a steady beat while another waits on something outside its control: a camera frame, a file being written, a reply from a network. Once a waiting job shares a program with a beating job, the beat inherits every pause of the wait, and you see that as a robot that jerks or drifts while the camera thinks. At that point the parts become separate programs and need a way to hand data across. What you put between them is the subject of this post, and the choice stays cheap to change later if you keep deciding-code separate from fetching-code.

What does it mean for one part of a robot to publish a message?

Publishing means putting a piece of data under a name so anyone interested can take a copy. The camera program does not send a picture to the motor program. Instead it publishes under a name — front camera, wheel odometry, target position — and any program that has asked for that name receives what appears there. The publisher does not know who is listening, or whether anyone is. A subscriber does not know which program produced the data, or whether it came from a live sensor or a recording made last Tuesday. That indifference is the entire point. It means you can restart the motor program without restarting the camera, log everything the camera saw from a second program, or replace a real sensor with a replay of a failed run. A name is a promise about the shape of the data and nothing else, which is why this idea shows up in every robot larger than a single script. The plain-English guide to middleware covers the same idea from the other direction.

What are the actual ways to connect the parts of a robot?

There are six ways in common use and most robots end up using two or three at once. Inside one program, parts talk by calling functions and sharing variables, which costs nothing until timing gets in the way. Between programs on one computer, the options are a full robotics middleware such as ROS 2, which brings drivers, mapping tools and a large ecosystem with it; or a smaller shared-memory middleware such as HORUS, an open-source real-time middleware for Rust, Python and C++ where all three languages read and write the same shared-memory ring buffers, so a message passed between processes on one machine is not serialised on the way. Between computers, robots use a network: a middleware that spans machines, plain sockets, or a small message broker. Below all of it are the wires: serial links, CAN buses and the protocols motor drivers actually speak. Read the table as a set of situations rather than a ranking.

OptionWho it is forWhat it assumes you knowWhen to pick itWhen not to
One program, direct callsA first robot with one loopBasic Python or C++ and your sensor libraryNothing needs its own rhythmThe camera makes the wheels hesitate
ROS 2 topicsAnyone wanting existing drivers and navigationLinux, workspaces, launch files, package layoutBorrowed packages are most of the robotYou need movement in days
A shared-memory middleware such as HORUSMixed-language robots on one computerYour message shapes and loop timingPython, C++ and Rust parts share one boardBorrowed packages are the point
Plain socketsBuilders joining two machines simplyAddresses, ports, what happens when one side diesOne link between two programs you wroteMany parts must find each other
A small message brokerTeams reusing web or home-automation habitsBrokers, topics, keeping a service runningThe robot reports and takes remote commandsA control loop depends on message order
Serial or CAN to microcontrollersAnyone driving real motorsYour board's protocol and wiringThe message must reach hardwareTwo programs on one board share data

What should you use if you are learning on your own?

Use one program until the robot itself forces you to split, then use the smallest thing that separates the parts. A person learning alone has one scarce resource, and it is not compute or money — it is the number of evenings before interest runs out. Spending four of them on a build system means the robot still has not moved, and a robot that has not moved teaches you nothing about the real problem: the world is messier than the tutorial. Write the loop. Watch the wheel overshoot the line. Fix it. When the camera starts making the motors hesitate, you will have a specific, felt reason to split the program, and that reason will tell you where the split belongs. A moving robot teaches you the shape of your own problem, and the shape of your problem is the only thing that can pick a foundation for you. Splitting first, from a diagram, usually produces neat boundaries that turn out to be in the wrong place once real timing arrives.

What if the whole robot runs on one small computer?

Then almost all of the talking is between programs on that one board, and how those handoffs work matters more than anything about networking. This is the normal case for hobby robots, small bases and most arms: one board running a handful of programs, plus a microcontroller doing the motor timing. On one machine, a message does not need to be packed up, addressed and shipped anywhere; it can be written into memory that another program can already see. Many systems pack and unpack it anyway, because the same code path is used whether the other program sits on the same board or across a network, and that work lands on the processor you also want running your control loop. The result of avoiding it is not a number you would quote: the arm still stops where you told it to while the camera and the planner are both busy. If your robot is one board, prefer approaches that treat one board as the normal case, and look at what zero-copy messaging means before you assume the copying is free.

What if you need something working in two weeks?

Pick the option that gets a wheel turning today, and connect the parts with whatever you already understand. Two weeks is enough to make a robot do one thing convincingly and not enough to learn an ecosystem and then use it. If your demo is your own behaviour on your own hardware — follow the line, avoid the obstacle, pick up the block — write one program, let the parts call each other directly, and spend your remaining days on the behaviour, because that is what people will watch. If your demo needs a map of a room, or a sensor whose only working driver is a package somebody else wrote, the decision has been made for you and the first days go into setup rather than robotics. The mistake in both directions is choosing for an imagined future. What you should protect is the boundary between the code that decides things and the code that fetches things, because that boundary is what makes the next version cheap regardless of which foundation you land on.

What if you only know a little Python?

Then stay in Python and let the messaging stay boring, because language is not the thing that will hurt you. Python is enough to read a sensor, decide something and drive a motor for a large class of robots, and the parts that genuinely need another language are usually written by somebody else already — the motor firmware, the camera driver, the maths library. What trips people up at this stage is not syntax; it is the first time two things happen at once. A loop that both waits for a camera and keeps the wheels updated is a genuinely harder idea than anything in the language, and it arrives whether you chose it or not. Learn it deliberately in one program first: one part that waits, one part that must not, and a clear rule for what the motor should do with a picture that has gone stale. Once that idea is solid, splitting into separate programs is mechanical. Choosing a middleware before understanding it means inheriting decisions you cannot yet evaluate.

What kinds of data actually move between the parts of a robot?

Four kinds, and they behave differently enough that mixing them up causes most beginner bugs. First, sensor streams: a continuous flow of readings where only the newest one matters and losing an old one is harmless. Second, commands: single instructions such as open the gripper, which must arrive exactly once and must never be applied twice. Third, shared state: where the robot thinks it is, what mode the machine is in, values that any part can read at any time. Fourth, configuration and one-off requests: a question asked and an answer awaited, like asking a planner for a route. The mistake is treating all four as one thing. A dropped camera frame is a shrug; a dropped stop command is an accident. A stale position reading feels harmless until the arm moves toward where the block used to be. When people say a messaging layer has quality settings, this is what those settings exist for: telling the system which of these four a name carries, and what to do when the receiver cannot keep up.

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

Almost everyone tries a shared file or a global variable first, and it stops working the moment timing matters. The file approach fails because two programs sharing a file have no agreement about when a write is finished, so the reader eventually gets half a number and the robot acts on nonsense. The single-giant-script approach fails differently: it works beautifully until one part of the loop needs to wait, and then everything waits with it. The third attempt is usually threads and a queue, which is a real improvement and buys months, but it quietly raises the question nobody wanted to answer — what should the motor do when the queue is full of data that is no longer true? A messaging layer exists to give that question a standard answer instead of a bespoke one in every project. You can keep answering it yourself, and plenty of good robots do, right up until a second person joins and inherits your private conventions with no documentation.

What do you give up by putting a messaging layer in?

You give up simplicity you can see, and you trade it for behaviour you have to trust. In one program, you can follow a value from the sensor to the motor by reading down the page. With separate programs, that trail crosses a boundary, and a bug now means knowing what the messaging layer does when a subscriber is slow, when a publisher dies mid-run, or when programs start in an unexpected order. You also give up some directness: there is a moving part between your parts, and it has settings that will eventually matter. And you give up start-up time and memory, which is real on a small board where every program brings its own runtime. Those are genuine costs and they are the reason not to reach for messaging on the first evening. What you buy is the ability to restart one piece without the others, to record everything that moved between parts and replay it at your desk, and to let a teammate work on the planner while you work on the drivers.

When is ROS 2 the better choice?

ROS 2 is the better choice whenever the valuable part of your robot is code somebody else already wrote. A robot that must build a map of a building and drive to a goal is a ROS 2 project on day one, because mapping and navigation represent years of work you cannot reproduce on evenings and weekends. An arm doing collision-aware motion planning is the same story. If the sensor you bought ships its driver only as a ROS 2 package, that settles it before any other consideration. If you are joining a lab, taking a course, or applying for jobs that name the tooling in the advert, learn what everyone else uses, because shared vocabulary beats any property of a transport. HORUS is not the answer for those projects, and choosing it there means rebuilding plumbing to avoid learning a package layout, which is a bad trade. A messaging layer is only ever worth choosing on its own terms when you are the one writing most of what runs.

Do the parts of a robot have to talk over a network?

No, and here is why: a network is for crossing between computers, and most robots are one computer. Two programs on the same board can exchange data through memory both of them can reach, with no addresses, no ports and nothing to configure, and that path is the shortest one available. Networking enters when the robot genuinely spans machines — an onboard computer talking to a workstation, a fleet reporting to a server, a second board handling vision. Confusion arises because several popular systems use network machinery even when both programs are on one machine, since it keeps the code identical either way. That uniformity is a convenience with a cost, paid in packing, addressing and discovery on the same processor trying to run your control loop. For a beginner the practical takeaway is simple: if everything is on one board, treat network setup problems as a sign you have taken a detour, not as a normal part of building a robot.

Is a messaging layer just extra work for a beginner?

Partly, but not the way you think. The extra work is real, and on a one-loop robot it is pure overhead — a first robot genuinely does not need a middleware, and telling beginners otherwise has sent a lot of people into workspace tutorials instead of into robotics. But the work is not wasted the way people assume, because the ideas you learn are the ones that survive every tool change. Naming your data, deciding what happens to stale readings, keeping deciding-code apart from fetching-code and being able to record and replay a run are habits, not features, and they make any foundation you later choose easier to adopt. The genuinely wasted effort is different: it is learning one ecosystem's ceremony — its build steps, its launch descriptions, its configuration files — before you have a robot that needs any of it. Learn the ideas early and the ceremony late. The full case for whether beginners need middleware at all goes deeper than there is room for here.

How do you decide how your robot's parts should talk?

Count the computers and count the programs, and let those two numbers decide before anything else. One computer and one program means direct calls and nothing else; adding messaging there is adding a moving part to a machine that has none. One computer and several programs is the common case, and there the question is what those programs are made of: borrowed packages point back to the ecosystem they came from, and code you wrote yourself, especially in more than one language, points to a shared-memory middleware. Several computers means a network is unavoidable, and the mature ecosystems have travelled that road for years. Ask one more question to break a tie: what would you be sad to have to write yourself? If the answer is a navigation stack or a driver, the ecosystem holding it wins and the rest is detail. If the answer is nothing — if the interesting part is behaviour only you understand — you are free to choose the smaller thing.

Decide by situation rather than by preference:

When two of those describe you at once, 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 option that loses on the fewest. No scores: five plain questions about your situation rather than about the software. If your answers keep landing on one board, more than one language, and behaviour only you understand, star HORUS on GitHub so it is in your list when you start building.

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