Communitygithub.com

johannstark/GeminiRobotics-Skills

MuJoCo シミュレーションを Three.js でレンダリングします(ブラウザ内 WASM、ストリーミング Python MuJoCo プロセス、またはログ)。Z-up シーン設定、MuJoCo ジオメトリとメッシュの 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 の間の軸とクォータニオンの変換を自動化し、フレームごとのボディポーズ同期を効率的に行います。3 つのアプローチ(ボディごとの Group、mjv_updateScene による公式 WASM デモ連携、リモート Python プロセスからの WebSocket ストリーム)をサポートし、用途に応じて選択できます。さらに、Gemini Robotics-ER 向けのカメラスナップショットや動画キャプチャ、ER 座標 [y, x] のレイキャストによる 3D 点への変換、3D 点から画像座標への投影などの知覚機能も提供します。典型的なレンダリングの不具合(横向き、ねじれ、サイズ違い、空白など)の原因と解決策をまとめたデバッグガイドも含まれており、Franka ピックアンドプレースデモがリファレンス実装として付属しています。

対応~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. ボディごとに 1 つの Group(Franka デモ)各ボディグループは自身の geom_pos/geom_quat でジオメトリを保持。フレームごとに xpos/xquat をコピー。デフォルト。userData.bodyID によるピッキング。任意のポーズソースで動作。
B. mjv_updateScene(公式デモ)scene.geoms エントリごとに 1 つのプールされたメッシュを同期MuJoCo 独自の装飾(接触、腱、サイト)が必要な場合
C. リモート PythonPython がステップ実行し、xpos/xquat を WebSocket でストリーミング。ブラウザは A を実行。GPU、ROS、実機ロボットブリッジ、高負荷計算

ジオメトリ変換: 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 を参照)。ジオメトリ単位の代替方法は data.geom_xpos と data.geom_xmat(行優先 3×3 → Matrix4.set(r00,r01,r02,0, r10,…))を使用し、ジオメトリは単位ポーズに配置します。

4. ER 点 ↔ 3D

// キャプチャ画像上の ER [y, x] 0-1000 -> キャプチャに使用した SAME カメラからのレイ
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 の並び替え忘れ
ボックスが 2 倍の大きさサイズが半サイズであることを忘れている
円柱やカプセルの向きが逆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(ボディごとに 1 つのグループ、userData.bodyID)、erGroup(上下に動くコーンマーカー)、インスタンス化された接触マーカー、 カメラトゥイーン(moveCameraTo)、および getCanvasSnapshot(w, h, mime) を使用します。
  • project2DTo3D(x, y, camPos, lookAt) はメインカメラをクローンし、キャプチャ視点に配置し((0, -0.01, 2.0) で原点を注視)、simGroup メッシュをレイキャストします。
  • rendering/GeomBuilder.ts はジオメトリ→メッシュファクトリを処理します。グループ 3 のジオメトリはスキップされ、平面は Reflector 床になり、 メッシュジオメトリは 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.

関連スキル