MotionBricks¶

Kinematic rollout in MuJoCo (headless, MUJOCO_GL=egl): the G1 cycles through walk, stealth_walk, and walk_boxing styles.
MotionBricksPolicy
wraps NVIDIA's MotionBricks generative
motion model (the motionbricks/ subproject of
GR00T-WholeBodyControl).
MotionBricks is a generative kinematic motion model: given a high-level
style (a clip mode such as walk / stealth_walk / walk_boxing) plus a
movement/facing command, it synthesises per-frame full-body qpos for the
Unitree G1, faster than real time.
Like the other non-VLA providers (wbc, cuRobo, MoveIt2) it runs in the same
process (torch, no sidecar):
requires_images = False- driven by a style + direction command, never camera frames.get_actionsreads its goal from the well-known**kwargs(style/mode,target_velocity,target_heading), ignoring the instruction string.- Each call advances the generator one frame synchronously, no threads and returns the G1's 29 leg+waist+arm joint targets keyed by joint name.
Where it sits in the stack¶
MotionBricks is a higher layer than WBC, not a replacement. It generates the motion targets; a tracking controller turns them into torques under physics:
high-level intent (style, direction)
|
v
MotionBricks <- THIS provider: per-frame motion targets (root + joint refs)
|
v
WBCPolicy / GEAR-SONIC <- tracks the targets: joint torques / position targets
|
v
robot (sim or hardware)
The two compose through
CompositePolicy:
MotionBricks emits the joint references, WBC tracks them. The 29 joints are
keyed by the same canonical ordering as WBC (MOTIONBRICKS_G1_JOINTS is
WBC_G1_ALL_JOINTS), so the two name the same joints without a remapping table.
Standalone, the policy's output is a kinematic reference - the faithful way to
visualise a kinematic generator is to set the synthesised qpos
(policy.last_qpos) and run forward kinematics, exactly like the upstream
interactive_demo_g1.py.
Install¶
The motionbricks package is not on PyPI. Install the PyPI support
libraries via the extra, then the upstream package editable, then fetch the
checkpoints with git-LFS (~2.2 GB, NVIDIA Open Model License - no weights are
bundled):
pip install "strands-robots[motionbricks]"
git clone https://github.com/NVlabs/GR00T-WholeBodyControl.git
cd GR00T-WholeBodyControl
git lfs install
# The parent repo skips MotionBricks checkpoints by default (.lfsconfig
# fetchexclude); --exclude="" overrides that so the weights actually download.
git lfs pull --include="motionbricks/out/**" --exclude=""
git lfs pull --include="motionbricks/assets/skeletons/g1/meshes/**" --exclude=""
pip install -e motionbricks
# Verify the checkpoints are real files, not LFS pointers:
ls -lh motionbricks/out/G1-clip.ckpt # ~7.5 MB
ls -lh motionbricks/out/motionbricks_pose/version_1/checkpoints/*.ckpt # ~1.6 GB
ls -lh motionbricks/out/motionbricks_root/version_1/checkpoints/*.ckpt # ~391 MB
ls -lh motionbricks/out/motionbricks_vqvae/version_1/checkpoints/*.ckpt # ~273 MB
A CUDA GPU is recommended; device="cpu" also works (slower, but the kinematic
generator still runs well above real time on CPU).
Usage¶
from strands_robots.policies.motionbricks import MotionBricksConfig, MotionBricksPolicy
config = MotionBricksConfig(
result_dir="/path/to/GR00T-WholeBodyControl/motionbricks/out",
device="cuda", # or "cpu"
style="walk",
)
policy = MotionBricksPolicy(config=config)
# One synthesis frame -> 29 joint targets keyed by G1 joint name.
actions = policy.get_actions_sync({}, "", style="stealth_walk")
joint_targets = actions[0] # {"left_hip_pitch_joint": ..., ...}
full_qpos = policy.last_qpos # [root(7), joints(29)] for kinematic viz
Or through the factory / a simulation:
from strands_robots.policies import create_policy
policy = create_policy(
"motionbricks",
result_dir="/path/to/.../motionbricks/out",
style="walk",
)
Configuration¶
MotionBricksConfig field |
Meaning | Default |
|---|---|---|
result_dir |
Path to the upstream out/ checkpoint tree |
required |
skeleton_xml / scene_xml |
G1 skeleton / scene MuJoCo XML | derived from result_dir |
clips |
Clip set name | "G1" |
style |
Default mode (index or name) | "walk" |
generate_dt |
Synthesis horizon multiplier | 2.0 |
fps |
Motion frame rate | 30 |
device |
Torch device | "cuda" |
speed_scale |
(min, max) root-velocity perturbation |
(1.0, 1.0) |
Per-call goal kwargs¶
| kwarg | Meaning |
|---|---|
style / mode |
Clip mode - an int index or str name (e.g. "walk", "stealth_walk", "walk_boxing", "hand_crawling"). |
target_velocity |
[vx, vy] desired planar movement direction (world frame); only the direction is used. |
target_heading |
[hx, hy] facing direction, or target_heading_angle (radians). |
An unknown style or out-of-range index raises ValueError listing the
available modes. A missing [motionbricks] install or checkpoint raises
RuntimeError with an install hint - there is no silent fallback.
Driving the gait from a KinematicPlanner¶
A KinematicPlanner steers MotionBricks at
the intent layer: each control tick it emits a locomotion_style (its fixed
SONIC-demo vocabulary - run, happy, stealth, injured, hand_crawling,
elbow_crawling, boxing) plus a target_velocity. MotionBricks names its G1
clips differently, so the policy translates locomotion_style to the matching
clip via PLANNER_STYLE_TO_G1_CLIP:
planner locomotion_style |
MotionBricks clip |
|---|---|
run |
walk |
happy |
walk_happy_dance |
stealth |
stealth_walk |
injured |
injured_walk |
hand_crawling |
hand_crawling |
elbow_crawling |
elbow_crawling |
boxing |
walk_boxing |
from strands_robots import Robot
from strands_robots.planning import KinematicPlanner
from strands_robots.planning.inputs import ScriptedInput
from strands_robots.planning.base import PlannerCommand
robot = Robot("unitree_g1")
planner = KinematicPlanner(ScriptedInput([
(0.0, PlannerCommand(root_vel=(0.5, 0.0, 0.0), style="run")),
(3.0, PlannerCommand(root_vel=(0.3, 0.0, 0.0), style="stealth")),
(6.0, PlannerCommand(root_vel=(0.0, 0.0, 0.0), style="boxing")),
]))
robot.run_policy(policy_provider="motionbricks", planner=planner,
policy_config={"result_dir": "/path/to/.../motionbricks/out"},
duration=9.0, control_frequency=30.0)
Resolution order per tick: an explicit style=/mode= kwarg pins the clip
(overriding any planner style); otherwise locomotion_style is translated;
otherwise the configured default style is used. The planner's kneeling
style has no G1 clip - emitting it raises ValueError rather than miming the
wrong motion. Supply a style_map (on the policy or in MotionBricksConfig) to
remap styles or target a custom clip set.
Visualisation¶
Render a style sequence headless (MUJOCO_GL=egl) with the bundled example:
MUJOCO_GL=egl python examples/wbc/motionbricks_g1_mujoco.py \
--result-dir /path/to/GR00T-WholeBodyControl/motionbricks/out \
--device cuda --styles walk,stealth_walk,walk_boxing \
--out /tmp/motionbricks_g1.mp4
Testing¶
# Unit tests (no GPU, no checkpoints - stubbed generator via the motion_agent seam):
pytest tests/policies/motionbricks/
# Live integration (real generator):
MOTIONBRICKS_CKPT=/path/to/.../motionbricks/out pytest -m motionbricks tests_integ/policies/motionbricks/
Out of scope¶
- Training (upstream ships
train_vqvae.py/train_pose.py/train_root.py). - VR teleoperation.
- Non-G1 embodiments (each needs its own joint mapping table + checkpoints).