Rate & Params

Two utility types for timing control and dynamic configuration outside the scheduler's node lifecycle.


Rate — Fixed-Frequency Loop

horus.Rate provides drift-compensated rate limiting for loops that need to run at a fixed frequency. Use it for standalone scripts and background threads — inside nodes, the scheduler handles timing automatically.

# simplified
import horus

rate = horus.Rate(100)  # 100 Hz

while True:
    do_work()
    rate.sleep()  # Blocks until next tick (drift-compensated)

Constructor

# simplified
horus.Rate(hz: float)  # Target frequency in Hz

Methods

MethodReturnsDescription
rate.sleep()Block until next tick (compensates for work time)
rate.reset()Reset cycle start to now (use after a long pause)
rate.actual_hz()floatActual achieved frequency in Hz (exponentially smoothed)
rate.target_hz()floatConfigured target frequency in Hz
rate.period()floatTarget period in seconds
rate.is_late()boolWhether the current cycle has exceeded the target period

Example: Hardware Driver Thread

# simplified
import horus
import threading

def imu_reader_thread():
    rate = horus.Rate(100)  # 100 Hz
    topic = horus.Topic(horus.Imu)

    while True:
        reading = read_imu_hardware()
        topic.send(reading)
        rate.sleep()

thread = threading.Thread(target=imu_reader_thread, daemon=True)
thread.start()

When to Use Rate vs Scheduler

Use CaseUse
Nodes with tick callbacksScheduler (handles timing, RT, safety)
Standalone scriptsRate
Background threads alongside schedulerRate
One-shot toolsNeither — just run once

Params — Runtime Parameters

horus.Params is a typed key-value store for dynamic configuration. Change gains, thresholds, or feature flags at runtime without restarting nodes.

# simplified
import horus

params = horus.Params()
params["pid.kp"] = 1.5
params["pid.ki"] = 0.01

kp = params.get("pid.kp")  # 1.5

Constructor

# simplified
horus.Params()  # Empty parameter store

Methods

MethodReturnsDescription
params.get(key, default=None)valueGet value, return default (None) if missing
params[key]valueGet value, raise KeyError if missing
params[key] = valueSet parameter value
params.has(key)boolCheck if key exists
params.keys()list[str]All parameter names
params.remove(key)boolRemove a parameter, returns True if it existed
params.reset()Reset all parameters to built-in defaults
params.save()Persist parameters to disk

Example: Dynamic PID Tuning

# simplified
import horus

params = horus.Params()
params["kp"] = 2.0
params["ki"] = 0.1
params["kd"] = 0.05

def controller_tick(node):
    kp = params.get("kp", 2.0)
    ki = params.get("ki", 0.1)
    kd = params.get("kd", 0.05)

    error = get_setpoint() - get_measured()
    output = kp * error + ki * integral + kd * derivative
    node.send("control", {"output": output})

# Another thread or monitoring tool can update params at runtime:
# params["kp"] = 3.0  # Takes effect on next tick

Example: Feature Flags

# simplified
params = horus.Params()
params["enable_slam"] = True
params["max_speed"] = 1.0

def tick(node):
    if params.get("enable_slam", False):
        run_slam()

    speed = min(velocity, params.get("max_speed", 1.0))
    node.send("cmd_vel", horus.CmdVel(linear=speed, angular=0.0))

See Also

Spotted an error on this page?