Python Message Library
HORUS provides typed message classes for robotics applications in Python. These messages enable cross-language communication with Rust nodes.
Availability
The full standard message set — around 75 classes — ships today in the Rust extension and
is importable straight from the top-level horus package:
from horus import CmdVel, Pose2D, Imu, Odometry, LaserScan
from horus import Twist, Transform, MotorCommand, BatteryState, NavGoal, OccupancyGrid
Everything documented on this page is available now. The Rust types behind them live in
horus_types (geometry and universal IPC types), horus_robotics::messages::* (sensor,
control, navigation, vision, force) and horus_core::memory (the pool-backed domain types
Image, PointCloud and DepthImage); Transform comes from the horus_tf crate.
Two names on this page exist only in Rust and are not exposed to Python:
ImageEncoding (use plain lowercase strings such as "rgb8") and GoalStatus
(use the u8 GoalResult.status).
Overview
Key Features:
- Cross-language compatible - Binary-compatible with Rust message types
- Nanosecond timestamps - Most message types carry a
timestamp_nsfield. Geometry value types (Point3,Vector3,Quaternion,Transform), bounding boxes (BoundingBox2D,BoundingBox3D),Waypoint,DetectionandCostMapdo not - Typed Topic support - Use
Topic(CmdVel)for type-safe pub/sub
Geometry Messages
Pose2D
2D robot pose (position + orientation).
from horus import Pose2D
# Create pose (positional or keyword args)
pose = Pose2D(1.0, 2.0, 0.5)
pose = Pose2D(x=1.0, y=2.0, theta=0.5)
# Origin
origin = Pose2D(0.0, 0.0, 0.0)
# Properties (read/write)
pose.x = 1.5
pose.y = 2.5
pose.theta = 0.785
print(pose) # Pose2D(x=1.500, y=2.500, theta=0.785, timestamp_ns=0)
Fields:
x(f64): X position in metersy(f64): Y position in meterstheta(f64): Orientation in radianstimestamp_ns(u64): Nanosecond timestamp
Use with Topic:
from horus import Topic, Pose2D
topic = Topic(Pose2D) # Typed topic
topic.send(Pose2D(x=1.0, y=2.0, theta=0.5))
pose = topic.recv() # Returns Pose2D or None
Twist
Full 6-DOF velocity (linear + angular). Backed by horus_types::Twist. For 2D velocity
commands, use CmdVel.
from horus import Twist
twist = Twist(
linear_x=1.0, linear_y=0.0, linear_z=0.0, # m/s
angular_x=0.0, angular_y=0.0, angular_z=0.5 # rad/s
)
# Positional args work too, in the same order
twist = Twist(1.0, 0.0, 0.0, 0.0, 0.0, 0.5)
print(twist) # Twist(lin=[1.000, 0.000, 0.000], ang=[0.000, 0.000, 0.500], timestamp_ns=0)
Fields:
linear_x,linear_y,linear_z(f64): Linear velocity in m/sangular_x,angular_y,angular_z(f64): Angular velocity in rad/stimestamp_ns(u64): Nanosecond timestamp
There are no linear/angular list attributes — the six components are separate scalars.
Transform
3D transformation (translation + rotation quaternion). Backed by horus_tf::Transform.
from horus import Transform
tf = Transform(
translation=[1.0, 2.0, 0.0], # [x, y, z]
rotation=[0.0, 0.0, 0.0, 1.0] # Quaternion [x, y, z, w]
)
Fields:
translation(list[3]): Position [x, y, z] in metersrotation(list[4]): Orientation quaternion [x, y, z, w]
Transform carries no timestamp. For a stamped transform, use TransformStamped.
Point3, Vector3, Quaternion
Basic 3D geometric types. Backed by horus_types::{Point3, Vector3, Quaternion}.
from horus import Point3, Vector3, Quaternion
point = Point3(x=1.0, y=2.0, z=3.0)
vec = Vector3(x=1.0, y=0.0, z=0.0)
quat = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0)
Control Messages
CmdVel
2D velocity command (linear + angular).
from horus import CmdVel
# Create velocity command (positional or keyword args)
cmd = CmdVel(1.5, 0.3)
cmd = CmdVel(linear=1.0, angular=0.5)
# Stop command
stop = CmdVel(0.0, 0.0)
# Properties (read/write)
cmd.linear = 1.5 # m/s
cmd.angular = 0.3 # rad/s
print(cmd) # CmdVel(linear=1.500, angular=0.300, timestamp_ns=0)
Fields:
linear(f32): Forward velocity in m/sangular(f32): Angular velocity in rad/s (positive = counter-clockwise)timestamp_ns(u64): Nanosecond timestamp
Use with Topic:
from horus import Topic, CmdVel
topic = Topic(CmdVel) # Typed topic
topic.send(CmdVel(linear=1.0, angular=0.5))
cmd = topic.recv() # Returns CmdVel or None
Sensor Messages
LaserScan
2D LIDAR scan data (fixed 360-entry range array).
from horus import LaserScan
# Create laser scan with parameters
scan = LaserScan(
angle_min=-3.14159, # Start angle (radians)
angle_max=3.14159, # End angle (radians)
angle_increment=0.01745, # Angular resolution (radians)
range_min=0.1, # Minimum valid range (meters)
range_max=10.0, # Maximum valid range (meters)
ranges=[1.0, 1.1, 1.2], # Distance measurements (zero-padded to 360 entries)
)
# Properties (read/write)
scan.ranges = [1.0] * 360 # Always stored as 360 floats
scan.angle_min = -1.57
# Length = readings inside [range_min, range_max], not the array size
length = len(scan)
print(scan) # LaserScan(angle=[-1.57, 3.14], range=[0.10, 10.00], 360 points, timestamp_ns=0)
Fields:
ranges(list[f32]): Distance readings in meters — always 360 entries; a longer list is truncated and a shorter one is zero-filledangle_min(f32): Start angle in radiansangle_max(f32): End angle in radiansrange_min(f32): Minimum valid range in metersrange_max(f32): Maximum valid range in metersangle_increment(f32): Angular resolution in radianstimestamp_ns(u64): Nanosecond timestamp
Because the array is zero-padded, min(scan.ranges) is usually 0.0. Use scan.min_range()
for the nearest valid reading (None if there is none), scan.is_range_valid(i) for a single
index, and len(scan) for the count of valid readings.
Use with Topic:
from horus import Topic, LaserScan
topic = Topic(LaserScan) # Typed topic
# Send
scan = LaserScan(angle_min=-3.14, angle_max=3.14, ranges=read_lidar())
topic.send(scan)
# Receive
scan = topic.recv() # Returns LaserScan or None
if scan:
print(f"Got {len(scan)} valid range readings")
Imu
Inertial Measurement Unit data (acceleration + gyroscope).
from horus import Imu
# Create with positional or keyword args
imu = Imu(0.0, 0.0, 9.81, 0.0, 0.0, 0.0)
imu = Imu(
accel_x=0.0, accel_y=0.0, accel_z=9.81, # m/s²
gyro_x=0.0, gyro_y=0.0, gyro_z=0.1, # rad/s
)
# Properties (read/write)
imu.accel_x = 1.0
imu.gyro_z = 0.5
print(imu) # Imu(accel=[1.000, 0.000, 9.810], gyro=[0.000, 0.000, 0.500], timestamp_ns=0)
Fields:
accel_x,accel_y,accel_z(f64): Linear acceleration in m/s²gyro_x,gyro_y,gyro_z(f64): Angular velocity in rad/stimestamp_ns(u64): Nanosecond timestamp
Odometry
Robot odometry (2D pose + velocity).
from horus import Odometry
# All fields have defaults (0.0)
odom = Odometry()
odom = Odometry(x=1.0, y=2.0, theta=0.5, linear_velocity=0.5, angular_velocity=0.1)
# Properties (read/write)
odom.x = 1.0 # meters
odom.y = 2.0 # meters
odom.theta = 0.5 # radians
odom.linear_velocity = 0.5 # m/s (scalar)
odom.angular_velocity = 0.1 # rad/s (scalar)
print(odom) # Odometry(x=1.000, y=2.000, theta=0.500, v_lin=0.500, v_ang=0.100, timestamp_ns=0)
Fields:
x(f64): X position in metersy(f64): Y position in meterstheta(f64): Orientation in radianslinear_velocity(f64): Forward velocity in m/sangular_velocity(f64): Rotational velocity in rad/stimestamp_ns(u64): Nanosecond timestamp
BatteryState
Battery status information. Backed by horus_robotics::messages::sensor::BatteryState.
from horus import BatteryState
battery = BatteryState(voltage=12.6, percentage=85.0)
battery.current = 2.5 # Amps (positive = discharging)
battery.temperature = 25.0 # Celsius
Signature: BatteryState(voltage=0.0, percentage=0.0, current=0.0, temperature=25.0, power_supply_status=0, timestamp_ns=0).
NavSatFix
GPS/GNSS position fix. Backed by horus_robotics::messages::sensor::NavSatFix.
from horus import NavSatFix
gps = NavSatFix(latitude=37.7749, longitude=-122.4194, altitude=10.0)
Signature: NavSatFix(latitude=0.0, longitude=0.0, altitude=0.0, timestamp_ns=0).
RangeSensor
Single-point distance sensor (ultrasonic, IR, etc.). Backed by
horus_robotics::messages::sensor::RangeSensor.
from horus import RangeSensor
range_sensor = RangeSensor()
range_sensor.range = 1.5 # meters
range_sensor.min_range = 0.02 # minimum valid range
range_sensor.max_range = 4.0 # maximum valid range
range_sensor.field_of_view = 0.26 # radians (~15°)
Signature: RangeSensor(range=0.0, sensor_type=0, field_of_view=0.1, min_range=0.02, max_range=4.0, timestamp_ns=0).
Control Messages (Extended)
Backed by horus_robotics::messages::control in Rust. For basic velocity control, use CmdVel.
MotorCommand
Individual motor control.
from horus import MotorCommand
cmd = MotorCommand()
cmd.motor_id = 0 # Motor index (u8)
cmd.mode = 0 # Control mode (u8: 0=velocity, 1=position, 2=torque, 3=voltage)
cmd.target = 1.0 # Target value (f64, units depend on mode)
cmd.max_velocity = 10.0 # Velocity limit (f64)
cmd.max_acceleration = 5.0 # Acceleration limit (f64)
cmd.feed_forward = 0.0 # Feed-forward term (f64)
cmd.enable = True # Enable motor (bool — an int raises TypeError)
Defaults: MotorCommand(motor_id=0, mode=0, target=0.0, max_velocity=inf, max_acceleration=inf, feed_forward=0.0, enable=True, timestamp_ns=0).
DifferentialDriveCommand
Differential drive robot control.
from horus import DifferentialDriveCommand
cmd = DifferentialDriveCommand()
cmd.left_velocity = 1.0 # Left wheel velocity (m/s or rad/s)
cmd.right_velocity = 1.0 # Right wheel velocity
ServoCommand
Servo motor control.
from horus import ServoCommand
cmd = ServoCommand()
cmd.servo_id = 0 # Servo index (u8)
cmd.position = 1.57 # Target position in radians (f32)
cmd.speed = 1.0 # Normalized speed, clamped to 0.0-1.0 (f32)
cmd.enable = True # Torque enable (bool — an int raises TypeError)
Defaults: ServoCommand(servo_id=0, position=0.0, speed=0.5, enable=True, timestamp_ns=0).
PidConfig
PID controller configuration.
from horus import PidConfig
pid = PidConfig()
pid.kp = 1.0 # Proportional gain
pid.ki = 0.1 # Integral gain
pid.kd = 0.05 # Derivative gain
Vision Messages
CompressedImage and CameraInfo are backed by horus_robotics::messages::vision in
Rust, and Detection by horus_robotics::messages::detection. Image is separate — it
is a pool-backed domain type from horus_core::memory, not a vision message.
Image
Pool-backed image with zero-copy transport. Image is an RAII type that allocates from a global tensor pool — you don't set fields directly.
from horus import Image
# Create image (allocates from pool). Encoding is a plain lowercase string.
img = Image(height=480, width=640, encoding="rgb8")
# Or wrap an existing array
img = Image.from_numpy(arr)
# Copy pixel data in
img.copy_from(pixel_bytes)
# Access data — height/width/encoding are properties, not methods
arr = img.to_numpy() # zero-copy (H, W, C) numpy view
h = img.height
w = img.width
enc = img.encoding # e.g. 'rgb8'
px = img.pixel(10, 20) # single pixel as bytes
crop = img.roi(0, 0, 320, 240) # cropped region as bytes
There is no ImageEncoding class in Python. Valid encoding strings are
"rgb8", "bgr8", "rgba8", "bgra8", "mono8", "mono16", "yuv422",
"mono32f", "rgb32f", "bayer_rggb8" and "depth16".
CompressedImage
JPEG/PNG compressed image.
format is fixed at construction (read-only afterwards); data, width, height and
timestamp_ns stay writable.
from horus import CompressedImage
img = CompressedImage(
format="jpeg", # "jpeg" or "png"
data=jpeg_bytes, # Compressed image data
width=640,
height=480,
)
img.data = new_jpeg_bytes
print(img.data_len()) # method, not a property
CameraInfo
Camera calibration parameters.
Intrinsics are supplied through the constructor; the matrices are derived read-only views.
from horus import CameraInfo
info = CameraInfo(
width=640, height=480,
fx=525.0, fy=525.0, # Focal lengths in pixels
cx=320.0, cy=240.0, # Principal point in pixels
)
# with_distortion_model returns a NEW CameraInfo — there is no distortion_model attribute
info = info.with_distortion_model("plumb_bob")
# Read-only properties
info.camera_matrix # 3x3 intrinsic matrix, 9 floats (row-major)
info.distortion_coefficients # Up to 8 distortion coefficients
info.rectification_matrix # 3x3 rectification matrix
info.projection_matrix # 3x4 projection matrix, 12 floats (row-major)
# width and height stay writable
info.width = 1280
info.height = 720
Detection
Object detection result with 2D bounding box.
from horus import Detection, BoundingBox2D
det = Detection(
class_name="person", # Class label (truncated to 31 bytes)
confidence=0.95, # Detection confidence 0-1 (f32)
x=100.0, y=50.0, # Top-left corner in pixels (f32)
width=200.0, # Width in pixels (f32)
height=400.0, # Height in pixels (f32)
class_id=0, # Class index (u32)
instance_id=1, # Instance ID for tracking (u32)
)
# The scalar fields are read/write
det.confidence = 0.92
det.class_name = "cyclist"
Careful —
det.bboxreturns a copy of the box, sodet.bbox.x = 100.0is silently discarded. To change the geometry after construction, assign a whole box.
det.bbox = BoundingBox2D(100.0, 50.0, 200.0, 400.0) # x, y, width, height
Navigation Messages
Backed by horus_robotics::messages::navigation in Rust.
NavGoal
Navigation goal.
from horus import NavGoal
goal = NavGoal()
goal.x = 10.0 # Target X position
goal.y = 5.0 # Target Y position
goal.theta = 0.0 # Target orientation
goal.tolerance_position = 0.1 # Position tolerance (meters)
goal.tolerance_angle = 0.1 # Orientation tolerance (radians)
# Or via the constructor — note the kwargs are named the other way round:
goal = NavGoal(x=10.0, y=5.0, theta=0.0,
position_tolerance=0.1, angle_tolerance=0.1, timeout=30.0)
Goal status: there is no Python GoalStatus class — the Rust enum is not exported.
Progress and outcome come back on GoalResult, whose status is a u8:
from horus import GoalResult
result = GoalResult(goal_id=1, status=2, progress=1.0) # progress is a 0.0-1.0 fraction
result.status # 0=Pending, 1=Active, 2=Succeeded, 3=Aborted,
# 4=Cancelled, 5=Preempted, 6=TimedOut
NavPath
Sequence of waypoints.
from horus import NavPath, Waypoint
# Waypoints are appended one at a time (max 256); there is no `waypoints` attribute
path = NavPath()
for wp in [
Waypoint(x=0.0, y=0.0, theta=0.0),
Waypoint(x=5.0, y=0.0, theta=0.0),
Waypoint(x=5.0, y=5.0, theta=1.57),
]:
path.add_waypoint(wp)
print(path.waypoint_count) # 3 (read-only property)
print(path.total_length) # 0.0 — a plain writable field in meters; add_waypoint
# does not accumulate it, so set it yourself if you need it
for wp in path.get_waypoints():
print(wp.pose)
OccupancyGrid
2D occupancy map.
from horus import OccupancyGrid, Pose2D
# Dimensions are fixed at construction; width/height are read-only afterwards
grid = OccupancyGrid(
width=100, # Grid width (cells)
height=100, # Grid height (cells)
resolution=0.05, # Meters per cell
)
grid.origin = Pose2D(-2.5, -2.5, 0.0) # Map origin (single Pose2D, not origin_x/origin_y)
grid.data = [0] * (100 * 100) # i8 values: 0=free, 100=occupied, -1=unknown
# Helpers
grid.set_occupancy(10, 10, 100)
print(grid.occupancy(10, 10)) # 100
print(grid.world_to_grid(0.0, 0.0)) # (50, 50)
CostMap
Navigation cost map.
from horus import CostMap, OccupancyGrid
# A CostMap wraps an OccupancyGrid and inflates obstacles into it
grid = OccupancyGrid(width=100, height=100, resolution=0.05)
costmap = CostMap(grid=grid, inflation_radius=0.55) # meters
costmap.compute_costs()
c = costmap.cost(1.0, 2.0) # Cost 0-255 at world coordinates (x, y)
# costmap.costs is read-only (bytes, one u8 per cell).
# Also writable: inflation_radius, cost_scaling_factor, lethal_cost
Perception Messages
PointCloud and DepthImage are pool-backed domain types from horus_core::memory;
BoundingBox3D comes from horus_robotics::messages::detection.
PointCloud
Pool-backed 3D point cloud with zero-copy transport. PointCloud is an RAII type that allocates from a global tensor pool.
from horus import PointCloud
# Create point cloud (allocates from pool)
cloud = PointCloud(num_points=1000) # XYZ float32 by default
cloud = PointCloud(num_points=1000, fields=4) # XYZI
# Or wrap existing point data ((N, 3) or (N, 4) array)
cloud = PointCloud.from_numpy(arr)
# Access data
arr = cloud.to_numpy() # zero-copy (N, 3) numpy view
n = cloud.point_count
xyz = cloud.is_xyz()
p = cloud.point_at(0)
DepthImage
Pool-backed depth image with zero-copy transport. DepthImage is an RAII type that allocates from a global tensor pool.
from horus import DepthImage
# Create depth image (allocates from pool)
depth = DepthImage(height=480, width=640) # float32 meters
depth = DepthImage(height=480, width=640, dtype="uint16") # uint16 millimeters
# Or wrap existing data
depth = DepthImage.from_numpy(arr)
# Access data and parameters (height/width/depth_scale are properties, not calls)
arr = depth.to_numpy() # zero-copy (H, W) numpy view
h, w = depth.height, depth.width
scale = depth.depth_scale # Scale factor (f32)
d = depth.get_depth(320, 240) # Single depth reading in meters
stats = depth.depth_statistics() # (min, max, mean) in meters over valid pixels, or None
BoundingBox3D
3D bounding box.
from horus import BoundingBox3D
box = BoundingBox3D(
cx=1.0, cy=2.0, cz=0.5, # Center position [x, y, z]
length=0.5, width=0.5, height=1.8, # Dimensions in meters
yaw=0.0, # Heading in radians
)
# Full 3-axis rotation (roll/pitch/yaw), positional only:
box = BoundingBox3D.with_rotation(1.0, 2.0, 0.5, 0.5, 0.5, 1.8, 0.0, 0.0, 0.0)
print(box.volume) # 0.44999998... (volume is computed in f32)
The box carries geometry only — it has no class label or confidence. For labelled 3D
objects use Detection3D instead.
Force/Tactile Messages
Backed by horus_robotics::messages::force in Rust.
WrenchStamped
Force and torque measurement.
from horus import WrenchStamped
wrench = WrenchStamped(
fx=10.0, fy=0.0, fz=-9.81, # Force [fx, fy, fz] in Newtons
tx=0.0, ty=0.5, tz=0.0, # Torque [tx, ty, tz] in Nm
)
print(wrench.force_magnitude()) # 14.008...
print(wrench.torque_magnitude()) # 0.5
TactileArray
Tactile sensor array.
from horus import TactileArray
tactile = TactileArray(rows=4, cols=4) # array is sized by the constructor
tactile.forces = [0.0] * 16 # Per-taxel pressure values (f32)
tactile.set_force(1, 2, 3.5) # Set one taxel
print(tactile.get_force(1, 2))
ForceCommand
Force control command.
from horus import ForceCommand
cmd = ForceCommand(
fx=0.0, fy=0.0, fz=-10.0, # Desired force [fx, fy, fz] in Newtons
tx=0.0, ty=0.0, tz=0.0, # Desired torque [tx, ty, tz] in Nm
)
# Individual scalar properties (read/write)
cmd.fz = -12.0
cmd.timeout_seconds = 0.5
Cross-Language Compatibility
The Python message types are binary-compatible with Rust via shared memory. Python
writes horus.CmdVel, C++ writes horus::msg::CmdVel, and Rust writes
horus::msg::CmdVel — one spelling per language, and the same one in each:
| Python Class | C++ Type | Rust Type | Defining Crate |
|---|---|---|---|
CmdVel | horus::msg::CmdVel | horus::msg::CmdVel | horus_robotics |
Pose2D | horus::msg::Pose2D | horus::msg::Pose2D | horus_types |
Imu | horus::msg::Imu | horus::msg::Imu | horus_robotics |
Odometry | horus::msg::Odometry | horus::msg::Odometry | horus_robotics |
LaserScan | horus::msg::LaserScan | horus::msg::LaserScan | horus_robotics |
Twist | horus::msg::Twist | horus::msg::Twist | horus_types |
NavGoal | horus::msg::NavGoal | horus::msg::NavGoal | horus_robotics |
Transform | horus::Transform | horus::prelude::Transform | horus_tf |
Image | horus::Image | horus::prelude::Image | horus_core |
PointCloud | horus::PointCloud | horus::prelude::PointCloud | horus_core |
DepthImage | (no C++ type) | horus::prelude::DepthImage | horus_core |
The Defining Crate column is there for cargo doc, not for your import lines. Which
crate a type happens to live in is a packaging decision — horus_robotics and horus_tf
are separate git repositories — and it used to leak into every Rust use statement:
the same seven wire types had three unrelated paths (horus_robotics::CmdVel,
horus_robotics::messages::sensor::Imu, horus_types::Pose2D) while C++ spelled all of
them horus::msg::. horus::msg re-exports them under the C++ spelling; the old paths
still resolve, so nothing you have written breaks.
The last four are not in horus::msg: they are frame and buffer types rather than wire
messages, and they arrive through horus::prelude (which also pulls in everything
horus::msg has, so use horus::prelude::*; remains the one import an application
needs).
Example - Python to Rust:
# Python sender
from horus import Topic, CmdVel
topic = Topic(CmdVel) # Typed topic
topic.send(CmdVel(linear=1.0, angular=0.5))
// Rust receiver
use horus::prelude::*;
let topic: Topic<CmdVel> = Topic::new("cmd_vel")?;
if let Some(cmd) = topic.recv() {
println!("Received: linear={}, angular={}", cmd.linear, cmd.angular);
}
Usage Patterns
Robot Controller with Multiple Sensors
from horus import Node, Topic, CmdVel, LaserScan
# Create topics outside tick function
scan_topic = Topic(LaserScan)
cmd_topic = Topic(CmdVel)
def controller_tick(node):
scan = scan_topic.recv()
if scan:
# Simple obstacle avoidance — min_range() skips the zero padding
min_dist = scan.min_range() # nearest valid reading, or None
if min_dist and min_dist < 0.5:
# Too close - stop
cmd_topic.send(CmdVel(0.0, 0.0))
else:
# Safe - move forward
cmd_topic.send(CmdVel(linear=0.5, angular=0.0))
node = Node(name="controller", tick=controller_tick, rate=10)
Pose Tracking
import math
from horus import Topic, Pose2D
# Track robot pose
pose_topic = Topic(Pose2D)
current_pose = Pose2D(0.0, 0.0, 0.0)
def update_pose(delta_x, delta_y, delta_theta):
global current_pose
current_pose.x += delta_x
current_pose.y += delta_y
current_pose.theta += delta_theta
# Normalize angle to [-pi, pi]
current_pose.theta = math.atan2(
math.sin(current_pose.theta),
math.cos(current_pose.theta)
)
pose_topic.send(current_pose)
Message Reference
The types documented on this page, all importable via from horus import <Type>. The
extension ships more than these — around 75 message classes in total, including
TransformStamped, BoundingBox2D, Detection3D, JointState and PoseStamped;
dir(horus) lists every name:
| Category | Python Classes |
|---|---|
| Geometry | Pose2D, Twist, Transform, Point3, Vector3, Quaternion |
| Control | CmdVel, MotorCommand, DifferentialDriveCommand, ServoCommand, PidConfig |
| Sensor | LaserScan, Imu, Odometry, BatteryState, NavSatFix, RangeSensor |
| Vision | Image (pool-backed domain type), CompressedImage, CameraInfo, Detection |
| Navigation | NavGoal, NavPath, Waypoint, OccupancyGrid, CostMap, GoalResult |
| Perception | PointCloud, DepthImage (pool-backed domain types), BoundingBox3D |
| Force/Tactile | WrenchStamped, TactileArray, ForceCommand |
Not exposed to Python: ImageEncoding and GoalStatus are Rust-only enums. In Python,
image encodings are plain strings ("rgb8") and goal status is the u8 GoalResult.status.
See Also
- Python Bindings - Full Python API guide
- Multi-Language Support - Cross-language communication
- Message Types - Rust message type documentation