Export a policy to ONNX
simulo models gives you the trained checkpoint. This gives you something you can put on a
robot: an ONNX bundle that runs with Simulo uninstalled, plus the evidence that it
reproduces Simulo’s own inference.
Export
Section titled “Export”simulo exportNo arguments. Simulo resolves your latest training job, selects its best checkpoint, converts it, and downloads the result — it does not ask you which tensor names to use, which normalization to apply, which observations to test with, or what numerical tolerance is appropriate, because it already knows all of them.
Exporting best.pt from job 8b52…9f PyTorch → ONNX … done Validating 100 golden vectors … passed Maximum action difference: 2.4e-07
Deployment bundle downloaded: ./cartpole-policy-best/
Simulo export validation: PASSED
Verify it on this machine: cd cartpole-policy-best python -m pip install -r requirements.txt python verify.pyEvery flag overrides a default that was already correct: --kind latest to export the last
checkpoint instead of the best, --model <id> for a specific one, -o for somewhere else,
--json for machine output.
Two validations, not one
Section titled “Two validations, not one”Conversion completing is not success. There are two separate checks, and they answer different questions.
Simulo-side. Before publishing anything, Simulo runs the same observations through the source PyTorch policy and through the exported ONNX model, and compares the actions. A disagreement beyond tolerance fails the export rather than handing you a bundle that loads and misbehaves. That answers: did Simulo convert the policy faithfully?
Your machine. The bundle ships its own verifier. That answers: does this model actually run, correctly, here?
cd cartpole-policy-bestpython3 -m venv .venv && source .venv/bin/activatepython -m pip install -r requirements.txt # onnxruntime + numpy, nothing elsepython verify.pyIt needs no Simulo SDK, no simulator, no CUDA, no GPU, no credentials, and no access to the training project. It does not need PyTorch either.
What is in the bundle
Section titled “What is in the bundle”A bare model.onnx is not enough to use a policy correctly, so the bundle is a contract:
| File | What it carries |
|---|---|
model.onnx |
the portable model |
model.pt |
the source checkpoint it came from |
manifest.json |
identity, provenance, control period, format and schema versions |
observation_schema.json |
ordered inputs — names, shapes, dtypes, units, frames |
action_schema.json |
ordered outputs — names, units, bounds |
normalization.json |
scaling parameters, or an explicit “none” |
test_vectors.npz |
golden inputs and the actions Simulo computed for them |
verify.py |
the standalone check above |
requirements.txt |
numpy and onnxruntime |
README.md |
the same instructions, inside the bundle |
The golden vectors come from the trusted export, so verify.py compares the downloaded model
against known-good results rather than merely confirming ONNX Runtime returned something.
Use it from your own code
Section titled “Use it from your own code”import json, numpy as np, onnxruntime as ort
manifest = json.load(open("manifest.json"))session = ort.InferenceSession("model.onnx", providers=["CPUExecutionProvider"])
obs = np.zeros((1, 4), dtype=np.float32) # your robot's real observationaction = session.run(["actions"], {"observations": obs})[0]
period = manifest["control_period_s"] # seconds, or the string "unknown"print(action, f"every {period}s" if period != "unknown" else "(control rate not recorded)")observation_schema.json tells you what belongs in each slot of obs and in what order;
action_schema.json tells you what the outputs mean and what bounds they respect.
Facts Simulo never recorded
Section titled “Facts Simulo never recorded”A policy trained before Simulo captured control period and action bounds exports fine — those
fields read unknown in the manifest and in verify.py, and they stay unknown rather than
becoming a plausible default. Branch on that rather than formatting it, as the snippet above
does. A number you can act on and an honest “never recorded” are both useful; a guess is the
only outcome that can hurt you on hardware.
When an export fails
Section titled “When an export fails”Four failures are reported differently, because they need different responses:
- Conversion failed — a transient or model-specific conversion fault. Retrying is reasonable.
- Unsupported model — the policy uses a structure or operator the exporter cannot represent faithfully. Retrying reaches the identical refusal; the model has to change.
- Validation mismatch — the ONNX model disagreed with the source policy beyond tolerance. Simulo publishes nothing rather than ship a bundle that would mislead you.
- Local validation failed —
verify.pydid not reproduce the expected outputs on your machine. Simulo already proved the conversion, so start with the download and your local ONNX Runtime install.
What this proves
Section titled “What this proves”Passing local verification means the exported policy executes independently and reproduces Simulo’s inference results on that machine. It says nothing about whether the policy is safe or effective on real hardware — sim-to-real transfer, robot I/O, actuator limits and physical safety are separate problems. This is the prerequisite for tackling them, not a substitute.
Score a policy before exporting it: Evaluate & roll out. List and download the raw checkpoints instead: Retrieve & verify models.