Trajectory Module

The trajectory module provides data structures for representing time-series pose data in world coordinates.

WorldTrajectory

class mimic_data_api.trajectory_numpy.WorldTrajectory(*, timestamps, object2world, angular_velocity=None, linear_velocity=None)[source]

Bases: BaseModel

Time-series trajectory of an object in world coordinates.

This class represents a sequence of poses (position and orientation) for an object over time, stored as 4x4 homogeneous transformation matrices. Optionally includes angular and linear velocity information.

The transformation matrices follow the convention where the matrix transforms points from the object’s local coordinate frame to the world coordinate frame:

\[p_{world} = T_{object \rightarrow world} \cdot p_{object}\]
timestamps

1D array of N timestamps in seconds. Must be strictly increasing (monotonic, no duplicates) and match the number of transformation matrices.

Type:

NDArray[Shape[”* x”], float]

object2world

Array of shape (N, 4, 4) containing homogeneous transformation matrices. Each matrix transforms points from object coordinates to world coordinates.

Type:

NDArray[Shape[”* x, 4 y, 4 z”], float]

angular_velocity

Optional array of shape (N, 3) containing angular velocity vectors in world coordinates, measured in rad/s. Order is (roll, pitch, yaw) rate.

Type:

NDArray[Shape[”* x, 3 y”], float] or None

linear_velocity

Optional array of shape (N, 3) containing linear velocity vectors in world coordinates, measured in m/s. Order is (vx, vy, vz).

Type:

NDArray[Shape[”* x, 3 y”], float] or None

Raises:

ValueError – If timestamps and transforms have different lengths, or if velocity arrays (when provided) don’t match the timestamp count.

Note

This class uses validate_assignment=True, meaning validation runs on attribute assignment, not just initialization.

Example

Creating a trajectory with velocity information:

import numpy as np
from mimic_data_api.trajectory_numpy import WorldTrajectory

n_frames = 100
trajectory = WorldTrajectory(
    timestamps=np.linspace(0, 10, n_frames),
    object2world=np.stack([np.eye(4)] * n_frames),
    linear_velocity=np.zeros((n_frames, 3)),
    angular_velocity=np.zeros((n_frames, 3))
)

Accessing pose at a specific time index:

pose_at_t5 = trajectory.object2world[5]  # 4x4 matrix
position = pose_at_t5[:3, 3]  # Translation vector
rotation = pose_at_t5[:3, :3]  # Rotation matrix

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Usage Example

import numpy as np
from mimic_data_api import WorldTrajectory

# Create a 100-frame trajectory
trajectory = WorldTrajectory(
    timestamps=np.linspace(0, 10, 100),
    object2world=np.stack([np.eye(4)] * 100),
    linear_velocity=np.zeros((100, 3)),
    angular_velocity=np.zeros((100, 3))
)

# Access pose at frame 50
pose = trajectory.object2world[50]
position = pose[:3, 3]  # Translation
rotation = pose[:3, :3]  # Rotation matrix
timestamps: NDArray[Shape['* x'], float]
object2world: NDArray[Shape['* x, 4 y, 4 z'], float]
angular_velocity: NDArray[Shape['* x, 3 y'], float] | None
linear_velocity: NDArray[Shape['* x, 3 y'], float] | None
len_validation()[source]

Validate that all arrays have consistent lengths.

Returns:

The validated trajectory instance.

Return type:

WorldTrajectory

Raises:

ValueError – If array lengths are inconsistent, or if timestamps is not strictly increasing (monotonic, no duplicates).

__eq__(other)[source]

Check equality with another WorldTrajectory.

Two trajectories are considered equal if they have the same timestamps, transforms, and velocity arrays (using approximate comparison for floats).

Parameters:

other (object) – The object to compare against.

Returns:

True if trajectories are equal, False otherwise.

Return type:

bool

model_config: ClassVar[ConfigDict] = {'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

Sensor World Poses

The ego vehicle trajectory stores vehicle-to-world poses. For a mounted sensor, trajectory.object2world stores sensor-to-world poses, not the raw ego vehicle poses. Compose each sensor pose as:

\[T_{sensor \rightarrow world}(i) = T_{vehicle \rightarrow world}(i) \cdot T_{sensor \rightarrow vehicle}\]

The sensor’s extrinsics field is T_sensor_to_vehicle. This contract applies to cameras, LiDARs, and radars. Sensor data files themselves remain in the sensor-local coordinate frame.

Extrinsic Calibration

mimic_data_api.extrinsic.SensorExtrinsic

Type alias for a 4x4 sensor extrinsic calibration matrix.

This matrix transforms points from the sensor coordinate frame to the vehicle body coordinate frame:

\[p_{vehicle} = T_{sensor \rightarrow vehicle} \cdot p_{sensor}\]
Type:

NDArray[Shape[“4 x, 4 y”], float]

import numpy as np
from mimic_data_api.extrinsic import SensorExtrinsic

# Identity transform (sensor aligned with vehicle)
extrinsic: SensorExtrinsic = np.eye(4)

# LiDAR mounted 1.5m above vehicle origin
extrinsic = np.array([
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 1.5],
    [0, 0, 0, 1]
])