Introduction#
I-BFM is an open-source behavior foundation model for humanoid–object interaction. It uses forward–backward representations and unsupervised reinforcement learning to learn a joint representation of the humanoid, objects, and contacts. A downstream reward function is enough to infer an intent vector, allowing the same policy to carry, push, or kick boxes without task-specific retraining.
For box carrying, I-BFM achieves 94.3% success without disturbances and 89.3% after a robot fall. By comparison, the planning-based baseline achieves only 1.3% after a fall.
Resources:
1. Overview#
This section covers how I-BFM works, release contents, and environment setup. By the end, you can download the released model and run a box-carrying task in simulation.
1.1 What I-BFM Can Do#
| Workflow | Purpose | See |
|---|---|---|
| Run interaction tasks | Use the released model in MuJoCo to carry, push, and kick boxes; move targets with the keyboard and observe recovery after falls | Section 2 |
| Prepare motion data | Convert your motions to the training format | Section 3 |
| Training | Train from scratch with the released configuration on 8 H200 GPUs | Section 4 |
| Inference | Run motion tracking, goal reaching, and reward inference, and export ONNX | Section 5 |
For a quick preview, open the Live MuJoCo Session on the project page to try box carrying and keyboard control in your browser.
1.2 How It Works#
Shared representation. I-BFM uses forward–backward (FB) representations to learn the coupled dynamics of the humanoid, objects, and contacts. The Backward encoder maps states into a 256-dimensional intent space, and the policy outputs actions conditioned on intent vector z.
Unsupervised pretraining. Pretraining targets no particular task. It mixes LaFAN motions (30%), box-carrying motions (40%), and box-kicking motions (30%) as behavioral priors while interacting online in simulation.
Dual-timescale objectives. Training combines short-term interaction objectives with longer-term objectives (target horizon: 8 steps), enabling interaction control at different timescales.
Reward inference. Given a downstream reward function, replay samples weighted by that reward determine the task's intent vector z, without policy retraining.
Task controllers. Carrying, pushing, and kicking each have a controller that switches between pre-inferred intent vectors across phases—approaching, aligning, lifting, carrying, placing, recovering from falls, and so on—to form a closed loop.
1.3 Release Contents#
| Item | Location | Description |
|---|---|---|
| Released model | checkpoints/ibfm-166m/ |
Model at global step 166,559,744, shared by all three interaction tasks; weights split into two parts, downloaded through Git LFS |
| Interaction tasks | ibfm/tasks/carry_box/, push_box/, kick_box/ |
Launch scripts, task controllers, and inferred intent vectors |
| Training source and configuration | ibfm/ |
Training implementation, data mixture, and task configuration used for the released model |
| Training data | ibfm/humanoidverse/data/, ibfm/cache/motion_data/ |
LaFAN motions and carrying/kicking motion caches, downloaded through Git LFS |
| Replay buffer | Hugging Face | Replay corresponding to the released model: 8 shards, approximately 210 GiB, for reward inference (Section 5.3) |
The repository does not include real-robot deployment software.
Note Training source retains upstream BFM-Zero's CC BY-NC 4.0 license and is restricted to noncommercial use.
1.4 System Requirements#
| Item | Requirement |
|---|---|
| Operating system | Linux |
| Python | 3.10 |
| Tools | uv, Git LFS |
| Main dependencies | MJLab 1.4.0, MuJoCo 3.8, PyTorch 2.7 (pinned by ibfm/uv.lock) |
| Interaction tasks | Policy inference runs on CPU; no GPU required. The viewer needs a desktop environment |
| Training | 8 H200 GPUs |
| Disk | Approximately 4 GB of Git LFS files (two model parts total about 3.4 GB), plus about 3.4 GB to restore the model; full replay is about 210 GiB |
1.5 Installation#
git clone https://github.com/Roboparty/I-BFM-Code.git
cd I-BFM-Code
git lfs install
git lfs pull
bash restore_checkpoint.sh
cd ibfm
uv syncgit lfs pulldownloads both weight parts, LaFAN motions, and carrying/kicking motion caches.restore_checkpoint.shchecks both parts' SHA-256 hashes, joins them intocheckpoints/ibfm-166m/model/model.safetensors, and verifies the result. It printsRestored …on success, orCheckpoint is already restored.if already restored.uv syncinstalls dependencies fromuv.lockintoibfm/.venv.
Note Without
git lfs pull, the weight parts are only pointer files of a few hundred bytes, causingrestore_checkpoint.shto exit on a SHA-256 mismatch. Rungit lfs pull, then restore again.
Tip All subsequent commands in this guide run inside
ibfm/.
1.6 Run a Quick Check#
On a machine without a display, run 100 steps of box carrying in headless mode:
HEADLESS=1 ./tasks/carry_box/run_interactive.shThe script generates a random scene, loads the released model, and runs it. At completion, it prints the trajectory, controller-summary, and height-test paths (Trace:, Controller:, Height tests:), confirming that the model and environment are ready.
With a desktop environment, run ./tasks/carry_box/run_interactive.sh directly to open the MuJoCo viewer and record video; see Section 2.2.
1.7 Directory Layout#
I-BFM-Code/
├── restore_checkpoint.sh # Restore model weights
├── checkpoints/ibfm-166m/ # Released model
└── ibfm/ # Run subsequent commands here
├── tasks/
│ ├── carry_box/ # Box carrying
│ ├── push_box/ # Box pushing
│ ├── kick_box/ # Box kicking
│ └── common/checkpoint_ref/ # Link to the released model; use as --model-folder
├── scripts/train_ibfm_h200.sh # Training launcher
├── configs/ # Robot, data-mixture, and task configurations
├── humanoidverse/ # Source and inference scripts
└── runs/ # Training outputs (Section 4)2. Interactive Tasks#
This section runs three interaction tasks in MuJoCo with the released model. All three share one model; their task controllers and intent vectors differ. Run commands inside ibfm/.
2.1 Viewer Controls#
With the viewer open, use the keyboard to change targets or introduce disturbances:
| Key | Action |
|---|---|
| W / S | Move the target forward/backward by 0.25 m along X |
| A / D | Move the target left/right by 0.25 m along Y |
| H | Restore the initial target |
| R | Reset the scene |
| F | Make the robot fall and observe recovery and task resumption (pushing and kicking only) |
| P | Print the current state in the terminal |
The viewer uses GLFW and requires a desktop environment. If DISPLAY is unset, the carrying script defaults to :1; set the correct DISPLAY first if your display number differs.
2.2 Carry a Box#
./tasks/carry_box/run_interactive.shThe robot approaches the box, aligns, lifts it, carries it to the target, and puts it down. If it falls, it gets up before continuing.
The script performs two steps:
Generate a random scene: robot–box distance 1.5–2.0 m and target height 0.65 m, saved to
tasks/carry_box/random_height_scene/.Run the carrying controller with a 0.30 m cubic box weighing 0.70 kg.
For carrying, R also scores the current trial, places the box at a random height between 0.15 and 0.80 m, and starts a new trial. Each trial records successful lifting and the 3D distance between the box center and target; placement succeeds when this distance is below 0.20 m.
Common environment variables:
| Variable | Purpose | Default |
|---|---|---|
HEADLESS |
Set to 1 to disable the viewer and video recording |
0 |
MAX_STEPS |
Steps in headless mode | 100 |
TARGET_HEIGHT |
Target placement height (m) | 0.65 |
HEIGHT_MIN / HEIGHT_MAX |
Random box-height range when resetting with R (m) | 0.15 / 0.80 |
ROBOT_BOX_DISTANCE_MIN / MAX |
Initial robot–box distance range (m) | 1.5 / 2.0 |
SCENE_SEED / HEIGHT_SEED |
Random seeds for scene and height | 166559744 |
RENDER_SIZE / VIDEO_FPS |
Video resolution and frame rate | 720 / 30 |
RECORD_TAG |
Output directory name suffix | Current time |
Outputs are written to tasks/carry_box/interactive/random_height_<RECORD_TAG>/:
| File | Contents |
|---|---|
session.mp4 |
Recorded video (when the viewer is open) |
session.csv |
Step-by-step trajectory |
controller_summary.json |
Controller summary |
height_trials.json |
Lifting and placement results for each trial, plus cumulative success rates |
2.3 Push a Box#
./tasks/push_box/run_interactive.shThe robot moves behind the box, aligns, and pushes it toward the target with its hands. The target defaults to 2.0 m along X from the box's initial position. Success requires the box–target horizontal distance to be within the threshold (default 0.40 m), and the controller also checks that the box is stationary and flat on the ground. The first argument can set the output directory name, for example ./tasks/push_box/run_interactive.sh test1.
| Variable | Purpose | Default |
|---|---|---|
VIEWER |
1 opens the viewer; 0 uses EGL offscreen rendering |
1 |
MAX_STEPS |
Run length; 0 means unlimited |
0 with viewer, otherwise 3000 |
SAVE_MP4 |
Record video | 1 |
INITIAL_DISTANCE |
Initial robot–box distance (m) | 1.0 |
GOAL_OFFSET_X / GOAL_OFFSET_Y |
Target offset relative to the box (m) | 2.0 / 0.0 |
SUCCESS_DISTANCE |
Arrival distance threshold (m) | 0.40 |
OUTPUT_ROOT |
Output root | tasks/push_box/rollouts |
Outputs are <OUTPUT_ROOT>/<name>/push_box.mp4, rollout.csv, and summary.json.
2.4 Kick a Box#
./tasks/kick_box/run_interactive.shThe robot contacts the box with its foot and kicks it toward the target, located 2.0 m along X from the box's initial position. The target marker has a radius of 0.20 m. By default, the script opens the viewer, does not record video, and runs inference on CPU.
| Variable | Purpose | Default |
|---|---|---|
VIEWER |
Open the viewer | 1 |
SAVE_MP4 |
Record video | 0 |
MAX_STEPS |
Run length; 0 means unlimited |
0 |
OUTPUT_ROOT |
Output root | tasks/kick_box/local_cpu_rollouts |
Outputs are rollout.csv and summary.json, plus kick_box.mp4 when recording.
Note The kicking script defaults to GLFW even with
VIEWER=0. On a machine without a desktop, also setMUJOCO_GL=egland a finiteMAX_STEPS, for exampleVIEWER=0 MUJOCO_GL=egl MAX_STEPS=3000 ./tasks/kick_box/run_interactive.sh.
2.5 Reference Results#
Simulation success rates (%) published on the project page:
| Condition | Carrying | Pushing | Kicking |
|---|---|---|---|
| No external disturbance | 94.33 | 90.00 | 81.67 |
| Object disturbance | 91.00 | 85.33 | 79.67 |
| Robot disturbance | 89.33 | 84.67 | 86.67 |
3. Motion Data#
This section describes the training motion data and how to convert your own motions. Run commands inside ibfm/.
3.1 Data in the Released Configuration#
The released model's data mixture is defined in configs/data/g1_lafan_carry_kick_30_40_30.yaml. All three sources are downloaded with git lfs pull:
| Data | Sampling weight | File |
|---|---|---|
| LaFAN motions (10-second clips) | 30% | humanoidverse/data/lafan_29dof_10s-clipped.pkl |
| Box-carrying motions | 40% | Training caches under cache/motion_data/g1_carry_box_omniretarget_no_foot/ |
| Box-kicking motions | 30% | Training caches under cache/motion_data/g1_kick_box_omniretarget/ |
Weights apply per data source, independently of the number of motions in each source.
3.2 Convert Your Motions#
Your motions can use robot-state CSV or NPZ format. A CSV has one frame per row, containing root position (such as root_pos_x/y/z), root quaternion (such as root_quat_x/y/z/w, in xyzw order), and joint angles in robot XML order. Specify the frame rate with --fps.
Check that the data can be read correctly:
uv run python -m humanoidverse.tools.data_inspect \ --robot configs/robots/g1_29dof.yaml \ --source <your-CSV-or-NPZ-file-or-directory>Generate a data manifest and build the cache:
uv run python -m humanoidverse.tools.data_build \ --robot configs/robots/g1_29dof.yaml \ --source <your-CSV-or-NPZ-file-or-directory> \ --name <dataset-name> --fps <frame-rate> \ --out <manifest-path>.yamlBy default, clips are split at 10 seconds (
--clip-seconds); clips shorter than 10 seconds but longer than 1 second are retained.Train with
--data-manifest <manifest-path>.yaml(Section 4.4). To mix with released data, followconfigs/data/g1_lafan_carry_kick_30_40_30.yaml: list multiple sources in one manifest and set theirweightvalues.
See configs/data/example_robot_state_auto_build.yaml for a complete manifest example.
4. Training#
This section trains I-BFM from scratch with the released configuration. It requires 8 H200 GPUs. Run commands inside ibfm/.
4.1 Start Training#
./scripts/train_ibfm_h200.sh ibfm_reproductionThe argument names this run, with outputs written to runs/ibfm_reproduction/. Names cannot contain slashes; the script refuses to overwrite an existing directory.
The script uses the released configuration:
| Setting | Value |
|---|---|
| Task | carry_box, with task configuration configs/tasks/carry_box_mass0p7_range0p5_1p0_pd_delay_rfi_footmu0p4_1p25.yaml |
| Data | configs/data/g1_lafan_carry_kick_30_40_30.yaml (Section 3.1) |
| GPUs | All visible GPUs (--gpu-ids all), with 1024 parallel environments per GPU |
| Total steps | 250,000,000 global environment steps; override with NUM_ENV_STEPS |
| Save interval | Rolling checkpoint every 3,200,000 steps |
| Replay capacity | 5,120,000 transitions per GPU |
| Random seed | 4728 |
| Logging | W&B offline mode, saved locally |
The released model was taken at step 166,559,744 of this type of training run.
Training randomization from the task configuration: nominal box mass 0.7 kg, randomized over 0.5–1.0 kg; joint PD gains scaled by 0.75–1.25; control delay 0–1 steps (20 ms per step); foot friction randomized over 0.4–1.25.
4.2 Training Outputs#
| Path | Contents |
|---|---|
runs/<name>/checkpoint/ |
Rolling checkpoint with model and optimizer, for resuming training |
runs/<name>/policies/latest |
Latest completed policy |
runs/<name>/policies/best |
Policy with the lowest mean EMD over 2,719 evaluation motions; step and score recorded in best.json |
runs/<name>/launcher.log |
Training log |
runs/<name>/policy_retention.log |
Policy retention log |
Policies in policies/ contain weights and configurations, without the optimizer or replay. Replay is written only once to the final rolling checkpoint when training finishes normally.
4.3 Resume After Interruption#
RESUME=1 ./scripts/train_ibfm_h200.sh ibfm_reproductionResuming restores the model and optimizer from the rolling checkpoint.
Note Intermediate checkpoints do not contain replay, which must be collected again after resuming. Resumed training therefore differs from uninterrupted training.
4.4 Check Training with Smoke Mode#
Run a short training job on one GPU to confirm the environment works:
./run_train.sh --agent fb \
--data-manifest configs/data/example_mix.yaml \
--gpu-ids single --smoke \
--work-dir /tmp/ibfm_smokeSmoke mode uses 16 environments and 2048 environment steps, without W&B logging. run_train.sh wraps the humanoidverse.train entry point and sets cache directories before launching. See scripts/train_ibfm_h200.sh for the full training arguments.
Note The final step in
scripts/smoke_release.shreadshumanoidverse/data/examples/g1_robot_state_sample.csv, which is absent from the repository, so that step fails. Use the command above if you only need to check training.
5. Inference#
This section uses the released model or your own model for motion tracking, goal reaching, and reward inference. Run commands inside ibfm/. --model-folder points to a directory containing checkpoint/: use tasks/common/checkpoint_ref for the release or runs/<name> for your own model.
5.1 Motion Tracking#
uv run python -m humanoidverse.tracking_inference \
--model-folder tasks/common/checkpoint_ref \
--motion-list 20 --save-mp4 \
--output-dir runs/tracking_previewThe policy encodes the reference motion as intent vectors and tracks it in simulation. --save-mp4 produces a comparison video of the reference and policy execution. Default data: humanoidverse/data/lafan_29dof_10s-clipped.pkl.
| Parameter | Purpose | Default |
|---|---|---|
--motion-list |
Motion indices; accepts multiple values | 20 |
--output-dir |
Output directory | <model-folder>/tracking_inference |
--lie-down-init |
Start the robot lying on its side to test recovery followed by tracking | No |
--downward-force-n |
Downward impulse force at the root (N); 0 disables it |
0 |
--force-start-s / --force-duration-s |
Impulse start time and duration (seconds) | 2.4 / 0.25 |
--export-onnx |
Export ONNX to <model-folder>/exported/ before inference |
Yes |
--device |
Inference device | cuda:0 |
Outputs are zs_<index>.pkl (intent vectors) and tracking_<index>.mp4.
5.2 Goal Reaching#
uv run python -m humanoidverse.goal_inference \
--model-folder tasks/common/checkpoint_ref --save-mp4The policy reaches a sequence of target poses. The default list is humanoidverse/data/robots/g1/goal_frames_lafan29dof.json; replace it with --goal-json. The default run lasts 2100 steps, switching targets every 100 steps (--episode-len, --goal-switch-interval).
5.3 Reward Inference#
Reward inference requires replay. The released model's replay is on Hugging Face: 8 shards totaling about 210 GiB. Download it from inside ibfm/:
uvx --from huggingface_hub hf download HanSj77/I-BFM-166560M-replay-buffer --repo-type dataset \
--local-dir runs/ibfm_166560m_replayAfter downloading, each directory from runs/ibfm_166560m_replay/checkpoint/buffers/train_rank_0/ through train_rank_7/ contains buffer.hdf5 and config.json. Use one shard for reward inference:
uv run python -m humanoidverse.reward_inference \
--model-folder tasks/common/checkpoint_ref \
--buffer-path runs/ibfm_166560m_replay/checkpoint/buffers/train_rank_0 \
--save-mp4| Parameter | Purpose | Default |
|---|---|---|
--buffer-path |
Replay shard directory | Searches <model-folder>/checkpoint/buffers/ when omitted |
--tasks |
Run only selected tasks | All locomotion tasks |
--num-samples |
Samples taken from replay | 150000 |
--skip-rollouts |
Compute intent vectors without simulation | No |
--episode-length |
Steps per task | 500 |
Outputs are <model-folder>/reward_inference/reward_locomotion.pkl and one video per task.
Note Reading a shard loads the entire file into memory. Each shard is about 28 GB, so ensure sufficient RAM before running.
Tip Intent vectors for the interaction tasks have already been inferred and are stored in each task's
latents/directory. Running Section 2's tasks does not require downloading replay.