Python Message Library
55+ typed message classes for Python robotics. Zero-copy shared memory transport, nanosecond timestamps, and binary-compatible cross-language IPC.
Which messages do I need?
| I'm building a... | Start with these |
|---|---|
| Mobile robot | CmdVel, Odometry, LaserScan, Imu |
| Robot arm | JointState, JointCommand, WrenchStamped, TrajectoryPoint |
| Drone | Imu, NavSatFix, MotorCommand, BatteryState |
| Vision system | Image, Detection, PointCloud, DepthImage |
| Multi-robot | Pose2D, Heartbeat, DiagnosticStatus, TransformStamped |
| Teleoperation | JoystickInput, CmdVel, EmergencyStop |
Overview
Key Features:
- 55+ message types — All standard robotics messages available in Python
- Zero-copy IPC — POD types transfer via shared memory with no serialization overhead
- Cross-language compatible — Binary-compatible across all horus language bindings
- Nanosecond timestamps — All messages include
timestamp_nsfield - Typed Topic support — Use
Topic(CmdVel)for type-safe pub/sub
All message types are importable directly from the top-level horus module (e.g., from horus import CmdVel, LaserScan). Category-based imports (from horus.messages.geometry import Pose2D) are also supported.
Quick Reference — All 55+ Types
Geometry (13 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
Vector3 | 3D vector for directions, forces, velocities | magnitude(), normalize(), dot(), cross(), zero() | docs |
Point3 | 3D point (location in space) | distance_to(), origin() | docs |
Quaternion | Rotation without gimbal lock | from_euler(), identity(), normalize(), is_valid() | docs |
Pose2D | 2D position + heading for mobile robots | distance_to(), normalize_angle(), origin() | docs |
Pose3D | 3D position + quaternion orientation | from_pose_2d(), identity(), distance_to() | docs |
Twist | 6-DOF velocity (linear + angular) | stop(), new_2d(), is_valid() | docs |
CmdVel | 2D velocity command for ground robots | zero() | docs |
TransformStamped | 3D coordinate frame transform | identity(), from_pose_2d(), normalize_rotation() | docs |
PoseStamped | Pose with timestamp and frame ID | is_valid() | docs |
PoseWithCovariance | Pose + uncertainty estimate (6x6) | position_variance(), orientation_variance() | docs |
TwistWithCovariance | Velocity + uncertainty estimate | linear_variance(), angular_variance() | docs |
Accel | Linear + angular acceleration | is_valid() | docs |
AccelStamped | Acceleration with timestamp | is_valid() | docs |
Sensor (13 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
LaserScan | 2D LiDAR scan (array of range readings) | angle_at(), is_range_valid(), min_range(), len() | docs |
Imu | Accelerometer + gyroscope + optional orientation | set_orientation_from_euler(), has_orientation(), angular_velocity_vec() | docs |
Odometry | 2D pose + velocity from wheel encoders | set_frames(), update(), is_valid() | docs |
JointState | Multi-joint positions, velocities, efforts | position(name), velocity(name), effort(name) | docs |
BatteryState | Battery voltage, percentage, current | is_critical(), is_low(), time_remaining() | docs |
NavSatFix | GPS/GNSS position with fix status | from_coordinates(), has_fix(), distance_to() | docs |
RangeSensor | Single-point distance measurement | Field access only | docs |
MagneticField | Magnetometer reading (Tesla) | Field access only | docs |
Temperature | Temperature in Celsius + variance | Field access only | docs |
FluidPressure | Barometric/fluid pressure (Pascals) | Field access only | docs |
Illuminance | Ambient light level (lux) | Field access only | docs |
Clock | Time source (wall, sim, replay) | wall_clock(), sim_time(), replay_time(), elapsed_since() | docs |
TimeReference | External time sync (GPS, NTP) | correct_timestamp() | docs |
Control (7 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
MotorCommand | Individual motor control | velocity(), position(), stop() | docs |
ServoCommand | Angle-based servo control | from_degrees(), with_speed(), disable() | docs |
DifferentialDriveCommand | Left/right wheel speeds | from_twist(), stop() | docs |
PidConfig | PID controller gains + presets | proportional(), pi(), pd(), with_limits() | docs |
TrajectoryPoint | Position + velocity + time for paths | new_2d(), stationary() | docs |
JointCommand | Multi-joint position/velocity commands | add_position(), add_velocity() | docs |
CmdVel | 2D velocity (also in Geometry) | zero() | docs |
Navigation (9 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
NavGoal | Target position with tolerance checking | is_reached(), is_position_reached(), with_timeout() | docs |
NavPath | Ordered waypoint sequence | closest_waypoint_index(), calculate_progress() | docs |
Waypoint | Single path point with velocity constraints | with_velocity(), with_stop() | docs |
GoalResult | Navigation goal execution feedback | with_error() | docs |
OccupancyGrid | 2D obstacle map (free/occupied/unknown) | world_to_grid(), grid_to_world(), is_free(), is_occupied() | docs |
CostMap | Inflated cost map for path planning | cost(), compute_costs() | docs |
PathPlan | Planned path output | add_waypoint(), from_waypoints(), is_empty() | docs |
VelocityObstacle | Dynamic obstacle for reactive avoidance | Field access only | docs |
VelocityObstacles | Collection of velocity obstacles | Field access only | docs |
Diagnostics (8 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
DiagnosticStatus | Node health with severity levels | ok(), warn(), error(), fatal(), with_component() | docs |
EmergencyStop | E-stop trigger and release | engage(), release(), with_source() | docs |
ResourceUsage | CPU, memory, temperature monitoring | is_cpu_high(), is_memory_high(), is_temperature_high() | docs |
SafetyStatus | Safety system state machine | is_safe(), set_fault(), clear_faults() | docs |
DiagnosticReport | Typed key-value diagnostics | add_string(), add_int(), add_float(), add_bool() | docs |
DiagnosticValue | Single diagnostic key-value pair | Field access only | docs |
Heartbeat | Topic-based "I'm alive" signal | update() | docs |
NodeHeartbeat | Filesystem-based cross-process heartbeat | update_timestamp(), is_fresh() | docs |
Force and Haptics (6 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
WrenchStamped | 6-DOF force/torque measurement | force_magnitude(), torque_magnitude(), exceeds_limits(), filter() | docs |
ForceCommand | Force-controlled actuator command | force_only(), surface_contact(), with_timeout() | docs |
ImpedanceParameters | Spring-damper compliance model | compliant(), stiff(), enable(), disable() | docs |
HapticFeedback | Haptic patterns for teleoperation | vibration(), force(), pulse() | docs |
ContactInfo | Contact detection and classification | is_in_contact(), contact_duration_seconds() | docs |
TactileArray | Grid of force readings (tactile sensor pad) | set_force(), get_force() | docs |
Perception (13 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
BoundingBox2D | 2D axis-aligned bounding box | iou(), area(), from_center(), as_xyxy() | docs |
BoundingBox3D | 3D oriented bounding box | with_rotation() | docs |
Detection | 2D object detection (class + bbox) | is_confident(), with_class_id() | docs |
Detection3D | 3D object detection with velocity | with_velocity() | docs |
TrackedObject | Multi-frame tracked object | is_tentative(), is_confirmed(), confirm(), update(), speed() | docs |
TrackingHeader | Tracking metadata (frame count, etc.) | Field access only | docs |
Landmark | 2D body pose keypoint | is_visible(), distance_to(), visible() | docs |
Landmark3D | 3D body pose keypoint | to_2d() | docs |
LandmarkArray | Skeleton with model presets | coco_pose(), mediapipe_pose(), mediapipe_hand(), mediapipe_face() | docs |
PlaneDetection | Detected planar surface | distance_to_point(), contains_point() | docs |
PlaneArray | Collection of detected planes | Field access only | docs |
SegmentationMask | Pixel-level image segmentation | semantic(), instance(), panoptic(), is_semantic() | docs |
PointField | Point cloud field descriptor | field_size() | docs |
Vision (7 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
Image | Zero-copy image (pool-backed) | from_numpy(), to_numpy(), to_torch() | docs |
PointCloud | Zero-copy 3D point cloud (pool-backed) | from_xyz(), point_count() | docs |
DepthImage | Zero-copy depth map (pool-backed) | get_depth(), set_depth(), depth_statistics() | docs |
CompressedImage | JPEG/PNG for network transport | format_str() | docs |
CameraInfo | Camera intrinsic calibration | focal_lengths(), principal_point(), with_distortion_model() | docs |
RegionOfInterest | Rectangular image region | contains(), area() | docs |
StereoInfo | Stereo camera calibration | depth_from_disparity(), disparity_from_depth() | docs |
Input and Audio (3 types)
| Type | One-Line Description | Key Methods | Details |
|---|---|---|---|
JoystickInput | Gamepad/joystick events | new_button(), new_axis(), is_button(), is_connected() | docs |
KeyboardInput | Keyboard events with modifiers | is_ctrl(), is_shift(), is_alt() | docs |
AudioFrame | Audio data from microphones | mono(), stereo(), multi_channel(), duration_ms() | docs |
Using Types with Topics
# simplified
import horus
# Typed topic — zero-copy Pod transport (~1.7us)
node = horus.Node(
pubs=[horus.CmdVel], # auto-creates topic named "cmd_vel"
subs=[horus.LaserScan], # auto-creates topic named "scan"
tick=my_tick,
rate=50
)
# String topic — GenericMessage with serialization (~6-12us)
node = horus.Node(
pubs=["my_data"], # GenericMessage — any dict works
subs=["sensor_raw"],
tick=my_tick,
rate=50
)
# Send typed
node.send("cmd_vel", horus.CmdVel(linear=1.0, angular=0.0))
# Send dict (generic)
node.send("my_data", {"x": 1.0, "y": 2.0})
Typed topics auto-derive the topic name from the type's __topic_name__ class attribute (e.g., CmdVel.__topic_name__ = 'cmd_vel'). Override with dict syntax: pubs={'my_name': horus.CmdVel}.
Cross-Language Compatibility
All 55+ Python message types are binary-compatible with their counterparts in other horus language bindings via zero-copy shared memory. A Python node publishing CmdVel on a topic can be consumed by any other language node subscribed to the same topic, with no serialization overhead.
# simplified
# Python publisher
from horus import Topic, Twist
topic = Topic(Twist)
topic.send(Twist(linear_x=1.0, angular_z=0.5))
Any node subscribed to the same topic receives the exact same bytes with zero-copy semantics.
Usage Patterns
Robot Controller with Multiple Sensors
# simplified
from horus import Node, Topic, CmdVel, LaserScan, Imu
scan_topic = Topic(LaserScan)
imu_topic = Topic(Imu)
cmd_topic = Topic(CmdVel)
def controller_tick(node):
scan = scan_topic.recv(node)
imu = imu_topic.recv(node)
if scan:
closest = scan.min_range()
if closest is not None and closest < 0.5:
cmd_topic.send(CmdVel.zero(), node) # Stop
else:
cmd_topic.send(CmdVel(linear=0.5, angular=0.0), node)
node = Node(name="controller", tick=controller_tick, rate=10,
pubs=["cmd_vel"], subs=["scan", "imu"])
Safety Monitor
# simplified
from horus import Node, run, BatteryState, EmergencyStop, DiagnosticStatus, Topic
battery_topic = Topic(BatteryState)
estop_topic = Topic(EmergencyStop)
diag_topic = Topic(DiagnosticStatus)
def safety_monitor(node):
battery = battery_topic.recv(node)
if battery is None:
return
if battery.is_critical():
estop_topic.send(
EmergencyStop.engage("Battery critical").with_source("safety"),
node
)
elif battery.is_low(20.0):
diag_topic.send(
DiagnosticStatus.warn(100, f"Battery at {battery.percentage:.0f}%"),
node
)
else:
diag_topic.send(DiagnosticStatus.ok("Battery OK"), node)
run(Node(tick=safety_monitor, rate=1,
pubs=["estop", "diagnostics"], subs=["battery"]))
Design Decisions
Why 55+ types instead of a minimal set? Mixed-language systems are the norm in robotics: compiled languages for real-time control, Python for ML and prototyping. If Python lacked certain message types, cross-language pipelines would break. Full parity means any topic can be consumed from Python with binary-compatible zero-copy transport.
Why PyO3 bindings instead of Python dataclasses? PyO3 bindings expose the actual compiled structs, so Python messages are binary-identical in shared memory. Python dataclasses would require a serialization layer, adding latency and risking format mismatches. The tradeoff is that adding a new message type requires a compiled rebuild, but the horus.msggen module provides runtime custom messages for rapid iteration.
Why from horus import X instead of horus.messages.X? Flat imports reduce typing and match the convention used across all horus language bindings. All 55+ types are importable from the top-level horus module. Category-based imports (from horus.messages.geometry import Pose2D) are also supported for code organization.
Why nanosecond timestamps on every message? Nanosecond precision is needed for sensor fusion (IMU at 1kHz needs sub-millisecond accuracy) and for correlating events across nodes. Embedding the timestamp in every message (instead of a separate Header) keeps messages flat and Pod-compatible.
See Also
- Python Bindings — Full Python API guide
- Python Perception Types — DetectionList, TrackedObject, COCOPose
- Custom Messages — Runtime and compiled message generation
- Python API Reference — Complete Python API for Node, Scheduler, Topics
- Message Types Overview — Conceptual message type documentation