Control Messages

HORUS provides comprehensive message types for controlling motors, servos, and multi-joint robotic systems. All control messages are fixed-size types with #[repr(C)] layout, enabling zero-copy shared memory transport at ~50ns latency.

MotorCommand

Direct motor control with multiple control modes.

use horus::prelude::*; // Provides control::MotorCommand;

// Velocity control
let vel_cmd = MotorCommand::velocity(0, 1.5);  // motor_id=0, 1.5 rad/s

// Position control with max velocity
let pos_cmd = MotorCommand::position(0, 3.14, 2.0);  // motor_id=0, 3.14 rad, max 2 rad/s

// Stop motor
let stop_cmd = MotorCommand::stop(0);

// Check validity
if vel_cmd.is_valid() {
    println!("Target: {:.2}", vel_cmd.target);
}

Control Modes:

ConstantValueDescription
MODE_VELOCITY0Velocity control (rad/s)
MODE_POSITION1Position control (rad)
MODE_TORQUE2Torque control (Nm)
MODE_VOLTAGE3Direct voltage control

Fields:

FieldTypeDescription
motor_idu8Motor identifier
modeu8Control mode
targetf64Target value
max_velocityf64Velocity limit (position mode)
max_accelerationf64Acceleration limit
feed_forwardf64Feed-forward term
enableu8Motor enabled (1=enabled, 0=disabled)
timestamp_nsu64Nanoseconds since epoch

DifferentialDriveCommand

Commands for differential drive robots.

use horus::prelude::*; // Provides control::DifferentialDriveCommand;

// Direct wheel velocities
let cmd = DifferentialDriveCommand::new(1.0, 1.2);  // left=1.0, right=1.2 rad/s

// From linear and angular velocities
let wheel_base = 0.5;   // 50cm between wheels
let wheel_radius = 0.1; // 10cm wheels
let cmd = DifferentialDriveCommand::from_twist(
    0.5,  // 0.5 m/s forward
    0.2,  // 0.2 rad/s rotation
    wheel_base,
    wheel_radius
);

// Stop
let stop = DifferentialDriveCommand::stop();

Fields:

FieldTypeDescription
left_velocityf64Left wheel velocity (rad/s)
right_velocityf64Right wheel velocity (rad/s)
max_accelerationf64Acceleration limit (rad/s²)
enableu8Motors enabled (1=enabled, 0=disabled)
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
new(left, right)Create with wheel velocities (rad/s)
from_twist(linear, angular, wheel_base, wheel_radius)Create from linear/angular velocity
stop()Stop both motors
is_valid()Check if all values are finite

ServoCommand

Position-controlled servo commands.

use horus::prelude::*; // Provides control::ServoCommand;

// Position command (radians)
let cmd = ServoCommand::new(0, 1.57);  // servo_id=0, 90 degrees

// With specific speed (0-1)
let cmd = ServoCommand::with_speed(0, 1.57, 0.3);  // 30% speed

// From degrees
let cmd = ServoCommand::from_degrees(0, 90.0);

// Disable servo (release torque)
let disable = ServoCommand::disable(0);

Fields:

FieldTypeDescription
servo_idu8Servo identifier
positionf32Target position (radians)
speedf32Movement speed (0-1, 0=max speed)
enableu8Torque enabled (1=enabled, 0=disabled)
timestamp_nsu64Nanoseconds since epoch

PidConfig

PID controller configuration.

use horus::prelude::*; // Provides control::PidConfig;

// Full PID
let pid = PidConfig::new(1.0, 0.1, 0.05);  // Kp, Ki, Kd

// P-only controller
let p_only = PidConfig::proportional(2.0);

// PI controller
let pi = PidConfig::pi(1.5, 0.2);

// PD controller
let pd = PidConfig::pd(1.0, 0.1);

// With limits
let pid_limited = PidConfig::new(1.0, 0.1, 0.05)
    .with_limits(10.0, 100.0);  // integral_limit, output_limit

// Validation
if pid.is_valid() {
    println!("Kp={}, Ki={}, Kd={}", pid.kp, pid.ki, pid.kd);
}

Fields:

FieldTypeDescription
controller_idu8Controller identifier
kpf64Proportional gain
kif64Integral gain
kdf64Derivative gain
integral_limitf64Integral windup limit
output_limitf64Output saturation limit
anti_windupu8Anti-windup enabled (1=enabled, 0=disabled)
timestamp_nsu64Nanoseconds since epoch

TrajectoryPoint

Single point in a trajectory.

use horus::prelude::*; // Provides control::TrajectoryPoint;

// Simple 2D trajectory point
let point = TrajectoryPoint::new_2d(
    1.0, 2.0,   // x, y position
    0.5, 0.3,   // vx, vy velocity
    1.5         // time from start (seconds)
);

// Stationary point (x, y, z)
let waypoint = TrajectoryPoint::stationary(1.0, 2.0, 0.0);

Fields:

FieldTypeDescription
position[f64; 3]Position [x, y, z]
velocity[f64; 3]Velocity [vx, vy, vz]
acceleration[f64; 3]Acceleration [ax, ay, az]
orientation[f64; 4]Quaternion [x, y, z, w]
angular_velocity[f64; 3]Angular velocity [wx, wy, wz]
time_from_startf64Time offset (seconds)

JointCommand

Multi-joint command for robot arms and manipulators.

use horus::prelude::*; // Provides control::JointCommand;

let mut cmd = JointCommand::new();

// Add position commands
cmd.add_position("shoulder", 0.5)?;
cmd.add_position("elbow", 1.0)?;
cmd.add_position("wrist", -0.3)?;

// Add velocity commands
cmd.add_velocity("gripper", 0.2)?;

Control Modes:

ConstantValueDescription
MODE_POSITION0Position control (rad)
MODE_VELOCITY1Velocity control (rad/s)
MODE_EFFORT2Torque/effort control (Nm)

Fields:

FieldTypeDescription
joint_names[[u8; 32]; 16]Joint name strings
joint_countu8Number of joints (max 16)
positions[f64; 16]Position commands (rad)
velocities[f64; 16]Velocity commands (rad/s)
efforts[f64; 16]Effort commands (Nm)
modes[u8; 16]Control mode per joint
timestamp_nsu64Nanoseconds since epoch

Motor Control Node Example

use horus::prelude::*;

struct MotorDriverNode {
    cmd_sub: Topic<CmdVel>,
    left_motor_pub: Topic<MotorCommand>,
    right_motor_pub: Topic<MotorCommand>,
    wheel_base: f64,
    wheel_radius: f64,
}

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

    fn tick(&mut self) {
        if let Some(cmd) = self.cmd_sub.recv() {
            // Convert CmdVel to differential drive
            let diff = DifferentialDriveCommand::from_twist(
                cmd.linear as f64,
                cmd.angular as f64,
                self.wheel_base,
                self.wheel_radius
            );

            // Send motor velocity commands
            self.left_motor_pub.send(MotorCommand::velocity(0, diff.left_velocity));
            self.right_motor_pub.send(MotorCommand::velocity(1, diff.right_velocity));
        }
    }
}

See Also