← 목록
MolmoBot · pi0.5 · 2026-09-01 → 09-03

Hand-off Place 실험 기록

2-stage(pick → navigation → place) 파이프라인의 low-level VLA가 물체를 든 채 넘겨받아 "Place the X next to the Y"만 듣고 잘 놓는가를 재기 위해 무엇을 넣었고, 어떤 이미지가 들어갔고, sim rollout이 어떻게 돌아가고, 무엇이 실패하고 무엇이 됐는지의 전체 기록.

hand-off 성공률 (6조건)
19.7 – 23.9%
expert 상한
71.8%
val L1: goal on / off
0.072 / 0.090
val L1: 틀린 goal
0.129
학습 2회 · eval 잡 14개
≈ 60 GPU·h
한눈에

open-loop에서는 goal이 크게 작용했지만, closed-loop hand-off 성공률은 어떤 조건에서도 움직이지 않았다

조건 (공통 117편)무엇이 다른가성공률val L1 all판정
mix20kplace 데이터 없음 (기존 5종 혼합, 20k step)23.1%0.1116기준선
place10k+ place 클립 (가중치 0.20), goal 없음22.2%0.0870무변화
place_goal10k · goal offgoal로 학습(dropout 0.5), 평가 때 goal 안 줌22.2%0.0898무변화
place_goal10k · goal onexpert가 놓은 장면(관측 카메라)을 goal로23.9%0.0720p=0.86
place_goal10k · goal on, 늦은 프레임팔이 물체에서 떨어진 뒤(go_home +30 step)의 장면23.9%—on과 12:12
place_goal10k · 틀린 goal다른 에피소드의 goal 이미지19.7%0.1285p=0.41
scripted expert데이터 생성기가 직접 끝까지 수행 (pass 1)71.8%—상한

학습 분포(val)에서는 goal 이미지가 팔 궤적 오차를 23% 줄이고, 틀린 goal은 50% 늘린다 — 모델은 3번째 이미지 슬롯의 내용을 읽는다. 그런데 sim에서 물체를 든 채 넘겨받아 실제로 놓게 하면 place 데이터도, goal도, goal의 시점·프레임을 바꿔도 성공률은 21–24%에서 움직이지 않는다.

실패는 "놓지 못해서"가 아니다(97–100% 놓음). 참조 물체까지 마지막 15–40 cm를 못 맞춘다: 최종 표면거리 중앙값 0.17–0.24 m, 10 cm 이내는 25–32%뿐이다.

1

질문과 측정 정의

사용자의 2-stage 제어에서 pick과 navigation은 다른 모듈이 하고, pi0.5는 이미 물체를 든 상태에서 제어권을 받아 내려놓기만 한다. 그 hand-off 이후가 잘 안 된다. 두 질문:

  • Q1 — place 구간만 잘라 만든 데이터(Keh0t0/molmo-augment)로 추가 학습하면 잘 놓는가?
  • Q2 — 사전에 저장해 둔 "물체가 놓여 있던 장면" 이미지를 goal로 넣어 주면 큰 의미가 있는가?

처음에는 이를 val open-loop 오차와 기존 HardBench(Pick / PnP / NextTo, 깨끗한 초기 상태에서 pick부터)로 답했다. 사용자가 정정했다: 원하는 것은 "물체를 든 채 시작하고 Place 명령만 줬을 때"의 성공률이다. 기존 벤치는 pick부터 하고, val L1은 action 예측 오차일 뿐이다. 그래서 그 상황을 그대로 재현하는 closed-loop 벤치를 만들었다(§6).

2

데이터: molmo-augment place 트랙

출처
MolmoBot-Data NextTo(Franka) 원본 궤적의 preplace → retreat 구간만 잘라낸 클립
규모
15,043 클립 / 121 GB · 클립당 ~99 frame(≈6.5 s, 66 ms/step)
분할
house 단위 — train 4,819 house / 14,567 궤적, val 149 house / 460 궤적(39,040 프레임)
지시문
원본 "Pick up the X and place it next to the Y" → 변환본 h5에 "Place the X next to the Y"로 재작성 (~11% 빈 referral은 그대로 두면 "Pick up"으로 떨어짐)
제외
카메라 누락 16궤적 (mid-training KeyError 방지)
goal 이미지
goal_im/<ep>/{exo_camera_1, exo_camera_2, wrist_camera}.png, 480×640 — 클립 이후 구간(goal window: retreat~go_home, 약 5.5 s)의 완료 장면

학습 goal 카메라로 exo_camera_2를 썼다. 이 카메라가 정확히 무엇인지는 이번 기록을 쓰면서 다시 확인했다: h5에 저장된 관측 센서 4종(randomized_zed2_analogue_1/2, randomized_gopro_analogue_1, droid_shoulder_light_randomization) 중 어느 것과도 일치하지 않는 별도 카메라이고, 에피소드마다 위치가 달라 관측 exo 시점과도 다르다.

관측 센서 4종 vs goal exo_camera_1/2 비교
학습 데이터의 관측 카메라 4종(왼쪽 4열, 클립 마지막 프레임) vs goal_im의 exo_camera_1/2(오른쪽 2열, 4:3을 관측 기하로 center-crop). val 5개 에피소드. goal 카메라는 관측 센서 중 어느 것도 아니며 시점이 에피소드마다 다르다. 즉 학습에서 goal은 "다른 시점에서 본 완료 장면"이었다.

이 사실은 sim closed-loop 결과 해석에 영향을 준다. sim에서는 관측 카메라와 같은 카메라로 goal을 만들었다(§6). 학습 goal 분포(다른 시점)와 다른 시점 도메인이며, 이를 가르는 진단(goal 카메라 = randomized_zed2_analogue_2, job 107886)이 진행 중이다.

3

모델 입력과 goal 주입 방식

pi0.5(openpi PyTorch)의 입력은 이미지 슬롯 3개, 관절 상태, 언어 프롬프트다. 출력은 16 step × 32차원 action chunk이며 실제 의미 있는 차원은 8개(팔 관절 상대 위치 7 + 그리퍼 1). 평가에서는 이를 관절 속도 명령으로 실행한다.

base_0_rgb
외부(exo) 카메라. 학습: randomized_zed2_analogue_1 480×640 → resize + center-crop 360×640 → 224×224 pad. mask = True
left_wrist_0_rgb
손목 카메라 wrist_camera_zed_mini, 같은 전처리. mask = True
right_wrist_0_rgb ← goal
사전학습 내내 zeros + mask=False였던 세 번째 슬롯. 여기에 goal 이미지를 넣고 mask를 켠다. 토큰 수는 변하지 않고, 이 자리에 학습된 prior가 없으니 충돌도 없다(백지).
state
관절 위치 7 + 그리퍼 1 (정규화). 프롬프트는 토큰화되어 prefix에 붙는다.

goal이 들어가는 경로 (molmobot_pi0/goal_image.py)

dataset
goal_im/<ep>/exo_camera_2.png 480×640을 PIL로 읽어(cv2.imread는 비디오 디코드 직후 libpng 오류) 샘플에 observation.goal_image(1,H,W,3) + observation.goal_valid(1,) 부착. goal이 없는 데이터셋 샘플에는 placeholder + valid=False
→
GoalDroidInputs
DroidInputs를 상속. valid이고 dropout에 안 걸리면 goal을 360×640으로 squash(4:3 → 16:9, 비율 무시) 후 right_wrist_0_rgb에 넣고 mask=True. dropout 0.5: 샘플의 state 합을 해시한 결정적 난수로 절반은 다시 zeros+mask=False
→
model transforms
세 슬롯 모두 resize_with_pad 224×224 → SigLIP. 정규화·토큰화는 기존과 동일

dropout을 둔 이유는 두 가지다. 배포에서 goal을 못 만드는 상황에도 동작해야 하고, 한 번도 안 쓰던 슬롯을 켜면 attention 패턴이 바뀌어 기존 능력이 깎일 수 있어서다. 실제로 goal off 성능은 place10k와 3% 차이(0.0898 vs 0.0870)로 유지됐다.

배포 경로(pi.py)에서는 base가 이미 224×224로 들어오므로 goal에 같은 순서(squash → pad)를 재현하도록 obs_hw를 두었고, obs["goal_image"]를 넣으면 슬롯으로 가는 passthrough를 붙였다. 아래가 실제 학습 샘플 하나에서 모델이 받는 세 장이다.

학습 샘플의 세 슬롯 입력
학습 샘플(val house_1126, 클립 20번째 프레임). 위: 원본 해상도의 base / wrist / goal. 아래: 모델이 실제로 받는 224×224 세 장. goal은 관측과 다른 시점의 완료 장면이고, 4:3을 16:9로 눌러(squash) 넣었기 때문에 세로가 압축돼 있다. 프롬프트: "Place the translucent glass bottle with narrow neck next to the medium brown rustic bread loaf".
4

학습

runinit혼합 (샘플링 확률)goalstep / batch시간최종 lossjob
mix (v2)pi05_droidmain 0.35 / Pick 0.20 / PnP-p0 0.10 / NextTo 0.20 / Color 0.15—20k / 25611h35m0.003884260
placepi05_droidmain 0.30 / Pick 0.15 / PnP-p0 0.10 / NextTo 0.15 / Color 0.10 / place 0.20—10k / 2567h13m0.0050105831
place_goalpi05_droidplace와 동일exo_camera_2, dropout 0.510k / 2567h19m0.0050107382

place와 place_goal은 init·step·혼합비·지시문이 같고 goal 유무만 다르다(통제 A/B). place 클립은 전체 프레임의 0.7%지만 0.20 가중치로 약 28배 오버샘플링된다. 8×B200, 로그인 노드가 아닌 sbatch(own QOS). goal 학습은 세 번 죽고 네 번째에 돌았다(§8).

5

평가 ①: val open-loop L1

val 460궤적의 모든 프레임(39,040)에서 정규화 공간의 예측 chunk(16×8)와 GT의 L1. 모든 프레임이 "든 채 시작"한 분포이므로 hand-off 능력의 대리 지표로 썼다 — 나중에 이 가정이 틀렸음이 드러난다(§9). 네 가지 뷰: all, arm(7), gripper(1, 릴리즈 타이밍), step0(즉시 실행되는 첫 step). 조건은 --goal-camera(on), 없음(off), --goal-shuffle(다른 궤적의 goal, 460궤적 derangement), --goal-preproc crop(관측처럼 center-crop한 goal).

모델 · goal 조건allarmgripperstep0읽기
DROID (MolmoSpaces 학습 전)0.39260.36840.56180.4249zero 예측(0.2362)보다 나쁨
mix20k0.11160.10620.14950.1097place 데이터 없는 기준선
place10k0.08700.08590.09500.0856all −22%, gripper −36% vs mix20k
place_goal10k · off0.08980.08850.09830.0881goal 없어도 place10k 수준 유지
place_goal10k · on0.07200.06830.09790.0732arm −22.8% vs off, gripper 불변
place_goal10k · on, crop0.07680.07390.09780.0770기하를 바꿔도 이득의 73% 유지
place_goal10k · 틀린 goal0.12850.13240.10160.1263arm +50% vs off — 내용을 읽고, 틀리면 오도됨
val L1 막대 그래프
val open-loop L1. goal은 팔(어디로)만 바꾸고 그리퍼(언제)는 세 조건 모두 ~0.10으로 같다. gripper는 place 데이터가 이미 해결한 부분이다.
6

평가 ②: closed-loop hand-off 벤치

사용자 정의 측정을 그대로 재현한다. 물체를 "든 채 시작"하는 상태를 만들려면 누군가 집어야 한다. h5에는 물체 포즈가 저장돼 있지 않아 저장 상태 복원은 불가능했고(EnvStateSensor가 비어 있음), 물체를 그리퍼에 용접하는 코드도 없다. 대신 molmo_spaces에 들어 있는 데이터 생성용 scripted expert(PickAndPlaceNextToPlannerPolicy)를 그대로 써서 집게 하고, 학습 클립이 잘린 바로 그 지점에서 정책에 넘긴다.

에피소드
NextTo HardBench 200편 (procthor-10k val house, held-out). JSON에 house·물체·수용체·로봇 베이스 포즈·지시문 표현이 들어 있음
expert phase
gripper-open → pregrasp → grasp → gripper-close → lift → preplace → place → retreat → go_home (학습 클립 = preplace..retreat)
hand-off 시점
phase가 preplace로 바뀐 직후(1 step 실행 후) — phase 센서는 step 뒤에 읽히므로 클립 첫 프레임과 같은 시점
정책 지시문
원 태스크 문장을 학습과 같은 regex로 "Place the X next to the Y"로 변환해 task.get_task_description을 덮어씀
horizon
20 s = 303 step(66 ms/step). 학습 클립 ~6.5 s의 3배. hand-off 시 step 카운터를 0으로 리셋
성공 판정
NextTo 기준: 물체–수용체 표면 간격 ≤ 5 cm · 같은 지지면 · 수용체 이동 ≤ 15 cm/90°, 0.5 s 연속. 놓았는지는 판정에 없어 released_at_end 별도 기록
분모
expert가 preplace에 도달하고 물체를 쥔 편만. expert 계획·grasp 샘플링을 (house, episode) 시드로 고정 → ckpt 간 동일 에피소드
추론
정책 서버(websocket, 1 GPU) + sim worker 16개. 관측 dict가 그대로 서버로 가고 서버 쪽 PiPolicy가 전처리·추론

sim rollout 절차

reset
JSON 에피소드 spec으로 scene 구성(물체 포즈, 로봇 init qpos, 카메라). planner용 보조 body(grasp_collision_*) 추가. expert 등록 → task.reset()에서 계획(grasp 샘플 + 배치 위치 샘플 + IK 검사)
→
expert · ~90–120 step
gripper-open → pregrasp → grasp → close → lift. 배치 위치는 24회 × grasp 2회까지 재샘플(원본은 1회 후 즉시 예외). 실패하면 그 에피소드는 평가 대상 아님
→
hand-off @ preplace
task.register_policy(policy); policy.reset(); episode_step_count = 0. 물체 파지 확인(grasp_state.held)
→
policy · 303 step
매 step: exo·wrist 이미지 + 관절/그리퍼 상태 + 프롬프트 → 16-step chunk 예측 → 관절 속도 명령으로 실행(buffer 소진 후 재추론)
→
기록
h5(관측·행동·성공 플래그·task_info·hand-off 메타) + 카메라별 mp4. expert 구간도 남겨 영상으로 pick 과정 확인 가능

정책이 매 step 받는 것은 아래와 같다. sim 관측 카메라는 droid_shoulder_light_randomization(352×624)로, 학습 관측 카메라와 다른 이름·랜덤화이지만 이 프로젝트의 모든 rollout 수치가 같은 설정이다.

sim hand-off 시점의 세 슬롯 입력
sim hand-off 시점(house_100, goal-on 러너, t=120)의 세 슬롯. exo·wrist는 pi.py에서 360×640 crop → 224 pad, goal은 GoalDroidInputs에서 squash → pad. 이 에피소드의 goal은 expert가 감자를 파란 냄비 옆에 놓은 뒤 팔이 위에 머무는 순간(go_home 진입 직후)이다. 프롬프트: "Place the small red potato next to the blue pot with handles". 이 조건에서 정책은 실패했고, 같은 에피소드가 다른 실행에서는 성공하기도 했다(flow-matching 샘플링 분산).

goal-on 변형 (handoff_goal.py)

snapshot
task.reset() 직후 MuJoCo 상태를 mj_getState(mjSTATE_INTEGRATION)로 저장
→
pass 1 · expert 완주
place → retreat → go_home 진입 순간의 exo 프레임을 goal로 저장(PNG로도 남김). expert 자체 성공률 71.8%
→
restore
mj_setState + forward, 캐시 무효화, 같은 시드로 task.reset() → 같은 계획. qpos drift 2e-16 assert
→
pass 2 · hand-off
위와 동일. 정책에 주는 관측 복사본에 goal_image를 붙여 서버로 전송 → GoalDroidInputs

변형 3종: 늦은 프레임(HANDOFF_GOAL_DELAY=30, 팔이 물체에서 떨어진 뒤), 틀린 goal(HANDOFF_GOAL_BANK, 다른 에피소드의 goal PNG), 다른 시점(HANDOFF_GOAL_CAMERA=randomized_zed2_analogue_2, 진행 중). goal 없이 학습한 ckpt는 이 키를 버리므로 같은 러너에서 goal-off와 동일하게 동작한다.

goal 이미지와 각 모델의 최종 프레임 예시
goal 이미지(왼쪽)와 각 모델의 최종 프레임. house 100: 감자를 파란 냄비 옆에(goal off만 성공). house 104: 빨간 꽃병을 잡지 옆에 — goal-on은 꽃병을 세워 놓았지만 판정 실패, 나머지는 다른 곳. house 109: 병을 파란 스프레이 옆에 — 모두 실패. S/F는 집계 당시 판정.
7

결과

분모: 200편 중 무엇이 평가됐나

단계goal-off 계열 (mix20k · place10k · goal off)goal-on 계열 (on · 늦은 프레임 · 틀린 goal)이유
벤치 에피소드200200
expert 계획 예외 (h5 없음)−45−58grasp 후보에 대해 preplace/place IK가 24×2회 재샘플로도 불가. goal-on은 pass 1 완주까지 필요해 더 많음
hand-off 시 물체 미파지 / expert 포기−13−25grasp 실패·lift 중 낙하, pass 1의 place/retreat 타임아웃 9편
평가된 편수142117시드 고정으로 계열 내 집합 동일. 비교는 공통 117편

성공률

조건전체공통 117편놓음goal-on과 쌍별 (only-a : only-b, McNemar p)
mix20k30/142 = 21.1%27 = 23.1%96.5%19 : 18, p=1.00
place10k30/142 = 21.1%26 = 22.2%99.3%16 : 14, p=0.86
place_goal10k · off31/142 = 21.8%26 = 22.2%99.3%17 : 15, p=0.86
place_goal10k · on28/117 = 23.9%28 = 23.9%100%—
place_goal10k · on, 늦은 프레임28/117 = 23.9%28 = 23.9%100%12 : 12, p=1.00
place_goal10k · 틀린 goal23/117 = 19.7%23 = 19.7%99.1%14 : 9, p=0.41
expert (pass 1)102/142 = 71.8%벤치의 현실적 상한 — 정책은 그 1/3

mix20k vs place10k는 142편에서 17:17로 완전 동률이다. goal 조건은 on > off > wrong의 방향은 있지만 어느 쌍도 유의하지 않다(n=117, 성공률 ~22%면 표준오차 ≈ 3.8%p).

에피소드별 일치도(공통 117편, 6조건): 어느 조건에서도 성공 못 한 편 55, 1조건만 19, 2조건 18, 3조건 8, 4조건 9, 5조건 5, 6조건 모두 3. 절반은 어떤 모델도 못 풀고, 나머지는 실행마다 성패가 갈린다 — 모델 차이보다 에피소드 난이도와 샘플링 분산이 크다.

실패 분해: 물체가 어디에 놓였나

마지막 프레임 분류mix20kplace10kgoal offgoal on늦은 프레임틀린 goal
성공 (≤5 cm, 같은 면)19.7%19.0%17.6%20.5%19.7%17.1%
근접 실패 (≤15 cm, 같은 면)18.3%16.9%16.9%16.2%15.4%16.2%
15–40 cm 벗어남28.9%35.9%32.4%29.1%37.6%29.9%
> 40 cm23.2%18.3%22.5%18.8%15.4%23.9%
다른 면 (바닥 등)8.5%8.5%9.9%11.1%11.1%12.8%
수용체 밀림1.4%1.4%0.7%4.3%0.9%—
최종 표면거리 중앙값 (hand-off 시 0.396 m)0.171 m0.204 m0.227 m0.240 m0.194 m0.208 m
10 cm 이내 비율32%27%29%32%25%29%
성공률과 실패 분포
왼쪽: 성공률. 오른쪽: 마지막 프레임에서 물체가 있던 곳. 네 모델의 분포가 사실상 같다. 모두 물체를 hand-off 지점에서 절반쯤 가져가 놓는다.

무엇이 실패하는가. 놓는 행동은 문제가 아니다(released 97–100%, 수용체 밀침 1–4%). 참조 물체 방향으로 가긴 하지만 마지막 15–40 cm를 못 맞춘다. place 데이터도, goal의 내용·시점·프레임도 이 분포를 바꾸지 못했다.

한 에피소드의 두 실행: 같은 house_100, 실패와 성공

실패 시퀀스
실패 (place10k, 스모크). 위 exo, 아래 wrist. t≤119 expert가 감자를 집고(손목 뷰), t=121 hand-off. 정책은 팔을 크게 들어 올렸다가(t=160–220) 냄비 옆이 아닌 곳에 놓고(t=300) 물러난다.
성공 시퀀스
성공 (place10k, goal 변형 스모크 — goal 키는 DroidInputs가 버리므로 실질 goal off). 같은 에피소드·같은 hand-off 상태. 정책이 감자를 든 채 냄비로 이동(t=160–250, 손목 뷰에 냄비 테두리), t≈300에 냄비 옆에 내려놓고 물러난다. 동일 조건에서 성패가 갈리는 전형적인 예.
영상

rollout 영상: 같은 에피소드를 다섯 조건으로

각 영상은 행 = 조건(goal ON / goal OFF / 틀린 goal / place10k / mix20k), 열 = goal 이미지 | exo 카메라 | wrist 카메라. t=0이 hand-off 순간이다: scripted expert가 집어 올린 뒤(그 구간은 영상에서 뺐다) 물체를 든 상태에서 VLA가 제어를 넘겨받는 시점부터 303 step(20 s)을 15 fps 실시간으로 보여 준다. 첫 0.5 s의 노란 테두리가 hand-off 표시다. 라벨의 판정은 종료 시점 상태(성공 / 근접 실패 ≤15 cm / 15–40 cm / far / 다른 면)이고, 본문 집계의 성공은 0.5 s 지속 판정이라 드물게 다를 수 있다(house_117).

house_100 · 같은 hand-off 상태, 같은 ckpt(place10k)의 두 실행 — 위: 실패 실행 — 팔을 크게 들었다가 냄비 옆이 아닌 곳에 놓음. 아래: 성공 실행 — 냄비로 이동해 옆에 내려놓음. t=0 = hand-off(든 상태). 동일 조건에서 flow-matching 샘플링만으로 성패가 갈리는 전형.
house_116 · goal이 있을 때만 성공 — goal ON 성공, 틀린 goal도 성공(!), goal OFF·place10k·mix20k는 15–40 cm 벗어남. goal 슬롯에 '어떤 이미지든' 들어오면 행동이 달라지는 사례 — 내용보다 존재가 작용한 것으로 보인다.
house_100 · goal OFF만 성공 — goal ON은 off target, 틀린 goal은 다른 면(바닥), place10k 근접 실패(≤15 cm), mix20k far. 예시 그림(§6)과 같은 에피소드.
house_117 · place10k·mix20k만 최종 성공 — goal ckpt 세 조건은 모두 근접 실패(≤15 cm). goal ON은 0.5 s 지속 판정으로는 성공(집계 S)이었으나 종료 시점엔 5 cm 밖 — 놓은 뒤 물체가 밀린 경우. 판정 정의(지속 vs 종료)의 차이가 드러나는 편.
house_375 · 거의 전 조건 성공 — goal ON/OFF/틀린 goal/place10k 성공, mix20k는 종료 시 다른 면. 모델 차이보다 에피소드가 쉬운 경우.
house_104 · 전 조건 실패 — 15–40 cm 벗어남 — 빨간 꽃병을 잡지 옆에. 모든 조건이 잡지 근처까지 가서 세워 두지만 5 cm 안에 못 넣는다(mix20k는 far). 이 벤치의 대표 실패 유형(29–38%).
house_109 · 전 조건 실패 — 다른 면 — 병을 파란 스프레이 옆에. 다섯 조건 모두 카운터가 아닌 면(바닥/다른 가구)에 놓는다. 참조 물체 접지 실패.
house_148 · 전 조건 실패 — far — 40 cm 이상 벗어남(goal OFF만 15–40 cm). 목표 방향으로 가다 마는 유형.

영상 8편(약 13 MB)은 대시보드 사본에 모두 들어 있다. 아티팩트 사본은 용량 제한으로 4편(두 실행 비교, house_116, house_100, house_104)만 내장했다.

8

무엇이 깨졌고 무엇이 됐나

학습 3회 실패, 벤치 스모크 11회. 각각 원인과 수정(모두 claude/context.md 함정 17–27로 기록).

goal 학습 (4차에 성공)

1
bash ${VAR:-default} 우선순위 — EXP_NAME이 place/에 붙어 place/10000에서 auto-resume. 1분 만에 취소, 덮어써진 train_config.pkl은 ckpt assets에서 복구. resume 가드 추가
2
KeyError 'observation.goal_image' — goal_im 없는 데이터셋(main·Pick…) 샘플에 키가 없는데 RepackTransform은 무조건 찾음. 단일 데이터셋 테스트로는 안 걸림 → placeholder + valid=False 항상 부착
3
IndexError — UnchunkObservationTransform이 observation.* 전 키에 v[0]을 적용. 0-d valid가 터지고 이미지 첫 축이 잘릴 뻔 → 선두 horizon 차원(1) 추가. 로더·transform 단위 테스트가 아니라 전체 체인 + 이질 데이터셋 혼합으로만 잡힌다
4
cv2.imread가 같은 __getitem__ 안 비디오 디코드 직후 libpng error로 전부 실패(0/200) → PIL로 교체

hand-off 벤치 스모크 (11회)

1
hydra: import된 모듈의 @hydra.main은 config_path를 패키지 경로로 해석 → 진입 파일에 main을 따로 두고 __wrapped__ 호출
2
로그인 노드 8000 포트를 다른 프로세스가 점유 → 정책 서버 클라이언트·대기 루프의 하드코딩을 POLICY_SERVER_PORT로
3
Invalid name 'grasp_collision_0' — JSON eval sampler는 policy의 add_auxiliary_objects를 부르지 않음 → sampler 서브클래스에서 planner 보조 body 추가
4
float + None — bench JSON에 min/max_surface_to_surface_gap이 없음 → 태스크와 같은 fallback(0 / 0.05 m)으로 채움
5
planner가 task_sampler_config.max_place_receptacle_sampling_attempts를 읽음 → expert config에 데이터 생성용 sampler 설정
6
collect_info KeyError position_error — NextTo task_info는 xy_distance만 있음. Q1까지의 NextTo 17%도 이 KeyError 뒤 pipeline 요약에서 읽은 값(비교 유효). 키 fallback 패치
7
IK failed for preplace pose — 원본 planner는 배치 위치를 1회만 샘플. 프로브: grasp는 시드 무관하게 같고 성공 배치는 x≈1.25–1.28 m의 좁은 영역, 시드 0은 8샘플 전부 밖 → 24회 × grasp 2회 재샘플(예외 대신 None 루프)
8
시드가 모든 집에서 0(episode_idx) → (house, episode) 기반 시드로 집마다 다르게, ckpt 간 재현
—
미해결 expert 계획 불가 29%(58/200): 같은 grasp(z=0.952)가 반복 선택되는 집은 재샘플로도 불가. 분모 확대는 grasp 후보 다양화나 bench 필터가 필요

확인된 것

  • hand-off 상태는 학습 클립과 같은 phase에서 생성되고, 시드 고정으로 세 ckpt가 정확히 같은 142편을 평가했다(expert 불가 집합 동일).
  • goal-on 변형의 상태 복원은 qpos drift 2e-16, 복원 후 재계획도 같은 지점(119 step)에서 hand-off.
  • goal 주입 경로는 학습 경로 출력이 기존과 바이트 동일, 추론 경로(base 224×224)가 학습 goal의 224 pad와 정확히 일치.
  • goal_shuffle(틀린 goal) 대조군은 460궤적 derangement, 같은 house로 떨어진 6편 제외 전부 다른 장면.
9

해석과 한계

open-loop vs closed-loop. val L1은 시연 상태 분포 위에서 "다음 16 step을 시연처럼 예측하는가"를 잰다. place 데이터와 goal은 이 예측을 확실히 좋게 했다. 그러나 closed-loop에서 정책은 자기 행동으로 만든 상태를 보게 되고, 그 상태에서의 마지막 15–40 cm 정밀 접근은 두 개입 모두 바꾸지 못했다. val open-loop L1은 이 질문의 대리 지표로 부족하다 — 이번에 −22%가 성공률 0%p였다.

goal 도메인. 기하(squash vs crop)는 crop 프로브로 대부분 기각됐다(이득의 73% 유지). 프레임 시점(팔이 위에 있느냐)도 무관했다. 남은 것은 시점이다: 학습 goal은 관측과 다른 제3 시점의 완료 장면인데 sim goal은 관측 카메라와 같은 시점이었다. 다른 랜덤 exo 카메라로 goal을 만드는 진단(107886)이 이를 가른다.

벤치 자체의 한계. (i) expert 불가 29%가 빠져 분모가 142/117편이고, 그 집합은 "expert가 풀 수 있는 쪽"으로 치우친다. (ii) 성공 판정 5 cm는 엄격하다 — 근접 실패(≤15 cm)까지 합치면 36–40%다. (iii) sim 관측 카메라(droid_shoulder)가 학습 관측 카메라와 다르다는 점은 모든 rollout 수치가 공유하는 조건이다. (iv) n=117에서 ±4%p 이하 효과는 검출되지 않는다.

사용자 파이프라인에 대해. 현재 ckpt 세 개(mix20k·place10k·place_goal10k) 중 어느 것을 hand-off에 써도 sim 기준 성공률은 같다. goal을 줄 거라면 저장된 사전 관측을 관측과 같은 전처리로 넣으면 되지만(pi.py passthrough), 이번 결과로는 그것이 성공률을 올린다는 근거가 없다.

10

다음

  1. 시점 진단(107886) 결과 확인. 효과가 있으면 goal을 "관측과 다른 고정 카메라"로 정의하고 그 조건으로 배포·재평가.
  2. 효과가 없으면 goal 재학습보다 정밀 배치 자체를 다룬다: 배치 직전 구간 오버샘플링, wrist 카메라 비중, place 지점을 명시하는 저수준 목표(좌표·마커) 조건화, NextTo 판정 거리별 커리큘럼.
  3. 벤치 보강: grasp 후보 다양화로 expert 불가 29%를 줄이고, 근접 실패(≤15 cm)를 보조 지표로 같이 보고.
  4. 지표: 이후 모든 hand-off 관련 판단은 이 벤치의 성공률 + 최종 표면거리 분포로 한다. val L1은 학습 진단용으로만.
부록

경로와 잡

항목위치
벤치 러너MolmoBot-Pi0/molmobot_pi0/eval/handoff.py, handoff_goal.py, 진입 eval_handoff*.py
제출·집계scripts/rollout_handoff.sh (env: MODEL_DIR, CKPT_STEP, TAG, HANDOFF_GOAL, HANDOFF_GOAL_DELAY, HANDOFF_GOAL_BANK, HANDOFF_GOAL_CAMERA), scripts/summarize_handoff.py
val open-loopscripts/eval_place_val.py, run_place_val.sh (GOAL_CAMERA, GOAL_SHUFFLE, GOAL_PREPROC)
goal 주입molmobot_pi0/goal_image.py (GoalImageLoader, GoalDroidInputs), dataset.py, dataset_openpi.py, eval/policies/pi.py
데이터~/Dataset/molmo_augment_place/{train,val} (변환 scripts/convert_molmo_augment.py, 분할 split_augment_val.py)
ckpt~/ckpts/molmobot_pi05_runs/pi05_mlspaces_finetune/{pi05_mlspaces_mix/20000, pi05_mlspaces_place/10000, pi05_mlspaces_place_goal/10000}
결과 JSON~/ckpts/molmobot_pi05_runs/handoff_eval/results/*.json, ~/ckpts/molmobot_pi05_runs/place_val/*.json; 에피소드 h5·mp4·goal PNG는 handoff_eval/<tag>/step10000/eval_data/pi/10000/...
잡학습 105831(place) 107382(place_goal) · val 107310-12 107598-600 107743 · hand-off 107595-97 107603 107752 107753 107886
로그claude/260902/analysis-place_q1_results.md, progress-handoff_place_bench.md, analysis-handoff_closed_loop_q1q2.md, claude/context.md 함정 17–27