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 robotCmdVel, Odometry, LaserScan, Imu
Robot armJointState, JointCommand, WrenchStamped, TrajectoryPoint
DroneImu, NavSatFix, MotorCommand, BatteryState
Vision systemImage, Detection, PointCloud, DepthImage
Multi-robotPose2D, Heartbeat, DiagnosticStatus, TransformStamped
TeleoperationJoystickInput, 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_ns field
  • 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)

TypeOne-Line DescriptionKey MethodsDetails
Vector33D vector for directions, forces, velocitiesmagnitude(), normalize(), dot(), cross(), zero()docs
Point33D point (location in space)distance_to(), origin()docs
QuaternionRotation without gimbal lockfrom_euler(), identity(), normalize(), is_valid()docs
Pose2D2D position + heading for mobile robotsdistance_to(), normalize_angle(), origin()docs
Pose3D3D position + quaternion orientationfrom_pose_2d(), identity(), distance_to()docs
Twist6-DOF velocity (linear + angular)stop(), new_2d(), is_valid()docs
CmdVel2D velocity command for ground robotszero()docs
TransformStamped3D coordinate frame transformidentity(), from_pose_2d(), normalize_rotation()docs
PoseStampedPose with timestamp and frame IDis_valid()docs
PoseWithCovariancePose + uncertainty estimate (6x6)position_variance(), orientation_variance()docs
TwistWithCovarianceVelocity + uncertainty estimatelinear_variance(), angular_variance()docs
AccelLinear + angular accelerationis_valid()docs
AccelStampedAcceleration with timestampis_valid()docs

Sensor (13 types)

TypeOne-Line DescriptionKey MethodsDetails
LaserScan2D LiDAR scan (array of range readings)angle_at(), is_range_valid(), min_range(), len()docs
ImuAccelerometer + gyroscope + optional orientationset_orientation_from_euler(), has_orientation(), angular_velocity_vec()docs
Odometry2D pose + velocity from wheel encodersset_frames(), update(), is_valid()docs
JointStateMulti-joint positions, velocities, effortsposition(name), velocity(name), effort(name)docs
BatteryStateBattery voltage, percentage, currentis_critical(), is_low(), time_remaining()docs
NavSatFixGPS/GNSS position with fix statusfrom_coordinates(), has_fix(), distance_to()docs
RangeSensorSingle-point distance measurementField access onlydocs
MagneticFieldMagnetometer reading (Tesla)Field access onlydocs
TemperatureTemperature in Celsius + varianceField access onlydocs
FluidPressureBarometric/fluid pressure (Pascals)Field access onlydocs
IlluminanceAmbient light level (lux)Field access onlydocs
ClockTime source (wall, sim, replay)wall_clock(), sim_time(), replay_time(), elapsed_since()docs
TimeReferenceExternal time sync (GPS, NTP)correct_timestamp()docs

Control (7 types)

TypeOne-Line DescriptionKey MethodsDetails
MotorCommandIndividual motor controlvelocity(), position(), stop()docs
ServoCommandAngle-based servo controlfrom_degrees(), with_speed(), disable()docs
DifferentialDriveCommandLeft/right wheel speedsfrom_twist(), stop()docs
PidConfigPID controller gains + presetsproportional(), pi(), pd(), with_limits()docs
TrajectoryPointPosition + velocity + time for pathsnew_2d(), stationary()docs
JointCommandMulti-joint position/velocity commandsadd_position(), add_velocity()docs
CmdVel2D velocity (also in Geometry)zero()docs
TypeOne-Line DescriptionKey MethodsDetails
NavGoalTarget position with tolerance checkingis_reached(), is_position_reached(), with_timeout()docs
NavPathOrdered waypoint sequenceclosest_waypoint_index(), calculate_progress()docs
WaypointSingle path point with velocity constraintswith_velocity(), with_stop()docs
GoalResultNavigation goal execution feedbackwith_error()docs
OccupancyGrid2D obstacle map (free/occupied/unknown)world_to_grid(), grid_to_world(), is_free(), is_occupied()docs
CostMapInflated cost map for path planningcost(), compute_costs()docs
PathPlanPlanned path outputadd_waypoint(), from_waypoints(), is_empty()docs
VelocityObstacleDynamic obstacle for reactive avoidanceField access onlydocs
VelocityObstaclesCollection of velocity obstaclesField access onlydocs

Diagnostics (8 types)

TypeOne-Line DescriptionKey MethodsDetails
DiagnosticStatusNode health with severity levelsok(), warn(), error(), fatal(), with_component()docs
EmergencyStopE-stop trigger and releaseengage(), release(), with_source()docs
ResourceUsageCPU, memory, temperature monitoringis_cpu_high(), is_memory_high(), is_temperature_high()docs
SafetyStatusSafety system state machineis_safe(), set_fault(), clear_faults()docs
DiagnosticReportTyped key-value diagnosticsadd_string(), add_int(), add_float(), add_bool()docs
DiagnosticValueSingle diagnostic key-value pairField access onlydocs
HeartbeatTopic-based "I'm alive" signalupdate()docs
NodeHeartbeatFilesystem-based cross-process heartbeatupdate_timestamp(), is_fresh()docs

Force and Haptics (6 types)

TypeOne-Line DescriptionKey MethodsDetails
WrenchStamped6-DOF force/torque measurementforce_magnitude(), torque_magnitude(), exceeds_limits(), filter()docs
ForceCommandForce-controlled actuator commandforce_only(), surface_contact(), with_timeout()docs
ImpedanceParametersSpring-damper compliance modelcompliant(), stiff(), enable(), disable()docs
HapticFeedbackHaptic patterns for teleoperationvibration(), force(), pulse()docs
ContactInfoContact detection and classificationis_in_contact(), contact_duration_seconds()docs
TactileArrayGrid of force readings (tactile sensor pad)set_force(), get_force()docs

Perception (13 types)

TypeOne-Line DescriptionKey MethodsDetails
BoundingBox2D2D axis-aligned bounding boxiou(), area(), from_center(), as_xyxy()docs
BoundingBox3D3D oriented bounding boxwith_rotation()docs
Detection2D object detection (class + bbox)is_confident(), with_class_id()docs
Detection3D3D object detection with velocitywith_velocity()docs
TrackedObjectMulti-frame tracked objectis_tentative(), is_confirmed(), confirm(), update(), speed()docs
TrackingHeaderTracking metadata (frame count, etc.)Field access onlydocs
Landmark2D body pose keypointis_visible(), distance_to(), visible()docs
Landmark3D3D body pose keypointto_2d()docs
LandmarkArraySkeleton with model presetscoco_pose(), mediapipe_pose(), mediapipe_hand(), mediapipe_face()docs
PlaneDetectionDetected planar surfacedistance_to_point(), contains_point()docs
PlaneArrayCollection of detected planesField access onlydocs
SegmentationMaskPixel-level image segmentationsemantic(), instance(), panoptic(), is_semantic()docs
PointFieldPoint cloud field descriptorfield_size()docs

Vision (7 types)

TypeOne-Line DescriptionKey MethodsDetails
ImageZero-copy image (pool-backed)from_numpy(), to_numpy(), to_torch()docs
PointCloudZero-copy 3D point cloud (pool-backed)from_xyz(), point_count()docs
DepthImageZero-copy depth map (pool-backed)get_depth(), set_depth(), depth_statistics()docs
CompressedImageJPEG/PNG for network transportformat_str()docs
CameraInfoCamera intrinsic calibrationfocal_lengths(), principal_point(), with_distortion_model()docs
RegionOfInterestRectangular image regioncontains(), area()docs
StereoInfoStereo camera calibrationdepth_from_disparity(), disparity_from_depth()docs

Input and Audio (3 types)

TypeOne-Line DescriptionKey MethodsDetails
JoystickInputGamepad/joystick eventsnew_button(), new_axis(), is_button(), is_connected()docs
KeyboardInputKeyboard events with modifiersis_ctrl(), is_shift(), is_alt()docs
AudioFrameAudio data from microphonesmono(), 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