← 목록
2026-08-05 · X-WAM · Progress

DROID world-frame pointmap 데이터 파이프라인 구축

depth → pointmap A/B 실험용 학습 데이터 — 정합 검증 · 정규화 확정 · 미니 데이터셋 생성

TL;DR

2.3cm
exterior 정합
0mm
wrist 마운트 std
6.2mm
pointmap 양자화
maxΔ=0
mp4 무손실
4,702 2,469
사용 가능 scene (260805 재정정)

1배경 — 왜 pointmap이고 왜 DROID인가

목표는 X-WAM을 depth 대신 world-frame pointmap으로 학습해 효과를 재는 것입니다. 앞선 조사(260731·260803)에서 확인한 X-WAM depth의 결함이 동기입니다:

world-frame pointmap이 이 둘을 동시에 없앱니다. XYZ는 metric이고 3뷰가 하나의 좌표계를 공유하므로, 정규화만 고정 box로 하면 뷰별 스케일 얽힘과 복원 불가가 구조적으로 사라집니다. 그리고 아키텍처 변경이 없습니다 — X-WAM은 depth를 이미 3채널 pseudo-RGB로 RGB와 같은 VAE에 통과시키므로, XYZ 3채널은 shape 동일한 drop-in입니다.

DROID를 택한 이유: base부터 학습(사전학습 prior 없이)하는 설계라 데이터 양이 성패를 가르고, 실사 GT depth(Zed) + 실제 calibration이 pointmap의 근거를 강화합니다. 평가는 이미 연동돼 있는 RoboLab(Franka)로 도메인을 맞춥니다.

2정합 검증 — 통과

① 3뷰 world-frame 정합 (GT depth)

pair@20%@50%@80%
ext1 ↔ ext23.05 cm3.46 cm4.20 cm
ext1 ↔ wrist8.5612.3213.16
ext2 ↔ wrist11.1112.7916.43

workspace 박스로 자르면 exterior 정합은 2.3 cm까지 좁혀집니다. depth clip을 8 m→1.2 m로 바꿔도 정합은 2.69~3.05 cm로 안정적 — 즉 캘리브는 정상이고 원거리 배경이 공간 extent만 부풀립니다(두 exterior가 서로 다른 방향을 봐 방 전체가 들어옴).

DROID world-frame pointmap fused
workspace 박스 기준 3뷰 fused pointmap. "oblique (by camera)"에서 빨강(ext1)·초록(ext2)이 같은 테이블 평면을 공유하며 맞물림. 파랑(wrist)은 그리퍼 주변 소영역.

② wrist extrinsic — overlap과 무관한 판정

wrist↔exo가 느슨한 것이 캘리브 오류인지 overlap 부족인지 가리기 위해, wrist 카메라가 로봇 eef를 따라가는지를 확인했습니다(강체 마운트라면 eef 좌표계에서 본 카메라 위치가 상수여야 함).

eef 좌표계에서 본 wrist 카메라 위치 mean = [-0.0795, 0.0138, 0.0307] m std = [0, 0, 0] mm ← 완벽한 강체 일관 (카메라 41.2 cm / eef 40.0 cm 이동하는 동안)
표준편차가 정확히 0 → wrist extrinsic 신뢰 가능. 느슨한 수치는 overlap 부족입니다(wrist z 범위가 0.07~0.40 m로 좁아 그리퍼만 봄). 시간이 갈수록 나빠지는 패턴(8.6→12.3→13.2 cm)이 z 범위 축소(0.40→0.32→0.23 m)와 정확히 대응합니다. RoboCasa에서 완벽한 sim GT로도 exo↔wrist가 17.7 cm였던 것과 같은 현상입니다.

3정규화 범위 확정 — 고정 box B

24 scene × 3 frame의 world XYZ 분포를 낸 뒤 후보 박스별 커버리지/양자화를 비교했습니다.

박스 (robot-base, m)ALLext1ext2wristmm/level
A tight x[-0.2,1.0] y[±0.6] z[-0.2,0.8]59%45%41%93%4.7/4.7/3.9
B mid x[-0.4,1.2] y[±0.8] z[-0.4,1.0]68%56%52%95%6.2/6.2/5.5
C wide x[-0.6,1.6] y[±1.2] z[-0.6,1.2]74%65%61%97%8.6/9.4/7.0
D huge x[-1.0,2.0] y[±2.0] z[-1.0,1.5]83%77%75%99%11.7/15.6/9.8

B를 선택했습니다. 양자화 6.2 mm는 depth 방식(11.5 mm)보다 우수하고, wrist는 95% 유지됩니다. exterior에서 버려지는 44~48%는 방 벽·바닥이라 조작 과제에 정보가 거의 없고, 오히려 모델 capacity를 workspace에 집중시킵니다.

핵심은 이 박스가 전 scene·전 뷰에 고정이라는 점입니다. 복원이 정확히 역산됩니다:
v = lo + (px − 1)/254 × (hi − lo) — X-WAM의 "min/max 미저장 → 복원 불가" 문제가 사라집니다.

4⚠️ 발견한 함정 — mp4 기본 인코딩이 pointmap을 파괴

XYZ 3채널을 mp4에 쓸 때 h264 크로마 서브샘플링이 Y·Z 채널을 망깁니다. 합성 pointmap으로 라운드트립을 측정했습니다.

인코딩maxΔmeanΔinvalid sentinel 정확도용량
libx264 yuv420p (imageio 기본)1478.6851%0.2 MB
libx264 yuv444p -qp 020.33999.93%0.65 MB
ffv1 (기본 pixfmt)1305.9554%0.5 MB
libx264rgb -qp 0 (rgb24)00.0000100%0.87 MB
ffv1 gbrp / rgb2400.0000100%1.18 MB
기본값을 그대로 썼다면 pointmap 타깃이 조용히 손상된 채로 학습됐을 것입니다. yuv444p로 올려도 RGB↔YUV 변환 반올림 때문에 Δ2가 남습니다. libx264rgb -qp 0은 RGB를 그대로 저장해 완전 무손실이면서 용량도 최소이고 .mp4 컨테이너를 유지합니다.

참고로 X-WAM 릴리스 데이터셋의 depth mp4도 그레이스케일(R=G=B)이라 이 문제를 피했지만, 3채널 pointmap에는 그대로 적용되지 않습니다.

5데이터 파이프라인 (전부 로컬, 읽기 전용 소스)

요소소스비고
RGBraw recordings/MP4/<serial>.mp41280×720 → 320×180. 프레임 수가 PointWorld depth와 정확히 일치(132=132) → 인덱스 1:1
depthPointWorld depth_320x180/<uuid>_depth.h5uint16 mm, 0=invalid, valid 79~93%
extrinsicraw trajectory.h5 (133 프레임)프레임별 (wrist 필수). 13/13 lab 보유
proprioraw trajectory.h5 robot_statecartesian_position(6)+gripper → 16-dim(우팔 0패딩)
actionraw trajectory.h5 actioncartesian_velocity(6)+gripper → raw_actions 7/arm
intrinsicraw trajectory_im128.h5CLVR만 채워져 있음 — 규모 병목 (§6)
UUID joinPointWorld cameras.jsonscene_path로컬 raw 경로로 직접 매핑, 3000/3000 존재 확인

프레임 동기화

PointWorld depth(132)와 raw h5(133 또는 114)의 길이가 다르지만 같은 epoch-ms 클럭에 ~15 fps입니다. 겹치는 구간만 쓰고 최근접 매칭하면 Δt 25~41 ms(67 ms 간격의 절반 이하 → 카메라 이동 ~2 mm 오차, depth 노이즈 cm 단위 대비 무시 가능).

인코딩 규약

valid : px = 1 + round(norm × 254) norm = (v − lo)/(hi − lo) invalid : px = 0 (depth≤0, 범위 밖, box 밖) 복원 : v = lo + (px − 1)/254 × (hi − lo) 정확히 역산 pointmap : box B (전 scene·전 뷰 고정) depth 대조군 : 고정 [0.05, 3.0] m 선형 ← Cosmos Policy와 동일 철학 (A/B 대칭)

6정정 — 규모는 29k가 아니라 4,702

앞서 "8개 lab / 29,091 scene 사용 가능"이라고 보고했는데 틀렸습니다. trajectory_im128.h5 파일 존재만 확인했고 내용을 안 봤습니다. 실제로는 CLVR만 intrinsics 그룹이 채워져 있고 나머지 lab은 비어 있습니다.
소스exterior intrinsicwrist intrinsic
PointWorld flows h5✅ 42,935 전부❌ 없음
raw trajectory_im128.h5CLVR만CLVR만
depth h5 metadata❌ (uuid/frame_count뿐)
재정정 (260805 후속): 4,702는 과대였다. 실제 집계 — PointWorld cameras json 42,935 → CLVR 2,754 → raw dir 존재 2,754(100%) → trajectory_im128.h5 존재 2,469(285 누락). 즉 사용 가능 = 2,469 scene, RoboCasa 1,235의 2.0배. 예상 용량도 140GB → 69GB.

확정 사용 가능: CLVR 4,702 scene — RoboCasa X-WAM 세트(1,235)의 3.8배. 파이프라인이 전 구간 검증됐으므로 이 규모로 즉시 A/B 착수 가능합니다.

확장 경로 — 병목은 딱 9개 숫자

wrist 카메라 serial을 조사하니 lab당 정확히 1종, 전체 9종뿐이고 intrinsic은 카메라 상수입니다(CLVR fx=731.8 완전 고정, cx/cy만 ±1 px 변동).

labwrist seriallabwrist serial
CLVR16787047 ✅ 확보PennPAL14436910
IRIS, TRI19824535ILIAD17368348
IPRL12391924GuptaLab16291792
REAL11744905AUTOLab18026681
RAIL13062452
모든 scene에 recordings/SVO/<serial>.svo(Zed 공장 캘리브 포함)가 있습니다. Zed SDK로 serial당 1회씩 9번만 읽어 룩업 테이블을 만들면 42,935 scene 전체가 열립니다. 다만 SDK 설치 가능성은 미확인이고, base 학습에 4.7k가 부족한지도 아직 모르므로 별건으로 분리합니다.

7미니 데이터셋 + 로더 검증 — 통과

tmp/droid_xwam_mini/ — 10 에피소드 / 1,744 프레임 / 90 mp4 / 302 MB. RGB·depth·pointmap 3종 × 3뷰(ext1·ext2 static, wrist dynamic).

pointmap mp4 round-trip max |Δpx| = 0 ← 무손실 확인

X-WAM 자체 RobotDataset으로 로드 검증했습니다:

shaperange
video(3, 9, 3, 256, 320)[-1.000, 0.995]
depths(3, 9, 3, 256, 320)[-1.000, 1.000]
proprios / proprio_mask(9, 16)[-0.998, 0.985]
actions / action_mask(32, 14)[-2.515, 0.856]
camera_type_mask(3,)0/1 (wrist=dynamic)
prompt"pull a tissue from a box" (DROID current_task)

invalid sentinel이 정규화 후 −1.0으로 보존되며 비율 15.7%. 로더가 depth를 NEAREST_EXACT로 리사이즈하므로 sentinel이 보간되지 않습니다.

도중에 겪은 함정: metadata.json의 키에 chunk 접두어가 필요합니다("chunk-0000/episode_0000000"). 로더가 data_root/{episode_key}.json으로 경로를 만들기 때문입니다. README의 레이아웃 그림만 보면 놓치기 쉽습니다.
남은 작업: 현재 JSON은 depth_path(depth)와 pointmap_path를 모두 갖고 있는데, 로더는 depth_path만 읽습니다. pointmap arm 학습에는 depth_path를 pointmap으로 가리키는 변형 JSON(또는 빌더 플래그)이 필요합니다 — 사소하지만 필수.

8다음 단계

  1. compute_stats.py로 DROID 통계 생성 — 현재 로더 검증은 RoboCasa 통계를 임시로 썼습니다. action range가 [-2.5, 0.86]으로 정규화 범위를 벗어난 것이 그 증거입니다.
  2. pointmap용 JSON 변형depth_path → pointmap 경로.
  3. 본 데이터셋 생성 — CLVR 4,702 scene (미니 10개로 302 MB → 전체 약 140 GB 예상, 무손실이라 큼. 필요시 해상도·프레임 서브샘플 검토).
  4. 100 step 스모크 ×2 — depth arm / pointmap arm 양쪽. loss가 정상 감소하는지, pointmap 슬롯이 sentinel을 학습하는지.
  5. 본 학습 + RoboLab 평가.
  6. (별건) SVO에서 9개 wrist intrinsic 추출 → 규모 9배 확장.
미해결 리스크: ① base부터 학습 시 4.7k scene이 충분한지 미지(vanilla_repro는 1,235 scene·1 GPU로 SR 11%). ② RoboLab 평가 프로토콜(태스크 수·SR 측정)을 아직 확인하지 않았습니다. ③ invalid sentinel(−1)이 VAE에 불연속으로 작용할 위험 — 스모크에서 관찰 필요. ④ 무손실 인코딩 때문에 데이터 용량이 큼.