Skip to content

Create your first app

Terminal window
simulo create mybot
Created 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 training

simulo 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.

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.

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: F401

simulo/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.

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.

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 use preview, eval, and play.
  • system=simulo.SystemType.TIER_1 picks the GPU the job runs on. simulo systems lists the tiers.
  • timeout=10 * 60 stops the job after 10 minutes. Raise it before a long run.
  • The function’s parameters become simulo run flags: --num-envs and --max-iterations.
  • RLTrainer saves the best and latest checkpoints 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 result prints.
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. Its policy parameter is annotated simulo.PolicyCheckpoint, which is how --policy policy_x:best finds where to bind.
  • StarterTask(with_camera=True) adds one camera just for playing, so training stays headless and unchanged.
  • RLPlayer steps the task with the checkpoint’s actions instead of training it, and record= turns on the MCAP recording simulo recordings later shows.

See Play a policy and record it for the checks play runs before it plays a checkpoint.

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 8
Each 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.

Change two things in task.py:

  1. Replace simulo/robot/cartpole:v1 with another robot. Choose and validate assets shows how to find one in the global catalog or publish your own.
  2. Rewrite the task class for that robot: its observations, reward, episode end, and actions. If the number of observations or actions changes, update observation_dim and action_dim to match. A checkpoint trained before the change no longer fits the task, and play.py refuses to play it.
Terminal window
simulo create mybot --dir ~/projects
simulo 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.