Index
2026-08-19 — Experiment

FD Replay v4 — World Model을 실행 전 액션 검증기로 쓸 수 있는가

Cosmos3 (NVIDIA cosmos-framework) | 사건 정렬 코호트 48 + 타워 24 + 접촉 앵커 16 · 재접지 주기 3조건 · jobs 86010-2 / 86171-3 / 87612-4 / 87638-40

TL;DR

93%
N=2 balanced acc
50%
persistence
33/48
beats-copy (N=2)
0/48
beats-copy (OL)
48
에피소드

1 배경 — 왜 v3를 다시 짰나

v3는 "정책이 실행한 액션을 base Cosmos3-Nano fd에 teacher-forcing했을 때 실패를 예측하는가"에 최종 프레임의 state match로 답했고 recall 100%를 얻었다. 그런데 실제로 알고 싶은 건 실행 전 검증기로서의 성능이다 — 그리퍼가 못 집는지, 이송 중 떨어뜨리는지, 쌓다가 쳐서 무너뜨리는지를 WM이 같이 그려주는가.

그 질문에 맞춰 셋업을 검토하니 결함 두 개가 드러났다.

(1) replay 윈도우가 실패 사건을 담고 있지 않았다. 윈도우가 "첫 gripper close − 32스텝, 최대 8청크"로 고정인데 실패는 타임아웃까지 돈다 → 커버율 9–62%(GreenSpoons T=2700 → 9%). 성공은 T=126–440이라 90–97%. 성공은 배치 장면까지 전부 재생하고 실패는 첫 시도만 재생하는 비대칭이었다.
(2) 실패는 "정적"이 아니라 "반복 재시도"였다. 499개 usable 에피소드 trace 전수 계산: 실패의 gripper close 중앙값 7회 vs 성공 2회, 8cm 이상 이송한 파지 사이클 보유 392/435. v3에 남았던 "저변화 실패가 관성으로 자동 정답" 교란의 실체는 실패가 정적이어서가 아니라 판정 시점에 실패가 아직 안 일어나서였다.

2 설계 — 사건 정렬 코호트

실패 모드 자동 분류 (scripts/fd_replay_modes.py)

trace npz에는 로봇 상태만 있고 물체 pose가 없다. 그래서 파지 사이클(close→open)마다 travel(EE 최대 이동), gap(다음 close까지), return_dist(release 지점→다음 close 지점)를 복원해 모드를 뗐다. 분류 풀 232개.

grasp_miss 모든 사이클 travel < 8cm 인데 close ≥ 3회 → 계속 헛집음 (21) drop travel > 15cm 이송 후 release, 150스텝 내 12cm 안으로 복귀해 재파지 (106) stack_disturb 쌓기 계열 태스크에서 실제 이송·배치가 있었는데 실패 (77) success 마지막 이송 사이클의 release = 성공 배치 (대조군) (28)

윈도우 — 사건에서 끝나게 자른다

사건 E = release(낙하/배치) 또는 헛파지 사이클의 re-open(그리퍼가 빈 채 벌어지는 순간). 8청크(128스텝) 윈도우를 E에서 끝나게 잘랐다. 중앙 정렬이 아니라 끝 정렬인 이유는 성공 쪽에 있다 — sim이 성공 판정 순간 에피소드를 끊으므로(E = T−2 … T−9) 성공에는 사건 이후 영상이 없고, 중앙 정렬하면 성공만 더 먼 horizon으로 밀려 불리해진다. 실제 offset은 실패 112, 성공 127–128로 전부 마지막 청크 안.

사건 자체는 절대 조건 프레임으로 새지 않는다. 마지막 실제 프레임이 사건보다 최소 1청크 앞이라, N=2는 1.1–2.1초 / N=4는 3.2–4.3초 / open-loop는 7.5–8.5초를 혼자 외삽한 뒤 사건을 그려야 한다.
관성 예측은 이 지표를 흉내낼 수 없다. 사건 정렬 덕분에 "마지막 재접지 프레임 유지"(copy)는 판정 시점에 물체를 아직 쥐고 있음 / 스택이 아직 서 있음을 그린다 — 낙하·전도·성공 배치 전부에서 오답이다. 이것이 v3에 없던 대조군이다.

3 결과 — 객관 지표 (전체 48개)

scripts/fd_replay_v4_lpips.py: 판정 청크(offset 113–128)를 LPIPS로 세 방향 비교. d_post=예측 vs 실제, d_pre=예측 vs 정지 기준, gap=실제가 얼마나 움직였나(= copy의 d_post).

모드ngap (중앙값)N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)
파지 실패120.1722/120/120/12
이송 중 낙하120.33510/122/120/12
전도 / 무너뜨림120.35112/123/120/12
성공 배치 (대조)120.2379/120/120/12

표의 조건 열은 beats-copy = 마지막 재접지 프레임을 그대로 유지하는 것보다 실제 최종 청크를 더 잘 설명한 에피소드 수.

beats-copy — 마지막 재접지 프레임을 유지하는 것보다 나은 비율 (전체 48개, LPIPS)02550751001700파지 실패83170이송 중 낙하100250전도 / 무너뜨림7500성공 배치 (대조)N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)%
Figure 1: 모드별 beats-copy 비율. 낙하·전도·성공에서는 N=2가 관성을 압도하지만 파지 실패에서는 못 이긴다 — 그 모드는 "아무 일도 안 일어남"이 정답이라 관성이 강한 베이스라인이기 때문이다.
gap < 0.05인 에피소드 0개. 코호트 48개 전부가 판정 청크에서 실제로 움직인다 → v3에 남아 있던 "저변화 실패" 교란은 이 코호트에선 성립하지 않는다.

4 결과 — 사건 단위 판정 (31/48)

손목 카메라 원본 해상도(640×360) 3시점 × 5행(실제·N=2·N=4·open-loop·copy) strip으로 판정. 라벨 두 개를 분리해 기록했다 — event(이 에피소드의 물리 사건 자체를 그렸는가)와 verdict(정답 모르고 예측 영상만 보고 내릴 결론). 예측이 이유가 틀린 채로 맞히는 경우가 실제로 있었기 때문이다.

모드N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)copy (관성)
이송 중 낙하7/8 (88%) +4?1/7 (14%) +5?0/11 (0%) +1?2/12 (17%)
전도 / 무너뜨림7/10 (70%) +2?6/11 (55%) +1?0/12 (0%)0/12 (0%)
성공 배치 (대조)6/7 (86%)1/5 (20%) +2?0/7 (0%)0/7 (0%)
사건 재현율 — 예측이 실제 물리 사건 자체를 그렸는가02550751008814017이송 중 낙하705500전도 / 무너뜨림862000성공 배치 (대조)N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)copy (관성)%
Figure 2: 사건 재현율. 회색이 관성 베이스라인 — 낙하·전도·성공 어디서도 사건을 그리지 못한다.

실행 전 검증기 관점

조건recall (실패 탐지)specificity (성공 통과)balanced accscene intact/drift/corrupt
N=2 (1.1–2.1s)18/18 = 100%6/7 = 86%93%25 / 5 / 1
N=4 (3.2–4.3s)18/18 = 100%1/5 = 20%60%13 / 15 / 3
open-loop (7.5–8.5s)17/18 = 94%0/7 = 0%47%0 / 7 / 24
copy (관성 베이스라인)24/24 = 100%0/7 = 0%50%31 / 0 / 0
실행 전 검증기 성능 — 판별력은 recall이 아니라 specificity에서 갈린다02550751001008693N=2 (1.1–2.1s)1002060N=4 (3.2–4.3s)94047open-loop (7.5–8.5s)100050copy (관성)recall (실패 탐지)specificity (성공 통과)balanced accuracy%
Figure 3: 실패 recall은 관성도 100%를 낸다 — "아직 안 끝났음"을 그리면 실패는 자동으로 맞기 때문. 조건을 가르는 것은 성공을 성공으로 통과시키는 능력이다.
낙관 오판은 사실상 없다. 31건 × 4조건 = 124건 중 실패를 성공으로 그린 사례는 1건(Mustard_env6 open-loop)뿐. 편향 방향은 v3와 같은 under-commit(계속 쥐고 있음)이다.

5 대표 사례 (영상)

영상 읽는 법 — 6칸 배치. 윗줄 실제 sim · WM N=2 · WM N=4, 아랫줄 WM open-loop · copy(관성 베이스라인) · 정보. 각 칸은 모델이 실제로 보는 composite(위 손목 / 아래 외부 2뷰)다. 패널 헤더 아래 파란 눈금이 그 조건이 실제 프레임을 다시 받은 지점, 하단 타임라인의 노란 EVENT 선이 실제로 사건이 일어난 프레임(그 전후 8프레임 동안 패널 테두리가 노랗게 켜진다). 하단 색 띠는 모델에 들어간 gripper 명령(열림/닫힘). copy 칸이 끝까지 물체를 쥐고 있으면, 그보다 나아간 칸만 실제로 예측을 한 것이다.
cfgoff_RubiksCubeBehindBowlTask_run0_env0 · FAIL 이송 중 낙하
실제는 큐브를 그릇에서 꺼내 테이블에 놓는다. N=2는 그대로 재현하고, N=4는 큐브를 그릇 안에 남겨두며, open-loop에서는 큐브가 소멸한다.
cfgoff_StackWhiteMugsTask_run0_env2 · FAIL 전도 / 무너뜨림
머그가 옆으로 넘어지는 전도 사건. N=2와 N=4 모두 넘어뜨린다 — 전도는 낙하보다 horizon에 강했다.
cfgoff_BBQSauceInBinTask_run0_env0 · FAIL 이송 중 낙하
병이 그리퍼 밖으로 쓰러진다. N=2만 재현하고 N=4/open-loop는 계속 세워 쥐고 있다 (under-commit).
cfgoff_FruitsGreenLimesOnPlateTask_run0_env5 · FAIL 이송 중 낙하
실패 사례: 실제로는 라임을 놓치는데 모든 조건이 계속 쥔다. LPIPS로는 N=2가 copy를 이기지만(0.318 vs 0.377) 사건은 재현하지 못한다.

6 전체 갤러리 — 48 에피소드

모드별 전체. 배지는 N=2가 사건을 재현했는지에 대한 육안 판정(31/48 실시), 옆의 수치는 LPIPS gap(실제가 얼마나 움직였나) → N=2 d_post(예측이 실제와 얼마나 다른가)이며 d_post < gap이면 관성 베이스라인을 이긴 것이다. 재생을 누르기 전엔 다운로드되지 않는다.

파지 실패 (12개)

육안 판정 미실시 GrabABagelTask env1 · FAIL · gap 0.09 → N=2 0.21
육안 판정 미실시 GrabABagelTask env3 · FAIL · gap 0.30 → N=2 0.36
육안 판정 미실시 GrabABagelTask env4 · FAIL · gap 0.11 → N=2 0.22
육안 판정 미실시 OneBottleOnShelfTask env8 · FAIL · gap 0.30 → N=2 0.38
육안 판정 미실시 PutBowlOnShelfTopTask env1 · FAIL · gap 0.14 → N=2 0.20
육안 판정 미실시 ReorientRedMugTask env2 · FAIL · gap 0.24 → N=2 0.20 (beats copy)
육안 판정 미실시 ReorientRedMugTask env5 · FAIL · gap 0.14 → N=2 0.26
육안 판정 미실시 SmallPumpkinInBinTask env3 · FAIL · gap 0.26 → N=2 0.26 (beats copy)
육안 판정 미실시 ToolsPickingDrillTask env2 · FAIL · gap 0.12 → N=2 0.29
육안 판정 미실시 SmallPumpkinInBinTask env6 · FAIL · gap 0.17 → N=2 0.24
육안 판정 미실시 ToolsPickingAllHammersTask env4 · FAIL · gap 0.14 → N=2 0.22
육안 판정 미실시 ToolsPickingDrillTask env3 · FAIL · gap 0.26 → N=2 0.39

이송 중 낙하 (12개)

N=2 재현 BBQSauceInBinTask env0 · FAIL · gap 0.26 → N=2 0.29
N=2 재현 BananaThenRubiksCubeTask env5 · FAIL · gap 0.37 → N=2 0.27 (beats copy)
N=2 판정불가 BlackItemsInBinTask env2 · FAIL · gap 0.22 → N=2 0.21 (beats copy)
N=2 재현 CleanUpToysTask env0 · FAIL · gap 0.32 → N=2 0.23 (beats copy)
N=2 판정불가 DishesInBinTask env0 · FAIL · gap 0.41 → N=2 0.43
N=2 재현 FoodPacking1CansTask env3 · FAIL · gap 0.35 → N=2 0.30 (beats copy)
N=2 놓침 FruitsGreenLimesOnPlateTask env5 · FAIL · gap 0.38 → N=2 0.32 (beats copy)
N=2 판정불가 FruitsOnionToPlateTask env1 · FAIL · gap 0.28 → N=2 0.26 (beats copy)
N=2 재현 MustardInRightBinTask env6 · FAIL · gap 0.49 → N=2 0.30 (beats copy)
N=2 재현 RubiksCubeBehindBowlTask env0 · FAIL · gap 0.34 → N=2 0.29 (beats copy)
N=2 재현 ClampInRightBinTask env0 · FAIL · gap 0.32 → N=2 0.29 (beats copy)
N=2 판정불가 GreenSpoonsInPotTask env1 · FAIL · gap 0.28 → N=2 0.25 (beats copy)

전도 / 무너뜨림 (12개)

N=2 재현 BowlStackingRightOnLeftTask env1 · FAIL · gap 0.40 → N=2 0.21 (beats copy)
N=2 재현 BowlStackingRightOnLeftTask env7 · FAIL · gap 0.36 → N=2 0.28 (beats copy)
N=2 재현 ButterAboveRaisinTask env3 · FAIL · gap 0.25 → N=2 0.22 (beats copy)
N=2 놓침 ButterAboveRaisinTask env9 · FAIL · gap 0.27 → N=2 0.25 (beats copy)
N=2 판정불가 OneBottleOnShelfTask env0 · FAIL · gap 0.33 → N=2 0.22 (beats copy)
N=2 재현 OneBottleOnShelfTask env1 · FAIL · gap 0.27 → N=2 0.22 (beats copy)
N=2 재현 PutBowlOnShelfTopTask env0 · FAIL · gap 0.35 → N=2 0.25 (beats copy)
N=2 놓침 PutBowlOnShelfTopTask env2 · FAIL · gap 0.35 → N=2 0.21 (beats copy)
N=2 판정불가 PutMugsOnShelfTask env0 · FAIL · gap 0.27 → N=2 0.23 (beats copy)
N=2 재현 PutMugsOnShelfTask env1 · FAIL · gap 0.25 → N=2 0.21 (beats copy)
N=2 놓침 RecycleCartonsVerticalCrateTask env0 · FAIL · gap 0.43 → N=2 0.42 (beats copy)
N=2 재현 StackWhiteMugsTask env2 · FAIL · gap 0.37 → N=2 0.26 (beats copy)

성공 배치 (대조) (12개)

N=2 재현 BananaThenRubiksCubeTask env8 · SUCCESS · gap 0.25 → N=2 0.25 (beats copy)
육안 판정 미실시 GreenSpoonsInPotTask env2 · SUCCESS · gap 0.15 → N=2 0.15
육안 판정 미실시 PutMugsOnShelfTask env5 · SUCCESS · gap 0.18 → N=2 0.20
N=2 재현 ReorientRedMugTask env9 · SUCCESS · gap 0.24 → N=2 0.18 (beats copy)
육안 판정 미실시 RubiksCubeRightOfBowlTask env4 · SUCCESS · gap 0.22 → N=2 0.22 (beats copy)
육안 판정 미실시 RubiksCubeTask env1 · SUCCESS · gap 0.24 → N=2 0.24 (beats copy)
N=2 놓침 WoodSpatulaToBowlTask env0 · SUCCESS · gap 0.20 → N=2 0.16 (beats copy)
N=2 재현 FoodPacking1CansTask env1 · SUCCESS · gap 0.35 → N=2 0.33 (beats copy)
N=2 재현 FruitsGreenLimesOnPlateTask env5 · SUCCESS · gap 0.34 → N=2 0.19 (beats copy)
N=2 재현 FruitsOnionToPlateTask env8 · SUCCESS · gap 0.40 → N=2 0.33 (beats copy)
육안 판정 미실시 RubiksCubeBehindBowlTask env9 · SUCCESS · gap 0.07 → N=2 0.15
N=2 재현 UtensilsInMugTask env0 · SUCCESS · gap 0.20 → N=2 0.16 (beats copy)

7 추가 코호트 — 실제 블록 타워 쌓기

v4 코호트의 구멍. full-120 벤치마크는 30태스크짜리 샤드 4개로 나뉘어 있는데 v1–v4 파이프라인은 full0 쌍만 하드코딩해 읽고 있었다 → 30/120 태스크만 봤고, literal 블록 타워 쌓기(BlockStacking*, Stack3RubiksCube, StackYellowOnRed)는 전부 다른 샤드에 있어 한 건도 코호트에 들어가지 않았다. 4샤드 전부 읽도록 고치니 분류 풀이 232 → 812로 늘었다.

타워 쌓기 전용 코호트 24개(실패 16 + 성공 8, 5개 태스크)를 다시 잘라 같은 파이프라인으로 돌렸다 (jobs 86171/86172/86173). 실패 16건은 모두 실제 릴리즈 사건이 기록된 에피소드다. 참고로 BlockStackingSpecifiedOrderTask20전 20패 — 정책이 한 번도 성공하지 못했다.

사건 재현율 (육안 판정 15/24)

모드N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)copy (관성)
타워 붕괴 / 배치 실패10/10 (100%) +2?4/8 (50%) +4?0/12 (0%)1/12 (8%)
성공 스택 (대조)2/2 (100%) +1?0/3 (0%)0/3 (0%)3/3 (100%)
타워가 무너지는 것을 N=2는 판정 가능한 10건 전부에서 그렸다 (10/10). 관성 베이스라인은 12건 중 1건. LPIPS beats-copy도 같은 방향으로 N=2 13/16 · N=4 1/16 · open-loop 0/16.
이 코호트에서는 detector 수치를 쓰지 말 것. 쌓기 성공은 sim이 접촉 순간 판정을 내고 에피소드를 끊기 때문에 판정 청크에 들어올 때 이미 타워가 서 있다 → 관성도 성공을 100% 맞힌다. 실행 전 검증기 성능은 릴리즈 사건이 남아 있는 §4 코호트 수치가 정본이다.
cfgoff_Stack3RubiksCubeTask_run0_env1 · FAIL 전도 / 무너뜨림
실제로는 위 큐브가 기울어 떨어진다. N=2가 넘어가는 것까지 그대로 그린다. N=4는 위 큐브를 장면에서 아예 지워버리고, open-loop는 붕괴.
cfgoff_BlockStackingOrderAgnosticTask_run0_env0 · FAIL 전도 / 무너뜨림
노란 블록이 빨간 블록 위에 얹혔다가 무너져 분리된다. N=2·N=4 모두 분리를 그리고, copy는 끝까지 쌓인 채다.
cfgoff_BlockStackingSpecifiedOrderTask_run0_env3 · FAIL 전도 / 무너뜨림
빨간 블록이 스택에서 떨어진 곳에 놓인다(순서 실패). N=4는 계속 쥐고 있고 나머지 블록을 장면에서 잃는다.
cfgon_BlockStackingOrderAgnosticTask_run0_env1 · SUCCESS 전도 / 무너뜨림
성공 대조: 3단 타워가 선다. N=2는 타워를 조립하지만 N=4는 초록 블록을 공중에 띄운 채 끝낸다.

전체 24개

N=2 재현 BlockStackingOrderAgnosticTask env0 · FAIL · gap 0.21 → N=2 0.17 (beats copy)
육안 판정 미실시 BlockStackingOrderAgnosticTask env3 · FAIL · gap 0.37 → N=2 0.26 (beats copy)
N=2 판정불가 BlockStackingOrderAgnosticTask env4 · FAIL · gap 0.15 → N=2 0.18
N=2 재현 BlockStackingOrderAgnosticTask env5 · FAIL · gap 0.14 → N=2 0.16
N=2 재현 BlockStackingOrderAgnosticTask env6 · FAIL · gap 0.17 → N=2 0.16 (beats copy)
N=2 재현 BlockStackingSpecifiedOrderTask env0 · FAIL · gap 0.14 → N=2 0.13 (beats copy)
육안 판정 미실시 BlockStackingSpecifiedOrderTask env1 · FAIL · gap 0.21 → N=2 0.16 (beats copy)
N=2 재현 BlockStackingSpecifiedOrderTask env2 · FAIL · gap 0.20 → N=2 0.19 (beats copy)
N=2 재현 BlockStackingSpecifiedOrderTask env3 · FAIL · gap 0.33 → N=2 0.28 (beats copy)
육안 판정 미실시 BlockStackingSpecifiedOrderTask env4 · FAIL · gap 0.16 → N=2 0.15 (beats copy)
N=2 재현 BowlStackingLeftOnRightTask env1 · FAIL · gap 0.45 → N=2 0.30 (beats copy)
N=2 재현 BowlStackingLeftOnRightTask env3 · FAIL · gap 0.37 → N=2 0.21 (beats copy)
육안 판정 미실시 BowlStackingLeftOnRightTask env6 · FAIL · gap 0.32 → N=2 0.28 (beats copy)
N=2 재현 BowlStackingLeftOnRightTask env7 · FAIL · gap 0.29 → N=2 0.23 (beats copy)
N=2 재현 Stack3RubiksCubeTask env1 · FAIL · gap 0.19 → N=2 0.19 (beats copy)
N=2 판정불가 StackYellowOnRedTask env2 · FAIL · gap 0.35 → N=2 0.36
육안 판정 미실시 BlockStackingOrderAgnosticTask env2 · SUCCESS · gap 0.08 → N=2 0.14
육안 판정 미실시 BowlStackingLeftOnRightTask env5 · SUCCESS · gap 0.30 → N=2 0.28 (beats copy)
육안 판정 미실시 BowlStackingLeftOnRightTask env9 · SUCCESS · gap 0.10 → N=2 0.12
N=2 판정불가 StackYellowOnRedTask env1 · SUCCESS · gap 0.19 → N=2 0.14 (beats copy)
N=2 재현 BlockStackingOrderAgnosticTask env1 · SUCCESS · gap 0.16 → N=2 0.15 (beats copy)
육안 판정 미실시 BlockStackingOrderAgnosticTask env3 · SUCCESS · gap 0.04 → N=2 0.13
육안 판정 미실시 BowlStackingLeftOnRightTask env0 · SUCCESS · gap 0.36 → N=2 0.35 (beats copy)
N=2 재현 Stack3RubiksCubeTask env2 · SUCCESS · gap 0.18 → N=2 0.15 (beats copy)

8 사건 앵커 재정의 — 그리퍼 명령에서 접촉으로

§1–§7이 사건을 찾던 방식은 trace의 그리퍼 명령 채널 하나뿐이었다. executed_action[:,7]의 close/open edge로 파지 사이클을 만들고 특정 사이클의 open 스텝을 사건으로 삼았다 — 영상도 물체 상태도 보지 않는다. 그건 로봇의 의도이지 물리적 사건이 아니다. 측정해 보면 실제 화면 변화 피크가 그 지점 대비 −65 ~ +134스텝(최대 9초)까지 어긋나고, 팔이 지나가다 타워를 치는 충돌은 그리퍼 이벤트가 아예 없어서 못 잡는다 — §7 코호트가 실제로 잡은 것은 "쳐서 무너뜨림"이 아니라 "놓았는데 안 버팀"이었다.

접촉 검출기 — 세 번 고쳐서 확정

1) 관절 추종오차 |achieved − commanded| 스파이크 → 원시 오차는 명령 속도와 상관 0.44–0.75. 속도 비례 성분을 최소자승으로 제거 후 robust z. → z≥3.0으로 뽑아 영상 검증: 6개 중 1개가 −32~+56프레임 내내 무변화. 팔은 저항받았지만 물체는 안 움직임. 2) + 화면 변화가 에피소드 중앙값의 3배 이상일 것 → 여전히 부족. 팔이 정적 카메라 화면을 가로지르는 것만으로 변화가 크다. 3) + 화면 변화에서 팔 움직임을 회귀로 제거 ← 확정 → EE 속도·관절 속도가 화면 변화의 R² 0.55–0.75를 설명한다. 그 잔차의 robust z를 게이트로 사용. → 무변화 에피소드 objZ = −0.0, 진짜 사건 objZ = 4.0–8.0 으로 분리. 최종: 첫 gripper close 이후 · 그리퍼 명령 ±10프레임 밖 · 추종오차 잔차 z≥3.0 · 이후 32프레임 안에 팔로 설명 안 되는 화면 변화 z≥3.0 → 이를 처음 만족하는 스텝

윈도우도 사건 앞 8.5초 + 뒤 4.3초로 늘려 무너지는 과정이 끝까지 들어간다(§2의 8청크 → 12–13청크).

같이 잡은 판정 구간 버그. LPIPS가 윈도우의 마지막 청크를 판정하고 있었는데, 청크가 재접지되는지는 chunk_index % N으로 정해지므로 윈도우 길이에 따라 여러 조건이 판정 청크에서 동시에 재접지된다 — 13청크에서 청크 12는 2로도 4로도 나눠떨어져 N=2와 N=4가 둘 다 16/16을 찍었다. 실력이 아니라 정렬 때문. 판정 구간을 사건 이후 전 구간으로 바꿨다. §3–§7은 꼬리가 없어(post=0) 자동으로 마지막 청크와 같아지므로 수치는 그대로다.

앵커별 비교 — beats-copy (타워 쌓기 실패 16개)

앵커gap (중앙값)N=2 (1.1–2.1s)N=4 (3.2–4.3s)open-loop (7.5–8.5s)
그리퍼 명령 앵커 (v5)0.21413/161/160/16
접촉 앵커 · 접촉 프레임 봄 (v6)0.33016/1614/160/16
접촉 앵커 · 접촉 프레임 안 봄 (v6b)0.32516/1615/160/16
접촉 프레임을 모델에 주든 안 주든 결과가 같다 (N=4: 14/16 vs 15/16). 즉 N=4가 1/16 → 15/16으로 뛴 것은 leakage가 아니라 앵커가 제대로 잡혔기 때문이다. 그리퍼 앵커에서는 판정 시점에 사건이 아직 안 일어났거나 이미 지나가 있어서, 모델이 무엇을 맞혀야 하는지 자체가 정의되지 않았던 것. gap 중앙값이 0.212 → 0.321로 오른 것이 같은 사실의 다른 표현이다.

대표 사례 (접촉 프레임 미공개 조건, gap 상위 3)

캡션의 수치는 전부 측정값이다 — 육안 사건 판정은 이 코호트에서 아직 하지 않았다.

StackYellowOnRedTask env2 · 접촉 step 308 (추종오차 z=3.3, 물체움직임 z=4.6)
gap 0.38 → every2 0.17 · every4 0.27 · open_loop 0.49 · N=2 beats copy
BlockStackingOrderAgnosticTask env0 · 접촉 step 395 (추종오차 z=3.7, 물체움직임 z=4.8)
gap 0.36 → every2 0.20 · every4 0.27 · open_loop 0.50 · N=2 beats copy
BlockStackingOrderAgnosticTask env2 · 접촉 step 558 (추종오차 z=3.2, 물체움직임 z=5.6)
gap 0.35 → every2 0.21 · every4 0.27 · open_loop 0.47 · N=2 beats copy

전체 16개

StackYellowOnRedTask env2 · 접촉 step 308 (추종오차 z=3.3, 물체움직임 z=4.6)
gap 0.38 → every2 0.17 · every4 0.27 · open_loop 0.49 · N=2 beats copy
BlockStackingOrderAgnosticTask env0 · 접촉 step 395 (추종오차 z=3.7, 물체움직임 z=4.8)
gap 0.36 → every2 0.20 · every4 0.27 · open_loop 0.50 · N=2 beats copy
BlockStackingOrderAgnosticTask env2 · 접촉 step 558 (추종오차 z=3.2, 물체움직임 z=5.6)
gap 0.35 → every2 0.21 · every4 0.27 · open_loop 0.47 · N=2 beats copy
Stack3RubiksCubeTask env7 · 접촉 step 233 (추종오차 z=3.2, 물체움직임 z=5.0)
gap 0.35 → every2 0.24 · every4 0.30 · open_loop 0.60 · N=2 beats copy
BlockStackingOrderAgnosticTask env4 · 접촉 step 952 (추종오차 z=3.3, 물체움직임 z=4.5)
gap 0.34 → every2 0.16 · every4 0.20 · open_loop 0.51 · N=2 beats copy
BlockStackingSpecifiedOrderTask env8 · 접촉 step 440 (추종오차 z=3.1, 물체움직임 z=8.0)
gap 0.34 → every2 0.19 · every4 0.31 · open_loop 0.59 · N=2 beats copy
BlockStackingOrderAgnosticTask env0 · 접촉 step 975 (추종오차 z=3.6, 물체움직임 z=3.9)
gap 0.33 → every2 0.16 · every4 0.22 · open_loop 0.49 · N=2 beats copy
StackYellowOnRedTask env6 · 접촉 step 232 (추종오차 z=3.5, 물체움직임 z=3.7)
gap 0.32 → every2 0.18 · every4 0.25 · open_loop 0.55 · N=2 beats copy
BlockStackingSpecifiedOrderTask env6 · 접촉 step 235 (추종오차 z=3.3, 물체움직임 z=5.7)
gap 0.32 → every2 0.18 · every4 0.25 · open_loop 0.51 · N=2 beats copy
BlockStackingOrderAgnosticTask env6 · 접촉 step 660 (추종오차 z=3.3, 물체움직임 z=4.6)
gap 0.30 → every2 0.16 · every4 0.23 · open_loop 0.44 · N=2 beats copy
Stack3RubiksCubeTask env9 · 접촉 step 334 (추종오차 z=3.1, 물체움직임 z=3.7)
gap 0.30 → every2 0.19 · every4 0.23 · open_loop 0.56 · N=2 beats copy
BowlStackingLeftOnRightTask env0 · 접촉 step 221 (추종오차 z=3.1, 물체움직임 z=5.1)
gap 0.30 → every2 0.17 · every4 0.25 · open_loop 0.55 · N=2 beats copy
BlockStackingSpecifiedOrderTask env5 · 접촉 step 217 (추종오차 z=4.1, 물체움직임 z=4.6)
gap 0.28 → every2 0.22 · every4 0.31 · open_loop 0.47 · N=2 beats copy
BlockStackingSpecifiedOrderTask env4 · 접촉 step 172 (추종오차 z=3.7, 물체움직임 z=5.3)
gap 0.28 → every2 0.16 · every4 0.23 · open_loop 0.55 · N=2 beats copy
BlockStackingSpecifiedOrderTask env4 · 접촉 step 882 (추종오차 z=3.0, 물체움직임 z=6.5)
gap 0.26 → every2 0.19 · every4 0.23 · open_loop 0.42 · N=2 beats copy
StackYellowOnRedTask env4 · 접촉 step 605 (추종오차 z=3.8, 물체움직임 z=3.8)
gap 0.25 → every2 0.15 · every4 0.18 · open_loop 0.35 · N=2 beats copy
이 코호트에 없는 것. 육안 사건 판정 미실시(객관 지표만)이고, 성공 대조군이 없다 — 쌓기 성공은 sim이 접촉 순간 에피소드를 끊어서 4.3초 꼬리를 만들 수 없다. 따라서 실행 전 검증기 성능(balanced accuracy)은 §4 코호트가 정본이고, 이 섹션은 앵커 정확도에 대한 결과로만 읽어야 한다.

9 Takeaway

재접지 1–2초 조건에서는 실행 전 검증기로 쓸 수 있다

핵심은 recall이 아니라 specificity다. 실패 recall은 관성 베이스라인도 100%를 낸다. 구분은 성공을 성공으로 통과시키는가에서 갈리고, 여기서 N=2는 86%, 관성은 0%다.

지목된 세 실패 모드 중 둘은 재현된다. 이송 중 낙하 88%, 전도/무너뜨림 70%. 반면 파지 실패는 이 프레임워크로 측정할 수 없다 — 정답 자체가 "아무 일도 안 일어남"이라 관성 예측이 곧 정답이고(gap 0.155로 최소), N=2가 관성을 이긴 건 12건 중 2건뿐이다. 측정하려면 그리퍼–물체 접촉을 직접 재야 하고, 그건 sim 재실행으로 물체 pose를 로깅해야 가능하다.

horizon이 진짜 제약이다. 3.2–4.3초에서 이미 낙하 재현이 14%로 무너지고 specificity가 20%로 떨어진다. open-loop는 장면 무결성이 0/31다. v3의 "2–4초 재접지" 권고는 1–2초로 좁혀야 한다.

두 지표가 서로 다른 것을 잰다. FruitsGreenLimes_env5는 LPIPS로는 N=2가 관성을 이기지만 (0.318 vs 0.377) 라임을 놓치는 사건은 재현하지 못한다. 픽셀 근접과 사건 예측은 별개이며, 검증기로 쓸 거라면 사건 단위 지표를 봐야 한다.

10 Next Steps

물체 pose 로깅용 sim 재실행 — grasp_miss 측정에 필수이고, drop 휴리스틱 정밀도(영상 검증 8/12; 오분류 4건은 전부 빈 그리퍼가 15cm 이상 이동 후 열림)도 이걸로 해결된다.
판정 전수화 — 현재 육안 31/48. VLM judge를 붙여 232개 분류 풀 전체로 확장.
접촉 코호트 육안 판정 + 성공 대조군 — §8은 객관 지표만 있다. 성공 대조군은 sim이 접촉 순간 에피소드를 끊어 4.3초 꼬리를 못 만드는 문제부터 풀어야 한다(성공 판정 후 N프레임 더 굴리도록 sim 재실행).
N=3 지점 보강 — 낙하 재현이 88% → 14%로 무너지는 구간을 좁힌다.
seed ensemble — 오경보 1건(WoodSpatula: 실제는 주걱을 그릇에 눕히는데 계속 쥠)과 방향 오류 1건(PutBowlOnShelfTop_env2: 안 넘어진 그릇을 넘어뜨림)이 앙상블로 걸러지는지 확인.

11 재현

# 1) 코호트 선정 (login, CPU) packages/cosmos3/.venv/bin/python scripts/fd_replay_modes.py --pool-out tmp/fd_replay_v4/pool.json # 2) prep — --out은 반드시 절대경로 (manifest 경로가 워커에서 해석됨) packages/cosmos3/.venv/bin/python scripts/fd_replay_prep.py \ --cohort tmp/fd_replay_v4/cohort.json --out $PWD/tmp/fd_replay_v4 # 3) 추론 3조건 (1 GPU each, ~18분). compile.enabled=True → gcc 있는 이미지 필수 V4=$PWD/tmp/fd_replay_v4; IMG=nvcr.io/nvidia/cuda:12.8.0-devel-ubuntu22.04 sbm "FD_REPLAY_ROOT=$V4 bash scripts/run_fd_replay.sh every2 --reground-every 2" \ --qos=own --gres=gpu:1 -c 8 --mem 200GB --time=02:00:00 --container=$IMG # 4) 지표 + 시트 + 리포트 CUDA_VISIBLE_DEVICES=7 packages/cosmos3/.venv/bin/python scripts/fd_replay_v4_lpips.py packages/cosmos3/.venv/bin/python scripts/fd_replay_event_sheet.py --mode strip \ --views wrist --cell-width 640 --offsets 96 112 128 --persistence 2 --suffix _w packages/cosmos3/.venv/bin/python scripts/fd_replay_v4_metrics.py # 접촉 앵커 (§8): 사건 = 첫 unintended contact, 윈도우 = 앞 9청크 + 뒤 4청크 packages/cosmos3/.venv/bin/python scripts/fd_replay_modes.py --anchor contact --pre-chunks 9 --post-chunks 4 --tasks BlockStacking... --per-mode stack_disturb=16 --out tmp/fd_replay_v6b_noleak/cohort.json packages/cosmos3/.venv/bin/python scripts/fd_replay_v4_video.py # 48 comparison videos packages/cosmos3/.venv/bin/python scripts/fd_replay_v4_report.py