Communication Patterns Overview
HORUS provides three communication primitives that cover all robotics messaging needs:
| Pattern | Purpose | Example |
|---|---|---|
| Topic | Streaming data (pub/sub) | Sensor readings, motor commands, video frames |
| Action | Long-running tasks (goal/feedback/result) | Navigation, arm motion, calibration |
| Service | Request/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
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. Usetry_send()to get the message back on failure, orsend_blocking(msg, timeout)when loss is unacceptable. - Non-blocking recv:
recv()returnsOption<T>. ReturnsNoneif 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 need | Read |
|---|---|
| The Goal/Feedback/Result types and how to declare them | Defining an Action |
| Handling goals, streaming feedback, honouring cancellation | Action Server |
| Sending a goal and awaiting or cancelling it | Action Client |
| What states a goal moves through, and what preempts what | Goal 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
| Scenario | Use | Why |
|---|---|---|
| Sensor data streaming | Topic | Continuous data, latest value matters |
| Motor commands | Topic | High frequency, low latency |
| Camera frames | Topic | Streaming, multiple consumers |
| Navigate to position | Action | Long-running, needs progress feedback |
| Pick-and-place | Action | Multi-step, cancellable |
| Calibration routine | Action | Takes time, reports progress |
| Emergency stop | Topic | Must be instantaneous, no handshake |
| Diagnostics broadcast | Topic | Many publishers, flexible topology |
| Query a parameter / one-shot command | Service | Caller 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
- Topic Details - Full Topic API reference
- Actions - Long-running tasks with feedback and cancellation
- Services - Request/response RPC