Skip to content

Pose3D

Represents a single SE(3) pose: a 3D position plus an orientation quaternion.

Parameters

FieldTypeDescription
datanp.ndarray | list | tupleArray-like input of shape (7,): [x, y, z, qw, qx, qy, qz], converted to a contiguous float32 array.

Raises

ExceptionCondition
TypeErrordata can't be converted to float32 (e.g. non-numeric elements)
ValueErrordata is not rank-1, or its shape isn't (7,)
ValueErrordata contains a non-finite value (NaN/Inf)
ValueErrorThe quaternion sub-range data[3:7] is the zero quaternion (norm 0.0)
ValueErrorThe quaternion sub-range data[3:7]'s norm deviates from 1.0 by more than quat_norm_atol (1e-3)

Attributes

AttributeTypeDescription
datanp.ndarrayDefensive copy of the underlying (7,) float32 array. Assigning a new value re-validates it (shape, finiteness, unit-norm quaternion) the same way as construction.
shapetuple[int, ...]Always (7,).
ndimintAlways 1.
dtypenp.dtypeAlways float32.
sizeintAlways 7.
quat_slicesliceClass-level, slice(3, 7) — the last-axis range checked for unit norm.
quat_norm_atolfloatClass-level, 1e-3 — max allowed abs(norm - 1).
quat_orderstrClass-level, "wxyz" — this class's native scalar-first quaternion order.

Methods

MethodDescription
to_transform_matrix()Returns a plain (4, 4) np.ndarray (not a Transform3D) — the homogeneous transformation matrix equivalent to this pose. Wrap the result in Transform3D(...) if you need the validated datatype.
Pose3D.from_transform_matrix(transformation_matrix)Classmethod; the inverse of to_transform_matrix. Builds a Pose3D from a (4, 4) homogeneous matrix (np.ndarray or list). Raises TypeError if not an np.ndarray/list; ValueError if its shape isn't (4, 4).
convert_pose_format(rot_type)Returns a new np.ndarray with the rotation re-encoded per RotationType (or its string value): the quaternion unchanged (shape (7,)) for QUATERNION, or Euler angles/a rotation vector (shape (6,)) otherwise. Raises TypeError/ValueError for an unrecognized rot_type.
Pose3D.from_pose_format(pose, rot_type)Classmethod; the inverse of convert_pose_format. Builds a Pose3D from a pose array/list already expressed in rot_type's rotation format. Raises TypeError/ValueError for an unrecognized rot_type.
Pose3D.to_xyzw(q)Inherited from QuaternionValidationMixin. Converts a quaternion array from quat_order ("wxyz") to scalar-last [x, y, z, w], e.g. before handing it to scipy.
Pose3D.from_xyzw(q)Inherited from QuaternionValidationMixin. Converts a quaternion array from scalar-last [x, y, z, w] to quat_order ("wxyz"), e.g. after reading scipy's Rotation.as_quat().
to_numpy(copy=True)Returns the pose as np.ndarray. Pass copy=False for a reference to the internal array instead — faster, but mutating it mutates the Pose3D too.
copy()Returns a new Pose3D with an independent copy of the data.
Pose3D.coerce(value)Returns value unchanged if it's already a Pose3D; otherwise wraps an array-like into one (running full validation). Raises TypeError for any other input.

Operators

OperationBehavior
p == otherTrue only if other is also a Pose3D with element-equal data. False for anything else.
len(p)Always 7.
np.asarray(p) / np.reshape(p, ...)Works directly via __array__. Always returns a copy; use to_numpy(copy=False) for a zero-copy view.
hash(p)Not supported — mutable via the data setter.

Visualization

datatypes.visualize(pose, entity_path=...) logs the pose's posed frame (rotated basis axes from the quaternion, translated by [x, y, z]) alongside a labeled world-origin frame. Per-axis labels are omitted; only the world-origin frame is labeled "origin". Passing label="..." attaches a floating text label at the posed frame's translation.

Example

python
"""Demonstrates the Telekinesis Pose3D datatype."""

import time

import numpy as np
from loguru import logger
import rerun as rr

from telekinesis import datatypes

def pose3d_example():
    """Demonstrate creation, access, visualization, update, transform-matrix conversion, pose-format conversion, NumPy interop, and serialization."""

    # ======================= Create ============================================
    pose_data = [0.5, 0.2, 0.5, 0.4619398, 0.1913417, 0.4619398, 0.7325378]
    pose3d = datatypes.Pose3D(pose_data)

    logger.info(f"Original Pose3D: {pose3d}")

    # ======================= Inspect ===========================================
    data = pose3d.data
    shape = pose3d.shape
    size = pose3d.size
    dtype = pose3d.dtype
    ndim = pose3d.ndim
    numpy_pose3d = pose3d.to_numpy()
    pose3d_copy = pose3d.copy()

    logger.info(f"shape={shape}, size={size}, ndim={ndim}, dtype={dtype}")
    logger.info(f"Underlying data: {data}")
    logger.info(f"NumPy array: {numpy_pose3d}")
    logger.info(f"Copy: {pose3d_copy}")

    # ======================= Visualize =========================================
    rr.init("pose3d_example", spawn=True)
    datatypes.visualize(pose3d, entity_path="/Pose3D", label="my_pose3d")

    # ======================= Update ============================================
    pose3d.data = [0.1, 0.2, 0.3, 0.0, 0.0, 0.0, 1.0]
    logger.info(f"Updated Pose3D: {pose3d}")
    datatypes.visualize(pose3d, entity_path="/Pose3D/updated", label="updated_pose3d")

    # ======================= Transform Matrix ==================================
    matrix = pose3d.to_transform_matrix()
    logger.info(f"Pose3D as transformation matrix:\n{matrix}")
    transform3d = datatypes.Transform3D(matrix)
    datatypes.visualize(transform3d, entity_path="/Transform3D", label="pose3d_transform")

    pose3d_from_transform = datatypes.Pose3D.from_transform_matrix(transform3d.data)
    logger.info(f"Pose3D from transformation matrix: {pose3d_from_transform}")
    logger.info(f"Converted back to Pose3D is equal to original: {pose3d_from_transform == pose3d}")

    # ======================= Pose Formats ======================================
    pose3d_deg = datatypes.Pose3D.from_pose_format(
        [30, 45, 60, 0.4619398, 0.1913417, 0.4619398, 0.7325378], rot_type="deg"
    )
    logger.info(f"Pose3D from pose with rotation in degrees: {pose3d_deg}")

    pose3d_rad = datatypes.Pose3D.from_pose_format(
        [
            np.radians(30),
            np.radians(45),
            np.radians(60),
            0.4619398,
            0.1913417,
            0.4619398,
            0.7325378,
        ],
        rot_type="rad",
    )
    logger.info(f"Pose3D from pose with rotation in radians: {pose3d_rad}")

    pose3d_rotvec = datatypes.Pose3D.from_pose_format(
        [0.5235988, 0.7853982, 1.0471976, 0.4619398, 0.1913417, 0.4619398, 0.7325378],
        rot_type="rotvec",
    )
    logger.info(f"Pose3D from pose with rotation as rotation vector: {pose3d_rotvec}")

    # ======================= Convert ===========================================
    pose_as_deg = pose3d.convert_pose_format(rot_type="deg")
    logger.info(f"Pose3D as rotation in degrees: {pose_as_deg}")

    pose_as_rad = pose3d.convert_pose_format(rot_type="rad")
    logger.info(f"Pose3D as rotation in radians: {pose_as_rad}")

    pose_as_rotvec = pose3d.convert_pose_format(rot_type="rotvec")
    logger.info(f"Pose3D as rotation vector: {pose_as_rotvec}")

    # ======================= NumPy Interop =====================================
    reshaped = np.reshape(pose3d, (7,))
    logger.info(f"Underlying Pose3D with np.reshape: {reshaped}")

    # ======================= Serialize / Deserialize ===========================
    start = time.perf_counter()
    serialized = datatypes.serialize(pose3d)
    serialization_ms = (time.perf_counter() - start) * 1000

    start = time.perf_counter()
    deserialized = datatypes.deserialize(serialized)["param_0"]
    deserialization_ms = (time.perf_counter() - start) * 1000

    logger.info(f"Deserialized Pose3D: {deserialized}")
    logger.info(f"Round-trip successful: {deserialized == pose3d}")
    logger.info(f"Serialization time: {serialization_ms:.3f} ms")
    logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")


if __name__ == "__main__":
    pose3d_example()