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:
| Constant | Value | Description |
|---|---|---|
MODE_VELOCITY | 0 | Velocity control (rad/s) |
MODE_POSITION | 1 | Position control (rad) |
MODE_TORQUE | 2 | Torque control (Nm) |
MODE_VOLTAGE | 3 | Direct voltage control |
Fields:
| Field | Type | Description |
|---|---|---|
motor_id | u8 | Motor identifier |
mode | u8 | Control mode |
target | f64 | Target value |
max_velocity | f64 | Velocity limit (position mode) |
max_acceleration | f64 | Acceleration limit |
feed_forward | f64 | Feed-forward term |
enable | u8 | Motor enabled (1=enabled, 0=disabled) |
timestamp_ns | u64 | Nanoseconds 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:
| Field | Type | Description |
|---|---|---|
left_velocity | f64 | Left wheel velocity (rad/s) |
right_velocity | f64 | Right wheel velocity (rad/s) |
max_acceleration | f64 | Acceleration limit (rad/s²) |
enable | u8 | Motors enabled (1=enabled, 0=disabled) |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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:
| Field | Type | Description |
|---|---|---|
servo_id | u8 | Servo identifier |
position | f32 | Target position (radians) |
speed | f32 | Movement speed (0-1, 0=max speed) |
enable | u8 | Torque enabled (1=enabled, 0=disabled) |
timestamp_ns | u64 | Nanoseconds 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:
| Field | Type | Description |
|---|---|---|
controller_id | u8 | Controller identifier |
kp | f64 | Proportional gain |
ki | f64 | Integral gain |
kd | f64 | Derivative gain |
integral_limit | f64 | Integral windup limit |
output_limit | f64 | Output saturation limit |
anti_windup | u8 | Anti-windup enabled (1=enabled, 0=disabled) |
timestamp_ns | u64 | Nanoseconds 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:
| Field | Type | Description |
|---|---|---|
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_start | f64 | Time 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:
| Constant | Value | Description |
|---|---|---|
MODE_POSITION | 0 | Position control (rad) |
MODE_VELOCITY | 1 | Velocity control (rad/s) |
MODE_EFFORT | 2 | Torque/effort control (Nm) |
Fields:
| Field | Type | Description |
|---|---|---|
joint_names | [[u8; 32]; 16] | Joint name strings |
joint_count | u8 | Number 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_ns | u64 | Nanoseconds 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
- Navigation Messages - Path and goal messages
- Sensor Messages - Sensor feedback data