14/30 = 0.47(공식 600 step) → v2 22/30 → v2b 26/30 → v2c 29/30 = 0.97 (성공 시 중앙값 510 step, 두 번째 30-ep 표본 27/30) → 관절각 손목 가드를 더한 v2d 28/30 → 놓기 조건 강화·누르기 전용 사이클·nudge 제거한 v2e 27/30(v2c~v2e 합산 111/120 = 0.925, 표본 오차 안에서 동일) → 복구 경로의 heading 변화 금지·루프 캡을 넣은 v2f 57/60 = 0.95(29/30 + 28/30) → 나사산 방향 회전·안전한 recover 상승을 넣은 v2g(안착 22°) 57/60 → 안착 기준을 25°로 되돌린 v2g 최종 59/60 = 0.983(30/30 + 29/30, 동결). 파지 시 손을 180° 돌리는 FLIP 실험은 23/30으로 악화. CaP-X gpt-6-astra 최종은 1/30~3/30에 API ≈ $210이었다.match_leg_ori(세우기 90° 회전)에서 손목이 q6=π 특이점을 지나며 (q5,q7)이 무작위로 감김(완전 감김 5/30). v2c의 나사 접근(다리 5 cm 위에서 top-down 회전)이 이를 자연히 풀어 완전 감김 5/5 성공, v2d의 명시적 되감기 가드는 30 ep 동안 발동 0회. insert_wait 시점 손목 상태별 성공률(정상 8/9·부분 11/12·완전 8/9)이 같아 감김은 더 이상 주 원인이 아니고, 잔여 실패(v2c~v2e 120 ep 중 9)의 공통 구조는 감긴 손목 + 큰 재배향을 명령하는 복구 경로: 림 바운스 놓기·얕고 기울어진 안착 자체는 v2e가 막았지만, 그 뒤의 재파지(v2_regrasp_above)·nudge가 기운 방향 heading을 명령하면 q7이 한계인 손목으로는 회전할 수 없어 OSC가 base·팔꿈치로 팔 전체를 재구성하며 다리를 20 cm 이상 쓸어낸다(3 run에서 4건). → v2f(heading 0 재파지, upright↔align 캡, 안착 기준 하강 가드)로 60 ep 0.95. 그래도 남은 3건은 감긴 손목에서 v2_regrasp_above가 다리 상단 3 cm 위에서 회전하며 날린 것(살아남는 v2_screw_above는 +7.5 cm 위에서 회전)과 복구가 미삽입 다리를 공중에서 놓은 것 → v2g(recover 상승을 다리 상단 +7.5 cm로 통일, 회전 예산 단축, 든 채 내려놓기, 나사산 방향 회전)로 해소되어 최종 59/60. 통과 전 손목 상태·파지 yaw는 감김 부류를 전혀 예측하지 못하고(60 ep, 세 부류 관절 평균 동일), 분기는 stock 세우기의 첫 목표 노이즈 경로에서 결정된다.CaP-X(gpt-6-astra) 트랙은 단일 턴 1/30, 멀티턴 2/44, 누적 API ≈ $210에서 사용자 결정으로 종료했다. 새 방향: LLM 없이 기존 FurnitureBench 스크립트 정책을 참고해 직접 고성능 스크립트를 만든다. 규칙 — API 지출 없음, 치팅 금지: 벤치마크 액션 인터페이스(10 Hz 8-D delta-EE OSC, 실물과 동일)로만 행동, 관측은 stock 스크립트와 동일(시뮬 GT 부품 자세 + 로봇 상태), 텔레포트·물리 변경·채점기 조회 없음. 데모를 VLA 학습에 쓰기 위해 IK 관절 제어 대신 OSC delta-EE 유지.
insert_wait는 팔이 어디 있든 10 step 뒤 무조건 놓고, 놓기가 빗나가도 나사 phase를 계속 돌려 600 step 타임아웃. 파지 자세가 월드 고정이라 다리 yaw가 틀어지면 빈 손을 쥔다. 실측 14/30(@600), 17/30(@1500).LegV2(Leg)furniture-bench/furniture_bench/furniture/parts/leg_v2.py. FURNITURE_SCRIPTED_V2=1일 때만 square_table_leg.py가 이 클래스를 쓴다(opt-in, 기본 동작 불변). 액션은 그대로 env의 get_assembly_action(delta ×2, 클램프)으로 변환된다. 운반(lift → move_center → match_leg_ori → reach_table_top)은 stock을 재사용하고 그 앞뒤를 교체했다.
| 단계 | stock | v2 |
|---|---|---|
| 파지 | 월드 고정 자세, 25 step 예산 | 다리 yaw만큼 회전한 측면 파지, 폭 <2 cm면 재시도(≤2), 그래도 실패면 top-down 파지 후 공중 세우기; 운반 중 빈 손 감지 |
| 정렬 | 없음(고정 목표) | 입구 +10 mm에서 다리 끝점 수평 오차 서보 + 적분항(OSC 정상상태 오차 ~2 mm 해소), 기울기 서보 3°/step; 1.5 mm·4° 이내 5 step 연속일 때만 하강 |
| 하강·놓기 | 10 step 뒤 무조건 놓기 | 6 mm/step 하강(림 접촉 시간 최소화), 림에 걸리면 백오프·재정렬(≤4); 입구 3 mm(→ 8 mm) 안에서 놓고 8 step 뒤 판정 |
| 안착 판정 | 없음 | 끝점 깊이 <−6 mm & 수평 <7 mm & 기울기 <25° → 나사; 25~60°면 닫은 그리퍼로 밀어 세움(nudge); 아니면 복구 |
| 나사 접근 | pre_grasp: 측면 자세에서 top-down으로 이동+회전 동시 | v2c: 수직 상승(손끝이 다리 위 7.5 cm) → 다리 5 cm 위 top-down → 수직 하강; 하강 중 다리가 8° 이상 더 기울면 후퇴 |
| 비틀기 | −95° 목표, 허용오차 0.3, 최대 75 step | 빈 파지 검사; 정체 감지(6 step 동안 heading 변화 <2°면 즉시 놓기); 사이클마다 끝점 검사(수평 >8 mm/기울기 >30°/빠짐 → 복구, 2사이클 무진전 → 재배치) |
| 복구 | 없음 | 놓고 6 cm 위로 → 누웠으면 파지부터, 서 있으면 top-down 재파지 → 재정렬(≤4회); 바닥으로 떨어지면 즉시 포기 |
기타: stock의 tabletop push 전처리(tabletop을 장애물 모서리로 밀기)가 다리를 쓸어 떨어뜨려(3/30) v2에서는 생략(tabletop 위치를 측정해 쓰므로 불필요). mat2quat(torch eigh)이 180° 회전에서 발산 → scipy 변환. Leg.reset()이 phase 카운터를 안 지워 2번째 episode부터 초기화가 안 되던 문제 수정. 디버그: FURNITURE_SCRIPTED_V2_DEBUG=1이면 나사 사이클 step별 EE-다리 상대 자세와 관절각 출력.
| 정책 (job) | timeout | 성공 | 성공 step 중앙값 | 실패 유형 |
|---|---|---|---|---|
| stock (공식) | 600 | 14/30 = 0.47 | 470~520 | insert/screw 타임아웃 |
| stock | 1500 | 17/30 = 0.57 | — | 〃 |
| v2 (118937) | 1500 | 22/30 = 0.73 | 608 | 테이블 밖 낙하 5(그중 tabletop push 3), 기울어진 안착 나사 이탈 3 |
| v2b (119003 / 119032) | 1500 | 26/30 / 27/30 | 506 | 전부 손목 감김 → 나사 접근·비틀기에서 뽑힘 |
| v2c (119035 / 119039) | 1500 | 29/30 / 27/30 (56/60) | 510 (429~1460) | 얕은 안착 1, 손목 감김에 의한 삽입 부정확 3 |
| v2d (119065, 관절각 손목 가드) | 1500 | 28/30 = 0.93 | 500 (419~963) | 림 바운스 상태에서 놓기 → 넘어짐 1, 얕고 기울어진(22°) 안착 비틀기 무진전 → nudge가 날림 1. 되감기 가드 발동 0, pre-rotation 13 |
| v2e (119090, 놓기 3 step 조건·누르기 전용·nudge 제거) | 1500 | 27/30 = 0.90 | 527 (446~616) | 감긴 손목에서 재파지 경로가 큰 heading 변화를 명령해 팔 전체가 재구성되며 다리를 날림 2, upright↔align 무한 루프 1. 누르기 전용 사이클 16회 발동 |
| v2f (119115 seed 100 / 119116 seed 200: 기울어진 다리 재파지 heading 0, upright↔align 3회 캡, 안착 기울기 기준 하강 가드, 나사 접근 전 옆으로 1.2 cm) | 1500 | 29/30 / 28/30 = 57/60 = 0.95 | 539 (431~678) | 감긴 손목에서 `v2_regrasp_above`가 다리 상단 3 cm 위에서 회전하며 날림 1, 복구가 미삽입 다리를 공중에서 놓아 넘어짐 1, 23° 안착에 누르기 전용 무효·정체 복구 실패 1 |
| v2f + FLIP (119117, 파지 시 손 180° 회전 실험) | 1500 | 23/30 = 0.77 | — | 감김 분포를 반대쪽(q5 +, q7 +2.5 고정)으로 옮길 뿐 → 폐기 |
| v2g, 안착 22° (119147 / 119148: 나사산 방향 회전, recover 상승 +7.5 cm, 재파지 예산 단축, 든 채 내려놓기) | 1500 | 29/30 / 28/30 = 57/60 = 0.95 | 566 / 533 | 세 건 모두 22.7°/−11.6 mm 어트랙터 좌석 → 22° 기준이 MISSED→재배치로 보내 3/3 유실 |
| v2g 최종 (동결) (119163 / 119164: 위와 같고 안착 기준 25° 복귀, FLIP 코드 제거) | 1500 | 30/30 / 29/30 = 59/60 = 0.983 | 550 | 어트랙터 좌석 8/8을 누르기/비틀기 사이클로 조립. 유일한 실패는 축 이탈 측면 파지(닫힘 방향·축 0.26) → 32° 기울인 운반 → 복구 중 유실 |
실행 간 물리가 비결정적이라 같은 seed라도 ep1부터 초기 상태가 갈린다(ep0만 동일). 30-ep 세트는 독립 표본이다. v2c에서 비틀기 정체 감지는 29/30 episode에서 발동했고(정상 사이클도 −90.6°에서 멈춤: stock 목표 −95°·허용오차 0.3은 경계선), 복구는 9회.
v2b 실패 7건(두 run)은 모두 "정상 안착(기울기 3~23°) → 나사 사이클에서 뽑힘". 나사 사이클 step별 관절각을 찍자 실패 episode는 예외 없이 첫 나사 파지 시점에 q5≈−2.0~−2.7, q6→3.75(상한), q7→2.90(상한)의 감긴 손목이었고, 정상은 q5≈0, q6≈2.6, q7≈0.8~1.0. 감긴 손목에 stock pre_grasp(이동+회전 동시)를 주면 OSC가 한계에 막혀 EE가 3 step에 −x로 3.4 cm 튀며 다리를 넘어뜨린다.
모든 상태 전이에서 관절각을 찍은 119039(30 ep)로 발생 지점을 특정했다: stock match_leg_ori — 툴을 뒤로 54° 기울인 파지 자세에서 앞으로 36° 기울인 세우기 자세로 90° 회전하는 동안 q6이 π(5·7축 정렬 특이점)를 지나고, 그 직후 (q5, q7)의 분배가 사실상 무작위다. 세우기 직후 q5/q7 30건의 분포: 정상(−0.1/1.1) ~ 완전 감김(−2.2/2.9)의 연속체, 완전 감김(q7=2.897) 5/30. 놓기 자세(툴 앞으로 25°)가 특이점 너머(q6 3.1~3.5)에 있는 것이 구조적 원인이고 stock 파지 기하(툴-다리 36° 마진)가 이를 강제한다.
v2_screw_above는 q6이 π를 내려가며 통과하고, OSC의 null-space 자세항(init_joints, kp 10)이 리셋 자세로 당겨 감김이 풀린다(ep0: q5 −2.28→−0.38, q7 2.78→1.19). 119039의 완전 감김 5건은 전부 성공.성공률 0.03~0.10 → 0.97, API 비용 $6~500/30 trial → $0, 행동 전 지연 90 s → 0, 결정적 재현. 본질적으로 CaP-X 멀티턴이 하려던 "실행 결과를 보고 코드를 고친다"를 사람(모델)이 trace를 뜯어보며 7회 이상 반복한 것이고, 결과 차이는 모델 능력보다 반복 횟수와 피드백 해상도(관절각·끝점 기하)의 차이에 가깝다. 대신 일반성은 잃었다: one_leg 전용 상태기계라 task마다 새로 써야 하고, 관측은 여전히 시뮬 GT 부품 자세다.
part.robot_joint_positions)을 추가했다.관절각 손목 가드(나사 접근 전 되감기, 비틀기 전 q7 여유 확보, 하강 정체 가드, q6>3.6이면 기울기 서보 중지)를 넣은 v2d는 28/30. 되감기 가드는 한 번도 필요하지 않았고(감긴 손목은 v2_screw_above에서 자연히 풀림), pre-rotation 13회. insert_wait 시점 손목 상태별 성공률(정상 8/9, 부분 감김 11/12, 완전 감김 8/9)이 같아 "의도적 감기"·"삽입 전 되감기"는 폐기. 놓기 깊이 −8 mm는 쥔 채 밀면 림에 걸린 각도(24°)로 놓게 되어 −3 mm로 환원. 느린 세우기(1.5°/step)는 감김을 결정적으로 만들어 폐기.
v2f(heading 0 재파지, upright↔align 캡) 57/60 → v2g(나사산 방향 회전: −95° = 2.3 mm 진입·+95° = 후퇴·누르기만 = 0이라 항상 조이는 방향, recover 상승을 다리 상단 +7.5 cm로, 재파지 예산 45 step·기울기 변화 시 중단, 든 채 복구면 내려놓기) — 안착 22° 기준으로는 57/60(22.7° 어트랙터 좌석 3건이 MISSED→재배치로 가서 3/3 유실), 25° 기준으로 되돌리자 같은 좌석 8/8이 누르기/비틀기로 조립되어 59/60. 잔여: (1) 22.7°/−11.6 mm 어트랙터(수직으로 3 mm 넣고 놓으면 열린 손가락 안에서 근처 림에 기대는 안정점; 미시도 아이디어 — 기운 직후 열린 툴로 아래로 톡 치기, 첫 들어올리기 전 제자리 재파지), (2) 축 이탈 측면 파지(1/60: 닫힘 방향·축 0.26 → 32° 기울인 운반 → 복구 중 유실). 동결 파일 leg_v2.py 14:08:24.
파지 시 손을 자체 축으로 180° 돌려(q7≈−2.1) 세우기 통과 후 손목이 +2.9 한계에서 멀어지게 하려는 시도. one_leg 3 seed 로컬 3/3이었으나 30 ep에서는 감김이 반대쪽(q5 +1.6, q7 +2.5 고정)으로 옮겨가 0.77. 60 ep 표에서 통과 전 상태가 통과 후 부류를 전혀 예측하지 못한 것과 정합(분기는 stock 세우기의 첫 목표 노이즈 경로에서 결정).
v2를 데모 수집(collect_scripted.sh, timeout ~1500)에 적용 → lamp/round_table/cabinet에 같은 레시피(측정 기반 정렬·놓기 판정·사이클 검사) → desk/stool/chair/drawer 신규 상태기계.
v2 최종(seed 300/800/900, 각 3 ep)과 stock(seed 300/800)의 전면 카메라 + 손목 카메라 인셋 + 상태/기울기 오버레이 영상은 별도 쇼케이스 리포트 260911-scripted_one_leg_v2_videos.html에 있다(job 119213, scripts/record_scripted_video.py / record_scripted_videos.sh, 병렬 세션 작성).