← Back
2026-04-28 · experiment

BinFill ep0 Closed-loop — MemER LoRA × symbolic-grounded-subgoal VLA

TL;DR. VLM-only Phase 0–3 (260427) 에서 MemER LoRA (`Yinpei/vlm_subgoal_predictor /

배경/목적 (왜)

VLM-only Phase 0–3 (260427) 에서 MemER LoRA (Yinpei/vlm_subgoal_predictor / memer/grounded_subgoal/checkpoint-1300) 가 RoboMME BinFill 데이터에 in-domain subtask + grounded <x, y> 좌표를 출력함을 검증했음. 다음 단계는 closed-loop full pipeline:

sim env (sapien/ManiSkill BinFill) → MemER LoRA (subtask)
    → MME-VLA (symbolic-grounded-subgoal/79999, pi05 + memory-aware action expert)
    → action chunk → sim step → ...

검증 목표: (a) 환경 셋업과 wrapper 가 의도대로 동작 (b) ep0 single episode 가 완주되고 success/failure flag 가 찍히는지 (c) VLM 의 subtask 가 sim 의 실제 task progression 과 일치하는지.

작업 내용 (어떻게)

1. wrapper script — scripts/run_eval_with_mplib_patch.py

원본 examples/robomme/eval.pyexamples/robomme/subgoal_prediction/qwenvl/api_memer.py 둘 다 무수정 (git clean) 유지. driver 에 다음 3개 monkey-patch 만 적용:

  1. mplib set_base_pose: ManiSkill 3.0.0b21 이 numpy array 넘기는데 mplib 0.2.1 은 mplib.pymp.Pose 만 받음 → driver 시작 시 변환 wrapper.
  2. swift.llm.PtEngine attn_impl=sdpa: api_memer.py 의 hard-coded flash_attention_2 회피 (robomme env 에 flash-attn 없음, sdpa 와 수치 동일).
  3. Qwen3VLModelMemER.merge_key_frame_paths empty-dict guard: 첫 호출 시 key_frame_paths={}nums[0] 에서 IndexError → empty 시 early return.

추가로 --only-ep0 플래그: progress.json 에 ep1..49 = False 미리 채워 넣어 eval.py 의 setup_log_dict 가 이들을 skip 하게 → ep0 만 실행.

2. .venv (uv) 환경 보완

이전 세션에서 uv sync 가 mujoco 2.3.7 build 실패 (MUJOCO_PATH 미설정) 때문에 부분 설치 상태였음. policy server 실행에 필요한 누락 deps 를 직접 설치:

VIRTUAL_ENV=$(pwd)/.venv uv pip install --no-deps chex==0.1.89 \
    'jax==0.5.3' 'jaxlib==0.5.3' 'numpy==1.26.4' 'ml_dtypes'
VIRTUAL_ENV=$(pwd)/.venv uv pip install pytest          # → pluggy, iniconfig
VIRTUAL_ENV=$(pwd)/.venv uv pip uninstall opencv-python
VIRTUAL_ENV=$(pwd)/.venv uv pip install --no-deps opencv-python-headless

3. 누락된 history_config.txt 복구

runs/ckpts/mme_vla_suite/symbolic-grounded-subgoal/history_config.txt 를 zip 압축 해제 시 빠뜨림 (handoff doc 의 zip nesting 이슈로 home/ 정리할 때 실수). policy_config.create_trained_policy 가 이 파일 없으면 history_config=None 으로 폴백 → HistoryPi0.__init__ 에서 config.history_config.integration_type AttributeError. fix:

echo -n "symbolic-grounded-subgoal.yaml" \
    > runs/ckpts/mme_vla_suite/symbolic-grounded-subgoal/history_config.txt

(MODEL_TYPE 디렉토리명과 yaml 파일명이 일치하는 명명 규칙)

4. 실행 (login GPU, 90 분 timeout)

# Terminal A — VLA policy server (GPU 0)
CUDA_VISIBLE_DEVICES=0 OPENPI_DATA_HOME=~/openpi_data timeout 5400 \
    .venv/bin/python scripts/serve_policy.py \
        --seed=7 --port=8011 \
        policy:checkpoint \
        --policy.dir=runs/ckpts/mme_vla_suite/symbolic-grounded-subgoal/79999 \
        --policy.config=mme_vla_suite

# Terminal B — sim + VLM client (GPU 1)
ROBOMME=~/conda_envs/envs/robomme
CUDA_VISIBLE_DEVICES=1 LD_LIBRARY_PATH=$ROBOMME/lib:$LD_LIBRARY_PATH \
VK_ICD_FILENAMES=/etc/vulkan/icd.d/nvidia_icd.json USE_HF=1 timeout 5400 \
    $ROBOMME/bin/python scripts/run_eval_with_mplib_patch.py \
        --args.model_seed=7 --args.port=8011 \
        --args.policy_name=symbolic-grounded-subgoal --args.model_ckpt_id=79999 \
        --args.use-memer --args.subgoal-type=grounded_subgoal \
        --args.memer_adapter_path=~/ckpts/robomme-memer-lora-checkpoint-1300/checkpoint-1300 \
        --args.only_tasks=BinFill --only-ep0

GPU 0 (VLA): JAX init + Orbax restore 9 s, 첫 inference cuDNN autotune ~20 s, 이후 fast. GPU 1 (VLM): Qwen3-VL-4B-Instruct shard load 9 s + LoRA adapter 즉시.

결과 (수치)

runs/evaluation/symbolic-grounded-subgoal/ckpt79999/seed7/memer/:

progress.json     {"BinFill": {"0": true, "1": false, ..., "49": false}}
videos/BinFill_ep0_success_put one red cube into the bin, then press the button to stop_easy.mp4
                  995 KB

ep0 = success (task: "put one red cube into the bin, then press the button to stop", difficulty=easy).

VLM 호출 통계 (subgoal_keep_period=1, obs_horizon=16):

항목
총 VLM 호출 20
Distinct subtask 3 (pick up first red cube → put it into the bin → press the button)
Subtask 좌표 (293,344)→(289,344) cube, (457,582) bin, (238,465)→(230,469) button
첫 keyframe insert call 1 (keyframe_positions=[1])
Keyframe 추가 call 8 ([7]), call 12 ([5]) — 다른 subtask 시작

VLM 출력은 task progress 와 정확히 align 됨: 1. Frames 0–7: "pick up the first red cube at <289–293, 344>" (cube grasping phase) 2. Frames ~8–11: "put it into the bin at <457, 582>" (bin location 일관) 3. Frames 12–19: "press the button at <230–238, 465–469>" (button press phase)

GT VLM-only Phase 3 (260427) 의 108 calls vs 여기 20 calls — closed-loop 가 GT 보다 빠르게 수렴 (=18-step action chunk 한 번이 GT replay 의 5-frame subsample 18스텝과 다른 sim-time 흐름).

VLM-only 와 비교해 좌표 미세 변화 (e.g. cube (289,344) vs Phase 3 의 (293,344)) — closed-loop 의 actual rollout 에서 보는 frame 이 GT 와 다른 시점이라 픽셀 단위 좌표가 달라진 것. semantic 은 동일.

의미 (Takeaway)

  1. MemER (high-level VLM) + MME-VLA (low-level pi05) 풀 파이프라인이 B200 cluster 에서 동작. 환경 셋업 가이드 (260428/setup-robomme_b200_cluster.md) + 3개 monkey-patch 가 reproducible.
  2. "원본 코드 무수정" 원칙 유지: api_memer.py / eval.py 는 git clean, 모든 호환성 패치는 driver wrapper 안에. 사용자가 LoRA 결과의 의미를 놓고 검증할 때 "구현 변형 의심" 없이 신뢰 가능.
  3. history_config.txt 누락 회피법 발견: zip 압축 해제 후 항상 checkpoint_dir.parent / history_config.txt 존재 확인 필요.
  4. uv sync 회피로도 .venv 운영 가능: gym-aloha 의 mujoco 2.3.7 빌드 가 cluster 환경에서 실패하므로, 필요한 모듈만 --no-deps 로 추가하는 방식이 실용적.

보완점 / 다음 (Next)

참고 파일