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:
| Field | Type | Description |
|---|---|---|
force | Vector3 | Force [fx, fy, fz] in Newtons |
torque | Vector3 | Torque [tx, ty, tz] in Nm |
point_of_application | Point3 | Force application point |
frame_id | [u8; 32] | Reference frame |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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:
| Field | Type | Description |
|---|---|---|
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 |
enabled | u8 | Impedance control active (0 = off, 1 = on) |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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:
| Field | Type | Description |
|---|---|---|
target_force | Vector3 | Desired force [N] |
target_torque | Vector3 | Desired torque [Nm] |
force_mode | [u8; 6] | 1 = force control, 0 = position control per axis |
position_setpoint | Vector3 | Position target for position-controlled axes |
orientation_setpoint | Vector3 | Orientation target (Euler angles) |
max_deviation | Vector3 | Maximum position deviation |
gains | [f64; 6] | Control gains |
timeout_seconds | f64 | Command timeout (0 = no timeout) |
frame_id | [u8; 32] | Reference frame |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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:
ContactInfoandContactStateare 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:
| State | Value | Description |
|---|---|---|
NoContact | 0 | No contact detected (default) |
InitialContact | 1 | First contact moment |
StableContact | 2 | Established contact |
ContactLoss | 3 | Contact being broken |
Sliding | 4 | Sliding contact |
Impact | 5 | Impact detected |
Fields:
| Field | Type | Description |
|---|---|---|
state | u8 | Contact state (use ContactState as u8 to set) |
contact_force | f64 | Contact force magnitude |
contact_normal | Vector3 | Contact normal vector (estimated) |
contact_point | Point3 | Contact point (estimated) |
stiffness | f64 | Contact stiffness (estimated) |
damping | f64 | Contact damping (estimated) |
confidence | f32 | Detection confidence (0.0 to 1.0) |
contact_start_time | u64 | Time contact was first detected (ns) |
frame_id | [u8; 32] | Reference frame |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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:
HapticFeedbackis 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:
| Constant | Value | Description |
|---|---|---|
PATTERN_CONSTANT | 0 | Constant intensity |
PATTERN_PULSE | 1 | Pulsing pattern |
PATTERN_RAMP | 2 | Ramping intensity |
Fields:
| Field | Type | Description |
|---|---|---|
vibration_intensity | f32 | Vibration intensity (0.0 to 1.0) |
vibration_frequency | f32 | Vibration frequency in Hz |
duration_seconds | f32 | Duration of feedback in seconds |
force_feedback | Vector3 | Force feedback vector |
pattern_type | u8 | Feedback pattern (see constants) |
enabled | u8 | Enable/disable feedback (0 = off, 1 = on) |
timestamp_ns | u64 | Nanoseconds since epoch |
Methods:
| Method | Description |
|---|---|
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
- Geometry Messages - Vector3, Point3
- Control Messages - Motor and actuator commands