Choosing a Language

HORUS supports both Rust and Python. This guide helps you choose the right one for your project.


Quick Decision

Use Python if:

  • You're prototyping or experimenting
  • You're new to robotics programming
  • You want to integrate with ML/AI libraries (TensorFlow, PyTorch)
  • Development speed matters more than runtime performance

Use Rust if:

  • You need maximum performance
  • You're building production systems
  • You want compile-time safety guarantees
  • You're comfortable with Rust (or want to learn)

Side-by-Side Comparison

Hello World: Temperature Sensor

Python:

import horus

def sensor_tick(node):
    temp = 25.0  # Read sensor
    node.send("temperature", temp)

sensor = horus.Node(name="TempSensor", tick=sensor_tick, order=5,
                    pubs=["temperature"])
horus.run(sensor)

Rust:

use horus::prelude::*;

struct TempSensor {
    pub_topic: Topic<f32>,
}

impl TempSensor {
    fn new() -> Result<Self> {
        Ok(Self { pub_topic: Topic::new("temperature")? })
    }
}

impl Node for TempSensor {
    fn name(&self) -> &str { "TempSensor" }

    fn tick(&mut self) {
        let temp = 25.0;  // Read sensor
        self.pub_topic.send(temp);
    }
}

fn main() -> Result<()> {
    let mut scheduler = Scheduler::new();
    scheduler.add(TempSensor::new()?).order(5).build()?;
    scheduler.run()
}

Or with the Rust node! macro:

use horus::prelude::*;

node! {
    TempSensor {
        pub { temperature: f32 -> "temperature" }

        tick {
            let temp = 25.0;
            self.temperature.send(temp);
        }
    }
}

fn main() -> Result<()> {
    let mut scheduler = Scheduler::new();
    scheduler.add(TempSensor::new()).order(5).build()?;
    scheduler.run()
}

Detailed Comparison

AspectPythonRust
Learning curveEasySteeper
Setup time5 minutes10 minutes
Compile timeNoneA few seconds
Runtime performanceGoodExcellent
Memory safetyRuntime checksCompile-time guarantees
ML/AI integrationExcellent (numpy, torch, etc.)Limited
DebuggingSimple print debuggingMore tooling needed
Production readinessGood for prototypesProduction-grade

Performance Comparison

OperationPythonRustDifference
Node tick latency~10μs~1μs10x faster
Message send~2μs~400ns5x faster
Control loop (1kHz)AchievableEasy-
Control loop (10kHz)DifficultAchievable-

Bottom line: For most robotics applications, both are fast enough. Rust matters when you need very high-frequency control (>1kHz), hard real-time guarantees, or minimal memory footprint.

Scheduling Features

Both Rust and Python support the full scheduling API:

FeatureRustPythonNotes
.rate()YesYesTick rate in Hz
.order()YesYesExecution priority
.budget()YesYesTick time budget
.deadline()YesYesHard deadline
.on_miss()YesYesDeadline miss policy
.priority()YesYesOS thread priority (SCHED_FIFO)
.core()YesYesCPU core pinning
.watchdog()YesYesPer-node watchdog timeout
.compute()YesYesCPU-bound thread pool
.async_io()YesYesI/O-bound async pool
.on(topic)YesYesEvent-triggered execution
enter_safe_state()YesNoRequires implementing Node trait

Performance difference: While both languages expose the same API, Rust nodes achieve lower tick jitter and more predictable timing due to the absence of the GIL. For control loops above 1kHz with hard deadlines, Rust is recommended.

Best practice: Use Rust for RT-critical driver nodes (motors, safety). Use Python for application logic (planning, ML inference, behavior trees). They communicate via shared memory topics — zero overhead across the language boundary.


When to Choose Python

Rapid Prototyping

# Quick experiment - try different approaches fast
import horus

def controller_tick(node):
    input_val = node.recv("sensor") or 0.0
    strategy = "aggressive"

    if strategy == "aggressive":
        output = input_val * 2.0
    else:
        output = input_val * 0.5
    node.send("output", output)

ctrl = horus.Node(name="ExperimentalController", tick=controller_tick,
                  subs=["sensor"], pubs=["output"])

Machine Learning Integration

import torch
import horus

model = torch.load("my_model.pt")

def ml_tick(node):
    sensor_data = node.recv("sensor_data")
    if sensor_data is not None:
        with torch.no_grad():
            output = model(torch.tensor(sensor_data))
        node.send("control_output", output.item())

ml_node = horus.Node(name="MLController", tick=ml_tick,
                     subs=["sensor_data"], pubs=["control_output"])

Education and Learning

Python's readable syntax makes it easier to understand robotics concepts without fighting the language.


When to Choose Rust

Production Deployments

// Rust catches bugs at compile time
impl Node for SafetyMonitor {
    fn tick(&mut self) {
        // Compiler ensures we handle all cases
        match self.check_safety() {
            SafetyStatus::OK => self.continue_operation(),
            SafetyStatus::Warning(msg) => self.log_warning(&msg),
            SafetyStatus::Critical(msg) => self.emergency_stop(&msg),
        }
    }
}

High-Frequency Control

// Rust can sustain 10kHz+ control loops
impl Node for MotorController {
    fn tick(&mut self) {
        // Microsecond-level timing is reliable
        let error = self.target - self.position;
        let output = self.pid.compute(error);
        self.motor.send(output);
    }
}

Resource-Constrained Environments

// Rust has minimal runtime overhead
// Perfect for embedded systems and single-board computers

Mixed Language Projects

You can use both languages in the same project! HORUS nodes communicate via shared memory, which works across languages.

Example: Python for AI, Rust for control

Loading diagram...
Mixed language project: Python for ML, Rust for control - connected via shared memory

Python ML node:

import horus

def detector_tick(node):
    camera_image = node.recv("camera")
    if camera_image is not None:
        detections = model.detect(camera_image)
        node.send("detections", detections)

detector = horus.Node(name="ObjectDetector", tick=detector_tick,
                      subs=["camera"], pubs=["detections"])
horus.run(detector)

Rust control node:

impl Node for NavigationController {
    fn tick(&mut self) {
        if let Some(detections) = self.detection_sub.recv() {
            // React to Python node's output
            self.plan_path(&detections);
        }
    }
}

Recommendation by Use Case

Use CaseRecommended Language
Learning HORUSPython
University projectPython
Hobby robotEither
Machine learning robotPython + Rust
Industrial automationRust
Drone/UAVRust
Research prototypePython
Competition robotRust
Product developmentRust

Getting Started

Ready to start with Python?

Ready to start with Rust?


Still Unsure?

Start with Python. It's faster to get something working, and you can always port critical parts to Rust later. HORUS makes it easy to mix languages.