Tasks and presets#
A preset is a pure Python function apply_*(cfg) -> None that mutates an
existing ManagerBasedRlEnvCfg in place. A task is a registered name for
one specific stack: robot entity + preset(s) + optional wiring.
Not sure which task to run first? See Which task should I use? on the Quickstart.
Presets never add new MDP term implementations — they reweight rewards, swap RSI
flags, remove observation terms, or adjust termination thresholds on slots already
defined in make_base_wbc_env_cfg(). Tasks are not separate MDP forks.
How they connect#
make_base_wbc_env_cfg() # shared MDP template (env/wbc_env_cfg.py)
↓
<robot>_base_cfg() # robot entity: MJCF, body names, sensors
↓
apply_<preset>(cfg, ...) # paper / deploy recipe (presets/*.py)
↓
[optional] apply_se_actor(cfg) # composable obs swap
↓
WbcTaskConfig( # CLI registration (tasks/config.py)
task_id="Wbc-…",
build_env_cfg=<builder above>,
)
Preset = recipe (reusable across robots). Task = one named builder + log dir.
The same preset can back multiple tasks (e.g. apply_wbc on g1 and h2).
Available presets (core)#
Function |
Module |
Typical use |
|---|---|---|
|
|
Default WBC + deploy extras (Table S4 on all keybodies) |
|
|
ZEST paper repro (Table S4 on EE bodies only, no EE-z termination) |
|
|
BeyondMimic-style |
|
|
Actor obs swap (compose on top of wbc/zest) |
Preset → task map (in-tree example)#
Task id |
Preset stack |
Builder ( |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Extension tasks (e.g. Wbc-H2) use the same pattern — see
Extension packages.
Philosophy#
Principle |
Meaning |
|---|---|
Shared MDP |
One |
Tasks, not forks |
Paper differences = |
Robot entity separate |
Each platform registered via The robot entity |
Neutral code |
RSI strategies named generically; cite papers from Research & citations |
Quick start (example)#
uv run wbc-mjlab-data-to-npz --robot g1 --dataset samples --batch-size 8
uv run wbc-mjlab-list-envs
uv run wbc-mjlab-train --task Wbc-G1 --dataset samples
Paper ↔ task map (in-tree example)#
Paper |
Link |
Example task ids |
|---|---|---|
ZEST |
|
|
BeyondMimic |
|
|
SONIC |
Planned — Roadmap |
Preset comparison (in-tree example)#
|
|
|
|
|---|---|---|---|
Preset |
|
|
|
Body rewards |
all keybodies |
EE bodies only |
all keybodies |
RSI |
reward-aligned |
reward-aligned |
|
|
yes |
removed |
yes (base) |
Example: apply_zest (preset code)#
presets/zest.py — mutates rewards, RSI, observations, and
terminations on the cfg object passed in. Robot-specific body tuples come from
arguments (supplied by the task builder):
def apply_zest(
cfg: ManagerBasedRlEnvCfg,
*,
reward_body_names: tuple[str, ...],
contact_body_names: tuple[str, ...],
force_threshold: float = 2000.0,
) -> None:
rw = cfg.rewards
# Table S4 tracking kernels on selected bodies
rw["motion_body_pos"].weight = 1.0
rw["motion_body_pos"].params["sigma_per_keybody"] = 0.2
rw["motion_body_pos"].params["body_names"] = reward_body_names
rw["motion_body_ori"].params["sigma_per_keybody"] = 0.4
rw["motion_body_ori"].params["body_names"] = reward_body_names
# ... root + joint terms, kappa=0.25 ...
# Disable extras not in ZEST Table S4
rw["motion_body_lin_vel"].weight = 0.0
rw["motion_joint_vel"].weight = 0.0
rw["foot_slip"].weight = 0.0
# Reward-aligned RSI
motion_cmd = cfg.commands["motion"]
motion_cmd.rsi = replace(
motion_cmd.rsi,
similarity_from_rewards=True,
bin_width_s=4.0,
similarity_norm_by_remaining_clip=True,
min_bin_span_ratio=0.5,
persist_failure_levels=True,
)
# Actor: drop ref_joint_vel
cfg.observations["actor"].terms.pop("ref_joint_vel", None)
# Terminations: ZEST style — no EE height cutoff
cfg.terminations.pop("ee_body_pos", None)
cfg.terminations["anchor_pos"].params["threshold"] = 0.35
cfg.terminations["keybody_ground_contact_force"] = TerminationTermCfg(
func=mdp.excessive_keybody_ground_contact_force,
params={"body_names": contact_body_names, "force_threshold": force_threshold, ...},
)
Full source: src/wbc_mjlab/presets/zest.py.
Example: task builder + registration#
def g1_wbc_zest_env_cfg() -> ManagerBasedRlEnvCfg:
cfg = g1_base_cfg()
apply_zest(
cfg,
reward_body_names=G1_ENDEFFECTOR_BODY_NAMES,
contact_body_names=G1_MOTION_BODY_NAMES,
)
return cfg
G1_WBC_TASKS = (
WbcTaskConfig(
task_id="Wbc-G1-Zest",
robot_id="g1",
experiment_name="wbc_g1_zest",
build_env_cfg=g1_wbc_zest_env_cfg,
),
# ...
)
Training uses the task id, not the preset name:
uv run wbc-mjlab-train --task Wbc-G1-Zest --dataset samples
Composing presets#
def g1_wbc_zest_se_env_cfg() -> ManagerBasedRlEnvCfg:
cfg = g1_wbc_zest_env_cfg()
apply_se_actor(cfg)
wire_g1_imu_sensors(cfg)
return cfg
Order matters: base cfg → primary paper preset → optional obs/IMU tweaks.
When to add a preset vs a task#
Change |
Where |
|---|---|
New reward/RSI implementation |
|
New paper weights / RSI flags / term toggles |
New |
New |
Task builder + |
Same preset, different robot |
New task builder with that robot’s body name tuples — preset unchanged |
Task guides (in-tree)#
Task |
Logs |
Guide |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Related: Add a task, Shared MDP, Architecture overview.