Arsenal Guide
BulletLab Arsenal is the official robot asset registry for BulletLab — a curated collection of community-contributed, verified robot packages that load correctly out of the box.
Think of it as PyPI for robotics assets.
Quick Reference
import bulletlab
# ⚡ One-liner: Deploy any Arsenal model immediately with full UI
bulletlab.quickLaunch("arsenal:reference_bot")
# Permanently install to ~/.bulletlab/packages/
bulletlab.Robot.install("reference_bot")
# Load directly into a custom simulation (session cache — no permanent files)
robot = bulletlab.Robot.load("arsenal:reference_bot", sim=sim)
Installation
No additional setup is required. Arsenal integration is built into BulletLab. An internet connection is needed to fetch packages from the registry.
Robot.install()
Downloads a robot package permanently to the local machine.
from bulletlab import Robot
# Install the default model
Robot.install("reference_bot")
# Install a specific model
Robot.install("reference_bot/BLem1")
# Install to a custom directory (e.g. inside your project)
Robot.install("reference_bot", path="robots/")
Robot.install("reference_bot/BLem1", path="robots/")
After installation you can load the robot from its local path:
The default install location is ~/.bulletlab/packages/<package_name>/.
Robot.load() with Arsenal URI
Load a robot directly from the Arsenal registry without permanently installing it. Assets are downloaded into a session-scoped temporary cache and cleaned up automatically when the Python process exits.
URI format
Examples
from bulletlab import Simulation, Robot
from bulletlab.core.world import World
sim = Simulation(mode="gui").start()
World(sim).load_plane()
# Default model
robot = Robot.load("arsenal:reference_bot", sim=sim)
# Specific model
robot = Robot.load("arsenal:reference_bot/BLem1", sim=sim)
# With standard Robot.load() parameters
robot = Robot.load(
"arsenal:reference_bot",
sim=sim,
position=(0, 0, 0.5),
fixed_base=False,
scale=1.0,
tilt=((0, 1, 0), 10),
)
All Robot.load() parameters (position, orientation, fixed_base, scale,
flags, tilt, name) work identically with Arsenal sources.
⚡ Instant Deployment with quickLaunch()
For fast inspection and testing, quickLaunch() combines Arsenal downloading, world creation, physics stepping, and the complete BulletLab UI into a single line of code:
import bulletlab
# Deploy the default model of an Arsenal package
bulletlab.quickLaunch("arsenal:reference_bot")
# Deploy a specific model variant
bulletlab.quickLaunch("arsenal:reference_bot/BLem1")
Why use quickLaunch() for Arsenal models?
- Pre-Cached Window Creation: Arsenal assets are fully downloaded and resolved before the PyBullet simulation window opens. You never see an empty, unresponsive window during network downloads.
- Auto-Generated UI: Automatically builds a custom joint controller supporting Position, Velocity, and Torque modes per joint.
- Dynamic Camera Follow: Keeps the camera tracking the robot with an interactive capsule toggle switch.
- Live Telemetry & Console: Real-time state readouts and live Python REPL out of the box.
Install vs. Direct Load
| Feature | Robot.install() |
Robot.load("arsenal:...") |
|---|---|---|
| Files on disk after exit | ✅ Permanent | ❌ Deleted automatically |
| Works offline after first use | ✅ Yes | ❌ Requires network each session |
| Clutters local filesystem | Slightly (in ~/.bulletlab/) |
Never |
| Best for | Reproducible research, production | Quick experiments, demos |
| Loading API | Robot.load(str(installed_path), ...) |
Robot.load("arsenal:...", ...) |
Model Resolution
When you use "arsenal:reference_bot" (no model specified), BulletLab:
- Fetches the Arsenal robot manifest from the registry.
- Locates the
reference_botpackage. - Reads the package's
metadata.json. - Selects the model where
"default": true(or the first model if none is marked). - Downloads only the URDF and its referenced mesh files.
- Rewrites mesh paths to absolute local paths so PyBullet can find them.
When you use "arsenal:reference_bot/BLem1", step 4 selects the model whose id or
display_name matches "BLem1" (case-insensitive).
Session Cache
The session cache is stored in a system temp directory (e.g. %TEMP%\bulletlab_arsenal_...
on Windows, /tmp/bulletlab_arsenal_... on Linux/macOS).
- Created on the first
Robot.load("arsenal:...")call. - Shared across all Arsenal loads in the same session (no duplicate downloads).
- Deleted automatically via
atexitwhen the process exits. - Never visible to the user — no cleanup is needed.
Error Handling
All Arsenal errors are subclasses of ArsenalError:
from bulletlab import Robot, ArsenalError
from bulletlab.arsenal import (
PackageNotFoundError, # Package not in the registry
ModelNotFoundError, # Model ID not found in the package
NetworkError, # HTTP or connection failure
ManifestError, # Manifest unavailable or malformed
CorruptedPackageError, # Downloaded file is missing or empty
)
try:
robot = Robot.load("arsenal:my_package", sim=sim)
except PackageNotFoundError as e:
print(f"Package not found: {e}")
except ModelNotFoundError as e:
print(f"Model not found: {e}")
except NetworkError as e:
print(f"Network failure: {e}")
except ArsenalError as e:
print(f"Arsenal error: {e}")
What Gets Downloaded
Only the files required by the requested model are downloaded:
- The URDF file (entrypoint defined in
metadata.json). - All mesh files referenced by
<mesh filename="...">tags in the URDF. - No unrelated models, documentation, or verification artifacts.
Mesh paths in the downloaded URDF are automatically rewritten to absolute local paths, so PyBullet can find every asset regardless of the working directory.
Programmatic Access
You can access the Arsenal subpackage directly for advanced use:
from bulletlab.arsenal import install, DEFAULT_PACKAGES_DIR
from bulletlab.arsenal.resolver import resolve_package, resolve_model
from bulletlab.arsenal.paths import ARSENAL_ROBOTS_MANIFEST_URL
# Inspect the default install location
print(DEFAULT_PACKAGES_DIR) # ~/.bulletlab/packages
# Resolve without downloading
pkg = resolve_package("reference_bot")
model = resolve_model("reference_bot", None)
print(model["entrypoint"]) # urdf/BLem1.urdf