关于 spatial-reasoning
spatial-reasoning 是面向 Isaac Sim 用户、用于 OpenUSD 场景坐标计算与空间布局的技能,提供资产缩放、放置、摄像机朝向、网格和碰撞检查辅助功能。
主要功能
- 01根据场景单位计算资产缩放
- 02组合缩放、旋转和平移变换
- 03计算摄像机注视旋转
- 04生成网格位置
- 05烘焙路径点动画
- 06检查区域与布局重叠
适用场景
- 01在 OpenUSD 场景中放置不同尺度的资产
- 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的 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 | |
| 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 倍(小到看不见)。这个错误会悄无声息地破坏场景——资产在 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. 世界坐标增量 ≠ 子项局部坐标增量 — 若要在保留子项已设定的局部旋转和缩放的同时,按世界坐标向量移动子项,应通过父项的逆旋转和逆缩放映射该向量,然后只写入平移行。直接将世界坐标增量加到局部平移上,会使父项的旋转和缩放作用于该增量(导致错误移动,只要父项不只是单位变换或纯平移就会出现)。
Sdf.Layer.FindOrOpen无法打开二进制.usdcrate 文件 — 请使用Usd.Stage.Open- 矩阵顺序:
T * R * S,而不是S * R * T - 摄像机在摄像机坐标系中的默认朝向为 -Z,也就是在 Z-up 阶段中垂直向下(俯视无需旋转)
xformOp:translate already exists in xformOpOrder→ 使用MakeMatrixXform(),而不是AddTranslateOp()- DHGen 人物已定义 xformOps — 必须使用
MakeMatrixXform(),它会清除并替换现有变换 - 外壳约为 170×100 米 — 布局区域通常只是其中一部分。摄像机必须位于布局区域内,而不是原点。
- Isaac Sim 的
vp.camera_path有时会在快速连续渲染时悄无声息地失效 — 每个镜头都要通过stage.RemovePrim()+UsdGeom.Camera.Define()创建新的摄像机 prim - 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。