简介#
I-BFM 是开源的人形机器人–物体交互行为基础模型。它用 forward–backward 表示和无监督强化学习,学出人形机器人、物体和接触的共同表示;下游任务只需给出一个奖励函数,就能推断出对应的意图向量,让同一个策略完成搬箱、推箱、踢箱等交互,不需要为每个任务再训练。
在搬箱任务中,I-BFM 无扰动时的成功率为 94.3%,机器人摔倒后仍有 89.3%;作为对比,基于规划的基线方法在机器人摔倒后只有 1.3%。
资源入口:
1. 概览#
本节介绍 I-BFM 的工作原理、发布内容和环境配置。完成本节后,你可以下载发布的模型,并在仿真中运行一次搬箱任务。
1.1 I-BFM 能做什么#
| 工作流 | 做什么 | 见 |
|---|---|---|
| 运行交互任务 | 用发布的模型在 MuJoCo 中搬箱、推箱、踢箱,可用键盘移动目标、让机器人倒地后起身 | 第 2 节 |
| 准备动作数据 | 把自己的动作转换成训练格式 | 第 3 节 |
| 训练 | 用 8 张 H200 按发布配置从头训练 | 第 4 节 |
| 推理 | 用模型做动作跟踪、目标到达和奖励推断,导出 ONNX | 第 5 节 |
想先看效果,可以打开项目页的 Live MuJoCo Session,在浏览器中运行搬箱和键盘控制。
1.2 工作原理#
共享表示。 I-BFM 用 forward–backward(FB)表示学习人形机器人、物体和接触的耦合动力学。Backward 编码器把状态映射到 256 维的意图空间,策略以意图向量 z 为条件输出动作。
无监督预训练。 预训练时不针对任何具体任务,混合使用 LaFAN 动作(30%)、搬箱动作(40%)和踢箱动作(30%)作为行为先验,在仿真中在线交互。
双时间尺度目标。 训练同时使用短时的交互目标和较长时的目标(目标视野 8 步),让策略在不同时间尺度上都能控制交互。
奖励推断。 给出下游任务的奖励函数,用 replay 中的样本按这个奖励加权,就能算出完成该任务的意图向量 z,策略不需要再训练。
任务控制器。 搬箱、推箱、踢箱各有一个控制器,按阶段(接近、对准、抬起、搬运、放下、倒地起身等)切换预先推断好的意图向量,形成闭环。
1.3 发布内容#
| 内容 | 位置 | 说明 |
|---|---|---|
| 发布模型 | checkpoints/ibfm-166m/ |
训练到第 166,559,744 个全局步的模型,三个交互任务共用;权重拆成两段,通过 Git LFS 下载 |
| 交互任务 | ibfm/tasks/carry_box/、push_box/、kick_box/ |
启动脚本、任务控制器和推断好的意图向量 |
| 训练源码与配置 | ibfm/ |
发布模型使用的训练实现、数据配比和任务配置 |
| 训练数据 | ibfm/humanoidverse/data/、ibfm/cache/motion_data/ |
LaFAN 动作、搬箱和踢箱动作缓存,通过 Git LFS 下载 |
| Replay buffer | Hugging Face | 发布模型对应的 replay,8 个分片,共约 210 GiB,用于奖励推断(见 5.3 节) |
仓库不包含实机部署程序。
注意 训练源码沿用上游 BFM-Zero 的 CC BY-NC 4.0 许可证,仅限非商业用途。
1.4 系统要求#
| 项目 | 要求 |
|---|---|
| 操作系统 | Linux |
| Python | 3.10 |
| 工具 | uv、Git LFS |
| 主要依赖 | MJLab 1.4.0、MuJoCo 3.8、PyTorch 2.7(由 ibfm/uv.lock 锁定) |
| 交互任务 | 策略在 CPU 上推理,不需要 GPU;打开查看窗口需要桌面环境 |
| 训练 | 8 张 H200 |
| 磁盘 | Git LFS 文件约 4 GB(模型两段共约 3.4 GB);还原模型时再占约 3.4 GB;完整 replay 约 210 GiB |
1.5 安装#
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 pull下载模型的两段权重、LaFAN 动作和搬箱 / 踢箱动作缓存。restore_checkpoint.sh先核对两段的 SHA-256,再拼接成checkpoints/ibfm-166m/model/model.safetensors并核对结果。成功时打印Restored …;已经还原过时打印Checkpoint is already restored.。uv sync在ibfm/.venv中按uv.lock安装依赖。
注意 没有执行
git lfs pull时,两段权重只是几百字节的指针文件,restore_checkpoint.sh会因 SHA-256 不符而退出。先运行git lfs pull,再重新还原。
提示 本文后续命令都在
ibfm/目录中运行。
1.6 跑通快速测试#
在没有显示器的机器上,用无窗口模式跑 100 步搬箱:
HEADLESS=1 ./tasks/carry_box/run_interactive.sh脚本先生成一个随机场景,再加载发布模型运行。结束时打印轨迹、控制器摘要和高度测试结果的路径(Trace:、Controller:、Height tests:),说明模型和环境都已就绪。
有桌面环境时,直接运行 ./tasks/carry_box/run_interactive.sh,会打开 MuJoCo 窗口并录制视频,见 2.2 节。
1.7 目录布局#
I-BFM-Code/
├── restore_checkpoint.sh # 还原模型权重
├── checkpoints/ibfm-166m/ # 发布模型
└── ibfm/ # 以下命令都在这里运行
├── tasks/
│ ├── carry_box/ # 搬箱
│ ├── push_box/ # 推箱
│ ├── kick_box/ # 踢箱
│ └── common/checkpoint_ref/ # 指向发布模型的链接,推理时作为 --model-folder
├── scripts/train_ibfm_h200.sh # 训练启动脚本
├── configs/ # 机器人、数据配比和任务配置
├── humanoidverse/ # 源码与推理脚本
└── runs/ # 训练输出(第 4 节)2. 交互任务#
本节用发布模型在 MuJoCo 中运行三个交互任务。三个任务共用同一个模型,区别在于任务控制器和使用的意图向量。命令都在 ibfm/ 中运行。
2.1 查看窗口中的操作#
打开查看窗口时,可以用键盘改变目标或制造干扰:
| 按键 | 作用 |
|---|---|
| W / S | 目标点沿 X 方向前移 / 后移 0.25 m |
| A / D | 目标点沿 Y 方向左移 / 右移 0.25 m |
| H | 恢复初始目标点 |
| R | 重置场景 |
| F | 让机器人倒地,观察起身后继续任务(仅推箱和踢箱) |
| P | 在终端打印当前状态 |
查看窗口使用 GLFW,需要桌面环境。搬箱脚本在没有设置 DISPLAY 时默认使用 :1;如果你的显示器编号不同,先在终端中设置正确的 DISPLAY。
2.2 搬箱#
./tasks/carry_box/run_interactive.sh机器人走向箱子、对准、抱起,搬到目标位置后放下;中途倒地会先起身再继续。
脚本依次做两件事:
生成随机场景:机器人与箱子相距 1.5–2.0 m,目标高度 0.65 m,结果写入
tasks/carry_box/random_height_scene/。运行搬箱控制器:箱子为 0.30 m 的立方体,质量 0.70 kg。
搬箱的 R 键有额外作用:先给当前一轮记分,再把箱子放到 0.15–0.80 m 之间的一个随机高度,开始新一轮。每一轮记录是否成功抱起,以及箱子中心与目标的三维距离;距离小于 0.20 m 记为放置成功。
常用环境变量:
| 变量 | 作用 | 默认值 |
|---|---|---|
HEADLESS |
设为 1 时不开窗口、不录视频 |
0 |
MAX_STEPS |
无窗口模式下的运行步数 | 100 |
TARGET_HEIGHT |
目标放置高度(m) | 0.65 |
HEIGHT_MIN / HEIGHT_MAX |
按 R 重置时箱子高度的随机范围(m) | 0.15 / 0.80 |
ROBOT_BOX_DISTANCE_MIN / MAX |
初始时机器人与箱子的距离范围(m) | 1.5 / 2.0 |
SCENE_SEED / HEIGHT_SEED |
场景和高度的随机种子 | 166559744 |
RENDER_SIZE / VIDEO_FPS |
视频分辨率和帧率 | 720 / 30 |
RECORD_TAG |
输出目录的名称后缀 | 当前时间 |
输出写在 tasks/carry_box/interactive/random_height_<RECORD_TAG>/:
| 文件 | 内容 |
|---|---|
session.mp4 |
录制的视频(打开窗口时) |
session.csv |
逐步轨迹 |
controller_summary.json |
控制器摘要 |
height_trials.json |
每一轮的抱起、放置结果和累计成功率 |
2.3 推箱#
./tasks/push_box/run_interactive.sh机器人先绕到箱子后方、对准,再用手推着箱子走向目标。目标默认在箱子初始位置沿 X 方向 2.0 m 处;判定到达时,除了箱子与目标的水平距离要在阈值(默认 0.40 m)以内,控制器还会检查箱子是否已经停稳、平放在地面上。第一个参数可以指定输出目录名,例如 ./tasks/push_box/run_interactive.sh test1。
| 变量 | 作用 | 默认值 |
|---|---|---|
VIEWER |
1 打开窗口;0 不开窗口,改用 EGL 离屏渲染 |
1 |
MAX_STEPS |
运行步数,0 表示不限 |
开窗口时 0,否则 3000 |
SAVE_MP4 |
是否录制视频 | 1 |
INITIAL_DISTANCE |
初始时机器人与箱子的距离(m) | 1.0 |
GOAL_OFFSET_X / GOAL_OFFSET_Y |
目标相对箱子的偏移(m) | 2.0 / 0.0 |
SUCCESS_DISTANCE |
判定到达的距离阈值(m) | 0.40 |
OUTPUT_ROOT |
输出根目录 | tasks/push_box/rollouts |
输出 <OUTPUT_ROOT>/<名称>/push_box.mp4、rollout.csv 和 summary.json。
2.4 踢箱#
./tasks/kick_box/run_interactive.sh机器人用脚接触箱子,把它踢向目标。目标在箱子初始位置沿 X 方向 2.0 m 处,目标标记半径为 0.20 m。踢箱脚本默认打开窗口、不录视频、在 CPU 上推理。
| 变量 | 作用 | 默认值 |
|---|---|---|
VIEWER |
是否打开窗口 | 1 |
SAVE_MP4 |
是否录制视频 | 0 |
MAX_STEPS |
运行步数,0 表示不限 |
0 |
OUTPUT_ROOT |
输出根目录 | tasks/kick_box/local_cpu_rollouts |
输出 rollout.csv 和 summary.json,录制时另有 kick_box.mp4。
注意 踢箱脚本即使设置
VIEWER=0,也默认使用 GLFW。在没有桌面的机器上运行时,同时设置MUJOCO_GL=egl和一个有限的MAX_STEPS,例如VIEWER=0 MUJOCO_GL=egl MAX_STEPS=3000 ./tasks/kick_box/run_interactive.sh。
2.5 结果参考#
项目页公布的仿真成功率(%):
| 条件 | 搬箱 | 推箱 | 踢箱 |
|---|---|---|---|
| 无外部扰动 | 94.33 | 90.00 | 81.67 |
| 箱子受扰动 | 91.00 | 85.33 | 79.67 |
| 机器人受扰动 | 89.33 | 84.67 | 86.67 |
3. 动作数据#
本节说明训练用的动作数据在哪里,以及如何把自己的动作转换成训练格式。命令在 ibfm/ 中运行。
3.1 发布配置使用的数据#
发布模型的数据配比写在 configs/data/g1_lafan_carry_kick_30_40_30.yaml,三份数据都随 git lfs pull 下载:
| 数据 | 采样比例 | 文件 |
|---|---|---|
| LaFAN 动作(10 秒片段) | 30% | humanoidverse/data/lafan_29dof_10s-clipped.pkl |
| 搬箱动作 | 40% | cache/motion_data/g1_carry_box_omniretarget_no_foot/ 下的训练缓存 |
| 踢箱动作 | 30% | cache/motion_data/g1_kick_box_omniretarget/ 下的训练缓存 |
比例是按数据源计算的,与各数据源中的动作条数无关。
3.2 转换自己的动作#
自己的动作可以是机器人状态格式的 CSV 或 NPZ。CSV 每行一帧,包含根节点位置(如 root_pos_x/y/z)、根节点四元数(如 root_quat_x/y/z/w,xyzw 顺序)和按机器人 XML 顺序排列的关节角;帧率用 --fps 指定。
检查能否被正确读取:
uv run python -m humanoidverse.tools.data_inspect \ --robot configs/robots/g1_29dof.yaml \ --source <你的 CSV 或 NPZ,或所在目录>生成数据清单并构建缓存:
uv run python -m humanoidverse.tools.data_build \ --robot configs/robots/g1_29dof.yaml \ --source <你的 CSV 或 NPZ,或所在目录> \ --name <数据集名> --fps <帧率> \ --out <清单路径>.yaml默认按 10 秒切分(
--clip-seconds),保留短于 10 秒但长于 1 秒的片段。训练时用
--data-manifest <清单路径>.yaml指定数据(见 4.4 节)。要和发布数据混合,参照configs/data/g1_lafan_carry_kick_30_40_30.yaml的写法,在同一个清单中列出多个数据源并设置weight。
清单的完整写法可以参考 configs/data/example_robot_state_auto_build.yaml。
4. 训练#
本节按发布配置从头训练 I-BFM。需要 8 张 H200;命令在 ibfm/ 中运行。
4.1 启动训练#
./scripts/train_ibfm_h200.sh ibfm_reproduction参数是本次运行的名称,输出写在 runs/ibfm_reproduction/。名称不能包含斜杠,目录已存在时脚本拒绝覆盖。
脚本使用发布的配置:
| 设置 | 值 |
|---|---|
| 任务 | carry_box,任务配置 configs/tasks/carry_box_mass0p7_range0p5_1p0_pd_delay_rfi_footmu0p4_1p25.yaml |
| 数据 | configs/data/g1_lafan_carry_kick_30_40_30.yaml(见 3.1 节) |
| GPU | 所有可见 GPU(--gpu-ids all),每张 1024 个并行环境 |
| 总步数 | 250,000,000 个全局环境步,可用环境变量 NUM_ENV_STEPS 修改 |
| 保存间隔 | 每 3,200,000 步保存一次滚动 checkpoint |
| Replay 容量 | 每张 GPU 5,120,000 条 |
| 随机种子 | 4728 |
| 日志 | W&B 离线模式,写在本地 |
发布模型取自这类训练的第 166,559,744 步。
训练中的随机化(来自任务配置):箱子名义质量 0.7 kg,在 0.5–1.0 kg 间随机;关节 PD 增益在 0.75–1.25 倍间随机;控制延迟 0–1 步(一步 20 ms);脚底摩擦在 0.4–1.25 间随机。
4.2 训练产物#
| 路径 | 内容 |
|---|---|
runs/<名称>/checkpoint/ |
滚动 checkpoint,含模型和优化器,用于续训 |
runs/<名称>/policies/latest |
最新完成的策略 |
runs/<名称>/policies/best |
2,719 条评估动作上平均 EMD 最低的策略;对应步数和分数写在 best.json |
runs/<名称>/launcher.log |
训练日志 |
runs/<名称>/policy_retention.log |
策略保留记录 |
policies/ 中的策略只包含权重和配置,不含优化器和 replay。Replay 只在训练正常结束时写入最终的滚动 checkpoint 一次。
4.3 中断后续训#
RESUME=1 ./scripts/train_ibfm_h200.sh ibfm_reproduction续训从滚动 checkpoint 中的模型和优化器继续。
注意 中途保存的 checkpoint 不含 replay,续训时 replay 要重新收集,所以续训的结果会和不中断的训练不同。
4.4 用 smoke 模式检查训练环境#
用单张 GPU 跑一个很短的训练,确认环境可用:
./run_train.sh --agent fb \
--data-manifest configs/data/example_mix.yaml \
--gpu-ids single --smoke \
--work-dir /tmp/ibfm_smokesmoke 模式使用 16 个环境、2048 个环境步,不记录 W&B。run_train.sh 是训练入口 humanoidverse.train 的包装,会设置缓存目录后再启动;正式训练的完整参数见 scripts/train_ibfm_h200.sh。
注意
scripts/smoke_release.sh的最后一步会读取humanoidverse/data/examples/g1_robot_state_sample.csv,这个文件不在仓库中,该步骤会失败。只需检查训练时,用上面的命令即可。
5. 推理#
本节用发布模型或自己训练的模型做动作跟踪、目标到达和奖励推断。命令在 ibfm/ 中运行。--model-folder 指向包含 checkpoint/ 的目录:发布模型用 tasks/common/checkpoint_ref,自己训练的模型用 runs/<名称>。
5.1 动作跟踪#
uv run python -m humanoidverse.tracking_inference \
--model-folder tasks/common/checkpoint_ref \
--motion-list 20 --save-mp4 \
--output-dir runs/tracking_preview策略把参考动作编码成意图向量并在仿真中跟踪,--save-mp4 输出参考动作与策略执行的对比视频。默认数据是 humanoidverse/data/lafan_29dof_10s-clipped.pkl。
| 参数 | 作用 | 默认值 |
|---|---|---|
--motion-list |
动作编号,可以写多个 | 20 |
--output-dir |
输出目录 | <model-folder>/tracking_inference |
--lie-down-init |
让机器人侧躺开始,测试起身后跟踪 | 否 |
--downward-force-n |
在根部施加向下的冲击力(N),0 关闭 |
0 |
--force-start-s / --force-duration-s |
冲击力的开始时间和持续时间(秒) | 2.4 / 0.25 |
--export-onnx |
推理前把 ONNX 导出到 <model-folder>/exported/ |
是 |
--device |
推理设备 | cuda:0 |
输出 zs_<编号>.pkl(意图向量)和 tracking_<编号>.mp4。
5.2 目标到达#
uv run python -m humanoidverse.goal_inference \
--model-folder tasks/common/checkpoint_ref --save-mp4策略依次到达一组目标姿态,默认列表为 humanoidverse/data/robots/g1/goal_frames_lafan29dof.json,可用 --goal-json 替换。默认共运行 2100 步,每 100 步切换一个目标(--episode-len、--goal-switch-interval)。
5.3 奖励推断#
奖励推断需要 replay。发布模型的 replay 在 Hugging Face 上,共 8 个分片,约 210 GiB;在 ibfm/ 中下载:
uvx --from huggingface_hub hf download HanSj77/I-BFM-166560M-replay-buffer --repo-type dataset \
--local-dir runs/ibfm_166560m_replay下载后,runs/ibfm_166560m_replay/checkpoint/buffers/train_rank_0/ 到 train_rank_7/ 中各有一个 buffer.hdf5 和 config.json。用其中一个分片做奖励推断:
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| 参数 | 作用 | 默认值 |
|---|---|---|
--buffer-path |
使用的 replay 分片目录 | 不写时在 <model-folder>/checkpoint/buffers/ 中查找 |
--tasks |
只运行部分任务 | 全部移动类任务 |
--num-samples |
从 replay 中取的样本数 | 150000 |
--skip-rollouts |
只计算意图向量,不运行仿真 | 否 |
--episode-length |
每个任务的运行步数 | 500 |
输出 <model-folder>/reward_inference/reward_locomotion.pkl,以及每个任务一个视频。
注意 读取一个分片会把整个文件载入内存,单个分片约 28 GB,运行前确认内存足够。
提示 交互任务使用的意图向量已经推断好,放在各任务的
latents/目录中,运行第 2 节的任务不需要下载 replay。