Index
2026-08-12 — Analysis

RoboCasa 데이터 전수 조사 & action / state 값 규격 확정

lingbot-va | 소스 코드 + 수치 실측 이중 검증 · 적합성 조사 후속

TL;DR

5
데이터 위치
1235
X-WAM eps (완전)
410/1382
lerobot eps (파손)
20
실제 Hz (10 아님)
0.22
cmd→realized 기울기

1 배경 / 목적

1차 조사는 ~/Dataset/robocasa_human_lerobot 하나만 보고 "LIBERO 레시피와 정확히 일치"라고 결론냈다. 그런데 두 가지가 미검증이었다:

왜 중요한가: 이 둘이 used_action_channel_ids / action_per_frame / norm_stat 세 설정을 직접 좌우한다. 틀리면 학습은 돌지만 정책이 잘못 배운다.

2 작업 내용

robocasa/utils/lerobot_utils.py:65-69 → ACTION_KEY_ORDERING_HDF5 (인덱스 정본) robosuite/controllers/config/robots/default_pandaomron.json → input_type / ref_frame / output_max robosuite/robots/{robot,wheeled_robot}.py → part_controller_config 조립 순서 추적 openpi/examples/robocasa/convert_robocasa_to_lerobot.py → state 필드 구성의 원형 + 수치 실측: 상관분석 / 사원수 역산 / 회귀

소스 코드 순서와 실제 데이터가 어긋나는 것을 발견해, 데이터를 기준으로 재확정했다. state 25-dim은 문서가 없어 사원수 연산으로 역공학했다.

3 결과

3-1. 보유 RoboCasa 데이터 5곳

경로크기포맷내용
~/Datasets/X-WAM-RoboCasa5.2GX-WAM json+mp4주 소스. 1235 eps / 341K frames / 24 task / 256×256 / 20fps / 3 cam RGB+depth / abs EE + raw 7-dim
~/Datasets/robocasa_xwam1.6GX-WAM json+mp41320 eps / 365K frames. depth 없음, raw_actions
~/Dataset/robocasa_human_lerobot6.4GLeRobot v2.0410/1382만 존재(파손). 128×128. fps:10 오기
~/Datasets/robocasa_lerobot_cams690Mnpz카메라 T(4×4)/K(3×3) + base_to_eef_pos. 기하 전용, LingBot 무관
~/repos/Robotics/robocasa/datasets/v0.12.3G원본 hdf531개(single 26 + multi 5). state-only(이미지 없음) → 재렌더 필요

3-2. RoboCasa action 12-dim — 확정

정본은 robocasa/utils/lerobot_utils.py:65-69:

ACTION_KEY_ORDERING_HDF5 = { "end_effector_position": (0, 3), "end_effector_rotation": (3, 6), "gripper_close": (6, 7), "base_motion": (7, 11), # base 3 + torso 1 "control_mode": (11, 12), }
idx내용abs / rel기준 프레임정규화물리 스케일
0:3EEF positionrelative (델타)base[-1, 1]× 0.05 m
3:6EEF rotation (axis-angle)relative (델타)base[-1, 1]× 0.5 rad
6gripper_close명령±1 이진+1 닫기 / −1 열기
7:10base x / y / yaw속도 (JOINT_VELOCITY)base[-1, 1]× 0.5 rad/s
10torso관절 위치 (JOINT_POSITION)[-1, 1]× 0.05
11control_mode플래그±1−1 arm 모드 / +1 base 모드

default_pandaomron.json 원문: "input_type": "delta", "input_ref_frame": "base", output_max: [0.05,0.05,0.05, 0.5,0.5,0.5]. → 월드도 EEF도 아닌 로봇 베이스 프레임 기준이다.

소스 코드를 그대로 믿으면 틀린다. robosuite 1.5.2의 part_controller_config dict 조립 순서(right → torso → base → right_gripper)대로면 torso가 idx 6이어야 한다. 하지만 실제 데이터는 idx 6이 gripper다. RoboCasa v0.5 데이터셋은 그 순서와 다른 조합으로 생성됐다.

3-3. 수치로 재확정한 근거

dim검증 방법결과결론
6gripper_qpos 평균 비교 (5프레임 lag 포함)+1 → 0.0208 / −1 → 0.0387gripper
7 / 8 / 9base 프레임 Δx / Δy / Δyaw 상관+0.899 / +0.931 / +0.991forward / lateral / yaw
7:11dim11(mode)와의 동시발생non-zero 프레임의 100%가 mode == +1base 모드 전용
10희소성 + 값 분포15,571 중 10 프레임만 non-zero, 값 −0.5~−0.557 연속torso
델타는 실현 변위가 아니다. commanded_delta × 0.05 vs 실제 EEF 변위 회귀 → 기울기 0.22 (x 0.219 / y 0.218 / z 0.224, corr 0.91~0.93). OSC 임피던스 컨트롤러(kp=150)가 한 스텝에 ~20%만 실현한다. LIBERO도 동종 신호라 학습은 문제없지만, 액션을 적분해 궤적으로 해석하면 안 된다.

원본 hdf5의 action_dictrel_pos / rel_rot_axis_angle / rel_rot_6d / gripper — 전부 rel_ prefix로 relative임이 명시돼 있다. v0.5부터 actions_abs(절대) 키가 별도로 있으나, openpi 변환 스크립트는 actions(relative)만 썼다.

3-4. robocasa_human_lerobot state 25-dim (역공학)

0:3 eef_pos (world) 3:7 eef_quat xyzw (world) 7:9 gripper_qpos (finger 2) 9:11 gripper_qvel 11:14 robot0_base_to_eef_pos (base) 14:18 robot0_base_to_eef_quat (base) 18:21 robot0_base_pos (world) 21:25 robot0_base_quat xyzw (world, x=y=0)

검증: q_base⁻¹ ⊗ q_eef_world를 손으로 계산해 dims 14:18과 소수점 4자리까지 일치. R_baseT(p_eef − p_base) = dims 11:14 일치. LingBot-VA는 state를 안 쓰지만, 절대 pose를 재구성하려면 11:18이 필요하다.

3-5. 정정 fps: 10은 잘못된 메타데이터 — 실제 20 Hz

1차 분석의 "10fps → stride 1 → action_per_frame=4 = LIBERO 정확 일치"는 틀렸다. 독립 증거 3가지:

#증거수치
1Δgripper_qpos / gripper_qvel0.0487 → dt = 1/20.5 s (timestamp가 주장하는 0.1 s 아님)
2commanded↔realized 기울기를 X-WAM(공식 20fps)과 비교0.22 vs 0.20 — 동일 → 행 1개 = 컨트롤 스텝 1개, 데시메이션 없음
3에피소드 길이 분포를 X-WAM과 비교min 12 / median 245 vs 250 / max 889 vs 889 → 동일 원본 demo

→ 올바른 설정은 stride 2 → action_per_frame = 8 (영상 10fps, README 권장 5–15fps 범위). LIBERO cfg가 아니라 va_demo_cfg(256×256, action_per_frame=8)와 맞는다.

완화 요인: _action_post_process는 액션 배열에 stride를 적용하지 않는다 (required_action_num = latent_frame_num × frame_stride × 4). 영상만 2배 다운샘플되고 액션은 20Hz 전량이 들어간다 → 정보 손실 없음.

3-6. X-WAM-RoboCasa가 더 나은 소스

robocasa_human_lerobotX-WAM-RoboCasa
완결성410 / 1382 (파손)1235 / 1235
해상도128×128256×256
영상parquet 내 PNGmp4 H.264 (VAE 직투입)
액션12-dim rawraw_actions 7-dim + 절대 EE pose 둘 다
LingBot cfgva_demo_cfg (256, apf 8) 정합
proprios.left_ee_pos (3) 절대, base frame proprios.left_ee_rotm (9) 회전행렬 (det=1.0 확인) proprios.left_gripper_pos (1) 개방도 0.47~1.0 actions.left_* → actions[t] == proprios[t+1] (오차 0으로 성립) actions.raw_actions (7) OSC 델타 6 + gripper 1 observations.<cam>.rgb_path → video/<cam>/chunk-xxxx/episode_xxxxxxx.mp4, fps 20.0

실측 raw_actions q01/q99 (85K 프레임 샘플):

q01 [-1.0, -1.0, -0.736, -0.371, -0.323, -0.369, -1.0] q99 [ 1.0, 1.0, 1.000, 0.443, 0.283, 0.351, 1.0] gripper close(+1) 비율 34.7%

4 Takeaway

의미

소스 코드만 믿으면 틀린다. robosuite의 컨트롤러 조립 순서대로면 torso가 idx 6이어야 했지만 실제 데이터는 gripper였다. 그리고 fps 메타데이터마저 틀렸다. 이걸 그대로 믿었으면 action_per_frame을 절반으로 잡고 학습했을 것이고, 증상이 조용해서 한참 뒤에나 알아챘을 것이다.

주 데이터셋을 X-WAM-RoboCasa로 바꾸는 게 맞다. 완전성 · 해상도 · 영상포맷 · 액션표현 4개 축 모두 우세하고, 1차 조사의 최대 blocker(410/1382 파손으로 인한 silent index 오정렬)가 통째로 사라진다.

LingBot이 픽셀을 안 읽으므로 X-WAM이 LeRobot 포맷이 아닌 것은 문제가 아니다. action 컬럼만 있는 최소 LeRobot 껍데기로 충분하고, 이미지 키를 선언하지 않으면 meta.video_keys가 비어서 mp4 존재 검사도 걸리지 않는다.

5 Next Steps

먼저 결정할 것 — 액션 표현

  • (A) LIBERO 경로raw_actions[:, :7] 그대로, used_action_channel_ids=range(0,7), env_type='none'. 최소 변경.
  • (B) RoboTwin 경로left_ee_pos(3) + left_ee_rotm(9)→quat(4) = 7-dim 절대 pose를 get_relative_pose로 세그먼트 첫 프레임 기준 상대화. 단 _action_post_processenv_type=='robotwin_tshape'에서만 호출하므로 분기 추가 필요 (lerobot_latent_dataset.py:260## TODO support get_relative_pose for other dataset 주석이 이미 있다).

실행 순서

  1. 최소 LeRobot 껍데기 생성기: X-WAM json → meta/{info,episodes,tasks,stats} + action-only parquet (+ action_config 주입)
  2. latent 추출 스크립트 (mp4 → Wan2.2 VAE, stride 2, 256×256, 3 cam) + empty_emb.pt
  3. robbyant/lingbot-va-base 다운로드, lingbot env에 lerobot==0.3.3 scipy wandb --no-deps 설치
  4. va_robocasa_cfg.py (va_demo_cfg 베이스: 256×256, action_per_frame=8, 위 q01/q99)
  5. 로그인 노드 dataloader smoke test(≤100 step) → sbm으로 본 학습

미해결 설계 이슈

긴 에피소드 처리 — median 250 / p95 496 / max 889 frames @ 20Hz. stride 2여도 latent frame 최대 ~111, 카메라 3개면 latent width 48 → 시퀀스 길이가 크다. 세그먼트 분할 전략을 정해야 한다.