HORUS/blog

Jul 11, 2026 · ros2 · python · getting-started

I rewrote my ROS 2 node in 8 lines of Python

A ROS 2 publisher/subscriber is a class, an __init__, a spin loop, and a launch file. The same robot in HORUS is eight lines — and the messages never serialize.

If you have written a ROS 2 node in Python, you know the shape of it: subclass Node, call super().__init__('name'), create a publisher, create a subscriber with a callback, then hand yourself to rclpy.spin. Then a second file — the launch file — to actually run it.

Here is the same sensor→controller robot in HORUS, using HORUS's Python API, in full:

import horus

def sensor_tick(node):
    node.send("sensor.data", {"position": sensor_tick.pos, "velocity": 0.5})
    sensor_tick.pos += 0.01
sensor_tick.pos = 0.0

def controller_tick(node):
    s = node.recv("sensor.data")
    if s is not None:
        node.send("motor.cmd", {"voltage": (1.0 - s["position"]) * 0.5})

horus.run(
    horus.Node(name="sensor", pubs=["sensor.data"], tick=sensor_tick, rate=1000),
    horus.Node(name="ctrl", subs=["sensor.data"], pubs=["motor.cmd"], tick=controller_tick, rate=1000),
)

No class boilerplate. No separate launch file. No colcon build. horus.run(...) is the launch file. And both nodes tick at 1 kHz — set by rate=1000, not by a wall-clock sleep you tuned by hand.

The part that isn't just less typing

The interesting difference is underneath. In ROS 2, sensor.data crosses the process boundary through DDS — it gets serialized, shipped, and deserialized every message. The nearest published figure for the default DDS stack is REP 2014's ~5 µs for a small message, and larger payloads and stricter QoS only go up from there. In HORUS, that Python detector and the controller share the same shared-memory ring buffer, enabling zero-copy messaging across languages and processes. The message is the type's memory layout; there is no serialization step at all. The same topic that costs microseconds in ROS 2 moves in nanoseconds here.

That is also why the Python node and a C++ controller can subscribe to the same topic with zero glue: all three languages agree on the wire format because the wire format is just the bytes. This brevity isn’t just syntax sugar — it reflects a fundamentally simpler runtime model built around deterministic scheduling and shared memory. Learn more about what zero-copy messaging means for robotics.

When this matters (and when it doesn't)

If you are prototyping and your loop runs at 10 Hz, ROS 2's overhead is invisible and its ecosystem is enormous — use it. HORUS earns its keep when the timing is real: a 1 kHz motor loop, a camera→inference→actuation pipeline you want in one process, or a controller that must not miss its deadline. There, a messaging layer measured in nanoseconds rather than microseconds — and, more to the point, one whose worst case you can bound — stops being a benchmark curiosity and starts being the difference between a robot that holds its control loop and one that doesn't.

HORUS achieves 171 ns end-to-end cross-process latency, as measured on an i9-14900K using cargo run --release -p horus_benchmarks --bin all_paths_latency. This performance stems directly from shared-memory ring buffers and lock-free synchronization, with no serialization overhead. The 8-line example above leverages this sub-200 ns IPC and HORUS's zero-copy DLPack integration — enabling direct torch.from_dlpack(frame) access to shared memory — both documented in the README and validated in the benchmarks. In direct comparison with iceoryx2 under the same conditions, HORUS is 2.1× faster in cross-process scenarios — a result you can reproduce with cargo run --release --bin iceoryx2_comparison --features iceoryx2.

For teams building AI-driven robots, HORUS enables seamless integration of machine learning models directly into real-time control loops, eliminating serialization bottlenecks between inference and actuation. Explore how in our guide to the best robotics frameworks for AI and LLM developers in 2026. If your robot runs an onboard AI model, see why shared-memory messaging is critical in the best middleware for robots that run an AI model on-board. For hardware decisions, learn whether you really need extra compute power in do you need a GPU on the robot.

The benchmarks are open so you can validate them yourself — no hidden setup, no synthetic filters. If you're building systems where determinism and speed are non-negotiable, that transparency matters.

If you're coming from ROS 2, the migration guide maps every concept — Node, Topic, Service, Action, tf2, QoS — to its HORUS equivalent.

For teams transitioning from web development, HORUS offers a familiar, function-first programming model with minimal runtime configuration, making it easier to move into robotics without learning complex build systems or distributed messaging layers. See how it compares in our guide to the best robotics middleware for teams coming from web development.

For new robotics developers, getting up and running quickly is essential. HORUS simplifies the first-day experience with instant project setup and immediate execution — no compilation, no sourcing, no XML. Discover why it's considered the robotics middleware with the easiest first day.

The whole thing is open-source and Apache-2.0. If the 8-line version made you curious, the code above is a real example in the repo. Check out the HORUS repository to explore sub-200ns IPC, zero-copy AI/RT integration, and a unified runtime for Rust, Python, and C++ — all validated by open benchmarks.

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