mcpField
S
spatial-reasoning

spatial-reasoning

github.com/isaac-sim開発4.4k38 回インストール5 日前に更新
リポジトリ

spatial-reasoning について

spatial-reasoningは、Isaac Sim利用者向けにOpenUSDシーンの座標計算と空間配置を支援するスキルです。アセットのスケーリングや配置、カメラの向き、グリッド生成、衝突確認のヘルパーを備えています。

主な機能

  1. 01ステージ単位に基づくアセットスケール計算
  2. 02スケール・回転・平行移動の合成
  3. 03カメラの注視回転の計算
  4. 04グリッド位置の生成
  5. 05ウェイポイントアニメーションのベイク
  6. 06ゾーンや配置の重なり確認

活用シーン

  1. 01OpenUSDシーンへの異なるスケールのアセット配置
  2. 02倉庫設備の重複を避けた配置
  3. 03レイアウト検証用カメラの配置
  4. 04シーン内のウェイポイント移動生成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/isaac-sim/isaacsim --skill spatial-reasoning

インストール後、Claude Code で/spatial-reasoningを入力して読み込まれたか確認します。

同梱ファイルadvanced.mdscripts/spatial.py

詳細

3D空間推論

目的

USDシーンを構成する際に、座標計算、バウンディングボックス分析、衝突しないレイアウト、視線方向を考慮した配置、軽量なパス/グリッドヘルパーを適用します。

前提条件

  • Isaac Sim をビルド済みであること($ISAAC_SIM_DIR または _build/linux-x86_64/release)。
  • 最新のドライバーを備えた NVIDIA GPU(nvidia-smi)。
  • isaac-sim-orchestrator のシェル環境変数契約:$ISAAC_SIM_DIR、$ISAAC_LAB_DIR、$WORKSPACE_DIR。

制限事項

  • 特に記載がない限り、Isaac Sim 6/Kit 110 を対象とします。
  • サポート対象外のエッジケースについては、NVIDIA の公式ドキュメントに代わるものではありません。

トラブルシューティング

エラー/症状原因解決策
拡張機能またはインポートが見つからない$ISAAC_SIM_DIR の誤り、またはビルドが古い環境変数を _build/linux-x86_64/release に設定するか、再ビルドします
フレームが黒い、または空ライトがない、または RTX 以外のレンダリングモードドームライト/キーライトを追加し、RTX/PathTracing の設定を確認します
ステージの読み込み時または最初のレンダリング時にハングするMDL のコンパイル、またはステージが大きすぎるisaac-sim-troubleshooting の切り分け手順に従います

OpenUSD シーンでオブジェクトを正しく配置するための、空間計算の基礎です。

利用可能なスクリプト

スクリプト目的引数
scripts/spatial.py空間処理--help でスクリプトを参照

スクリプトの実行

スキル実行ヘルパーを公開するエージェントランタイムでは、run_script() を使用してヘルパーを呼び出します。

run_script("scripts/spatial.py", args=["--help"])

ビルド済みの Isaac Sim ツリーから実行する場合は、_build/*/release の ./python.sh(Linux)または python.bat(Windows)を使って同じファイルを実行します。または、シミュレーターを必要としない場合はシェルヘルパーを直接実行します。

座標系

  • ステージ:Z-up、metersPerUnit=1.0(メートル)
  • すべての配置座標の単位はメートルです
  • 回転には常に AddRotateXYZOp() を使用します。個別の軸操作は使用しないでください

metersPerUnit の変換(重要)

アセットにはそれぞれ独自の metersPerUnit があります。一般的な値:

  • 1.0 = メートル(ロボット、環境)
  • 0.01 = センチメートル(多くの KION lightspeed アセット、SimModelAnimation、コンポジション)

ルール: アセットを参照する前に、metersPerUnit を読み取ります。

asset_stage = Usd.Stage.Open(asset_path)  # Must use Usd.Stage, NOT Sdf.Layer
mpu = UsdGeom.GetStageMetersPerUnit(asset_stage)

Sdf.Layer.FindOrOpen() はバイナリの .usd crate ファイルで失敗しても通知しません。常に None を返すため、mpu の検出には絶対に使用しないでください。

アセットの実際のサイズを取得する方法:

bbox_cache = UsdGeom.BBoxCache(Usd.TimeCode.Default(), [UsdGeom.Tokens.default_])
raw_range = bbox_cache.ComputeWorldBound(default_prim).ComputeAlignedRange()
raw_size = raw_range.GetMax() - raw_range.GetMin()
real_size_meters = [raw_size[i] * mpu for i in range(3)]

変換行列の順序(重要)

スケール、回転、平行移動を適用してアセットを配置する場合:

# CORRECT: Translate * Rotate * Scale
# Scale shrinks asset to meters, Rotate orients it, Translate positions it
scale_mat = Gf.Matrix4d().SetScale(Gf.Vec3d(mpu, mpu, mpu))
rot_mat = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(0,0,1), heading_deg))
trans_mat = Gf.Matrix4d().SetTranslate(Gf.Vec3d(x, y, z))
xf.MakeMatrixXform().Set(trans_mat * rot_mat * scale_mat)

誤り: scale * translate は平行移動ベクトルもスケーリングするため、mpu=0.01 では 3m のオフセットが 0.03m になります。

配置ヘルパー

def place(stage, prim_path, asset_path, x, y, z=0, rot_z=0, mpu=0.01):
    """Place an asset with correct metersPerUnit scaling."""
    prim = stage.DefinePrim(prim_path, "Xform")
    prim.GetReferences().AddReference(asset_path)
    xf = UsdGeom.Xformable(prim)
    S = Gf.Matrix4d().SetScale(Gf.Vec3d(mpu, mpu, mpu))
    R = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(0,0,1), rot_z))
    T = Gf.Matrix4d().SetTranslate(Gf.Vec3d(x, y, z))
    xf.MakeMatrixXform().Set(T * R * S)
    return prim

視線方向を考慮したカメラ計算

def look_at_rotation(cam_pos, target_pos):
    """Compute XYZ Euler rotation for camera to look at target. Z-up stage."""
    dx = target_pos[0] - cam_pos[0]
    dy = target_pos[1] - cam_pos[1]
    dz = target_pos[2] - cam_pos[2]
    horiz = math.sqrt(dx*dx + dy*dy)
    pitch = math.degrees(math.atan2(horiz, -dz))
    yaw = math.degrees(math.atan2(dx, -dy))
    return Gf.Vec3f(pitch, 0, yaw)

グリッドレイアウト

def grid_positions(n_items, spacing, origin=(0,0)):
    """Generate grid positions for n items with given spacing."""
    cols = min(8, math.ceil(math.sqrt(n_items * 1.5)))
    positions = []
    for i in range(n_items):
        row, col = divmod(i, cols)
        x = origin[0] + col * spacing
        y = origin[1] + row * spacing
        positions.append((x, y))
    return positions, cols

ベイク済みウェイポイントアニメーション

scripts/spatial.py の bake_waypoints() を参照してください(28 行)。

ゾーン境界チェック

def in_zone(x, y, zone):
    """Check if (x,y) is within a zone dict with 'x':[min,max], 'y':[min,max]."""
    return zone['x'][0] <= x <= zone['x'][1] and zone['y'][0] <= y <= zone['y'][1]

外殻と内部の座標合わせ(重要)

mpu=0.01 の KION mega warehouse の外殻は、ステージ座標で約 170×100m です。特定のゾーン座標(例:X=30-60 のラック)に内部のキューブ/アセットを配置する場合、カメラは建物の原点ではなく、レイアウトゾーン内に配置する必要があります。

ヒーローカメラのルール: カメラは (zone_start_x + 3, zone_center_y, 2.5) に配置し、ゾーンの内側を向けます。外殻の壁のジオメトリ内に入る (0, y, z) には配置しないでください。

俯瞰カメラのルール: (layout_center_x, layout_center_y, max(W,D)*0.9) に配置し、焦点距離を 12mm にします。これはレイアウト検証に最適なショットです。必ず最初にこれをレンダリングしてください。

カメラとレイアウトの一致: レンダリング前に、カメラの位置が外殻内にあるだけでなく、配置したコンテンツのバウンディングボックス内にあることを確認します。

レンダリングファイルサイズの検証

サイズ意味
~82KB空の灰色 — 何もレンダリングされていません
~275KBビューポートのグリッドのみ — ステージは読み込まれていますが、カメラには何も映っていません
1-2MBシーンの一部 — 一部のジオメトリが表示されています
2-5MB詳細を含むシーン全体
すべてのビューで同じサイズカメラの切り替えに失敗しています — すべてのキャプチャが同じビューからのものです

ステージ内のすべてのレンダリングのバイト数が同一の場合、カメラパスの切り替えが反映されていません。 対処方法:パスを変更するだけでなく、各ショットで新しいカメラプリムを作成してください(削除して再定義)。

KION アセットのスケールマップ(重要 — 2026-03-14)

KION のアセットツリーでは単位が混在しています。必ず Usd.Stage.Open() と UsdGeom.BBoxCache で検証してください。

アセットカテゴリ例生の境界ボックス実際の単位スケール係数
シェル(sm_warehouse_mega)生で 18586×14200センチメートル0.01
GSRC モジュール生で 58.3×82.8×8.4メートル1.0
コンベヤー生で 0.9×6.7×1.7メートル1.0
PackStation生で 2.9×2.6×2.9メートル1.0
Depalletizer生で 2.8×1.2×3.3メートル1.0
PutAway Rack生で 0.8×5.3×2.0メートル1.0
Trolly生で 0.7×1.6×1.3メートル1.0
SimReady のラック2.5×1.3×3.7メートル1.0
SimReady のバリア6.1×0.5×1.7メートル1.0
claw_lighting.usd生で約 17000センチメートル0.01

ルール:0.01 が必要なのはシェルとライティングのみです。その他すべての KION 組み立てアセットの単位はメートルです。

メートル単位のアセットを mpu=0.01 で配置すると、100 分の 1 の大きさになり、見えなくなります(不可視)。このバグはシーンを気付かれないまま壊します。アセットは USD 内に「存在」していても、レンダリング時にはサブセンチメートルの小ささになります。

検証コマンド:

stage = Usd.Stage.Open(asset_path)
bbox = UsdGeom.BBoxCache(0, [UsdGeom.Tokens.default_]).ComputeWorldBound(stage.GetPseudoRoot()).ComputeAlignedRange()
size = bbox.GetMax() - bbox.GetMin()
# If any dimension > 1000 → centimeters (use 0.01)
# If all dimensions < 100 → meters (use 1.0)

よくある落とし穴

0a. シェル内部の境界 ≠ シェルの境界ボックス — sm_warehouse_mega の境界ボックスは (-13.2,-5.2)→(172.6,136.8) ですが、Corner モジュールは X=0.6 から、Center(床)は X=11.6 から始まります。実際に使える内部領域は X=3-170、Y=0-134 です。ルートだけでなく、必ずモジュールの子の境界ボックスを確認してください。 0b. UsdGeom.Cube の extent は [-1,1]³(サイズ 2)です — w/2, d/2, h/2 でスケールしてください。w, d, h ではありません。寸法全体を使うと、すべてのブロックが意図したサイズの 2 倍になります。このため、uber warehouse v2 で 14,570 件の誤検出の干渉が発生しました。 0b. SetTranslateOnly() は Gf.Matrix4d のスケールを消去します — mat[0][0]=0.01 を設定してから mat.SetTranslateOnly(...) を呼び出すと、スケールは単位行列に戻ります。16 個の値を明示して行列を構築してください:Gf.Matrix4d(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, tx,ty,tz,1)。これがシェルのスケールが 100 倍になるバグの原因でした。 0c. ワールド座標の差分 ≠ 子のローカル座標の差分 — 作成時に設定された子のローカル回転/スケールを維持したまま、ワールドベクトル分だけ子を移動するには、親の逆回転/逆スケールを通してベクトルを変換し、平行移動の行のみを書き換えます。ローカル平行移動にワールド差分をそのまま加えると、親の回転/スケールが適用されます(親が単位変換または平行移動のみでない場合、動きが誤ります)。

  1. Sdf.Layer.FindOrOpen はバイナリの .usd クレートファイルを開けません — Usd.Stage.Open を使用してください
  2. 行列の順序は T * R * S であり、S * R * T ではありません
  3. カメラはカメラ空間で -Z 方向、つまり Z-up ステージでは真下を向くようにデフォルト設定されています(上から見る場合、回転は不要です)
  4. xformOp:translate already exists in xformOpOrder → AddTranslateOp() ではなく MakeMatrixXform() を使用してください
  5. DHGen の人間モデルには xformOps がすでに定義されています — クリアして置き換える MakeMatrixXform() を使用する必要があります
  6. シェルは約 170×100m です — レイアウト領域は通常、その一部です。カメラは原点ではなく、レイアウト領域内に配置してください。
  7. Isaac Sim の vp.camera_path は、レンダリングを短時間に連続して行うと、反映されないことがあります — 各ショットで stage.RemovePrim() + UsdGeom.Camera.Define() を使って新しいカメラプリムを必ず作成してください
  8. RT2 は収束に 200 フレーム以上の安定化が必要です。それより少ないと、レンダリングにノイズが生じたり、不完全になったりします。

学んだ教訓 — GSRC の統合(2026-03-12)

スケールに関する注意
  • 設置面積が 50m 以上の大型設備を配置する場合、施設も必ず比例して拡張してください
  • 120×80m の倉庫に 58×83m のモジュールを配置すると、避けられない重なりが発生します
  • 常に equipment_area / facility_area を計算してください — 30% を超える場合、施設を拡張する必要があります
衝突の防止
  • 新しいゾーンを配置する前に、既存のすべてのゾーンのバウンディングボックスを確認します
  • ゾーンの重なりは、急速に規模を拡大する際の最大の失敗要因です(V7/V8 では重なりが発生しました)
  • 座標ベースの衝突検出を使用します:if new_x_range overlaps existing_x_range AND new_y_range overlaps existing_y_range → collision
バリエーションのワークフロー
  • バリエーションを一度に1つ作成し、レンダリングして提示し、フィードバックを得て、反復します
  • 中間結果を提示せずに、複数のバリエーションをまとめて作成しないでください
  • 各バリエーションには、ゾーンを移動するだけではなく、独自の空間設計思想を持たせます
検証としての占有マップ
  • 空間の妥当性を確認するため、各バリエーションの後に占有マップを生成します
  • free_space% < 40% の場合、レイアウトが密集しすぎており、ロボットのナビゲーションに適しません
  • free_space% > 80% の場合、レイアウトに無駄なスペースが多すぎます
  • 倉庫に適した範囲は、空きスペースが55~70%です

高度なトピック

詳細については、advanced.md を参照してください。

コメント