Standard Messages
Standard message types for robotics communication. Most messages are designed for zero-copy shared memory transport — see POD Types and Zero-Copy for the exceptions.
use horus::prelude::*;
Need to name a type rather than glob it in? Every message on this page also has a path:
horus::msg::Twist, horus::msg::Imu, horus::msg::CmdVel. That is the same spelling C++
uses (horus::msg::Twist out of <horus/msg/geometry.hpp>), and it is deliberate — the
crate a message is filed under (horus_types, horus_robotics) is a packaging decision
that used to leak into every use line and differ per type. horus::msg re-exports them
all under one path; the crate paths still resolve, so nothing that already compiles breaks.
Image, PointCloud, DepthImage and Transform are buffer and frame types rather than
wire messages, but they are re-exported under horus::msg too — along with the element types
you need to fill them (ImageEncoding, PointXYZ, PointXYZI, PointXYZRGB) — so
horus::msg::Image resolves right next to horus::msg::LaserScan. All of them stay on
horus::prelude:: as well.
Geometry
Spatial primitives for position, orientation, and motion.
Twist
3D velocity command with linear and angular components.
pub struct Twist {
pub linear: [f64; 3], // [x, y, z] in m/s
pub angular: [f64; 3], // [roll, pitch, yaw] in rad/s
pub timestamp_ns: u64, // Timestamp in nanoseconds since epoch
}
Constructors
// Full 3D velocity
let twist = Twist::new([1.0, 0.0, 0.0], [0.0, 0.0, 0.5]);
// 2D velocity (forward + rotation)
let twist = Twist::new_2d(1.0, 0.5); // 1 m/s forward, 0.5 rad/s rotation
// Stop command
let twist = Twist::stop();
Methods
| Method | Description |
|---|---|
is_valid() | Returns true if all values are finite |
Pose2D
2D pose (position and orientation) for planar robots.
pub struct Pose2D {
pub x: f64, // X position in meters
pub y: f64, // Y position in meters
pub theta: f64, // Orientation in radians
pub timestamp_ns: u64, // Timestamp in nanoseconds since epoch
}
Constructors
let pose = Pose2D::new(1.0, 2.0, 0.5); // x=1m, y=2m, theta=0.5rad
let pose = Pose2D::origin(); // (0, 0, 0)
Methods
| Method | Description |
|---|---|
distance_to(&other) | Euclidean distance to another pose |
normalize_angle() | Normalize theta to [-π, π] |
is_valid() | Returns true if all values are finite |
TransformStamped
Timestamped 3D transformation (translation + quaternion rotation) for message passing.
pub struct TransformStamped {
pub translation: [f64; 3], // [x, y, z] in meters
pub rotation: [f64; 4], // Quaternion [x, y, z, w]
pub timestamp_ns: u64,
}
Constructors
let tf = TransformStamped::identity();
let tf = TransformStamped::new([1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]);
let tf = TransformStamped::from_pose_2d(&pose);
Methods
| Method | Description |
|---|---|
is_valid() | Check if quaternion is normalized |
normalize_rotation() | Normalize the quaternion |
Note: For coordinate frame management (lookups, chains, interpolation), see Transform Frame which uses its own Transform type.
Point3
3D point in space.
pub struct Point3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
Methods
let p1 = Point3::new(1.0, 2.0, 3.0);
let p2 = Point3::origin();
let dist = p1.distance_to(&p2);
Vector3
3D vector for representing directions and velocities.
pub struct Vector3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
Methods
| Method | Description |
|---|---|
magnitude() | Vector length |
normalize() | Normalize to unit vector |
dot(&other) | Dot product |
cross(&other) | Cross product |
Quaternion
Quaternion for 3D rotation representation.
pub struct Quaternion {
pub x: f64,
pub y: f64,
pub z: f64,
pub w: f64,
}
Constructors
let q = Quaternion::identity();
let q = Quaternion::new(0.0, 0.0, 0.0, 1.0);
let q = Quaternion::from_euler(roll, pitch, yaw);
Sensor
Standard sensor data formats.
LaserScan
2D LiDAR scan data with 360 range measurements.
pub struct LaserScan {
pub ranges: [f32; 360], // Range measurements in meters
pub angle_min: f32, // Start angle in radians
pub angle_max: f32, // End angle in radians
pub range_min: f32, // Minimum valid range
pub range_max: f32, // Maximum valid range
pub angle_increment: f32, // Angular resolution
pub time_increment: f32, // Time between measurements
pub scan_time: f32, // Full scan time
pub timestamp_ns: u64,
}
Constructors
let scan = LaserScan::new();
let scan = LaserScan::default();
Methods
| Method | Description |
|---|---|
angle_at(index) | Get angle for range index |
is_range_valid(index) | Check if reading is valid |
valid_count() | Count valid readings |
min_range() | Get minimum valid range |
Example
if let Some(scan) = scan_sub.recv() {
if let Some(min_dist) = scan.min_range() {
if min_dist < 0.5 {
// Obstacle detected!
}
}
}
Imu
IMU sensor data (orientation, angular velocity, acceleration).
pub struct Imu {
pub orientation: [f64; 4], // Quaternion [x, y, z, w]
pub orientation_covariance: [f64; 9], // 3x3 covariance (-1 = no data)
pub angular_velocity: [f64; 3], // [x, y, z] in rad/s
pub angular_velocity_covariance: [f64; 9],
pub linear_acceleration: [f64; 3], // [x, y, z] in m/s²
pub linear_acceleration_covariance: [f64; 9],
pub timestamp_ns: u64,
}
Constructors
let imu = Imu::new();
Methods
| Method | Description |
|---|---|
set_orientation_from_euler(roll, pitch, yaw) | Set orientation from Euler angles |
has_orientation() | Check if orientation data is available |
is_valid() | Check if all values are finite |
angular_velocity_vec() | Get angular velocity as Vector3 |
linear_acceleration_vec() | Get linear acceleration as Vector3 |
Odometry
Combined pose and velocity estimate.
pub struct Odometry {
pub pose: Pose2D,
pub twist: Twist,
pub pose_covariance: [f64; 36], // 6x6 covariance
pub twist_covariance: [f64; 36],
pub frame_id: [u8; 32], // e.g., "odom"
pub child_frame_id: [u8; 32], // e.g., "base_link"
pub timestamp_ns: u64,
}
Methods
| Method | Description |
|---|---|
set_frames(frame, child) | Set frame IDs |
update(pose, twist) | Update pose and velocity |
is_valid() | Check validity |
NavSatFix
GPS/GNSS position data.
pub struct NavSatFix {
pub latitude: f64, // Degrees (+ North, - South)
pub longitude: f64, // Degrees (+ East, - West)
pub altitude: f64, // Meters above WGS84
pub position_covariance: [f64; 9],
pub position_covariance_type: u8,
pub status: u8, // Fix status
pub satellites_visible: u16,
pub hdop: f32,
pub vdop: f32,
pub speed: f32, // m/s
pub heading: f32, // degrees
pub timestamp_ns: u64,
}
Constants
NavSatFix::STATUS_NO_FIX // 0
NavSatFix::STATUS_FIX // 1
NavSatFix::STATUS_SBAS_FIX // 2
NavSatFix::STATUS_GBAS_FIX // 3
Methods
| Method | Description |
|---|---|
from_coordinates(lat, lon, alt) | Create from coordinates |
has_fix() | Check if GPS has fix |
is_valid() | Check coordinate validity |
horizontal_accuracy() | Estimated accuracy in meters |
distance_to(&other) | Distance to another position (Haversine) |
BatteryState
Battery status information.
pub struct BatteryState {
pub voltage: f32, // Volts
pub current: f32, // Amperes (negative = discharging)
pub charge: f32, // Amp-hours
pub capacity: f32, // Amp-hours
pub percentage: f32, // 0-100
pub power_supply_status: u8,
pub temperature: f32, // Celsius
pub cell_voltages: [f32; 16],
pub cell_count: u8,
pub timestamp_ns: u64,
}
Constants
BatteryState::STATUS_UNKNOWN // 0
BatteryState::STATUS_CHARGING // 1
BatteryState::STATUS_DISCHARGING // 2
BatteryState::STATUS_FULL // 3
Methods
| Method | Description |
|---|---|
new(voltage, percentage) | Create new battery state |
is_low(threshold) | Check if below threshold |
is_critical() | Check if below 10% |
time_remaining() | Estimated time in seconds |
RangeSensor
Single-point distance measurement (ultrasonic, IR).
pub struct RangeSensor {
pub sensor_type: u8, // 0=ultrasonic, 1=infrared
pub field_of_view: f32, // radians
pub min_range: f32, // meters
pub max_range: f32, // meters
pub range: f32, // meters
pub timestamp_ns: u64,
}
Vision
Image and camera data types.
Image
Pool-backed RAII camera image type with zero-copy shared memory transport. Fields are private — use accessor methods.
// Image is an RAII type, not a plain struct.
// Create with Image::new(width, height, encoding)?
// Access data with .data(), .pixel(), etc.
// See Vision Messages for full API.
let mut img = Image::new(640, 480, ImageEncoding::Rgb8)?;
img.copy_from(&pixel_data);
ImageEncoding
pub enum ImageEncoding {
Rgb8,
Bgr8,
Rgba8,
Bgra8,
Mono8,
Mono16,
Yuv422,
Mono32F,
Rgb32F,
BayerRggb8,
Depth16,
}
CameraInfo
Camera calibration information.
pub struct CameraInfo {
pub width: u32,
pub height: u32,
pub distortion_model: [u8; 16],
pub distortion_coefficients: [f64; 8], // [k1, k2, p1, p2, k3, k4, k5, k6]
pub camera_matrix: [f64; 9], // Intrinsic matrix (3x3)
pub rectification_matrix: [f64; 9], // Rectification (3x3)
pub projection_matrix: [f64; 12], // Projection (3x4)
pub frame_id: [u8; 32], // Camera identifier
pub timestamp_ns: u64,
}
Detection
Object detection result.
pub struct Detection {
pub bbox: BoundingBox2D,
pub confidence: f32,
pub class_id: u32,
pub class_name: [u8; 32],
pub instance_id: u32,
}
Control
Actuator command messages.
MotorCommand
Motor control command.
pub struct MotorCommand {
pub motor_id: u8,
pub mode: u8, // 0=velocity, 1=position, 2=torque, 3=voltage
pub target: f64,
pub max_velocity: f64,
pub max_acceleration: f64,
pub feed_forward: f64,
pub enable: u8,
pub timestamp_ns: u64,
}
ServoCommand
Servo position command.
pub struct ServoCommand {
pub servo_id: u8,
pub position: f32, // radians
pub speed: f32, // 0-1 (0 = max speed)
pub enable: u8,
pub timestamp_ns: u64,
}
PidConfig
PID controller configuration.
pub struct PidConfig {
pub controller_id: u8,
pub kp: f64,
pub ki: f64,
pub kd: f64,
pub integral_limit: f64,
pub output_limit: f64,
pub anti_windup: u8,
pub timestamp_ns: u64,
}
GenericMessage
Dynamic message type for cross-language communication. Uses a fixed-size buffer (4KB max payload) with MessagePack serialization, making it safe for shared memory transport.
pub struct GenericMessage {
inline_data: [u8; 256], // First 256 bytes (inline)
overflow_data: [u8; 3840], // Overflow up to 3840 more bytes
metadata: [u8; 256], // Optional metadata
// ... internal length tracking fields
}
Fields are private — use data(), metadata() and to_value::<T>().
Total maximum payload: 4,096 bytes (inline_data + overflow_data).
Constructors
// From raw bytes (returns Err if > 4KB)
let msg = GenericMessage::new(data_vec)?;
// From any serializable type (uses MessagePack)
let msg = GenericMessage::from_value(&my_struct)?;
// With metadata string (max 255 chars)
let msg = GenericMessage::with_metadata(data, "my_type".to_string())?;
Methods
| Method | Return Type | Description |
|---|---|---|
data() | Vec<u8> | Get payload bytes |
metadata() | Option<String> | Get metadata string if present |
to_value::<T>() | HorusResult<T> (i.e. Result<T, HorusError>) | Deserialize from MessagePack to typed value |
Example
use std::collections::HashMap;
// Send any serializable data
let mut data = HashMap::new();
data.insert("x", 1.0);
data.insert("y", 2.0);
let msg = GenericMessage::from_value(&data)?;
topic.send(msg);
// Receive and deserialize
if let Some(msg) = topic.recv() {
let data: HashMap<String, f64> = msg.to_value()?;
println!("x: {}", data["x"]);
}
Use typed messages (e.g., Twist, Pose2D) instead of GenericMessage whenever possible — they are far smaller on the wire, skip the MessagePack encode/decode, and are type-safe.
POD Types and Zero-Copy
Most HORUS message types are POD (Plain Old Data) — they are fixed-size and need no Drop, so HORUS can hand them straight to shared memory at ~50ns latency, with no serialization step.
POD types (the large majority of message types): Fixed-size, no heap allocation, zero-copy capable.
Non-POD types: Either pool-backed RAII handles (Image, PointCloud, DepthImage), which send a small fixed-size descriptor while the payload stays in shared memory, or types holding a Vec for variable-size data, which are serialized with bincode.
| Category | POD / Total | Non-POD Types |
|---|---|---|
| Geometry | 6/6 | — |
| Sensor | 6/6 | — |
| Control | 8/8 | — |
| Diagnostics | 8/8 | — |
| Force/Haptics | 5/6 | TactileArray (Vec) |
| Detection | 4/4 | — |
| Navigation | 7/9 | OccupancyGrid, CostMap |
| Vision | 3/5 | Image (pool-backed RAII), CompressedImage (Vec) |
| Perception | 2/4 | PointCloud (pool-backed RAII), DepthImage (pool-backed RAII) |
POD detection is automatic — you don't need to configure anything. HORUS checks at topic creation time whether your type is POD and selects the optimal backend.
Size Reference
| Message | Size | POD |
|---|---|---|
Twist | 56 bytes | Yes |
Pose2D | 32 bytes | Yes |
LaserScan | ~1.5 KB | Yes |
Imu | 304 bytes | Yes |
Image | Pool-backed RAII | Descriptor is Pod |
GenericMessage | ~4.3 KB (4364 bytes) | Yes — fixed-size buffer, but each send copies all 4364 bytes plus a MessagePack encode/decode |
Detailed Message Documentation
For comprehensive documentation of specialized message types, see:
| Category | Description |
|---|---|
| Vision Messages | Image, CameraInfo, Detection, CompressedImage |
| Perception Messages | PointCloud, DepthImage, BoundingBox3D, PlaneDetection |
| Control Messages | MotorCommand, ServoCommand, DifferentialDriveCommand, PidConfig, JointCommand |
| Force & Tactile Messages | WrenchStamped, TactileArray, ImpedanceParameters, ForceCommand |
| ML Messages | SegmentationMask, and how tensors travel by pool handle rather than as a message struct |
| Navigation Messages | Goal, Path, OccupancyGrid, CostMap, VelocityObstacle |
| Diagnostics Messages | Heartbeat, DiagnosticStatus, EmergencyStop, ResourceUsage, SafetyStatus |
| TensorPool API | Zero-copy tensor memory management for CPU/GPU |