HORUS provides a Python API for coordinate frame management — registering frames, updating transforms, and looking up transformations between any two frames in the tree.
Transform
A 3D rigid transformation (translation + quaternion rotation).
Creating Transforms
from horus import Transform
# Identity transform
tf = Transform.identity()
# From translation and rotation quaternion
tf = Transform(translation=[1.0, 2.0, 0.0], rotation=[0.0, 0.0, 0.0, 1.0])
# From translation only (identity rotation)
tf = Transform.from_translation([1.0, 2.0, 3.0])
# From Euler angles (translation + roll/pitch/yaw)
tf = Transform.from_euler([1.0, 0.0, 0.5], [0.0, 0.1, 1.57])
# From 4x4 homogeneous matrix
tf = Transform.from_matrix(matrix_4x4)
Properties
tf.translation # [x, y, z] as list of floats
tf.rotation # [x, y, z, w] quaternion as list of floats
Both are readable and writable:
tf.translation = [2.0, 3.0, 0.0]
tf.rotation = [0.0, 0.0, 0.383, 0.924] # 45 degrees around Z
Methods
# Convert to Euler angles
roll, pitch, yaw = tf.to_euler()
# Compose transforms: result = self * other
combined = parent_tf.compose(child_tf)
# Inverse transform
inv = tf.inverse()
# Apply to a point
point_in_world = tf.transform_point([1.0, 0.0, 0.0])
# Apply rotation only (no translation)
rotated = tf.transform_vector([1.0, 0.0, 0.0])
# Interpolate between two transforms (SLERP for rotation)
halfway = tf_a.interpolate(tf_b, t=0.5)
# Magnitudes
dist = tf.translation_magnitude() # Translation distance
angle = tf.rotation_angle() # Rotation angle in radians
# Export as 4x4 matrix
matrix = tf.to_matrix() # [[f64; 4]; 4]
Method
Returns
Description
identity()
Transform
No translation, no rotation
from_translation([x,y,z])
Transform
Translation only
from_euler([x,y,z], [r,p,y])
Transform
Translation + Euler angles
from_matrix(4x4)
Transform
From homogeneous matrix
to_euler()
[roll, pitch, yaw]
Get Euler angles
compose(other)
Transform
Chain transforms (self * other)
inverse()
Transform
Compute inverse
transform_point([x,y,z])
[x,y,z]
Apply full transform to point
transform_vector([x,y,z])
[x,y,z]
Apply rotation only to vector
interpolate(other, t)
Transform
SLERP interpolation (0.0-1.0)
translation_magnitude()
float
Translation distance
rotation_angle()
float
Rotation angle in radians
to_matrix()
4x4 list
Export as homogeneous matrix
TransformFrame
The frame tree manager. Stores a hierarchy of coordinate frames and computes transforms between any two frames.
# Register child frame under a parent — returns frame ID (int)
frame_id = tf_tree.register_frame("base_link", "world")
tf_tree.register_frame("lidar", "base_link")
tf_tree.register_frame("camera", "base_link")
# Unregister a frame (raises HorusNotFoundError if not found)
tf_tree.unregister_frame("camera")
Updating Transforms
from horus import Transform
# Set the transform from parent to child (raises on unknown frame)
tf_tree.update_transform("base_link", Transform(
translation=[1.0, 0.0, 0.0],
rotation=[0.0, 0.0, 0.0, 1.0]
))
# With optional explicit timestamp
tf_tree.update_transform("lidar", Transform.from_translation([0.0, 0.0, 0.3]),
timestamp_ns=1234567890)
Looking Up Transforms
# Get transform from source frame to target frame
tf = tf_tree.tf("lidar", "world")
print(f"Lidar in world: {tf.translation}")
# With specific timestamp (for interpolation)
tf = tf_tree.tf_at("lidar", "world", timestamp_ns=1234567890)
Querying the Tree
# List all registered frames
frames = tf_tree.all_frames() # ["world", "base_link", "lidar", "camera"]
# Get parent of a frame
parent = tf_tree.parent("lidar") # "base_link"
# Get children of a frame
children = tf_tree.children("base_link") # ["lidar", "camera"]
# Seconds since last transform update (None if never updated)
age = tf_tree.time_since_last_update("lidar") # e.g., 0.015
# Wait for a transform to become available (blocking with timeout)
tf = tf_tree.wait_for_transform("lidar", "world", timeout_sec=1.0)
print(f"Got transform: {tf.translation}")