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. 01在 OpenUSD 场景中放置不同尺度的资产
  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 的 Shell 环境变量约定:$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

运行脚本

在提供技能执行辅助功能的 Agent 运行时中,使用 run_script() 调用辅助功能:

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

在已构建的 Isaac Sim 目录中,从 _build/*/release 使用 ./python.sh(Linux)或 python.bat(Windows)运行同一文件;如果 Shell 辅助脚本不需要模拟器,也可以直接执行。

坐标系

  • 场景:Z 向上,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包含细节的完整场景
所有视图的文件大小相同摄像机切换失败 — 所有捕获画面都来自同一视图

如果某个阶段内的所有渲染文件字节数都相同,说明摄像机路径切换没有生效。 修复方法:为每个镜头创建新的摄像机 prim(移除后重新定义),而不是只更改路径。

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
PackStation2.9×2.6×2.9 原始值米1.0
Depalletizer2.8×1.2×3.3 原始值米1.0
PutAway Rack0.8×5.3×2.0 原始值米1.0
Trolly0.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 倍(小到看不见)。这个错误会悄无声息地破坏场景——资产在 USD 中“存在”,但渲染出来的尺寸不到 1 厘米。

验证命令:

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 crate 文件 — 请使用 Usd.Stage.Open
  2. 矩阵顺序:T * R * S,而不是 S * R * T
  3. 摄像机在摄像机坐标系中的默认朝向为 -Z,也就是在 Z-up 阶段中垂直向下(俯视无需旋转)
  4. xformOp:translate already exists in xformOpOrder → 使用 MakeMatrixXform(),而不是 AddTranslateOp()
  5. DHGen 人物已定义 xformOps — 必须使用 MakeMatrixXform(),它会清除并替换现有变换
  6. 外壳约为 170×100 米 — 布局区域通常只是其中一部分。摄像机必须位于布局区域内,而不是原点。
  7. Isaac Sim 的 vp.camera_path 有时会在快速连续渲染时悄无声息地失效 — 每个镜头都要通过 stage.RemovePrim() + UsdGeom.Camera.Define() 创建新的摄像机 prim
  8. RT2 需要 200 帧以上的稳定帧才能收敛。帧数更少会导致渲染嘈杂或不完整。

经验总结 — GSRC 集成(2026-03-12)

缩放注意事项
  • 放置大型设备(占地面积 50 米以上)时,设施必须按比例扩大
  • 在 120×80 米的仓库中放置 58×83 米的模块,必然会产生不可能的重叠
  • 务必计算:equipment_area / facility_area — 若结果 > 30%,就需要扩大设施
碰撞预防
  • 放置新区域前,检查所有现有区域的边界框
  • 快速扩展时,区域重叠是首要失败原因(V7/V8 曾出现重叠)
  • 使用基于坐标的碰撞检测:if new_x_range overlaps existing_x_range AND new_y_range overlaps existing_y_range → collision
变体工作流
  • 一次构建一个变体 — 渲染 — 展示 — 获取反馈 — 迭代
  • 不要在展示中间结果前批量制作多个变体
  • 每个变体都应具有独特的空间设计理念,而不只是移动区域
将占用图用作验证
  • 每次生成变体后,都生成占用图,进行空间合理性检查
  • 如果 free_space% < 40%,则布局过于密集,不利于机器人导航
  • 如果 free_space% > 80%,则布局浪费的空间过多
  • 仓库的理想范围:55-70% free space

高级主题

详情请参阅 advanced.md。

评论

支持 Markdown