Skip to content

Play a policy and record it

A play job runs a policy checkpoint back through a task instead of training it. Use it to watch how a trained policy behaves, or to record an MCAP of the run for later inspection.

A play job takes a checkpoint as a simulo.PolicyCheckpoint parameter, which --policy fills in when you submit it:

"""Play a trained policy and record it."""
from __future__ import annotations
import simulo
from task import StarterTask, app
@app.job(type="play", system=simulo.SystemType.TIER_1, timeout=15 * 60)
def play(policy: simulo.PolicyCheckpoint, episodes: int = 3) -> None:
"""Watch or record a policy: simulo run play.py --policy policy_x:best."""
env = simulo.LearningEnv(
task=StarterTask(with_camera=True),
num_envs=1,
device="cuda",
headless=True,
seed=42,
enable_cameras=True,
)
player = simulo.RLPlayer(env=env, policy=policy, device="cuda")
try:
player.play(num_episodes=episodes, record=simulo.RecordConfig(include_video=True))
finally:
player.close()
env.close()

simulo.RLPlayer loads the checkpoint and steps the task with the policy’s actions instead of training it. record=simulo.RecordConfig(include_video=True) turns on MCAP recording, with video from any camera the task adds. episodes is a normal job parameter, so it becomes the --episodes flag like any other.

simulo create NAME writes exactly this file as play.py, alongside task.py, preview.py, and train.py. See the training template for what each file does.

Terminal window
simulo run play.py --policy policy_brisk-heron-4f8k2m:best
PLAY job_calm-falcon-6p3k8s
Policy: policy_brisk-heron-4f8k2m:best (iteration 600)
...
[RLPlayer] Completed: 3 episodes, <steps> steps
Next: simulo recordings job_calm-falcon-6p3k8s

The header names the new play job and the checkpoint it’s playing, with the training iteration the checkpoint reached. Like a training job, a play job appears in simulo jobs and follows its own logs; --detach submits it without waiting.

Terminal window
simulo recordings job_calm-falcon-6p3k8s

record=simulo.RecordConfig(include_video=True) names no path, so the job records to <run-id>.mcap and the recording appears in simulo recordings once the job ends. It holds the policy’s actions on /policy/action, and, when the task adds a camera for play, that camera’s video. The starter’s play.py adds one overhead camera only while playing, so training stays headless and unchanged; its recording includes /sensors/camera/overhead_cam/video. See Record and inspect MCAP for reading a recording without Simulo installed.

A checkpoint was trained against a fixed observation size and action size. If task.py no longer matches, play refuses the job before its first episode, naming both shapes:

policy_brisk-heron-4f8k2m:best was trained with 4 observations and 1 action; CartpoleTask now has 5 observations and 1 action. Play a policy trained on this task, or retrain with simulo run train.py.

The job is created and ends failed; nothing about the checkpoint changes.

An edit that doesn’t change either size, such as a reward constant, doesn’t block play. The job runs, and its first player line says so:

task.py has changed since policy_brisk-heron-4f8k2m was trained

--policy and --from are for different files

Section titled “--policy and --from are for different files”

--from continues training from a checkpoint; --policy plays or evaluates one. Each is refused on the other kind of file, and each refusal names the flag you meant:

You typed What happens
--policy on a training file --policy works only on a play or eval file (@app.job(type="play")); train.py holds a train job. To continue training from a checkpoint, use --from policy_x:best.
--from on a play file --from works only on a training file (@app.job(type="train")); play.py holds a play job. To play a policy, use --policy policy_x:best.
Both --from and --policy use --from on a training file or --policy on a play file, not both
--policy on a play file with no value play needs a policy: simulo run play.py --policy policy_x:best