spatial-reasoning について
spatial-reasoningは、Isaac Sim利用者向けにOpenUSDシーンの座標計算と空間配置を支援するスキルです。アセットのスケーリングや配置、カメラの向き、グリッド生成、衝突確認のヘルパーを備えています。
主な機能
- 01ステージ単位に基づくアセットスケール計算
- 02スケール・回転・平行移動の合成
- 03カメラの注視回転の計算
- 04グリッド位置の生成
- 05ウェイポイントアニメーションのベイク
- 06ゾーンや配置の重なり確認
活用シーン
- 01OpenUSDシーンへの異なるスケールのアセット配置
- 02倉庫設備の重複を避けた配置
- 03レイアウト検証用カメラの配置
- 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. ワールド座標の差分 ≠ 子のローカル座標の差分 — 作成時に設定された子のローカル回転/スケールを維持したまま、ワールドベクトル分だけ子を移動するには、親の逆回転/逆スケールを通してベクトルを変換し、平行移動の行のみを書き換えます。ローカル平行移動にワールド差分をそのまま加えると、親の回転/スケールが適用されます(親が単位変換または平行移動のみでない場合、動きが誤ります)。
Sdf.Layer.FindOrOpenはバイナリの.usdクレートファイルを開けません —Usd.Stage.Openを使用してください- 行列の順序は
T * R * Sであり、S * R * Tではありません - カメラはカメラ空間で -Z 方向、つまり Z-up ステージでは真下を向くようにデフォルト設定されています(上から見る場合、回転は不要です)
xformOp:translate already exists in xformOpOrder→AddTranslateOp()ではなくMakeMatrixXform()を使用してください- DHGen の人間モデルには xformOps がすでに定義されています — クリアして置き換える
MakeMatrixXform()を使用する必要があります - シェルは約 170×100m です — レイアウト領域は通常、その一部です。カメラは原点ではなく、レイアウト領域内に配置してください。
- Isaac Sim の
vp.camera_pathは、レンダリングを短時間に連続して行うと、反映されないことがあります — 各ショットでstage.RemovePrim()+UsdGeom.Camera.Define()を使って新しいカメラプリムを必ず作成してください - 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 を参照してください。