Force & Haptic Messages

HORUS provides message types for force/torque sensors, impedance control, and haptic feedback systems commonly used in manipulation tasks.

Re-exported types (available via use horus::prelude::*): WrenchStamped, ImpedanceParameters, ForceCommand.

Non-re-exported types (require direct import): ContactInfo, ContactState, HapticFeedback — import from horus_library::messages::force::*.

WrenchStamped

6-DOF force and torque measurement from force/torque sensors. Implements PodMessage for zero-copy transfer.

use horus::prelude::*;

// Create wrench measurement
let force = Vector3::new(10.0, 5.0, -2.0);   // Newtons
let torque = Vector3::new(0.1, 0.2, 0.05);   // Newton-meters

let wrench = WrenchStamped::new(force, torque)
    .with_frame_id("tool0");

// Check magnitudes
println!("Force magnitude: {:.2} N", wrench.force_magnitude());
println!("Torque magnitude: {:.3} Nm", wrench.torque_magnitude());

// Safety check
let max_force = 50.0;   // N
let max_torque = 5.0;   // Nm
if wrench.exceeds_limits(max_force, max_torque) {
    println!("Safety limits exceeded!");
}

// Create from force only
let force_only = WrenchStamped::force_only(Vector3::new(0.0, 0.0, -10.0));

// Create from torque only
let torque_only = WrenchStamped::torque_only(Vector3::new(0.0, 0.0, 0.5));

// Low-pass filter noisy sensor readings
let mut current = wrench;
current.filter(&previous_wrench, 0.1);  // alpha = 0.1 (heavy filtering)

Fields:

FieldTypeDescription
forceVector3Force [fx, fy, fz] in Newtons
torqueVector3Torque [tx, ty, tz] in Nm
point_of_applicationPoint3Force application point
frame_id[u8; 32]Reference frame
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
new(force, torque)Create a new wrench measurement
force_only(force)Create from force only (zero torque)
torque_only(torque)Create from torque only (zero force)
with_frame_id(frame_id)Set reference frame (builder pattern)
force_magnitude()Get force vector magnitude
torque_magnitude()Get torque vector magnitude
exceeds_limits(max_force, max_torque)Check if limits exceeded
filter(&prev_wrench, alpha)Apply low-pass filter (alpha 0.0-1.0)

ImpedanceParameters

Impedance control parameters for compliant manipulation. Implements PodMessage for zero-copy transfer.

use horus::prelude::*;

// Default impedance (moderate compliance)
let mut impedance = ImpedanceParameters::new();

// Compliant mode (low stiffness - for delicate tasks)
let compliant = ImpedanceParameters::compliant();
// stiffness: [100, 100, 100, 10, 10, 10]
// damping: [20, 20, 20, 2, 2, 2]

// Stiff mode (high stiffness - for precision tasks)
let stiff = ImpedanceParameters::stiff();
// stiffness: [5000, 5000, 5000, 500, 500, 500]
// damping: [100, 100, 100, 10, 10, 10]

// Enable/disable
impedance.enable();
impedance.disable();

// Custom parameters
impedance.stiffness = [500.0, 500.0, 200.0, 50.0, 50.0, 50.0];  // [Kx, Ky, Kz, Krx, Kry, Krz]
impedance.damping = [30.0, 30.0, 20.0, 3.0, 3.0, 3.0];          // [Dx, Dy, Dz, Drx, Dry, Drz]
impedance.force_limits = [50.0, 50.0, 30.0, 5.0, 5.0, 5.0];     // Safety limits

Fields:

FieldTypeDescription
stiffness[f64; 6]Stiffness [Kx, Ky, Kz, Krx, Kry, Krz]
damping[f64; 6]Damping [Dx, Dy, Dz, Drx, Dry, Drz]
inertia[f64; 6]Virtual inertia
force_limits[f64; 6]Force/torque limits
enabledu8Impedance control active (0 = off, 1 = on)
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
new()Create with default moderate stiffness/damping
compliant()Create with low stiffness for delicate tasks
stiff()Create with high stiffness for precision tasks
enable()Enable impedance control
disable()Disable impedance control

ForceCommand

Hybrid force/position control command. Implements PodMessage for zero-copy transfer.

use horus::prelude::*;

// Pure force command
let force_cmd = ForceCommand::force_only(Vector3::new(0.0, 0.0, -5.0));  // 5N downward

// Hybrid force/position control
// Force control on Z axis, position control on X/Y
let force_axes: [u8; 6] = [0, 0, 1, 0, 0, 0];  // 1=force, 0=position per axis
let target_force = Vector3::new(0.0, 0.0, -10.0);
let target_position = Vector3::new(0.5, 0.3, 0.0);

let hybrid_cmd = ForceCommand::hybrid(force_axes, target_force, target_position);

// Surface contact following
let surface_normal = Vector3::new(0.0, 0.0, 1.0);  // Horizontal surface
let contact_force = 5.0;  // 5N contact force
let surface_cmd = ForceCommand::surface_contact(contact_force, surface_normal);

// Set timeout
let cmd_with_timeout = force_cmd.with_timeout(5.0);  // 5 second timeout

Fields:

FieldTypeDescription
target_forceVector3Desired force [N]
target_torqueVector3Desired torque [Nm]
force_mode[u8; 6]1 = force control, 0 = position control per axis
position_setpointVector3Position target for position-controlled axes
orientation_setpointVector3Orientation target (Euler angles)
max_deviationVector3Maximum position deviation
gains[f64; 6]Control gains
timeout_secondsf64Command timeout (0 = no timeout)
frame_id[u8; 32]Reference frame
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
force_only(target_force)Create pure force command (all axes force-controlled)
hybrid(force_axes, target_force, target_position)Create hybrid force/position command
surface_contact(normal_force, surface_normal)Create surface following command
with_timeout(seconds)Set command timeout (builder pattern)

ContactInfo

Contact detection and classification. Implements PodMessage for zero-copy transfer.

Note: ContactInfo and ContactState are not re-exported in the prelude. Import directly: use horus_library::messages::force::{ContactInfo, ContactState};

use horus_library::messages::force::{ContactInfo, ContactState};

// Create contact info
let contact = ContactInfo::new(ContactState::StableContact, 15.0);  // 15N force

// Check contact state
if contact.is_in_contact() {
    println!("Contact force: {:.1} N", contact.contact_force);
    println!("Duration: {:.2}s", contact.contact_duration_seconds());
    println!("Confidence: {:.0}%", contact.confidence * 100.0);
}

ContactState values:

StateValueDescription
NoContact0No contact detected (default)
InitialContact1First contact moment
StableContact2Established contact
ContactLoss3Contact being broken
Sliding4Sliding contact
Impact5Impact detected

Fields:

FieldTypeDescription
stateu8Contact state (use ContactState as u8 to set)
contact_forcef64Contact force magnitude
contact_normalVector3Contact normal vector (estimated)
contact_pointPoint3Contact point (estimated)
stiffnessf64Contact stiffness (estimated)
dampingf64Contact damping (estimated)
confidencef32Detection confidence (0.0 to 1.0)
contact_start_timeu64Time contact was first detected (ns)
frame_id[u8; 32]Reference frame
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
new(state, force_magnitude)Create new contact info (takes ContactState, stores as u8)
is_in_contact()Check if currently in contact (InitialContact, StableContact, or Sliding)
contact_duration_seconds()Get contact duration in seconds

HapticFeedback

Haptic feedback commands for user interfaces. Implements PodMessage for zero-copy transfer.

Note: HapticFeedback is not re-exported in the prelude. Import directly: use horus_library::messages::force::HapticFeedback;

use horus::prelude::*;
use horus_library::messages::force::HapticFeedback;

// Vibration feedback
let vibration = HapticFeedback::vibration(
    0.8,   // intensity (0-1)
    250.0, // frequency (Hz)
    0.5    // duration (seconds)
);

// Force feedback
let force = HapticFeedback::force(
    Vector3::new(1.0, 0.0, 0.0),  // Force direction
    1.0                           // Duration (seconds)
);

// Pulse pattern
let pulse = HapticFeedback::pulse(
    0.6,   // intensity
    100.0, // frequency
    0.3    // duration
);

Pattern Types:

ConstantValueDescription
PATTERN_CONSTANT0Constant intensity
PATTERN_PULSE1Pulsing pattern
PATTERN_RAMP2Ramping intensity

Fields:

FieldTypeDescription
vibration_intensityf32Vibration intensity (0.0 to 1.0)
vibration_frequencyf32Vibration frequency in Hz
duration_secondsf32Duration of feedback in seconds
force_feedbackVector3Force feedback vector
pattern_typeu8Feedback pattern (see constants)
enabledu8Enable/disable feedback (0 = off, 1 = on)
timestamp_nsu64Nanoseconds since epoch

Methods:

MethodDescription
vibration(intensity, frequency, duration)Create vibration feedback (intensity clamped to 0-1)
force(force, duration)Create force feedback
pulse(intensity, frequency, duration)Create pulse pattern feedback

Force Control Node Example

use horus::prelude::*;

struct ForceControlNode {
    wrench_sub: Topic<WrenchStamped>,
    cmd_pub: Topic<ForceCommand>,
    impedance_pub: Topic<ImpedanceParameters>,
    target_force: f64,
    prev_wrench: Option<WrenchStamped>,
}

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

    fn tick(&mut self) {
        if let Some(mut wrench) = self.wrench_sub.recv() {
            // Apply low-pass filter
            if let Some(prev) = &self.prev_wrench {
                wrench.filter(prev, 0.2);
            }
            self.prev_wrench = Some(wrench);

            // Safety check
            if wrench.exceeds_limits(100.0, 10.0) {
                // Switch to compliant mode
                let compliant = ImpedanceParameters::compliant();
                self.impedance_pub.send(compliant);
                return;
            }

            // Force control to maintain target contact force
            let error = self.target_force - wrench.force.z;
            let correction = error * 0.001;  // Simple P control

            let cmd = ForceCommand::force_only(
                Vector3::new(0.0, 0.0, self.target_force + correction)
            );
            self.cmd_pub.send(cmd);
        }
    }
}

See Also