200字
LeHome 跑 Diffusion Policy:不太理想的尝试

摘要

在 LeHome Challenge 中,我在跑通 ACT baseline 之后尝试了 Diffusion Policy。结果不太理想——top_short 任务上 30k 成功率为 0%,50k 仅 16.67%。本文记录完整的尝试过程:从 DP smoke test 到长训、从发现左手抓取偏置到逐步 debug(policy 输出日志、环境执行日志、expert replay 对照、单件测试),以及最终判断——问题更可能是 DP rollout 不稳定而非数据或配置错误。同时也发现当时的实验环境代码基线落后官方 main,可能影响了 DP 的结果。


正文

1. 为什么试 DP

ACT baseline 跑通之后,top_short 拿到了 58.33%。既然流程跑通了,自然想试试看其他方法能不能拉高分数。Diffusion Policy 也是模仿学习领域的主流方法之一,官方也提供了开箱即用的 train_dp.yaml 配置,试一下的成本不高。而且这次租的是48G的L40,之前就没打过这么富裕的仗。

DP 训练时间比 ACT 长很多,必须用 tmux 挂后台。另外我不打算上来就训很久——先做个 smoke test 验证流程。

2. DP Smoke Test

2.1 配置

cd ~/lehome-challenge
cp configs/train_dp.yaml configs/train_dp_top_short_ruali.yaml
dataset:
  repo_id: repo_dp_top_short_ruali
  root: Datasets/example/top_short_merged

policy:
  type: diffusion
  device: cuda
  push_to_hub: false

  input_features:
    observation.state:
      type: STATE
      shape: [12]
    observation.images.top_rgb:
      type: VISUAL
      shape: [3, 480, 640]
    observation.images.left_rgb:
      type: VISUAL
      shape: [3, 480, 640]
    observation.images.right_rgb:
      type: VISUAL
      shape: [3, 480, 640]

  output_features:
    action:
      type: ACTION
      shape: [12]

output_dir: /root/gpufree-data/lehome-outputs/train/dp_top_short
batch_size: 32
steps: 10000
save_freq: 5000
log_freq: 100

wandb:
  enable: false

batch_size 32 + 10k step,是一个比较稳妥的 smoke 配置。能跑完就说明环境没问题。

2.2 tmux 开训

tmux new -s lehome_dp
cd ~/lehome-challenge
source .venv/bin/activate

lerobot-train --config_path configs/train_dp_top_short_ruali.yaml \
  2>&1 | tee /root/gpufree-data/lehome-outputs/logs/dp_top_short_train.log

退出不杀任务:Ctrl+b 然后按 d
回来查看:tmux attach -t lehome_dp

另开终端监控:

tail -f /root/gpufree-data/lehome-outputs/logs/dp_top_short_train.log
watch -n 5 nvidia-smi

2.3 Smoke 评测

python -m scripts.eval \
  --policy_type lerobot \
  --policy_path /root/gpufree-data/lehome-outputs/train/dp_top_short/checkpoints/last/pretrained_model \
  --dataset_root Datasets/example/top_short_merged \
  --garment_type top_short \
  --num_episodes 1 \
  --max_steps 300 \
  --enable_cameras \
  --save_video \
  --video_dir /root/gpufree-data/lehome-outputs/eval/dp_top_short_smoke_gui \
  --device cpu

Smoke 评测看 Isaac Sim 面板,虽然失败率极高,但感觉"有潜力"——机械臂确实在朝衣服的方向运动,不是完全随机的。于是决定开 50k 长训。

3. DP 50k 训练

3.1 配置

cd ~/lehome-challenge
cp configs/train_dp_top_short_ruali.yaml configs/train_dp_top_short_ruali_50k.yaml

主要变化:steps: 50000save_freq: 10000,其余不变。

3.2 训练

tmux new -s lehome_dp_long
cd ~/lehome-challenge
source .venv/bin/activate
lerobot-train --config_path configs/train_dp_top_short_ruali_50k.yaml \
  2>&1 | tee /root/gpufree-data/lehome-outputs/logs/dp_top_short_50k_train.log


tmux 小技巧:怎么在 tmux 里往上翻

Ctrl+b 然后按 [

然后方向键/PageUp/PageDown 翻页,q 退出。这个能救命。


4. 结果:左手总是偏

4.1 带 GUI 评测

python -m scripts.eval \
  --policy_type lerobot \
  --policy_path /root/gpufree-data/lehome-outputs/train/dp_top_short_50k/checkpoints/last/pretrained_model \
  --dataset_root Datasets/example/top_short_merged \
  --garment_type top_short \
  --num_episodes 1 \
  --max_steps 300 \
  --enable_cameras \
  --save_video \
  --video_dir /root/gpufree-data/lehome-outputs/eval/dp_top_short_50k_gui \
  --device cpu

直接看 Isaac Sim 画面——左手总是抓偏一个位置。右手感觉挺稳的,但左手就是不对。

4.2 无头完整评测

cd ~/lehome-challenge
source .venv/bin/activate

python -m scripts.eval \
  --policy_type lerobot \
  --policy_path /root/gpufree-data/lehome-outputs/train/dp_top_short_50k/checkpoints/last/pretrained_model \
  --dataset_root Datasets/example/top_short_merged \
  --garment_type top_short \
  --num_episodes 1 \
  --enable_cameras \
  --save_video \
  --video_dir /root/gpufree-data/lehome-outputs/eval/dp_top_short_50k \
  --device cpu \
  --headless

结果让我有点崩溃。50k 步训练下来,全 failed 也太离谱了。

4.3 30k 也照样偏

为了排除"50k 训练出了什么奇怪问题",又跑了 30k checkpoint 的评测:

Total Episodes: 12
Average Return: 162.83 ± 40.93
Success Rate: 0.00%

Per-Garment:
  Top_Short_Seen_0: 0.00%
  Top_Short_Seen_1: 0.00%
  ...
  Top_Short_Unseen_1: 0.00%

12 个 episode,12 个 0%。但是 Average Return 有 162.83——这很微妙,说明奖励函数认为它在做正确的事,但几何判定标准认为它没做到。

5. 开始 Debug

5.1 排除"数据本身就左手偏"

先看 expert replay:

cd ~/lehome-challenge
source .venv/bin/activate

python -m scripts.dataset_sim replay \
  --dataset_root Datasets/example/top_short_merged \
  --num_replays 1 \
  --disable_depth \
  --device cpu \
  --enable_cameras

专家回放正常——左右手动作对称,抓取准确。数据没问题,问题出在策略端。

5.2 加 Policy 输出日志

scripts/eval_policy/lerobot_policy.py 里,找到 batch_action = self.policy.select_action(batch_obs),在它后面插入:

self._dbg_step = getattr(self, "_dbg_step", 0) + 1
if self._dbg_step % 20 == 0:
    act = batch_action[0].detach().float().cpu().numpy()
    logger.info(
        f"[POLICY DEBUG] step={self._dbg_step} "
        f"left={np.round(act[:6], 3).tolist()} "
        f"right={np.round(act[6:], 3).tolist()}"
    )

这层日志看的是:DP 自己吐出来的 12 维动作,左 6 维和右 6 维是不是已经很不对称。

5.3 加环境执行日志

source/lehome/lehome/tasks/bedroom/garment_bi_v2.py 找到 self.actions = self.action_scale * actions.clone(),在后面插入:

self._dbg_env_step = getattr(self, "_dbg_env_step", 0) + 1
if self._dbg_env_step % 20 == 0:
    act = self.actions[0].detach().float().cpu().numpy()
    logger.info(
        f"[ENV DEBUG] step={self._dbg_env_step} "
        f"left={np.round(act[:6], 3).tolist()} "
        f"right={np.round(act[6:], 3).tolist()}"
    )

这层看的是:进入环境、乘完 action_scale 之后,左右手是否才开始明显失衡。

5.4 单件测试

为了细粒度观察,我把测试列表改成只测一件衣服:

cp Assets/objects/Challenge_Garment/Release/Release_test_list.txt \
   /root/gpufree-data/Release_test_list.backup.txt
nano Assets/objects/Challenge_Garment/Release/Release_test_list.txt

nano 操作:Ctrl+O 保存,回车确认,Ctrl+X 退出。把文件内容改成单个 Top_Short_Seen_x

5.5 Debug 结论

结论 1:不是"左右手切片写反"

POLICY DEBUGENV DEBUG 都能看到左右 6 维动作在正常流动。不是那种明显的"左手拿到右手通道"的低级错误。

结论 2:问题更像是 DP 输出不稳定 / 模式跳变

从单件衣服日志看,DP 不是一直固定"左手抓偏同一个常数位置",而是:

  • 某些时刻一侧动作比较稳定,另一侧突然跳到另一种模式
  • 这种跳变有时发生在左手,有时发生在右手
  • 但整体 rollout 又长期过不了最终几何阈值

具体例子:

  • step 20 时 policy 左右都还算正常
  • 到 step 120,left 明显跳到另一种模式,而 right 还比较像"正常抓取"
  • 到 step 360、440,right 也会出现明显不同于前面稳定段的输出
  • ENV DEBUG 里也能看到执行前的 joint target 会跟着这些模式切换变化

所以更准确地说,不是单纯"左手通道坏了",而是:DP 在这个任务上学出了不够稳定的双臂动作分布,左手更容易先暴露这个问题。

结论 3:reward 在涨,但 success 条件一直没被真正满足

单件衣服跑完:Return = 117.98Success = False

中间多次 success check 都是:

  • dist(p[0], p[1])dist(p[4], p[5]) 这两条能过
  • dist(p[0], p[4])dist(p[2], p[3])dist(p[1], p[5]) 长期远超阈值

这说明它不是"完全不会动",而是一直在做某种看起来有进展的动作,但没把衣服真正收拢到目标折叠几何关系里。

6. 发现另一个问题:代码基线落后

在 debug DP 的过程中,顺便检查了镜像里的仓库版本:

git log --oneline -5
git fetch origin
git log --oneline origin/main -10

发现:

  1. 镜像里的仓库基线是 2953e2f,分支 main
  2. fetch 之后,官方当前 origin/main 已经到了 23127e9——落后官方 main 一串提交
  3. 当前工作目录不是干净的 2953e2f,而是旧基线 + 一堆本地改动 + 自己的 YAML 配置

缺的几条很相关的上游更新:

  • 32b5359 fix: unify garment success check coordinate space across CPU/GPU (#56)
  • 6b54226 Add image processing configurations in train_dp.yaml (#58)
  • 9c35592 Add .to(device) in image preprocessing to speed up eval (#52)

这些都和 success check / DP / eval 直接相关。

所以回答那个灵魂拷问——"不会我现在的镜像和官方还有 DataWhale 两个都不一样吧?"

很可能就是。 更准确地说,我当时实验环境的代码基线是一个旧官方版本,不是最新 main。这个差异对 DP 评测结果的影响有多大,因为没有在更新后重新跑对比实验,无法量化。但它至少是一个不可忽视的变量。

7. 最终判断

综合以上:

  • expert 正常
  • ACT 明显能成功(58.33%)
  • DP 30k 是 0%
  • DP 50k 只有 16.67%
  • 单件 debug 里 policy/环境都存在模式跳变

我会把这条线定性为:

当前这套 DP baseline 在 LeHome top_short 上不好用,问题更像 policy rollout 不稳定,而不是数据集下错、左右手切片写反、或者单纯步数不够。

有可能用更新后的代码基线 + 更多步数 + 调参能改善,但考虑到比赛截止时间和一份 L40 的算力限制,我没有继续在这条线上投入。转向下一个方法。


事后话:

再看到smolVLA scaling的表现之后,回想DP,也可能是DP就是训练不够,50k step对DP还是太少了。不过怎么说呢,这次比赛整体上是4类不同衣服,有限的机时给VLA来说确实比DP划算。

LeHome 跑 Diffusion Policy:不太理想的尝试
作者
若离
发表于
2026-04-14
License
CC BY-NC-SA 4.0

评论