Build robot cells as code, verify them deterministically, ship them as USD.
Live Demo: https://botrail.github.io/botrail/
pip install botrail, a few lines of Python, and you get an interactive 3D
studio in your browser for building robot cells — robots, obstacles,
conveyors, sensors, and PLC-style sequences. The core is written in Rust —
no ROS, no system dependencies, no GPU.
A cell in botrail is text (Python / .botrail JSON / USD): it diffs in git,
it bakes into a bit-identical timeline every run, and it regression-tests in
CI. Motions are planned, not taught point by point, so moving a pallet or
a sensor doesn't break the cell — re-simulate and read the new cycle time.
Highlights
- Robots from URDF, Xacro, or USD — including Isaac Sim articulations
(
bt.Robot.from_usd("franka.usd")), rendered at full visual fidelity with three-usd-robot. Multiple robots per cell, with tick-checked inter-robot collisions and zone interlocks. - USD scene import (usda/usdc/usdz, references, variants, instancing) — stages become obstacles and named mount frames, normalized to meters / Z-up.
- Environments that behave — a PLC-style step sequencer (entry actions + transition conditions on a fixed scan cycle), zone/beam sensors, conveyors and linear axes, and conveyor tracking: taught poses ride the moving part, so the belt never stops for the pick.
- Deterministic bake —
simulate_sequence()turns Scene + Sequence into a bit-identicalSequenceTimeline: cycle time, step spans, signal waveforms, object tracks. - Assertable timelines —
step_span()/signal()/min_clearance()turn a bake into pytest-able cell checks (cycle budgets, sensor timing, safety margins) that run in CI. - Open deliverables — USD animation (plays in usdview / Omniverse / Blender), CSV/JSON, robot programs (URScript), Python code generation — and Isaac Sim recordings play back through the same pipeline.
- Interactive posing — draggable TCP gizmo with live IK, joint sliders.
- Collision checking — primitives and STL/OBJ meshes (cached VHACD convex decomposition), live highlighting, clearance readout.
- Motion planning & authoring — RRT-Connect with time parameterization, waypoint motions with Cartesian-line segments and path constraints, trajectory playback in the studio.
- Portable projects — save/load
.botrailfiles (meshes and USD stages bundled), regenerate any scene as a Python script. - Runs entirely in the browser — the wasm build serves the full studio as a static page, no server; drop a USD file straight into the viewport.
Try it
Run the bundled demos — a Franka Panda in a small USD factory cell (the first run downloads NVIDIA's official Franka asset, ~10 MB):
python examples/demo.py # interactive studio: pose, plan, play
python examples/sequence_demo.py # 13-step cell: conveyor feed → tracked pick
# → pallet; prints the cycle time, exports USD
python examples/dual_cell_demo.py # two arms sharing one infeed, arbitrated by a
# zone interlock; --clash shows what happens
# without it
python examples/sweep_demo.py # parameter sweep: belt speed × lane position
# vs cycle time and clearance (no downloads)
python examples/play_record.py \
cell_dual.usda # replay a baked USD in the studio (any of
# the recordings above; omit for cell_seq)
Or try the browser-only build (deploys to GitHub Pages from main, or build
it locally):
./scripts/build_wasm_demo.sh # needs wasm-pack + wasm32 target
python -m http.server -d studio/dist-wasm 8899
Quickstart
import botrail as bt
robot = bt.Robot.from_urdf("robot.urdf") # or from_xacro(...) / from_usd(...)
scene = bt.Scene(robot)
scene.load_usd("cell.usda", prefix="env") # obstacles + frames
scene.set_robot_base_pose(*scene.frame("env/World/mount"))
scene.add_box("table", size=(0.6, 0.6, 0.05), position=(0.4, 0.0, 0.0))
bt.studio(scene) # opens the 3D studio in your browser
Everything you do in the studio is mirrored in Python, and vice versa:
scene.set_tcp_target((0.3, 0.1, 0.5)) # live IK, pushed to the browser
scene.in_collision() # False
scene.min_obstacle_distance() # clearance in meters
traj = scene.plan_to_pose((0.4, 0.1, 0.3)) # IK, then RRT-Connect + time param
traj.export_csv("motion.csv", dt=0.008)
scene.save_project("cell.botrail") # meshes/USD bundled when needed
print(scene.generate_python()) # script reproducing the scene
Verify the cell, not just the trajectory
Give the environment behavior, write the process as steps, and bake:
scene.add_box("crate", size=(0.04, 0.04, 0.04), position=(-0.5, 0.6, 0.3))
scene.add_conveyor("belt", zone_position=(-0.2, 0.6, 0.3),
zone_size=(1.2, 0.3, 0.3), velocity=(0.25, 0.0, 0.0),
running=False)
scene.add_beam_sensor("eye", frm=(0.0, 0.4, 0.3), to=(0.0, 0.8, 0.3))
scene.add_segment("approach", goal=[0.6, -0.5, 0.8, 0.0, 0.4, 0.0])
sq = scene.sequence("cycle")
sq.step("feed", actions=[bt.seq.start("belt")], transition=bt.seq.signal("eye"))
sq.step("stop", actions=[bt.seq.stop("belt")])
sq.step("pick", actions=[bt.seq.motion("approach")])
tl = scene.simulate_sequence("cycle") # deterministic: bit-identical every run
print(tl.duration) # cycle time in seconds
tl.export_usd("cycle.usda", fps=60) # replay in usdview / Omniverse / Blender
Because the bake is deterministic, the same numbers are regression tests — the workflow botrail exists for:
def test_cell_cycle():
tl = build_cell().simulate_sequence("cycle")
assert tl.duration <= 8.0 # cycle-time budget
assert tl.step_span("feed").end <= 2.0 # the crate arrives on time
assert tl.signal("eye").rising_edges() # the handshake happened
assert tl.min_clearance() > 0.05 # closest approach, meters
Move the beam sensor 0.25 m downstream and the cycle grows by exactly 1.0 s — a layout edit becomes a failing test instead of a shop-floor surprise. This repository runs such a cell in its own CI (python/tests/test_cell_regression.py), and examples/sweep_demo.py runs the same loop as a parameter study (belt speed moves the cycle; lane position eats the clearance).
Development
Requirements: Rust (stable), Python >= 3.9, maturin, uv, Node 20+ with pnpm.
./scripts/build_studio.sh # build the studio UI into the package
uv venv .venv && source .venv/bin/activate
maturin develop --uv
python examples/demo.py
Tests:
cargo test # Rust workspace
python -m pytest python/tests # Python bindings
Architecture and contributor notes live in docs/DESIGN.md and docs/DEVELOPMENT.md.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file botrail-0.1.1.tar.gz.
File metadata
- Download URL: botrail-0.1.1.tar.gz
- Upload date:
- Size: 229.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
891e88d51034c6c796737cb69364dea9ac032dd89905713d7ae063311a14d342
|
|
| MD5 |
ada7d7170ce77cd3596e178151cb6f9e
|
|
| BLAKE2b-256 |
7ab3a36e55369b0d5703ff31d93336e7c109dd57a2dd9b4f4b35474b32b1c1d5
|
Provenance
The following attestation bundles were made for botrail-0.1.1.tar.gz:
Publisher:
release.yml on botrail/botrail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
botrail-0.1.1.tar.gz -
Subject digest:
891e88d51034c6c796737cb69364dea9ac032dd89905713d7ae063311a14d342 - Sigstore transparency entry: 2332573796
- Sigstore integration time:
-
Permalink:
botrail/botrail@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/botrail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Trigger Event:
push
-
Statement type:
File details
Details for the file botrail-0.1.1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: botrail-0.1.1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 4.6 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
097ed7b4a4f1fca5bb9661d36cf269227d8747ec1ad60dce5b7889fbbef1ed4c
|
|
| MD5 |
94f4b69bab44a90c95d8e0584e1f71c8
|
|
| BLAKE2b-256 |
1da90c9267a526131b68a7b4e8407efecbe1f078c1be04edde976749303dc39f
|
Provenance
The following attestation bundles were made for botrail-0.1.1-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on botrail/botrail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
botrail-0.1.1-cp39-abi3-win_amd64.whl -
Subject digest:
097ed7b4a4f1fca5bb9661d36cf269227d8747ec1ad60dce5b7889fbbef1ed4c - Sigstore transparency entry: 2332573823
- Sigstore integration time:
-
Permalink:
botrail/botrail@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/botrail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Trigger Event:
push
-
Statement type:
File details
Details for the file botrail-0.1.1-cp39-abi3-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: botrail-0.1.1-cp39-abi3-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 5.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b613cf3247c66deb446eaaba0a23768ce3ca322a63e417c6166548b9650b32a6
|
|
| MD5 |
41f69f2aeb32fa2feef2668f6db9c028
|
|
| BLAKE2b-256 |
45f2e0fff5c0ccc37aa0e2cefff3a73692f6fe20c98cecd949b68d393d91c410
|
Provenance
The following attestation bundles were made for botrail-0.1.1-cp39-abi3-manylinux_2_34_x86_64.whl:
Publisher:
release.yml on botrail/botrail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
botrail-0.1.1-cp39-abi3-manylinux_2_34_x86_64.whl -
Subject digest:
b613cf3247c66deb446eaaba0a23768ce3ca322a63e417c6166548b9650b32a6 - Sigstore transparency entry: 2332573836
- Sigstore integration time:
-
Permalink:
botrail/botrail@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/botrail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Trigger Event:
push
-
Statement type:
File details
Details for the file botrail-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: botrail-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 4.7 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79849a1f09303bfe033198f719cc7489ff1c52fedbdf5aa8c77707e3630dbeeb
|
|
| MD5 |
5ff5e888c80fd3de99b592d4ad25e32a
|
|
| BLAKE2b-256 |
4425652094e7c1fecc99632c25428be55bf5149691b83b1e12bf447216456764
|
Provenance
The following attestation bundles were made for botrail-0.1.1-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on botrail/botrail
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
botrail-0.1.1-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
79849a1f09303bfe033198f719cc7489ff1c52fedbdf5aa8c77707e3630dbeeb - Sigstore transparency entry: 2332573808
- Sigstore integration time:
-
Permalink:
botrail/botrail@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/botrail
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee64c0a5d8ed8aa3702eee3943cf4b4f2c09223 -
Trigger Event:
push
-
Statement type: