Communication Patterns Overview

HORUS provides three communication primitives that cover all robotics messaging needs:

PatternPurposeExample
TopicStreaming data (pub/sub)Sensor readings, motor commands, video frames
ActionLong-running tasks (goal/feedback/result)Navigation, arm motion, calibration
ServiceRequest/response RPC (blocking call)Query state, trigger a one-shot command, config get/set

Topics and Actions work locally (shared memory) and across machines (network transport via configuration). A Service is built from a pair of Topics internally, so it follows the same transport rules.

Topic: Streaming Pub/Sub

Topic<T> is the primary communication primitive. It provides ultra-fast pub/sub with automatic optimization — a 16-byte message costs ~63ns to publish when both ends share a process and ~151ns one-way between processes on the reference machine.

use horus::prelude::*;

// Create a topic — backend is auto-detected
let pub_topic: Topic<f32> = Topic::new("sensor.temperature")?;
pub_topic.send(25.3);

// Multiple subscribers can listen (up to 16 participants per topic — see below)
let sub_topic: Topic<f32> = Topic::new("sensor.temperature")?;
if let Some(temp) = sub_topic.recv() {
    hlog!(info, "Temperature: {}", temp);
}

Automatic Optimization

You never choose a backend — HORUS detects the optimal one automatically based on:

  • Participants: How many publishers and subscribers are attached
  • Payload: Whether the message type is plain-old-data (zero-copy ring) or has to be serialized

Where the participants run is not a criterion. Every topic is shared-memory backed and every backend is cross-process, so the same path serves participants whether they share a thread, share a process, or live in separate processes — location changes the latency, not the choice:

Scenario (16 B message, p50)Latency
Same thread~20ns (publish)
Same process, separate threads~63ns (publish)
Cross-process, 1 publisher → 1 subscriber~151ns (one-way)
Cross-process, 1 publisher → many subscribers~191ns (one-way)
Cross-process, many publishers → 1 subscriber~279ns (one-way)

Measured by all_paths_latency on an Intel Core i7-10750H — see Benchmarks for the full table.

The communication path can change at runtime — if a second subscriber joins, HORUS automatically migrates to a multi-consumer path without dropping messages.

Key Characteristics

⚠️A topic holds at most 16 participants

Publishers and subscribers share one 16-slot table, counted per process and thread. The 17th registration fails, and it fails silently on the data path: the extra subscriber's recv() simply returns None forever, as though the topic were idle.

Sixteen is plenty for a normal robot, but a fan-out topic read by many nodes — or a process that opens the same topic from several threads — can reach it. If a subscriber that should be receiving never does, count the participants before suspecting the ring.

  • Non-blocking send: send() returns () — it never returns an error. On a full ring it retries briefly (spin + yield) and then drops the newest message, preserving the queued oldest ones in FIFO order (drop-newest). Broadcast topologies (multiple subscribers) instead overwrite the oldest slot — latest-wins, no backpressure. Use try_send() to get the message back on failure, or send_blocking(msg, timeout) when loss is unacceptable.
  • Non-blocking recv: recv() returns Option<T>. Returns None if no message available.
  • Zero introspection overhead: No timing, logging, or tracing in the default path. Content logging is off by default and is toggled by the topic's verbose header flag (horus_core::communication::set_topic_verbose), not by a builder method.

For full Topic API details, see Topic Communication.

Action: Long-Running Tasks

Actions handle tasks that take time to complete and benefit from progress feedback and cancellation. They follow the Goal / Feedback / Result pattern: a client sends one goal, the server streams feedback while it works, and the client gets a single result — or cancels partway through.

An action is three message types and a name. A server registers a handler for it; a client sends goals and receives a handle it can poll, await, or cancel.

This page is the overview. The full treatment — server configuration, preemption policies, goal states and priorities, server metrics, error handling and the horus action CLI — is in Actions.

You needRead
The Goal/Feedback/Result types and how to declare themDefining an Action
Handling goals, streaming feedback, honouring cancellationAction Server
Sending a goal and awaiting or cancelling itAction Client
What states a goal moves through, and what preempts whatGoal Lifecycle

Network Communication

By default, Topics and Actions use shared memory for ultra-fast communication between processes on the same machine. HORUS also supports zero-configuration peer discovery over UDP multicast (group 224.0.69.72, port 9100), letting robots on the same local network find each other automatically. Build with --features net — LAN replication is opt-in and off by default. Tune it with [network] in horus.toml or the HORUS_NET_* env vars.

When to Use What

ScenarioUseWhy
Sensor data streamingTopicContinuous data, latest value matters
Motor commandsTopicHigh frequency, low latency
Camera framesTopicStreaming, multiple consumers
Navigate to positionActionLong-running, needs progress feedback
Pick-and-placeActionMulti-step, cancellable
Calibration routineActionTakes time, reports progress
Emergency stopTopicMust be instantaneous, no handshake
Diagnostics broadcastTopicMany publishers, flexible topology
Query a parameter / one-shot commandServiceCaller needs an answer before continuing

Rule of thumb: If it's a continuous stream of data, use Topic. If it's a task you'd want to start, monitor, and potentially cancel, use Action. If the caller needs an answer back before it can continue, use Service.

Composing Patterns

A typical robot system combines topics and actions in a single node:

struct NavigationSystem {
    // Topics for streaming sensor data
    lidar_sub: Topic<LaserScan>,
    odom_sub: Topic<Odometry>,

    // Topics for publishing commands
    cmd_vel_pub: Topic<Twist>,
    status_pub: Topic<DiagnosticStatus>,

    // Action server for goal-based navigation
    nav_server: ActionServerNode<NavigateToGoal>,
}

The navigation action server subscribes to sensor topics internally, computes a path, publishes velocity commands via topics, and reports progress back to the action client — all using the same underlying shared memory infrastructure.

Next Steps