Communitygithub.com

johannstark/GeminiRobotics-Skills

MuJoCo 시뮬레이션을 Three.js로 렌더링합니다 (브라우저 내 WASM, 스트리밍 Python MuJoCo 프로세스 또는 로그 사용). Z-up 장면 설정, MuJoCo geom 및 메시를 Three.js 메시로 변환, 프레임별 바디 포즈 동기화(wxyz 쿼터니언), 카메라 스냅샷 및 캔버스 비디오 캡처를 Gemini Robotics-ER에 제공하고, ER [y, x] 0-1000 포인트를 레이캐스팅으로 3D에 매핑하며, 3D를 이미지 좌표로 다시 투영하고, 비뚤어지거나 꼬이거나 크기가 잘못되었거나 빈 렌더링을 디버깅합니다.

GeminiRobotics-Skills란 무엇인가요?

이 스킬은 MuJoCo 시뮬레이션을 Three.js를 사용하여 브라우저에서 렌더링하는 방법을 제공합니다. Z-up 좌표계, 하프 사이즈 및 wxyz 쿼터니언을 사용하는 MuJoCo와 Y-up, 풀 사이즈 및 xyzw 쿼터니언을 사용하는 Three.js 간의 차이를 자동으로 처리하여 개발자가 직접 변환 없이 장면을 설정할 수 있도록 합니다. WASM 기반 브라우저 내 실행, 원격 Python 프로세스 스트리밍, 로그 재생 등 세 가지 접근 방식을 지원하며, 각각 시뮬레이션 속도, 외부 시스템 통합, 디버깅 등의 필요에 따라 선택할 수 있습니다. 핵심 기능으로는 프레임별 바디 포즈 동기화, 스냅샷 및 비디오 캡처, Gemini Robotics-ER의 [y, x] 포인트를 3D로 또는 그 반대로 변환하는 레이캐스팅 유틸리티가 포함되어 있습니다. 또한 일반적인 렌더링 문제(예: 로봇이 옆으로 누워 있거나, 링크가 꼬이거나, 박스 크기가 두 배로 보이는 현상)를 진단하고 해결하는 방법을 상세히 설명하며, 특히 Gemini Robotics-ER과의 통합 및 디버깅에 중점을 둡니다.

지원 대상~Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/johannstark/GeminiRobotics-Skills/tree/HEAD/threejs-rendering

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

MuJoCo를 위한 Three.js 렌더링

정적 지오메트리를 한 번 빌드하고, 각 프레임마다 트랜스폼만 업데이트하세요. MuJoCo는 Z-up, 하프 사이즈, Z-정렬 실린더와 캡슐, wxyz 쿼터니언을 사용합니다. Three.js는 기본적으로 Y-up, 풀 사이즈, Y-정렬 실린더와 xyzw 쿼터니언을 사용합니다. 거의 모든 렌더링 버그는 이 중 하나에서 발생합니다.

1. 장면 설정 (Z-up, 캡처 준비)

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';   // 경로는 0.164 및 0.18x에서 유효

const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true }); // toDataURL에 필요
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
const camera = new THREE.PerspectiveCamera(45, w / h, 0.01, 100);
camera.up.set(0, 0, 1);                       // MuJoCo는 Z-up입니다. 이렇게 설정하면 어디서도 축 변환이 필요하지 않습니다.
camera.position.set(2, -1.5, 2.5);
const controls = new OrbitControls(camera, renderer.domElement);
const grid = new THREE.GridHelper(5, 50); grid.rotation.x = Math.PI / 2;   // XY 평면에 그리드 배치

camera.up은 OrbitControls를 생성하기 전에 설정하세요. 컨트롤이 up 벡터를 캐시하기 때문입니다.

2. 접근 방식 선택

접근 방식방법사용 시기
A. 바디당 하나의 Group (Franka 데모)각 바디 그룹은 자체 geom_pos/geom_quat에 지오메트리를 보유합니다. 각 프레임마다 xpos/xquat를 복사합니다.기본값. userData.bodyID를 통한 피킹. 모든 포즈 소스와 함께 작동합니다.
B. mjv_updateScene (공식 데모)scene.geoms 항목당 하나의 풀링된 메시를 동기화합니다.MuJoCo 자체 데코레이션(접촉, 힘줄, 사이트)을 원하는 경우
C. 원격 PythonPython이 단계를 진행하고 WebSocket을 통해 xpos/xquat를 스트리밍합니다. 브라우저는 A를 실행합니다.GPU, ROS 또는 실제 로봇 브리지, 대규모 연산

Geom 변환: references/geom-conversion.md. Python 스트리밍(테스트된 서버): references/remote-python.md. 스냅샷, 인식 카메라, 비디오 및 픽셀↔3D: references/perception-and-video.md.

3. 프레임별 동기화 (접근 방식 A)

for (let i = 0; i < model.nbody; i++) {
  bodies[i].position.set(data.xpos[3*i], data.xpos[3*i+1], data.xpos[3*i+2]);
  bodies[i].quaternion.set(data.xquat[4*i+1], data.xquat[4*i+2], data.xquat[4*i+3], data.xquat[4*i]); // wxyz -> xyzw
}
renderer.render(scene, camera);

루프 순서: 입력 → mj_step (시뮬레이션 시간 예산이 허용하는 만큼 여러 번) → 동기화 → 렌더링 (MujocoSim.startLoop 참조). per-geom 대안은 data.geom_xpos 및 data.geom_xmat (행 우선 3×3 → Matrix4.set(r00,r01,r02,0, r10,…))을 사용하며, 지오메트리는 항등 포즈에 있습니다.

4. ER 포인트 ↔ 3D

// 캡처된 이미지의 ER [y, x] 0-1000 -> 캡처한 동일한 카메라의 광선
const ndc = new THREE.Vector2((x / 1000) * 2 - 1, 1 - (y / 1000) * 2);
raycaster.setFromCamera(ndc, captureCamera);
const hit = raycaster.intersectObjects(simMeshes, false)[0];     // 또는 raycaster.ray.intersectPlane(table, out)

// 3D -> ER 스타일 [y, x] (기대치를 그리거나 프롬프트를 작성하기 위해)
const v = p.clone().project(captureCamera);
const yx = [(1 - v.y) * 500, (v.x + 1) * 500];

이것은 이미지가 정확히 카메라의 전체 뷰포트인 경우 (자르기 없음, 레터박스 없음) 그리고 카메라가 캡처와 레이캐스트 사이에 움직이지 않은 경우 모든 종횡비에 유효합니다. userData.bodyID까지 올라가서 로봇이나 바닥에 맞는 것은 건너뛰세요. 히트는 상단 표면이므로, 잡기 높이로 낮추세요.

5. 주의사항

증상원인
로봇이 옆으로 누워 있고, 궤도가 흔들림camera.up이 (0,0,1)이 아니거나 OrbitControls 생성 후에 설정됨
링크가 꼬임wxyz→xyzw 재정렬 누락
상자가 두 배로 커 보임크기가 반쪽 크기임을 잊음
실린더 또는 캡슐이 잘못된 방향을 가리킴geometry.rotateX(Math.PI/2) 누락
빈 스냅샷 또는 검은색 스냅샷preserveDrawingBuffer: false이거나 픽셀이 렌더링과 다른 작업에서 읽힘
인식 이미지가 화면보다 어두움렌더 타겟이 선형 색상을 출력함 (three r186에서 확인됨). sRGB로 변환하세요 (인식 참조 파일 참조).
ER 마커가 오프셋됨카메라가 움직였거나, [y, x]가 바뀌었거나, 이미지가 잘리거나 레터박스 처리되었거나, 장치 픽셀 비율 캔버스 크기가 다르게 조정됨
다시 로드할 때 메모리 증가geometry.dispose(), material.dispose(), renderTarget.dispose()를 호출하고 WASM 모델과 데이터를 삭제하세요
고속에서 끊김프레임당 mj_step 호출이 너무 많음. 단계 수를 제한하거나 물리 엔진을 Worker로 이동하세요.

6. Franka 데모에서

Franka 집어 옮기기 데모는 이러한 스킬이 빌드된 참조 앱입니다. 아래 파일 이름은 이를 참조합니다.

  • RenderSystem.ts는 preserveDrawingBuffer: true, Z-up PerspectiveCamera(45), 감쇠 OrbitControls, simGroup (바디당 하나의 그룹, userData.bodyID), erGroup (흔들리는 원뿔 마커), 인스턴스화된 접촉 마커, 카메라 트윈 (moveCameraTo) 및 getCanvasSnapshot(w, h, mime)을 사용합니다.
  • project2DTo3D(x, y, camPos, lookAt)은 주 카메라를 복제하여 캡처 시점 ((0, -0.01, 2.0)이 원점을 바라봄)에 배치하고 simGroup 메시를 레이캐스트합니다.
  • rendering/GeomBuilder.ts는 geom→메시 팩토리를 처리합니다. Group-3 geom은 건너뛰고, 평면은 Reflector 바닥이 되며, 메시 geom은 mesh_vert/mesh_face를 읽습니다.
  • DragStateManager.ts와 SelectionManager.ts는 xfrc_applied를 통해 마우스 피킹 및 드래그 힘을 처리합니다.
  • index.html 임포트 맵은 three 0.164.1을 고정하는 반면, package.json은 ^0.181을 가집니다. three/examples/jsm/... 임포트를 유지하세요. 두 버전에서 모두 작동합니다.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬