Examples

Thirty-three worked scenarios, ordered from the shortest script that does something to full scenarios you can hand to a colleague.

How to read this page

The first scenarios are complete files, login included. Start there.

The single-feature scenarios that follow drop the boilerplate and show one operation at a time. Each snippet assumes it sits inside this frame:

from mimic_sdk.scene_edition import MimicSceneEditor

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="my_sequence")

with scene.generate_variant("my_variant") as variant:
    ...          # <- the snippet goes here

print(client.document_path())

The last scenarios are full files again, this time combining several operations into a scenario worth rendering.

Three rules apply throughout:

  • Variants are forks. Every with block starts from the same baseline. A second block does not continue where the first one stopped.

  • Nothing runs locally. Each call records an op. The scene is edited when MIMIC replays the variant.

  • Values are bounded. The recorder refuses an out-of-range argument on the spot. The bounds differ for the ego, for actors already in the scene, and for actors your script inserts — see Limits.

Scenario: The Shortest Useful Script

Take one actor out of the scene. A single line of editing, and it exercises the whole path: login, open, record, save.

from mimic_sdk.scene_edition import MimicSceneEditor

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="my_sequence")

with scene.generate_variant("without_actor_0") as variant:
    variant.remove_actor(actor_id=0)

Scenario: Nudge The Ego And Submit

Move the ego a metre to the left, turn it slightly, and ask the Backend to render it.

from mimic_sdk.scene_edition import MimicSceneEditor

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="my_sequence")

with scene.generate_variant("ego_left_1m") as variant:
    variant.ego.shift(left=1.0)
    variant.ego.rotate(yaw=5.0)
    variant.recompute_all_sensor_world_poses()

recompute_all_sensor_world_poses() refreshes every sensor pose after the rig has moved. Add it whenever you move the ego and care about sensor output.

Scenario: Several Variants From One Baseline

Three blocks, three independent forks of the same scene. ego_right_1m does not start from the result of ego_left_1m.

from mimic_sdk.scene_edition import MimicSceneEditor

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="my_sequence")

with scene.generate_variant("ego_left_1m") as variant:
    variant.ego.shift(left=1.0)

with scene.generate_variant("ego_right_1m") as variant:
    variant.ego.shift(right=1.0)

with scene.generate_variant("ego_turned") as variant:
    variant.ego.rotate(yaw=2.0)

Scenario: Ego Shift

Six named directions, all optional, all in metres. referential="local" (the default) means the ego’s own axes; "world" means the scene’s.

variant.ego.shift(left=1.5)                       # one lane to the left
variant.ego.shift(forward=8.0, up=0.2)            # ahead and a bit higher
variant.ego.shift(right=2.0, referential="world")

Limits for the ego: ±5 m lateral, ±2 m vertical, ±20 m longitudinal.

Scenario: Editing Part Of The Trajectory

start_idx / end_idx restrict a transform to a frame window. Omit them and the whole trajectory moves.

variant.ego.shift(left=3.0, start_idx=40, end_idx=90)   # drift, then hold
variant.ego.rotate(yaw=-4.0, start_idx=60, end_idx=75)

Scenario: Ego Translate

ego.translate takes the same six directions, the same frame window and the same referential as ego.shift. On an actor it is the narrower of the two: actor.translate has no frame window and no referential, so it always moves the whole trajectory in the actor’s own axes.

variant.ego.translate(forward=5.0, left=0.5)
variant.ego.translate(forward=5.0, start_idx=0, end_idx=30)
variant.actor(4).translate(forward=5.0)      # whole trajectory, local axes

Scenario: Ego Rotate

Degrees, not radians. Pitch is deliberately asymmetric on the ego: −3° to +25°.

variant.ego.rotate(yaw=15.0)                         # heading
variant.ego.rotate(pitch=2.5, roll=-1.0)             # nose up, lean left
variant.ego.rotate(yaw=10.0, referential="world")

Scenario: Ego Transform

Translation and rotation in a single op. Watch the default: transform is referential="world" on the ego, unlike shift and rotate.

variant.ego.transform(forward=4.0, left=1.0, yaw=8.0)
variant.ego.transform(up=0.3, pitch=1.5, referential="local")

Scenario: Ego Resample And Offset

resample rebuilds the trajectory on a new rate; offset slides it in whole frames against everything else in the scene.

variant.ego.resample(hz=20.0)        # 0.1 – 200 Hz
variant.ego.offset(offset_frame=5)   # -20 – +20 frames

Scenario: Ego Apply T Sequence

Feed a stack of 4×4 matrices straight in. The recorder serialises the array, so the SDK never imports numpy — but your script can.

import numpy as np

WOBBLE = np.tile(np.eye(4), (4, 1, 1))
WOBBLE[1, 0, 3], WOBBLE[1, 1, 3] = 0.05, 0.02
WOBBLE[2, 0, 3] = 0.10
WOBBLE[3, 0, 3], WOBBLE[3, 1, 3] = 0.05, -0.02

variant.ego.apply_T_sequence(T=WOBBLE, start_idx=0, end_idx=4)
variant.ego.apply_T_sequence(
    T=WOBBLE,
    start_idx=10,
    end_idx=14,
    referential="local",
    mode="local",
    coverage="all",
)

At most 4096 scalars and three levels of nesting — a (256, 4, 4) stack is the widest legal input.

Scenario: Reaching An Actor

Three spellings, one actor. The integer and the "actor_<id>" string are equivalent for actors already in the scene; a handle is what an insert gives back.

variant.actor(4)                       # by scene id
variant.actor("actor_4")               # same actor, same recording
handle = variant.insert_actor(actor_id="actor_10001")
variant.actor(handle)                  # the actor this script inserted

Scenario: Actor Shift, Translate And Rotate

Same vocabulary as the ego, tighter bounds: ±5 m lateral, ±15 m longitudinal, ±0.15 m vertical, ±10° yaw for actors that were in the scene at load time.

variant.actor(4).shift(left=3.5)                          # one lane over
variant.actor(4).shift(forward=6.0, start_idx=0, end_idx=50)
variant.actor(4).translate(backward=4.0)
variant.actor(4).rotate(yaw=-8.0)

Scenario: Actor Speed And Move Forward

speed rescales the existing motion. move_forward overwrites it with a constant-speed run from a given frame, in m/s.

variant.actor(84).speed(1.25)                          # 25 % faster
variant.actor(84).speed(0.7)                           # 30 % slower
variant.actor(84).move_forward(start_idx=0, speed=6.0)

Scenario: Actor Stop And Make Static

Two ways to bring an actor to rest. stop decelerates into frame at_idx — give it either a duration in frames or a decel rate. make_static_at simply freezes the actor from that frame on.

variant.actor(84).stop(40, duration=30, profile="min_jerk")
variant.actor(84).stop(40, decel=12, profile="linear")
variant.actor(84).make_static_at(at_idx=30)

Scenario: Trajectory Slice, Resample And Offset

Cut the trajectory down to a window, change its rate, or slide it in time. slice takes its two frame indices positionally.

variant.actor(84).slice(10, 60)        # keep frames 10..60
variant.actor(84).resample(12.5)       # Hz
variant.actor(84).offset_frames(5)     # shift the actor's own frames

To slide an actor against the scene clock instead, use the scene-level op, which is bounded to ±20 frames:

variant.offset_actor(actor_id=84, offset_frame=-6)

Scenario: Actor Transform And Apply T Sequence

The combined op and the matrix op, both defaulting to referential="local" on an actor.

import numpy as np

variant.actor(1).transform(forward=2.0, left=0.5, yaw=6.0)

JITTER = np.tile(np.eye(4), (4, 1, 1))
JITTER[1, 0, 3] = 0.05
JITTER[2, 0, 3] = 0.10
variant.actor(1).apply_T_sequence(T=JITTER, start_idx=0, end_idx=4)

Scenario: Fluent Chains

Every op that edits in place returns its receiver, so calls chain. Ops that produce a new actor (the inserts, crossing) return a handle instead and end the chain.

variant.actor(84).move_forward(start_idx=0, speed=6.0).speed(1.1).stop(
    at_idx=45, decel=12
)

variant.ego.shift(forward=5.0).rotate(yaw=3.0)

Scenario: Insert Actor

The plainest insert. Name the asset by its durable id — copy it from the MIMIC library browser; the Backend resolves it at submit time.

car = variant.insert_actor(actor_id="actor_10001")
variant.actor(car).translate(forward=12.0, left=3.5)

Inserted actors get the wider limit table: ±50 m lateral and longitudinal, ±180° yaw.

Scenario: Insert Static Actor

Position is relative to the ego at frame at_idx. ground=True drops the asset onto the road surface.

truck = variant.insert_static_actor(
    actor_id="actor_10021",
    at_idx=60,
    forward=18.0,
    right=3.0,
    yaw=45.0,
    ground=True,
)

Scenario: Insert Actor On Ego Trajectory

The new actor inherits the ego’s own path, offset by the distances you give. Good for lead vehicles and followers.

lead = variant.insert_actor_on_ego_trajectory(
    actor_id="actor_10033", forward=25.0
)
follower = variant.insert_actor_on_ego_trajectory(
    actor_id="actor_10034", backward=15.0, left=3.5
)
variant.actor(lead).move_forward(start_idx=0, speed=9.0)

Scenario: Crossing

A crossing actor walks from lateral across the ego’s path, entering at frame start and clearing by frame end.

walker = variant.crossing(
    actor="actor_10040",
    start=20,
    end=45,
    forward=25.0,
    lateral=-10.0,
    direction="right_to_left",
    speed=1.4,
    ground=True,
    graceful_stop=False,
)
variant.actor(walker).speed(0.9)

graceful_stop=True has the actor decelerate at the far kerb instead of walking straight out of frame.

Scenario: Removing Actors

Clear one actor, or clear the board before rebuilding it.

variant.remove_actor(actor_id=0)
variant.remove_actor(actor_id="actor_11")
variant.remove_all_actors()

A handle stays a live Python name after its actor is removed. Editing it records the op and warns — the actor is still gone.

Scenario: Object Appearance

Scene-wide, then per actor. These two ops control the appearance of vehicle backlights and flashing lights per frames, at False by default.

variant.object_appearance(enabled=False)            # whole scene off
variant.actor(34).object_appearance(enabled=True)   # this one back on

Scenario: Moving A Sensor

Cameras, LiDARs and radars share one vocabulary; only the accessor differs. recompute=False defers the world-pose refresh when you are about to move several sensors.

variant.camera("FRONT").shift(up=0.12)
variant.camera("FRONT").rotate(pitch=-1.5)
variant.lidar("TOP").shift(forward=0.10, up=0.05, recompute=False)
variant.radar("FRONT_RADAR").rotate(yaw=1.0)
variant.recompute_all_sensor_world_poses()

Scenario: Sensor Conventions And Resolution

transform on a sensor takes a frame ("vehicle" or "sensor") and an axis convention ("vehicle" is X-forward, "opencv" is Z-forward). scale_camera_resolution rescales the image and the intrinsics with it.

variant.camera("FRONT").transform(
    up=0.10, pitch=-1.0, referential="sensor", convention="opencv"
)
variant.camera("FRONT_LEFT").convert_convention(
    from_convention="opencv", to_convention="vehicle"
)
variant.camera("SIDE_RIGHT").scale_camera_resolution(0.5)

Scenario: Pedestrian Crossing In Front Of The Ego

One walker steps out 25 m ahead, right to left, at a brisk pace. The ego eases right to give itself room.

from mimic_sdk.scene_edition import MimicSceneEditor

WALKER = "actor_10040"

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="urban_crossing")

with scene.generate_variant("pedestrian_steps_out") as variant:
    walker = variant.crossing(
        actor=WALKER,
        start=30,
        end=70,
        forward=25.0,
        lateral=-9.0,
        direction="right_to_left",
        speed=1.5,
        ground=True,
    )
    variant.actor(walker).rotate(yaw=4.0)

    variant.ego.shift(right=0.8, start_idx=25, end_idx=80)
    variant.recompute_all_sensor_world_poses()

Scenario: Lead Vehicle Brake Check

A car is dropped 30 m ahead on our own path, accelerates away, then brakes hard into frame 55.

from mimic_sdk.scene_edition import MimicSceneEditor

LEAD = "actor_10033"

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="highway_run")

with scene.generate_variant("lead_brakes_hard") as variant:
    lead = variant.insert_actor_on_ego_trajectory(actor_id=LEAD, forward=30.0)
    variant.actor(lead).move_forward(start_idx=0, speed=11.0).stop(
        55, duration=20, profile="min_jerk"
    )

Scenario: Roadworks Cone Corridor

Eight cones taper the lane down, and the ego threads through them. The loop is ordinary Python — the recorder sees only the eight resulting ops.

from mimic_sdk.scene_edition import MimicSceneEditor

CONES = ["actor_10019", "actor_10020"]

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="roadworks")

with scene.generate_variant("cone_corridor") as variant:
    for i in range(8):
        variant.insert_static_actor(
            actor_id=CONES[i % 2],
            at_idx=20,
            forward=12.0 + i * 3.5,
            right=3.0 - i * 0.25,
            ground=True,
        )

    variant.ego.shift(left=1.2, start_idx=20, end_idx=70)
    variant.recompute_all_sensor_world_poses()

Scenario: Convoy On The Ego Path

Three vehicles trail behind us, each further back and slightly quicker than the last, each braking a few frames later.

from mimic_sdk.scene_edition import MimicSceneEditor

CONVOY = ["actor_10033", "actor_10034", "actor_10035"]

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="convoy")

with scene.generate_variant("three_car_convoy") as variant:
    for i, asset in enumerate(CONVOY):
        car = variant.insert_actor_on_ego_trajectory(
            actor_id=asset, backward=8.0 + i * 12.0
        )
        variant.actor(car).move_forward(
            start_idx=0, speed=6.0 + i * 0.5
        ).speed(1.1).stop(at_idx=40 + i * 5, decel=12)

Scenario: Occlusion Study

A parked truck is angled into the kerb so it hides an actor that matters, and a distracting actor is taken out of the scene entirely.

from mimic_sdk.scene_edition import MimicSceneEditor

TRUCK = "actor_10021"

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="lane_occlusion")

with scene.generate_variant("truck_hides_cyclist") as variant:
    truck = variant.insert_static_actor(
        actor_id=TRUCK, at_idx=60, forward=14.0, right=2.5, yaw=45.0
    )
    variant.actor(truck).translate(forward=3.0)

    variant.actor(4).shift(left=1.0)      # the cyclist, nudged behind it
    variant.remove_actor(actor_id=0)      # remove the distraction

Scenario: Sensor Rig Rebuild

Raise and pitch the front camera, move the LiDAR forward, fix a camera that was calibrated in OpenCV axes, halve one side camera’s resolution, then refresh every world pose once at the end.

from mimic_sdk.scene_edition import MimicSceneEditor

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="rig_study")

with scene.generate_variant("rig_rebuild") as variant:
    variant.camera("FRONT").shift(up=0.12, recompute=False)
    variant.camera("FRONT").rotate(pitch=-1.5, recompute=False)
    variant.lidar("TOP").shift(forward=0.10, up=0.05, recompute=False)
    variant.camera("FRONT_LEFT").convert_convention(
        from_convention="opencv", to_convention="vehicle", recompute=False
    )
    variant.camera("SIDE_RIGHT").scale_camera_resolution(0.5)
    variant.recompute_all_sensor_world_poses()

Scenario: Parameter Sweep

Twelve variants out of one baseline: four lateral offsets crossed with three parked-car counts. One with block per variant, each a clean fork.

from mimic_sdk.scene_edition import MimicSceneEditor

CAR = "actor_10001"

client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="sweep")


def park(variant, count):
    """Clear the scene and line up ``count`` parked cars."""
    variant.remove_all_actors()
    for k in range(count):
        car = variant.insert_actor(actor_id=CAR)
        variant.actor(car).translate(forward=k * 6.0, right=3.0)


for offset in (0.0, 0.5, 1.0, 1.5):
    for count in (1, 2, 3):
        with scene.generate_variant(f"offset{offset}_cars{count}") as variant:
            park(variant, count)
            variant.ego.shift(left=offset)

Scenario: DataFrame Driven Schedule

The recorder takes plain numbers, so anything that produces them works — including pandas. Here a drift schedule becomes four matrix stacks.

import numpy as np
import pandas as pd

from mimic_sdk.scene_edition import MimicSceneEditor

DRIFT = pd.DataFrame(
    {
        "start_idx": [0, 4, 8, 12],
        "end_idx": [4, 8, 12, 16],
        "dx": [0.05, 0.10, 0.15, 0.20],
        "dy": [0.00, 0.02, 0.04, 0.06],
    }
)


def ramp(frames: int, dx: float, dy: float) -> np.ndarray:
    """A linear ramp to (dx, dy) over ``frames`` poses."""
    T = np.tile(np.eye(4), (frames, 1, 1))
    weight = np.linspace(0.0, 1.0, frames)
    T[:, 0, 3] = weight * dx
    T[:, 1, 3] = weight * dy
    return T


client = MimicSceneEditor.login()
scene = client.open_scene(sequence_id="drift_study")

with scene.generate_variant("scheduled_drift") as variant:
    for row in DRIFT.itertuples(index=False):
        start, end = int(row.start_idx), int(row.end_idx)
        variant.ego.apply_T_sequence(
            T=ramp(end - start, float(row.dx), float(row.dy)),
            start_idx=start,
            end_idx=end,
        )

    variant.ego.shift(forward=round(float(DRIFT["dx"].sum()), 3))

Where to go next

  • Getting Started — installing the SDK and running your first script.

  • Limits — the full bound table for the ego, scene actors and inserted actors.

  • API Reference — the reference for every editor and every operation.