Skip to content

simulo.Scenario

The interactive simulation contract — a sim that runs without a training loop.

A scenario builds a scene and then reacts to the simulation clock: on_step fires every physics step, for as long as the scenario runs (until the job is cancelled or the sim is shut down). Use it for demos, teleoperation, data capture, or any “just simulate” workload — there is no policy, no reward, and no episode lifecycle.

Structural mirror of simulo.scenario.Scenario, the abstract base your scenario subclasses on the worker.

Real usage, trimmed from the shipped scene app — a Scenario is the same lifecycle as a Task without training: build the scene once, then drive it every step yourself:

class SceneScenario(simulo.Scenario):
def build(self, scene: simulo.Scene) -> None:
self.robot = simulo.Robot(asset=cartpole, initial_pose=simulo.Pose.identity())
scene.add(self.robot, at="/World/Robot")
def on_start(self) -> None:
self._cart_dof_idx = self.robot.find_joints("slider_to_cart")
def on_step(self) -> None:
# efforts: one row per environment, built from a per-env phase offset.
self.robot.set_joint_effort_target(efforts)

Run it end to end with simulo.run(SceneScenario, device="cuda", headless=True, max_steps=1000, num_envs=4) — see simulo.run.

Scenario.build(scene: SceneProtocol) -> None

Populate scene with the world — robots, objects, lights, terrain.

Called once, before simulation starts.

Scenario.on_start() -> None

One-time hook after the sim is live — cache handles, set initial poses.

Scenario.on_step() -> None

Called once per physics step while the scenario runs.

Scenario.on_shutdown() -> None

Called once when the scenario stops — flush files, release resources.


Declared as:

@runtime_checkable
class ScenarioProtocol(Protocol)