Parallelized mjlab environments for nominal-policy training, safety-policy synthesis, and safety-filter evaluation.
Massively-parallel mjlab benchmark environments for safety_sb3
(safety-stable-baselines): reach-avoid / avoid-only × single-agent /
adversarial (ISAACS), on GPU end-to-end — plus a filters/ library with the
three deployment styles (value shielding, R-CBF/Q-CBF projection,
rollout shielding).
Renamed from
safe_mjlab_zoo(a deprecated import alias remains for one transition cycle).
from robot_safety_sandbox import make_tensor, list_tasks
from safety_sb3 import ReachAvoidPPO
env = make_tensor("go2_gap_chain", num_envs=2048) # ~50k steps/s on 12 GB
model = ReachAvoidPPO("MlpPolicy", env, normalize_obs=True, adaptive_lr=True,
ent_coef=1e-4, n_steps=48, batch_size=24576,
policy_kwargs=dict(log_std_init=-1.204))
model.learn(2_000_000_000)| channel | meaning |
|---|---|
| reward | g(s) — physical safety margin. Never normalize or reshape it. |
l_x |
l(s) — target margin (zeros for avoid-only tasks) |
| dones / timeouts | mjlab auto-resets; timeouts are never value-bootstrapped |
metrics() |
curriculum levels + task metrics, forwarded to the logger every rollout |
A task = cfg_builder(play) -> ManagerBasedRlEnvCfg (spawn events, curricula,
terrain — plain mjlab, algorithm-agnostic) + margin_fn(env) -> (g, l)
(compose from margins.py). Register a TaskSpec and both bridges
(make_tensor for PPO learners, make_numpy for the SAC family) work.
| task | objective | learner | warm-starts from |
|---|---|---|---|
go2_stabilize / go2_locomote |
stand / track a command vs adversarial force (the original task; simplest zoo entry) | ReachAvoidPPO | — |
digit_stabilize |
humanoid stand vs adversarial torso force (Digit analog of go2_stabilize) | ReachAvoidPPO | — |
go2_gap_landing |
soft-land from mid-air over a gap | SafetyPPO | — |
go2_gap_crossing |
reverse curriculum: landing → launch | SafetyPPO | landing |
go2_gap_chain |
takeover momentum → safe rest (brake/jump) | ReachAvoidPPO | crossing |
go2_gap_chain_isaacs |
chain + worst-case force adversary | IsaacsPPO | chain |
go2_crawl / _isaacs |
duck under a low bar or stop | ReachAvoidPPO | — |
Task structure varies: go2_stabilize needs no curriculum or staging at all,
while the gap family only forms its jump through staged warm-starts
(TaskSpec.warmstart_from) at real scale (~2B env-steps for the chain). See
PORTING.md for which machinery your task actually needs.
normalize_obs=True (obs only — reward normalization is refused by
safety_sb3), ent_coef=1e-4, log_std_init=ln(0.3), adaptive_lr=True
(desired_kl=0.01, lr 5e-4), n_steps=48. Watch the env/Curriculum/*
logger keys — a stalled curriculum looks exactly like converged training in
the reward curve.
- Write the mjlab env cfg: terrain, spawn events (takeover-momentum or
staged spawns), reverse curricula. Study
go2_gap— especially how the landing → crossing → chain pipeline seeds rare-win skills. - Compose
margin_fnfrommargins.py(or add new terms there). register(TaskSpec(...))intasks/<your_task>.py.- Train with
examples/train.py --task <id>; verify curricula CLIMB in wandb.
docs/EXTENDING.md walks the four extension axes with worked examples from
the shipped tasks: margin functions, sensors/observations, terrains
(heightfields, walls, gaps, obstacles), and contacts — plus the
new-robot checklist (go2 and digit are the two reference layouts).
SELF-CONTAINED: env cfgs, terrains, robot assets (Go2, Digit), and
the handover dataset are native under robot_safety_sandbox/envs/ + data/.
safety_sb3 is a pinned pip dependency
(safety-stable-baselines
v0.1.0); mjlab is a peer dep with its own pinned sim stack (INSTALL.md).