Quick Start (Python)
Looking for Rust? See Quick Start (Rust).
Prerequisites
- HORUS installed with
horus --helpworking - Python 3.9+ with HORUS bindings (
python3 -c "import horus"works) - A terminal and text editor
Installing the Python Bindings
Install the horus Python package from PyPI:
pip install horus-robotics
If you are building from source (e.g., developing HORUS itself or need unreleased features), use maturin instead:
cd horus_py
maturin develop --release
maturin develop --release compiles the Rust PyO3 bindings and installs them into your active Python environment. The --release flag enables optimizations — omit it only during development iteration where compile speed matters more than runtime speed.
Verify the installation:
python3 -c "import horus; print('horus OK')"
What You'll Build
A temperature monitoring system with two nodes:
- Sensor — generates temperature readings and publishes them
- Monitor — subscribes to the readings and displays them
The nodes communicate through HORUS's shared-memory Topics — the same zero-copy IPC as Rust, accessible from Python.
Time estimate: ~10 minutes
Step 1: Create a New Project
horus new temperature-monitor -p
cd temperature-monitor
You should see three items in the project directory:
src/main.py— your application codehorus.toml— project configuration.horus/— generated build files (managed automatically)
Project language flags for horus new:
| Flag | Language | Entry point | Description |
|---|---|---|---|
-p | Python | src/main.py | Python project (what we're using here) |
-r | Rust | src/main.rs | Rust project with manual node setup |
-m | Rust | src/main.rs | Rust project with the node! macro for concise definitions |
All three generate a horus.toml manifest and .horus/ build directory. The only difference is the language-specific entry point and generated build files (.horus/pyproject.toml for Python, .horus/Cargo.toml for Rust).
Step 2: Write the Code
Replace the contents of src/main.py with the following:
import horus
# ── Sensor Node ──────────────────────────────────────────────
# Publishes a simulated temperature reading every second.
def make_sensor():
temp = [20.0] # mutable state via closure
def tick(node):
temp[0] += 0.1
node.send("temperature", temp[0])
return horus.Node(name="TemperatureSensor", tick=tick, rate=1, order=0,
pubs=["temperature"])
# ── Monitor Node ─────────────────────────────────────────────
# Subscribes to temperature readings and prints them.
def monitor_tick(node):
temp = node.recv("temperature")
if temp is not None:
print(f"Temperature: {temp:.1f}°C")
monitor = horus.Node(name="TemperatureMonitor", tick=monitor_tick, rate=1, order=1,
subs=["temperature"])
# ── Run ──────────────────────────────────────────────────────
print("Starting temperature monitoring system...\n")
horus.run(make_sensor(), monitor)
Step 3: Run It
horus run
You should see output like:
Starting temperature monitoring system...
Temperature: 20.1°C
Temperature: 20.2°C
Temperature: 20.3°C
Temperature: 20.4°C
...
Press Ctrl+C to stop.
Step 3.5: Inspect Your System
While your system is running (restart it with horus run in one terminal), open a second terminal in the same project directory and try these debugging tools:
# List all active topics
horus topic list
You should see temperature in the list — this is the shared memory topic your sensor is publishing to.
# Watch messages on a topic in real time
horus topic echo temperature
This prints every message as it's published. You'll see the temperature values streaming. Press Ctrl+C to stop.
# Measure the actual publish rate
horus topic hz temperature
This shows the real frequency of messages. Since your sensor runs at rate=1, you should see ~1.0 Hz.
These tools are your first line of defense when debugging. If a subscriber isn't receiving data, check horus topic list to verify the topic exists, then horus topic echo to verify data is flowing. If the rate looks wrong, horus topic hz tells you exactly what's happening.
Always use horus run — do NOT run python src/main.py directly.
horus run sets up the SHM namespace, environment variables, and build pipeline before executing your code. Running python src/main.py directly skips all of this, which means:
- Topics won't connect to other processes (no SHM namespace)
- Environment variables like
HORUS_PROJECT_DIRwon't be set - Dependencies declared in
horus.tomlwon't be resolved - The
.horus/build pipeline is bypassed entirely
If you need to pass arguments, use: horus run -- --your-flag value
Step 4: Understand the Key Patterns
You just used three core HORUS concepts:
Node — Component Definition
Each component is a horus.Node with a tick function:
def tick(node):
node.send("temperature", value)
sensor = horus.Node(
name="TemperatureSensor",
tick=tick,
rate=1, # tick frequency in Hz
order=0, # execution priority (lower = runs first)
pubs=["temperature"], # declare published topics
)
The tick function receives the node instance and is called every cycle. State lives in the closure or as class attributes.
Topics — Communication
# Send data to a topic
node.send("temperature", value)
# Receive data from a topic (returns None if no messages)
temp = node.recv("temperature")
Both use the same topic name. HORUS manages shared memory automatically — same zero-copy IPC as Rust.
horus.run() — One-Liner Execution
horus.run(sensor, monitor)
Creates a scheduler, adds all nodes, and runs until Ctrl+C. For more control, use horus.Scheduler directly.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
ModuleNotFoundError: horus | Python bindings not installed | Run pip install horus-robotics. For source builds: cd horus_py && maturin develop --release |
Failed to create Topic | Stale shared memory from a previous run | Run horus clean --shm |
| Nothing prints | Monitor added but sensor missing | Ensure both nodes are passed to horus.run() |
| Topics not visible to other processes | Running python src/main.py directly | Always use horus run — it sets up the SHM namespace. See the warning above |
Permission denied on SHM files | Shared memory permissions mismatch | Run horus doctor to diagnose. On Linux, check /dev/shm/ permissions |
| Output looks slow or laggy | Running in debug mode | Use horus run --release for optimized builds. Debug builds are 10-50x slower for compute-heavy code |
horus: command not found | HORUS not in PATH | Re-run the installer or add ~/.horus/bin to your PATH |
TypeError in tick function | Wrong tick function signature | Tick functions must accept exactly one argument: def tick(node). The scheduler passes the node instance automatically |
Key Takeaways
- Nodes are created with
horus.Node(name, tick, rate, order, pubs, subs) node.send(topic, data)publishes data,node.recv(topic)subscribes (returnsNoneif empty)horus.run(*nodes)is the one-liner to run everything- State in tick functions lives in closures (lists for mutability) or class instances
- The Python API uses the same shared-memory Topics as Rust — no performance penalty for IPC
Next Steps
- Quick Start (Rust) — Build the same system in Rust
- Second Application (Python) — Build a 3-node pipeline with filtering and monitoring
- Choosing a Language — When to use Python vs Rust
- Common Mistakes (Python) — Avoid pitfalls specific to Python nodes
Beyond the Basics
You've seen Nodes, Topics, and horus.run(). HORUS has much more — here's what to explore next:
| Feature | What it does | Guide |
|---|---|---|
| Execution classes | Run nodes as RT, compute-bound, event-driven, or async I/O | Execution Classes |
| Watchdog & safety | Detect frozen nodes, enforce deadlines, graduated degradation | Safety Policies (Python) |
| BlackBox | Flight recorder for post-mortem crash analysis | BlackBox |
| Deterministic mode | Reproducible execution for simulation and CI | Deterministic Mode |
| Record & Replay | Tick-perfect replay for reproducing field bugs | Record & Replay |
| Fault tolerance | Per-node failure policies (restart, skip, fatal) | Circuit Breaker |
| Framework clock | horus.now(), horus.dt(), horus.budget_remaining() | Real-Time (Python) |
| Progressive config | From prototype to production in 5 levels | Choosing Your Configuration |
See Also
- Python API Reference — Full
horus.Node,horus.run(),horus.Schedulerdocs - Nodes (Concept) — How nodes work under the hood
- Topic (Concept) — Shared memory architecture
- Python Examples — More sample applications