简介#
1. 概览#
本节介绍 P-BFM 的工作原理、发布内容和环境配置。完成本节后,你可以装好环境、下载动作数据,并用测试和 smoke 训练确认环境可用。
1.1 P-BFM 能做什么#
| 工作流 | 做什么 | 见 |
|---|---|---|
| 准备动作数据 | 下载 LaFAN 训练数据,或把自己的动作转换成训练格式 | 第 2 节 |
| 训练 | 8 张 GPU 从零训练,或用 smoke 模式快速检查 | 第 3 节 |
| 推理 | 用动作参考、目标姿态或奖励函数给出意图,在仿真中运行并录制视频 | 第 4 节 |
| 地形评测 | 用同一个意图在多种地形上运行,比较结果 | 第 5 节 |
| 导出与部署准备 | 导出 ONNX,核对部署时的输入约定 | 第 6 节 |
1.2 工作原理#
统一意图空间。 P-BFM 使用 forward–backward 表示:Backward 编码器把机器人状态映射到意图空间。参考动作的各帧、一个目标姿态,或按奖励加权的状态样本,都能由它得到同一个空间中的意图向量 z。
感知执行。 Actor 同时读入本体观测、意图 z 和深度图(8 帧,每帧 18 × 32),在当前地形上实现这个意图。同一个意图在台阶、斜坡或箱子上会得到不同的动作。
显式根指令。 只靠隐向量控制时,机器人在全局上走多远、朝哪走不够准确。P-BFM 把航向和平面速度指令与 z 一起输入策略,单独控制根部运动。
速度估计。 策略内含一个由深度监督训练的速度估计器(721 → 512 → 256 → 3),输出偏航坐标系下的当前速度。部署时策略只用这个估计值,不需要仿真中的速度真值。
训练方式。 训练是无监督强化学习:40 条平地 LaFAN 动作作为行为先验,机器人在仿真地形上在线交互。四个独立的 Critic 分别对应行为先验(QD)、辅助奖励(QAux)、航向(QH)和速度(QV)。
1.3 发布内容#
已发布: 训练、推理、地形评测和 ONNX 导出代码;训练配置(
pbfm/configs/legacy_large.json);深度输入约定(pbfm/configs/depth_contract.json);机器人模型和配置。单独下载: LaFAN 训练动作数据(见 2.1 节)。
没有提供: 训练好的模型权重。仓库和项目页都没有给出权重下载,推理和导出需要先按第 3 节训练出自己的 run 目录。想先看效果,可以打开项目页的浏览器交互演示,在共享地形图上试参考动作、目标姿态和奖励预设(建议用电脑打开)。
仓库不包含: 向机器人发送关节指令的控制程序。第 6 节说明导出的模型包含什么、还需要你自己实现什么。
注意
P-BFM 基于 UFO,沿用上游的 CC BY-NC 4.0 许可证,仅限非商业用途。
1.4 系统要求#
| 项目 | 要求 |
|---|---|
| 操作系统 | Linux |
| Python | 3.10 |
| 包管理 | uv |
| 主要依赖 | MJLab 1.4.0、MuJoCo 3.8、PyTorch 2.7 |
| GPU | 完整训练:8 张 GPU,每张运行 1024 个并行环境,replay 也放在 GPU 上。推理默认使用 cuda:0。单元测试可以只用 CPU,需要 GPU 的测试会自动跳过 |
| 磁盘 | 训练时每个 rank 都会把自己的 replay 写进 checkpoint,长时间训练前预留足够空间 |
1.5 安装#
git clone https://github.com/Roboparty/P-BFM.git
cd P-BFM
uv sync --extra test--extra test 额外安装 pytest 和 onnxruntime。第 6 节的 ONNX 导出会用 onnxruntime 验证导出结果,所以建议一起装上。
1.6 跑通快速测试#
先下载训练数据(见 2.1 节),再运行单元测试:
bash scripts/download_data.sh
PYTHONPATH=. uv run python -m pytest tests需要外部数据或 GPU 的测试,在条件不满足时会自动跳过。测试通过只说明代码行为正确,不代表策略能收敛,也不代表实机安全。
有 8 张空闲 GPU 时,再按 3.3 节跑一次 smoke 训练,确认训练流程能走通。
1.7 目录布局#
P-BFM/
├── pbfm/
│ ├── train.py # 训练入口
│ ├── configs/ # 训练模板、仿真、机器人和深度约定
│ ├── data/ # 机器人模型、示例数据;2.1 节下载的数据也放在这里
│ ├── inference/ # 跟踪、目标、奖励三种推理和 ONNX 导出
│ ├── evaluation/ # 地形评测与诊断
│ └── motion/ # 动作数据读取、转换和清单
├── scripts/ # 训练、下载、导出和验证命令
├── tests/ # 测试
└── docs/ # 英文文档训练输出(--work-dir)的结构见 3.6 节。
2. 动作数据#
本节准备训练用的动作数据:直接下载作者提供的 LaFAN 数据,或者把自己的动作转换成 P-BFM 能读的格式。命令在仓库根目录运行。
2.1 下载 LaFAN 数据#
bash scripts/download_data.sh脚本从 Hugging Face 数据集 xuewang/UFO-MotionData 下载两个文件,核对 SHA-256 后放到 pbfm/data/:
| 文件 | 用途 |
|---|---|
pbfm/data/lafan_29dof_10s-clipped.pkl |
切成 10 秒片段的 LaFAN 动作,训练和推理的默认数据 |
pbfm/data/lafan_29dof.pkl |
不带"10 秒切分"后缀的 LaFAN 动作文件 |
文件已存在且哈希正确时直接跳过。脚本需要 hf 命令或 Python 包 huggingface_hub,uv sync 后的环境中已经有。
提示
推理命令在找不到 --data-path 时,默认读取 pbfm/data/lafan_29dof_10s-clipped.pkl(相对当前目录),所以在仓库根目录运行推理时可以不写数据路径。训练命令则必须写 --data-path,见 3.2 节。
2.2 准备自己的动作#
P-BFM 可以读取机器人状态格式的 CSV 或 NPZ 动作。CSV 示例在 pbfm/data/examples/g1_robot_state_sample.csv,每行一帧:
| 列 | 内容 |
|---|---|
time |
时间戳 |
root_pos_x、root_pos_y、root_pos_z |
根节点位置 |
root_quat_x、root_quat_y、root_quat_z、root_quat_w |
根节点姿态四元数(xyzw 顺序) |
dof_0 … dof_28 |
29 个关节角,单位弧度 |
检查数据能否被正确读取:
uv run python -m pbfm.motion.inspect \ --robot pbfm/configs/robots/g1_29dof.yaml \ --source <你的 CSV 或 NPZ,或所在目录>生成数据清单并构建训练缓存:
uv run python -m pbfm.motion.build \ --robot pbfm/configs/robots/g1_29dof.yaml \ --source <你的 CSV 或 NPZ,或所在目录> \ --name <数据集名> \ --out <清单路径>.yaml训练时用
--data-manifest <清单路径>.yaml代替--data-path(两者不能同时使用)。
要按比例混合多个数据源,可以给 --data-path 传多个文件,再用 --data-mix-weights 指定比例,例如 --data-mix-weights 0.95 0.05。
注意
使用地形训练时(除 --terrain-mode plane 外),训练启动前会检查每段动作的最大水平偏移,加上感知半径和余量后,是否仍在地形的安全半径内。超出的动作会让训练直接报错退出;通过时日志打印 terrain coverage preflight passed。
3. 训练#
本节从零训练 P-BFM。完整训练需要 8 张 GPU;命令在仓库根目录运行。
3.1 设置环境变量#
export PYTHONPATH="$PWD${PYTHONPATH:+:$PYTHONPATH}"
export OMP_NUM_THREADS=1 MUJOCO_GL=egl NCCL_NVLS_ENABLE=0
export UFO_CACHE_DIR=/path/to/writable-disk/pbfm-cacheUFO_CACHE_DIR 是缓存根目录,存放临时文件、PyTorch / Triton / CUDA / Warp 的编译缓存和专家数据缓存。把它设在空间足够、可写的磁盘上;不设置时使用当前目录下的 cache/。
3.2 从零训练#
uv run python -m torch.distributed.run --standalone --nnodes=1 --nproc_per_node=8 \
scripts/train_legacy_scratch.py \
--gpu-ids single --terrain-mode rp1_simple --seed 4831 \
--num-envs 1024 --num-env-steps 192020480 \
--data-path "$PWD/pbfm/data/lafan_29dof_10s-clipped.pkl" \
--work-dir /path/to/new-run/path/to/new-run 换成一个新的输出目录。torch.distributed.run 在本机启动 8 个进程,每个进程用一张 GPU、运行 1024 个环境,总计训练 192,020,480 个全局环境步。--gpu-ids 必须是 single,多卡由 torch.distributed.run 负责。
注意
一定要写 --data-path(或 --data-manifest)。配置模板中记录的默认数据路径是作者机器上的路径,在别的机器上不存在。
注意
从零训练请用 scripts/train_legacy_scratch.py。它把地形课程的起点设为第 0 步,并拒绝已经含有 checkpoint 的工作目录。直接运行 python -m pbfm.train 用的是续训计划:总步数 384,040,960,地形课程从第 192,020,480 步才开始。
3.3 用 smoke 模式快速检查#
在 3.2 节的命令末尾加 --smoke,并换一个新的 --work-dir:
uv run python -m torch.distributed.run --standalone --nnodes=1 --nproc_per_node=8 \
scripts/train_legacy_scratch.py \
--gpu-ids single --terrain-mode rp1_simple --seed 4831 \
--num-envs 1024 --num-env-steps 192020480 \
--data-path "$PWD/pbfm/data/lafan_29dof_10s-clipped.pkl" \
--work-dir /path/to/smoke-run --smokesmoke 模式总共运行 32,768 个全局步,每个进程最多 16 个环境,replay 容量缩小,关闭训练中的评测、replay checkpoint 写入和 W&B。它只确认流程能跑通。
注意
smoke 的 replay 很小,不能代表正式训练的显存占用。正式训练前,确认每张 GPU 放得下完整容量的 replay(默认每个 rank 5,120,000 条)、模型与优化器状态和训练 batch,并确认磁盘放得下每个 rank 的 replay checkpoint。
3.4 训练参数#
| 参数 | 作用 | 默认值 |
|---|---|---|
--terrain-mode |
训练地形:rp1_simple、plane、flat、slope、stairs、stairs_up、stairs_down、rough、platforms、mixed |
rp1_simple |
--num-envs |
每个进程的并行环境数 | 1024 |
--num-env-steps |
总全局环境步数(scratch 入口) | 192020480 |
--seed |
随机种子 | 4831 |
--checkpoint-every-steps |
保存 checkpoint 的间隔(全局步) | 9600000 |
--tracking-eval-every-steps |
训练中平地跟踪评测的间隔 | 9600000 |
--same-z-eval-every-steps |
训练中多地形评测的间隔,≤ 0 关闭 | 0 |
--buffer-size |
每个进程的 replay 容量 | 5120000 |
--disable-dr / --disable-obs-noise |
关闭域随机化 / 观测噪声 | 开启 |
--use-wandb |
记录到 W&B(先单独登录) | 关闭 |
plane 是全平地对照:地面全平,但保留地形观测。使用 W&B 时追加 --use-wandb --wandb-project PBFM --wandb-run-name pbfm。
3.5 训练地形#
默认的 rp1_simple 地形由边长 5 m 的地形块组成,机器人从地形块中心出发。各类地形所占比例:
| 地形 | 比例 | 从中心出发时 |
|---|---|---|
flat |
20% | 平地 |
perlin_rough |
10% | 粗糙地面 |
low_stairs_down |
20% | 上楼梯 |
low_stairs_up |
20% | 下楼梯 |
low_platforms |
10% | 低矮平台 |
hf_pyramid_slope_inv |
10% | 斜坡 |
boxes |
10% | 箱子障碍 |
台阶高 10–20 cm,粗糙地面起伏 2–6 cm,箱子高 5–15 cm。地形难度按课程逐步提高:从零训练时从第 0 步开始,每 19,200,000 个全局步提升一次。
注意
楼梯地形的名字描述的是"走向地形块中心"时的高度变化。机器人从中心出发向外走,所以 low_stairs_down 实际是上楼梯,low_stairs_up 是下楼梯。给视频打标签前,先确认起点、朝向和实际几何形状。
3.6 训练产物#
<work-dir>/
└── checkpoint/
├── model/ # 模型配置与权重(model.safetensors)
├── train_status.json # 训练进度
└── buffers/
└── train_rank_<r>/ # 每个 rank 的 replay推理和 ONNX 导出只需要模型;4.3 节的奖励推断还需要 replay。
续训依赖 replay 的格式和深度观测约定:
训练 replay 必须记录
explicit_next=true。没有这个字段的 replay,只把模型加载到一个新的训练目录,先用采集预热重建 replay,再开始更新。不要修改 replay 的元数据来强行加载不兼容的数据。
4. 推理#
本节用训练好的策略在仿真中运行三种推理:动作跟踪、目标到达和奖励推断。三种方式分别用动作参考、目标姿态和奖励函数生成意图 z。命令在仓库根目录运行,--model-folder 指向训练时的 --work-dir(其中要有 checkpoint/)。
4.1 动作跟踪#
uv run python -m pbfm.inference.tracking --model-folder /path/to/run \
--motion-list 20 --save-mp4策略逐帧把参考动作编码成意图,在仿真中跟踪这段动作。--save-mp4 输出左右对比视频:左边是参考动作,右边是策略的执行结果。
在训练用的地形块上跟踪,例如在 low_stairs_down 的第 7 级难度上运行:
uv run python -m pbfm.inference.tracking --model-folder /path/to/run \
--motion-list 20 --save-mp4 \
--terrain-family low_stairs_down --difficulty-row 7地形几何从该 checkpoint 的训练配置中读取。
| 参数 | 作用 | 默认值 |
|---|---|---|
--motion-list |
要跟踪的动作编号,可以写多个 | 20 |
--save-mp4 |
保存对比视频 | 否 |
--terrain-family |
选择一种训练地形块(名称见 3.5 节);不写时沿用 checkpoint 的环境设置 | 不写 |
--difficulty-row |
与 --terrain-family 一起使用的难度行 |
7 |
--reset-height-mode |
reference:保持参考动作的根部高度;terrain:加上地形块原点的高度 |
选了地形块时为 terrain,否则 reference |
--velocity-context |
auto:按 checkpoint 的规则从参考动作生成速度指令;valid:明确使用参考速度;invalid:不给速度指令 |
auto |
--max-steps |
最多运行的步数,用于快速预览 | 不限 |
--device |
推理设备 | cuda:0 |
--export-onnx |
推理前把 ONNX 导出到 <model-folder>/exported/ |
是 |
输出:
| 文件 | 内容 |
|---|---|
<model-folder>/tracking_inference/zs_<编号>.pkl |
这段动作的意图向量序列 |
<model-folder>/tracking_inference/context_<编号>.npz |
与意图对齐的航向、速度指令和有效标志 |
<model-folder>/tracking_inference/tracking_<编号>.mp4 |
对比视频(需 --save-mp4) |
<model-folder>/exported/FBcprAuxModel.onnx、FBcprAuxModel.meta.json、backward_encoder.onnx |
导出的策略、元数据和 Backward 编码器 |
4.2 目标到达#
uv run python -m pbfm.inference.goal --model-folder /path/to/run --save-mp4策略依次到达一组目标姿态。目标姿态取自动作数据中的指定帧,默认列表在 pbfm/data/robots/g1/goal_frames_lafan29dof.json,共 8 组,每组写明动作名称(motion_name)、动作编号(motion_id)和帧号(frames)。用 --goal-json 换成自己的列表。
| 参数 | 作用 | 默认值 |
|---|---|---|
--goal-json |
目标姿态列表 | 上面的默认文件 |
--episode-len |
总步数 | 2100 |
--goal-switch-interval |
每隔多少步换下一个目标 | 100 |
--save-mp4 |
保存视频 | 否 |
输出 <model-folder>/goal_inference/goal_reaching.pkl(各目标的意图向量)和 goal_inference/videos/goal.mp4。
4.3 奖励推断#
uv run python -m pbfm.inference.reward --model-folder /path/to/run --save-mp4奖励推断用训练 replay 中的样本,按指定奖励函数重新打分,再算出对应的意图向量,所以需要训练产物中的 replay(checkpoint/buffers/ 下的 train_reduced、train_rank_<n> 或 train)。smoke 训练不写 replay,不能用于奖励推断。
默认依次运行 38 个任务,可以用 --tasks 只选一部分。任务名的含义举例:
| 任务名 | 含义 |
|---|---|
move-ego-<角度>-<速度> |
相对自身朝向的移动方向(度)和目标速度,例如 move-ego-0-0.7、move-ego-90-0.3;move-ego-0-0 的目标速度为 0 |
move-ego-low<高度>-<角度>-<速度> |
同上,并要求保持较低的站立高度,例如 move-ego-low0.5-0-0 |
rotate-z-<角速度>-<骨盆高度> |
绕竖直轴旋转,正负号表示方向,例如 rotate-z-5-0.5、rotate-z--5-0.5 |
raisearms-<左>-<右> |
左右手臂分别保持 l、m 等不同姿态 |
crouch-0、sitonground |
下蹲、坐到地上 |
| 参数 | 作用 | 默认值 |
|---|---|---|
--tasks |
任务子集 | 全部 38 个 |
--num-samples |
从 replay 中取样的数量 | 150000 |
--episode-length |
每个任务的运行步数 | 500 |
--skip-rollouts |
只计算意图向量,不运行仿真、不录视频 | 否 |
--buffer-rank / --buffer-path |
指定使用哪个 rank 的 replay / 直接指定 replay 目录。存在 train_reduced 时优先使用它,--buffer-rank 不生效 |
0 / 不写 |
--max-workers |
重新打分的并行进程数 | 24 |
输出 <model-folder>/reward_inference/reward_locomotion.pkl,以及每个任务一个视频(需 --save-mp4)。
4.4 航向与速度指令#
除了意图 z,策略还读入航向和平面速度指令。三种推理对指令的处理不同:
动作跟踪: 意图用 8 帧参考窗口逐帧推进(动作末尾窗口变短)。航向和有效的平面速度指令来自同一段对齐的参考动作。
目标到达和奖励推断: 不会自动生成参考速度指令。
有效与无效: 有效的零速度表示"停下";无效表示"没有速度指令"。对自定义名称的动作,要有意识地用
--velocity-context指定。
保存或下发意图时,意图向量、航向与速度、有效标志、帧率和坐标约定要放在一起。只有意图向量,不是带指令策略的完整输入。
5. 地形评测#
本节用同一个意图在多种地形上运行,比较策略在各地形上的表现,用于检查地形适应能力。命令在仓库根目录运行。
uv run python -m pbfm.evaluation.terrain --model-folder /path/to/run \
--prompt-type tracking --terrains flat,slope,stairs,rough \
--output results/terrain_transfer_results.json| 参数 | 作用 | 默认值 |
|---|---|---|
--prompt-type |
意图来源:tracking、goal 或 reward |
必填 |
--terrains |
逗号分隔的地形列表。除 flat、slope、stairs、stairs_up、stairs_down、rough、platforms、course 外,也接受 3.5 节的训练地形块名称 |
flat,slope,stairs,rough |
--rp1-difficulty-row |
训练地形块的难度行(0–9) | 4 |
--motion-id |
tracking 使用的动作编号 |
0 |
--goal-index |
goal 使用的目标序号 |
0 |
--reward-task |
reward 使用的任务 |
move-ego-0-0.7 |
--episode-length |
每个地形的运行步数 | 500 |
--seed |
随机种子 | 4728 |
--output |
结果 JSON 路径 | terrain_transfer_results.json |
结果写成 JSON,同时在同一位置写一份同名 CSV,每个地形一行。
完整选项用 uv run python -m pbfm.evaluation.terrain --help 查看。
6. 导出与部署#
本节导出部署用的 ONNX 模型,并说明它的输入约定和需要你自己实现的部分。仓库不包含实机控制程序。命令在仓库根目录运行。
6.1 导出策略#
uv run python -m scripts.export_depth_policy_onnx \
--checkpoint /path/to/run/checkpoint --output /path/to/export/policy.onnx脚本在 CPU 上加载 checkpoint,导出 ONNX,用 ONNX 检查器和 onnxruntime 对比导出结果,通过时打印以 Verified <输出路径>: 开头的验证结果。
导出的 policy.onnx 是完整的部署图,不是只含动作网络的拆分模型:
| 包含在 ONNX 中 | 不包含,需要自己实现 |
|---|---|
| 深度编码器 | 真实相机的深度预处理 |
| 速度估计器 | 深度和观测的时间历史 |
| 观测归一化 | 意图与指令的帧对齐 |
| Actor | 动作缩放和关节 PD 控制 |
输入中没有任何 Critic,也没有只在仿真中才有的速度标签。不同 checkpoint 的输入名称和形状可能不同,以导出时生成的元数据为准,不要沿用别的 checkpoint 的接口。
6.2 导出 Backward 编码器#
Backward 编码器把机器人状态映射到意图空间,部署时用它从参考动作或目标姿态计算意图向量。它需要单独导出:
uv run python -m scripts.export_backward_encoder_onnx \
--model-folder /path/to/run --output /path/to/export/backward_encoder.onnx --device cpu6.3 深度输入约定#
深度约定记录在 pbfm/configs/depth_contract.json,部署时的预处理要与它一致:
| 项目 | 约定 |
|---|---|
| 深度量 | 光学 z 深度(optical_z) |
| 原始分辨率 | 36 × 64 |
| 裁剪后分辨率 | 18 × 32 |
| 时间历史 | 8 帧,采样间隔见 sampled_ages(35、30、25、20、15、10、5、0) |
| 无效深度 | 按 2.5 m 处理 |
| 处理方式 | 裁剪方式 max,σ = 1.0 的高斯平滑(处理配置 optical_z_maxcrop18_reset_v2) |
| 训练中模拟的安装误差 | 位置 1 cm;滚转 / 俯仰 / 偏航 2° / 10° / 2°;延迟 0–1 帧 |
注意
ONNX 的深度输入是 [0, 1] 范围的 float。仿真环境中的 uint8 深度要先转成 float32、再除以 255,只除一次;已经归一化的 float 深度不能再除。
6.4 用实机记录验证导出结果#
scripts/verify_depth_policy_trace.py 用实机记录的观测,对比 ONNX 与原 checkpoint 输出的动作和速度估计:
uv run python -m scripts.verify_depth_policy_trace \
--checkpoint /path/to/run/checkpoint --onnx /path/to/export/policy.onnx \
--traces <记录文件> --output <报告路径>这一步检查的是接口一致性,不评价控制效果。ONNX 一致性测试也不能验证相机预处理、时间历史、动作缩放和 PD 控制这些实机集成步骤。
仓库还附带一个只读的记录程序 pbfm/perception/capture.py,在机器人上同步记录深度相机画面和机器人状态,用于感知调试。它不加载策略,也不发送任何电机指令。
6.5 实机前检查清单#
相机深度预处理与 6.3 节的约定一致,并用实机记录验证过(6.4 节)
深度归一化只做了一次,输入范围是 [0, 1]
意图向量、航向与速度指令、有效标志、帧率和坐标约定一起下发(4.4 节)
输入名称和形状按本 checkpoint 导出的元数据核对过
动作缩放、关节顺序和 PD 增益与训练时的机器人配置一致
已在仿真中用同一个 checkpoint 跑过跟踪和地形评测(第 4、5 节)
首次上机时机器人吊起或有保护,硬件急停已测试,并与软件停止分开确认