Create your first app
simulo create mybotCreated app in mybot/ (eval.py + play.py + preview.py + task.py + train.py + .simuloignore).Next steps: cd mybot simulo run preview.py simulo run train.py simulo run eval.py --policy policy_<job>:best # evaluate the trained policy simulo run play.py --policy policy_<job>:best # after trainingsimulo create copies a starter app out of the installed package. It needs
no login and no network, and the app it writes runs unchanged: it previews a
cartpole task, trains a policy to balance its pole, and plays a checkpoint back
once you have one.
What’s in the folder
Section titled “What’s in the folder”| File | What it holds |
|---|---|
task.py |
The app, the robot, and the task: what the robot observes, the reward, and when an episode ends. |
preview.py |
One preview job that checks the robot inside the task and makes no policy. |
train.py |
One training job that imports the task from task.py and trains it with PPO. |
eval.py |
One evaluation job that imports the task and judges a trained checkpoint with a success rule. |
play.py |
One play job that imports the task and runs a trained checkpoint back to watch or record it. |
.simuloignore |
Files to leave out of the upload, such as checkpoints, virtual environments, and .env files. It uses .gitignore syntax. |
task.py
Section titled “task.py”Near the top, task.py picks the robot and names the app:
import simulo
# Replace this catalog asset and the task logic below for your own robot.cartpole = simulo.Asset.from_registry("simulo/robot/cartpole:v1")app = simulo.App("mybot")
with app.runtime.imports(): import torch # noqa: F401simulo/robot/cartpole:v1 is version 1 of the cartpole in the Simulo global
catalog. Declare assets at the top level of the file, as here, so
simulo run can find each one and resolve it to an exact version when it
submits the job.
PyTorch is imported inside with app.runtime.imports():. On your computer,
Simulo only records that import, so you don’t need PyTorch installed; on the
cloud GPU, the import runs normally. Put every GPU-side import inside that
block.
The rest of the file is a simulo.Task subclass. build() adds the ground,
a light, and the robot to the scene, and the other methods compute
observations, rewards, and episode ends, and apply the policy’s actions. See
Tasks and RL training for what each
method does.
preview.py
Section titled “preview.py”import simulo
from task import StarterTask, app
@app.job(type="preview", system=simulo.SystemType.TIER_1, timeout=15 * 60)def preview(num_envs: int = 16, checks: str = "all"): return simulo.preview(StarterTask(), num_envs=num_envs, checks=checks, video=True)The preview checks the robot inside the task, records video, and reports problems to fix before training. It does not make a policy. Run it before the first training job and after every task or robot change. Preview explains the four checks, findings, exit codes, and recording.
train.py
Section titled “train.py”from __future__ import annotations
import simulo
from task import StarterTask, app
@app.job(type="train", system=simulo.SystemType.TIER_1, timeout=10 * 60)def train(num_envs: int = 16, max_iterations: int = 2) -> dict: """Train Cartpole briefly. Simulo saves best.pt and latest.pt in SIMULO_CHECKPOINT_DIR.""" env = simulo.LearningEnv( task=StarterTask(), num_envs=num_envs, device="cuda", dt=1.0 / 120.0, physics_steps_per_action=2, env_spacing=4.0, headless=True, seed=42, ) trainer = simulo.RLTrainer(env=env, algorithm="PPO", device="cuda", seed=42) try: stats = trainer.train(max_iterations=max_iterations) return {"num_envs": num_envs, **stats} finally: trainer.close() env.close()type="train"makes this a training job. Every job declares a type. The starter’s other files usepreview,eval, andplay.system=simulo.SystemType.TIER_1picks the GPU the job runs on.simulo systemslists the tiers.timeout=10 * 60stops the job after 10 minutes. Raise it before a long run.- The function’s parameters become
simulo runflags:--num-envsand--max-iterations. RLTrainersaves thebestandlatestcheckpoints for you, and Simulo uploads them to the job’s policy when the job ends.- What the function returns is the job’s result, which
simulo resultprints.
play.py
Section titled “play.py”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()type="play"makes this a play job. Itspolicyparameter is annotatedsimulo.PolicyCheckpoint, which is how--policy policy_x:bestfinds where to bind.StarterTask(with_camera=True)adds one camera just for playing, so training stays headless and unchanged.RLPlayersteps the task with the checkpoint’s actions instead of training it, andrecord=turns on the MCAP recordingsimulo recordingslater shows.
See Play a policy and record it for the checks play runs before it plays a checkpoint.
One job per file
Section titled “One job per file”Each file you submit holds exactly one @app.job, and that job declares its
type=. The task lives in task.py so every job file can import it. simulo run refuses a file that breaks this rule before it uploads anything. For
example, a file with two jobs:
two.py declares more than one @app.job: - a: function a, two.py: line 4 - b: function b, two.py: line 8Each file holds one job. Move each job into its own file (for example `train.py` and `play.py`), each importing your task from `task.py`.Submit preview.py before train.py, evaluate the trained checkpoint with
eval.py, then run play.py to watch or record it. task.py has no job, so
simulo run task.py stops with
No @app.job found in task.py.
Running the file with Python doesn’t submit it either. python train.py
prints the simulo run command to use and exits.
Make it your own
Section titled “Make it your own”Change two things in task.py:
- Replace
simulo/robot/cartpole:v1with another robot. Choose and validate assets shows how to find one in the global catalog or publish your own. - Rewrite the task class for that robot: its observations, reward, episode
end, and actions. If the number of observations or actions changes, update
observation_dimandaction_dimto match. A checkpoint trained before the change no longer fits the task, andplay.pyrefuses to play it.
Options
Section titled “Options”simulo create mybot --dir ~/projectssimulo create mybot --force--dir creates the folder inside another directory. Without --force,
simulo create refuses to write into a folder that already has files in it.
An app name uses letters, digits, underscores, and hyphens, and starts with a
letter or underscore.
Run and submit the preview, then the training job.