Communitygithub.com

johannstark/GeminiRobotics-Skills

Rendert MuJoCo-Simulationen mit Three.js (im Browser via WASM, gestreamter Python-MuJoCo-Prozess oder Logs). Verwendbar für Z-up-Szenenaufbau, Konvertierung von MuJoCo-Geomen und -Meshes in Three.js-Meshes, körperweise Posensynchronisation pro Bild (wxyz-Quaternionen), Erfassen von Kamera-Snapshots und Canvas-Video für Gemini Robotics-ER, Abbildung von ER-[y, x]-Punkten (0-1000) auf 3D per Raycasting, Rückprojektion von 3D in Bildkoordinaten sowie Debugging von seitlichen, verdrehten, falsch skalierten oder leeren Renderergebnissen.

Was ist GeminiRobotics-Skills?

Diese Skill-Beschreibung dokumentiert, wie MuJoCo-Simulationen mit Three.js gerendert werden, entweder nativ im Browser (WASM), über einen gestreamten Python-MuJoCo-Prozess oder aus Logdateien. Der Fokus liegt auf der Z-up-Szene (MuJoCo) im Unterschied zum Y-up-Standard von Three.js, sowie auf der korrekten Konvertierung von Geometrien, Quaternionen (wxyz → xyzw) und Posen. Die Beschreibung deckt drei Ansätze ab: körperweise Gruppen (Standard), Synchronisation über mjv_updateScene oder Remote-Python-Streaming. Wichtige Bestandteile sind das Erfassen von Kamerabildern und Canvas-Videos für Gemini Robotics-ER, das Raycasting von ER-[y, x]-Punkten (0-1000) in den 3D-Raum sowie die Rückprojektion von 3D in Bildkoordinaten. Die Skill-Beschreibung richtet sich an Entwickler, die robotische Simulationen oder Perception-Workflows mit MuJoCo und Three.js umsetzen möchten, und enthält einen ausführlichen Abschnitt zu typischen Fehlerursachen (Gotchas) sowie Verweise auf eine Referenzimplementierung (Franka-Demo).

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Three.js-Rendering für MuJoCo

Statische Geometrie einmal aufbauen, dann pro Frame nur die Transformationen aktualisieren. MuJoCo verwendet Z-up, Halbgrößen, Z-ausgerichtete Zylinder und Kapseln sowie wxyz-Quaternionen. Three.js verwendet standardmäßig Y-up, Vollgrößen, Y-ausgerichtete Zylinder und xyzw-Quaternionen. Fast alle Rendering-Fehler gehen auf einen dieser Unterschiede zurück.

1. Szenenaufbau (Z-up, aufnahmebereit)

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';   // Pfad gültig ab 0.164 und 0.18x

const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true }); // benötigt für 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 ist Z-up. Damit ist keine Achsenkonvertierung nötig.
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;   // Gitter auf XY legen

Setze camera.up vor der Erstellung von OrbitControls, da die Controls den up-Vektor cachen.

2. Ansatz wählen

AnsatzWieVerwendung wenn
A. Eine Group pro Körper (die Franka-Demo)Jede Körper-Gruppe enthält ihre Geome an deren lokaler geom_pos/geom_quat. Kopiere xpos/xquat pro Frame.Standard. Auswahl via userData.bodyID. Funktioniert mit jeder Pose-Quelle.
B. mjv_updateScene (offizielle Demo)Synchronisiere einen gepoolten Mesh pro scene.geoms-EintragDu möchtest MuJoCos eigene Dekorationen (Kontakte, Sehnen, Sites)
C. Remote PythonPython führt Schritte aus und streamt xpos/xquat per WebSocket. Der Browser nutzt A.GPU-, ROS- oder Realroboter-Brücken, rechenintensive Aufgaben

Geom-Konvertierung: references/geom-conversion.md. Python-Streaming (getesteter Server): references/remote-python.md. Snapshots, Perzeptionskamera, Video und Pixel↔3D: references/perception-and-video.md.

3. Synchronisation pro Frame (Ansatz 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);

Schleifenreihenfolge: Eingaben → mj_step (so oft wie das Sim-Zeitbudget erlaubt) → sync → render (siehe MujocoSim.startLoop). Die Alternative pro Geom verwendet data.geom_xpos und data.geom_xmat (zeilenweise 3×3 → Matrix4.set(r00,r01,r02,0, r10,…)), mit Geometrie an der Identitätspose.

4. ER-Punkte ↔ 3D

// ER [y, x] 0-1000 auf dem aufgenommenen Bild -> Strahl von derselben Kamera, die es aufgenommen hat
const ndc = new THREE.Vector2((x / 1000) * 2 - 1, 1 - (y / 1000) * 2);
raycaster.setFromCamera(ndc, captureCamera);
const hit = raycaster.intersectObjects(simMeshes, false)[0];     // oder raycaster.ray.intersectPlane(table, out)

// 3D -> ER-artiges [y, x] (um Erwartungen zu zeichnen oder Prompts zu bauen)
const v = p.clone().project(captureCamera);
const yx = [(1 - v.y) * 500, (v.x + 1) * 500];

Dies gilt für jedes Seitenverhältnis, solange das Bild exakt das gesamte Viewport der Kamera ist (kein Zuschnitt, kein Letterbox) und die Kamera sich zwischen Aufnahme und Raycast nicht bewegt hat. Überspringe Treffer auf dem Roboter oder dem Boden, indem du bis zu userData.bodyID durchgehst. Der Treffer ist die obere Fläche; senke ihn auf Greifhöhe ab.

5. Häufige Fehler

SymptomUrsache
Roboter liegt seitlich, Orbit kipptcamera.up ist nicht (0,0,1) oder wurde nach der Erstellung von OrbitControls gesetzt
Links verdrehtFehlende wxyz→xyzw-Umordnung
Boxen doppelt so großVergessen, dass Größen Halbextents sind
Zylinder oder Kapseln zeigen in die falsche RichtungFehlendes geometry.rotateX(Math.PI/2)
Leeres oder schwarzes SnapshotpreserveDrawingBuffer: false, oder die Pixel wurden in einer anderen Task als dem Render ausgelesen
Perzeptionsbild dunkler als der BildschirmRender-Targets geben lineare Farbe aus (bestätigt in three r186). In sRGB konvertieren (siehe Perzeptionsreferenz).
ER-Marken versetztKamera bewegt, [y, x] vertauscht, Bild zugeschnitten oder mit Letterbox versehen, oder die Leinwand mit Geräte-Pixel-Verhältnis wurde anders skaliert
Speicher wächst bei Neuladengeometry.dispose(), material.dispose(), renderTarget.dispose(), und das WASM-Modell und die Daten löschen
Ruckeln bei hoher GeschwindigkeitZu viele mj_step-Aufrufe pro Frame. Anzahl der Schritte begrenzen oder Physik in einen Worker auslagern.

6. In der Franka-Demo

Die Franka-Pick-and-Place-Demo ist die Referenz-App, aus der diese Skills entwickelt wurden. Die folgenden Dateinamen beziehen sich auf diese Demo.

  • RenderSystem.ts verwendet preserveDrawingBuffer: true, eine Z-up PerspectiveCamera(45), gedämpfte OrbitControls, simGroup (eine Gruppe pro Körper, userData.bodyID), erGroup (wippende Kegelmarker), instanziierte Kontaktmarker, Kamera-Tweens (moveCameraTo) und getCanvasSnapshot(w, h, mime).
  • project2DTo3D(x, y, camPos, lookAt) klont die Hauptkamera, positioniert sie am Aufnahme-Viewpoint ((0, -0.01, 2.0) blickend auf den Ursprung) und führt Raycast auf simGroup-Meshes durch.
  • rendering/GeomBuilder.ts behandelt die Geom→Mesh-Factory. Geome der Gruppe 3 werden übersprungen, Ebenen werden zu einem Reflector-Boden, und Mesh-Geome lesen mesh_vert/mesh_face.
  • DragStateManager.ts und SelectionManager.ts behandeln Mausauswahl und Zugkräfte über xfrc_applied.
  • Die index.html-Import-Map pinnt three 0.164.1, während package.json ^0.181 hat. Verwende three/examples/jsm/...-Imports, die in beiden Versionen funktionieren.

Individual skills in this repo

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

Verwandte Skills