Using Pre-Built Nodes
The HORUS Philosophy: Don't reinvent the wheel. Use comprehensive, battle-tested nodes from the registry and horus_library, then configure them to work together.
Why Use Pre-Built Nodes?
Advantages of pre-built nodes:
- Production-ready and tested
- Configure instead of coding
- Focus on application logic, not infrastructure
- Nodes use standard HORUS interfaces for interoperability
Quick Example
Instead of writing a PID controller from scratch, just install and configure:
# Install from registry
horus install pid-controller
use pid_controller::PIDNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Configure the pre-built node
let pid = PIDNode::new(1.0, 0.1, 0.01); // kp, ki, kd
scheduler.add(pid).order(5).build()?;
scheduler.run()?;
Ok(())
}
That's it! Production-ready PID control in 3 lines.
Discovering Pre-Built Nodes
From the Registry
Web Interface:
# Visit the registry in your browser
https://registry.horusrobotics.dev
Browse by category:
- Control - PID controllers, motion planners
- Perception - Camera, LIDAR, sensor fusion
- Drivers - Motor controllers, sensor interfaces
- Safety - Emergency stop, watchdogs
- Utilities - Loggers, data recorders
CLI Search:
# Search for specific functionality
horus list sensor
horus list controller
horus list motor
From Standard Library
The horus_library crate includes standard message types used across nodes:
use horus::prelude::*;
// Motion messages
CmdVel, Twist, Pose2D, Odometry
// Sensor messages
LaserScan, Imu, BatteryState, PointCloud
// Input messages
KeyboardInput, JoystickInput
// And many more...
Note: Hardware-interfacing nodes (sensor drivers, motor controllers, etc.) are available as registry packages or Python nodes -- they are not built into horus_library. Search the registry for ready-made nodes.
Installation Patterns
Installing from HORUS Registry
# Latest version
horus install motion-planner
# Specific version
horus install sensor-fusion -v 2.1.0
# Multiple packages
horus install pid-controller motion-planner sensor-drivers
Adding crates.io Dependencies
# Add Rust project dependencies (writes to horus.toml)
horus add serde
horus add tokio@1.35.0
Adding PyPI Dependencies
# Add Python project dependencies (writes to horus.toml)
horus add numpy
horus add opencv-python
Using Standard Library
The standard library is available automatically with horus run:
horus run main.rs
# horus_library is included by default
Or explicitly in your Cargo.toml:
[dependencies]
horus = { path = "..." }
horus_library = { path = "..." }
The Idiomatic Pattern
1. Discover What You Need
Example Goal: Build a mobile robot with keyboard control
Required Nodes:
- Input: Keyboard control
- Control: Velocity command processing
- Output: Motor driver
2. Search and Install
# Check what's available
horus list keyboard
horus list motor
# Install what you need
horus install keyboard-input
horus install differential-drive
3. Configure and Compose
use keyboard_input::KeyboardNode;
use differential_drive::DiffDriveNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Keyboard input node (order 0 - runs first)
let keyboard = KeyboardNode::new("keyboard.input")?;
scheduler.add(keyboard).order(0).build()?;
// Differential drive controller (order 5)
let drive = DiffDriveNode::new(
"keyboard.input", // Input topic
"motor.left", // Left motor output
"motor.right", // Right motor output
0.5 // Wheel separation (meters)
)?;
scheduler.add(drive).order(5).build()?;
scheduler.run()?;
Ok(())
}
That's it! A functional robot in ~20 lines, no custom nodes needed.
Common Workflows
Mobile Robot Base
# Install components
horus install keyboard-input
horus install differential-drive
horus install emergency-stop
use keyboard_input::KeyboardNode;
use differential_drive::DiffDriveNode;
use emergency_stop::EStopNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Input
scheduler.add(KeyboardNode::new("keyboard")?).order(0).build()?;
// Safety (runs first!)
scheduler.add(EStopNode::new("estop", "cmd_vel")?).order(0).build()?;
// Drive control
scheduler.add(DiffDriveNode::new("cmd_vel", "motor.left", "motor.right", 0.5)?)
.order(1).build()?;
scheduler.run()?;
Ok(())
}
Sensor Fusion System
horus install lidar-driver
horus install imu-driver
horus install kalman-filter
use lidar_driver::LidarNode;
use imu_driver::ImuNode;
use kalman_filter::EKFNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Sensors (order 2)
scheduler.add(LidarNode::new("/dev/ttyUSB0", "scan")?).order(2).build()?;
scheduler.add(ImuNode::new("/dev/i2c-1", "imu")?).order(2).build()?;
// Fusion (order 3 - runs after sensors)
scheduler.add(EKFNode::new("scan", "imu", "pose")?).order(3).build()?;
scheduler.run()?;
Ok(())
}
Vision Processing Pipeline
# Install vision packages from registry
horus install camera-driver
horus install image-processor
horus install object-detector
use camera_driver::CameraNode;
use image_processor::ImageProcessorNode;
use object_detector::ObjectDetectorNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Using registry packages
let camera = CameraNode::new("/dev/video0", "camera.raw", 30)?;
let processor = ImageProcessorNode::new("camera.raw", "camera.processed")?;
let detector = ObjectDetectorNode::new("camera.processed", "objects")?;
scheduler.add(camera).order(2).build()?;
scheduler.add(processor).order(3).build()?;
scheduler.add(detector).order(3).build()?;
scheduler.run()?;
Ok(())
}
Configuration Best Practices
Use Builder Patterns
Many registry packages support fluent configuration:
// Example: camera-driver package from registry
let camera = CameraNode::new("/dev/video0")?
.with_resolution(1920, 1080)
.with_fps(60)
.with_format(ImageFormat::RGB8);
scheduler.add(camera).order(2).build()?;
Parameter-Based Configuration
Configure nodes via the parameter system:
use horus::prelude::*;
// Set parameters via RuntimeParams
let params = RuntimeParams::init()?;
params.set("motor.max_speed", 2.0)?;
params.set("motor.acceleration", 0.5)?;
// Node reads from parameters
let motor = MotorNode::from_params()?;
scheduler.add(motor).order(1).build()?;
Adjust at runtime via monitor!
Reproducible Setup
Commit horus.lock to git to pin all dependency versions. On another machine, horus build will install the exact same versions.
Composing Complex Systems
Pipeline Pattern
Chain nodes together via topics:
// Each node subscribes to previous, publishes to next
scheduler.add(sensor).order(2).build()?; // Publishes "raw"
scheduler.add(filter).order(3).build()?; // Subscribes "raw", publishes "filtered"
scheduler.add(controller).order(4).build()?; // Subscribes "filtered", publishes "cmd"
scheduler.add(actuator).order(5).build()?; // Subscribes "cmd"
Parallel Processing
Multiple nodes at same priority run concurrently:
// All run in parallel (order 2)
scheduler.add(lidar).order(2).build()?;
scheduler.add(camera).order(2).build()?;
scheduler.add(imu).order(2).build()?;
Safety Layering
Critical nodes run first:
// Order 0 - Safety checks (runs first)
scheduler.add(watchdog).order(0).build()?;
scheduler.add(estop).order(0).build()?;
// Order 1 - Control
scheduler.add(controller).order(1).build()?;
// Order 2 - Sensors
scheduler.add(lidar).order(2).build()?;
// Order 4 - Logging (runs last)
scheduler.add(logger).order(4).build()?;
When to Build Custom Nodes
Use pre-built nodes when:
- Functionality exists in the registry or
horus_library - Node can be configured to your needs
- Performance is acceptable
Build custom nodes when:
- No existing node matches your hardware
- Unique algorithm or business logic
- Extreme performance requirements
Pro tip: Even then, consider:
- Starting with a similar pre-built node
- Forking and modifying it
- Publishing your improved version back to the registry
Finding the Right Node
By Use Case
I need to...
- Control a motor
motor-driver,differential-drive,servo-controller - Read a sensor
lidar-driver,camera-node,imu-driver - Process data
kalman-filter,pid-controller,image-processor - Handle safety
emergency-stop,safety-monitor,watchdog - Log data
data-logger,rosbag-writer,csv-logger
By Hardware
# Search by device type
horus list lidar
horus list camera
horus list imu
By Category
Browse registry by category:
- control - Motion control, PID, path following
- perception - Sensors, computer vision, SLAM
- planning - Path planning, motion planning
- drivers - Hardware interfaces
- safety - Safety systems, fault tolerance
- utils - Logging, visualization, debugging
Package Quality Indicators
When choosing packages, look for:
High Download Count
Downloads: 5,234 (last 30 days)
Recent Updates
Last updated: 2025-09-28
Good Documentation
Documentation: 98% coverage
Active Maintenance
Issues: 2 open, 45 closed (96% resolution rate)
Complete Example: Autonomous Robot
Goal: Build an autonomous mobile robot that avoids obstacles
1. Install Components:
horus install lidar-driver
horus install obstacle-detector
horus install path-planner
horus install differential-drive
horus install emergency-stop
2. Compose System:
use lidar_driver::LidarNode;
use obstacle_detector::ObstacleDetectorNode;
use path_planner::LocalPlannerNode;
use differential_drive::DiffDriveNode;
use emergency_stop::EStopNode;
use horus::prelude::*;
fn main() -> Result<()> {
let mut scheduler = Scheduler::new();
// Safety (order 0 - runs first)
scheduler.add(EStopNode::new("estop", "cmd_vel")?).order(0).build()?;
// Sensors (order 1)
scheduler.add(LidarNode::new("/dev/ttyUSB0", "scan")?).order(1).build()?;
// Perception (order 2)
scheduler.add(ObstacleDetectorNode::new("scan", "obstacles")?).order(2).build()?;
// Planning (order 3)
scheduler.add(LocalPlannerNode::new("obstacles", "cmd_vel")?).order(3).build()?;
// Control (order 4)
scheduler.add(DiffDriveNode::new("cmd_vel", "motor.left", "motor.right", 0.5)?).order(4).build()?;
scheduler.run()?;
Ok(())
}
That's a full autonomous robot in ~40 lines of configuration!
Next Steps
- Package Management - Discover and manage packages
- node! Macro - When you need custom functionality
- Examples - See complete working systems