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: .. code-block:: python 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 :doc:`api/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. .. code-block:: python 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. .. code-block:: python 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``. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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°. .. code-block:: python 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``. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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_"`` string are equivalent for actors already in the scene; a handle is what an insert gives back. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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: .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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``. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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. .. code-block:: python 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 ---------------- * :doc:`getting_started` — installing the SDK and running your first script. * :doc:`api/limits` — the full bound table for the ego, scene actors and inserted actors. * :doc:`api/index` — the reference for every editor and every operation.