NVIDIA USD (Universal Scene Description) 在机器人仿真中的深度技术调研

一、USD 概述与核心概念

Universal Scene Description(USD)最初由 Pixar 开发,2016 年开源,现在是 Linux Foundation 旗下的 Academy Software Foundation(ASWF)项目。USD 是一个高性能的 3D 场景描述、合成和交换框架,在机器人仿真领域,它提供了统一的资产表示层,贯穿从 CAD 导入 -> 场景组装 -> 物理仿真 -> ROS 2 集成的全链路。

1.1 核心抽象概念

概念 定义 API 路径 关键方法
Prim(Primitive) USD 场景图的基本节点,类似文件系统中的目录 pxr.Usd.Prim UsdStage.DefinePrim(path), prim.GetChildren(), prim.GetParent()
Layer(图层) 携带场景描述的容器,支持非破坏性叠加 pxr.Sdf.Layer Sdf.Layer.FindOrOpen(path), stage.GetRootLayer(), layer.Export(path)
Stage(舞台) 由多层图层组合成的最终场景 pxr.Usd.Stage Usd.Stage.CreateNew(path), Usd.Stage.Open(path), stage.SetStartTimeCode()
Attribute(属性) Prim 上的具名数据值,可随时间变化 pxr.Usd.Attribute prim.CreateAttribute(name, type), attr.Get(time), attr.Set(value, time)
Relationship(关系) Prim 间的定向引用连接 pxr.Usd.Relationship prim.CreateRelationship(name), rel.AddTarget(path), rel.GetTargets()
Variant(变体) VariantSet 中的具名备选方案 pxr.Usd.VariantSet variantSet.AppendVariant(name), variantSet.SetVariantSelection(name)
VariantSet(变体集) 组合弧,用于打包离散替代方案 pxr.Usd.VariantSets prim.GetVariantSets(), variantSets.AppendVariantSet(name)
Payload(载荷) 延迟加载的引用弧,用于按需加载 pxr.Usd.Payloads prim.GetPayloads().Add(layerPath), payload.GetAssetPath()
Composition Arc(组合弧) 描述图层间关系的元数据 内置于 Layer/Stage references, payloads, inherits, specializes, variantSets

1.2 USD 文件格式

格式 扩展名 特点 使用场景
USDA .usda 纯文本格式,人类可读可编辑,UTF-8 编码 手动编辑、版本控制差异比较、Schema 定义
USDC .usdc 二进制格式,CRCs 校验,紧凑高效 运行时加载、大规模场景生产环境
USD .usd 任意格式封装器,自动检测 USDA/USDC 通用资产分发

USDA 示例 — 定义机器人关节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#usda 1.0

def Xform "robot" (
kind = "component"
prepend apiSchemas = ["PhysicsArticulationRootAPI"]
)
{
def Mesh "base_link" (
prepend apiSchemas = ["PhysicsCollisionAPI", "PhysicsMassAPI"]
)
{
float3[] extent = [(-0.5, -0.5, -0.5), (0.5, 0.5, 0.5)]
int[] faceVertexCounts = [4]
int[] faceVertexIndices = [0, 1, 2, 3]
point3f[] points = [(-0.5,-0.5,-0.5), (0.5,-0.5,-0.5), (0.5,0.5,-0.5), (-0.5,0.5,-0.5)]
float3 physics:angularVelocity = (0, 0, 0)
float3 physics:velocity = (0, 0, 0)
float physics:mass = 1.0
point3f physics:centerOfMass = (0, 0, 0)
matrix3d physics:inertiaTensor = [(1,0,0), (0,1,0), (0,0,1)]
rel material:binding = </Looks/robot_base_mat>
token physics:approximation = "convexHull"
}

def PhysicsRevoluteJoint "joint_1" (
prepend apiSchemas = ["PhysicsJointAPI"]
)
{
rel physics:body0 = </robot/base_link>
rel physics:body1 = </robot/link_1>
float physics:lowerLimit = -180.0
float physics:upperLimit = 180.0
float physics:maxForce = 1000.0
float physics:maxTorque = 1000.0
float3 physics:axis = (0, 0, 1)
point3f physics:localPos0 = (0, 0, 0)
point3f physics:localPos1 = (0, 0, 0.5)
quatf physics:localRot0 = (1, 0, 0, 0)
quatf physics:localRot1 = (1, 0, 0, 0)
}
}

1.3 USD 值类型(Sdf Value Types)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pxr import Sdf

# 常用值类型名称(Sdf.ValueTypeNames 枚举)
Sdf.ValueTypeNames.Bool # bool
Sdf.ValueTypeNames.Int # int
Sdf.ValueTypeNames.Float # float
Sdf.ValueTypeNames.Double # double
Sdf.ValueTypeNames.String # string
Sdf.ValueTypeNames.Token # token (字符串标识符)
Sdf.ValueTypeNames.Float3 # float[3]
Sdf.ValueTypeNames.Point3f # float[3] (点位语义)
Sdf.ValueTypeNames.Quatf # float[4] (旋转四元数)
Sdf.ValueTypeNames.Matrix3d # double[9] (3x3矩阵)
Sdf.ValueTypeNames.Matrix4d # double[16] (4x4变换矩阵)
Sdf.ValueTypeNames.Frame4d # double[16] (帧变换)
Sdf.ValueTypeNames.AssetPath # Sdf.AssetPath (资产引用路径)

1.4 USD 坐标系统

  • 右手坐标系(Y-up vs Z-up:USD 使用 Y-up,但 Isaac Sim 使用 Z-up
  • 单位:厘米(default,可在 Stage metadata 中覆盖)
  • 时间编码:(default 24 fps),可通过 stage.SetTimeCodesPerSecond() 调整

二、Isaac Sim 中的 USD 机器人描述模式

2.1 URDF Importer 扩展

扩展名称: omni.isaac.urdf
Python 包路径: omni.isaac.urdf
扩展 ID: omni.isaac.urdf-<version>

导入 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from omni.isaac.urdf import URDFImporter

# 方法 1:通过扩展 UI 操作(GUI 模式)
# Window -> URDF Importer

# 方法 2:通过 Python API 编程导入
importer = URDFImporter()
# 或通过扩展管理器获取
from omni.kit.viewport import get_viewport_interface
# ...

# 推荐的编程方式(Isaac Sim 4.x+)
from omni.isaac.core.utils.stage import add_reference_to_stage

# 加载 USD 转换后的机器人
usd_path = "/path/to/robot.usd"
add_reference_to_stage(usd_path, "/World/robot")

URDF → USD 转换参数表

URDF 元素 USD 对应 Schema 说明
<link> Xform + Mesh 渲染几何体 + 碰撞几何体
<link><inertial> PhysicsMassAPI, PhysicsCenterOfMassAPI 质量、质心、惯性张量
<joint type="revolute"> PhysicsRevoluteJoint 旋转关节
<joint type="prismatic"> PhysicsPrismaticJoint 滑动关节
<joint type="fixed"> PhysicsFixedJoint 固定关节
<joint type="continuous"> PhysicsRevoluteJoint(无 limit) 连续旋转关节
<joint><limit> physics:lowerLimit, physics:upperLimit 关节限位
<joint><origin> physics:localPos0/1, physics:localRot0/1 关节坐标系变换
<joint><axis> physics:axis 关节旋转/滑动轴
<transmission> PhysicsJointAPI 上的驱动参数 传动比、电机参数
<material> Material prim + Shader 材质颜色/纹理

URDF Import 配置 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"import_config": {
"fix_base": true,
"merge_fixed_joints": false,
"self_collision": false,
"joint_drive_type": "force",
"joint_drive_stiffness": 10000.0,
"joint_drive_damping": 100.0,
"joint_drive_max_force": 1000.0,
"density": 0.0,
"import_materials": true,
"import_inertia_tensor": true,
"distance_scale": 1.0,
"default_prim_type": "Mesh",
"make_physics": true,
"create_physics": true
}
}

2.2 机器人 USD Schema(Articulation Schema)

Isaac Sim 使用基于 USD Physics + PhysX Schema 的 Articulation 系统来定义机器人运动链。

核心 Schema 路径

1
2
3
4
# PhysX Articulation 核心 Schema
pxr/UsdPhysics → 物理属性定义
omni/physx/schema → PhysX 扩展属性
omni/isaac/core_prims → 高级 Python 封装

重要 API Schema

Schema 名称 导入路径 用途
PhysicsArticulationRootAPI pxr.UsdPhysics.PhysicsArticulationRootAPI 标记 Articulation 根节点
PhysicsJointAPI pxr.UsdPhysics.PhysicsJointAPI 关节基础属性
PhysicsRevoluteJoint pxr.UsdPhysics.PhysicsRevoluteJoint 旋转关节
PhysicsPrismaticJoint pxr.UsdPhysics.PhysicsPrismaticJoint 滑动关节
PhysicsFixedJoint pxr.UsdPhysics.PhysicsFixedJoint 固定关节
PhysicsSphericalJoint pxr.UsdPhysics.PhysicsSphericalJoint 球关节
PhysicsCollisionAPI pxr.UsdPhysics.PhysicsCollisionAPI 碰撞体标记
PhysicsMassAPI pxr.UsdPhysics.PhysicsMassAPI 质量属性
PhysicsRigidBodyAPI pxr.UsdPhysics.PhysicsRigidBodyAPI 刚体标记
PhysicsDriveAPI pxr.UsdPhysics.PhysicsDriveAPI 关节驱动参数
PhysicsDistanceJoint pxr.UsdPhysics.PhysicsDistanceJoint 距离约束关节
PhysicsD6Joint pxr.UsdPhysics.PhysicsD6Joint 6自由度关节(通用)

Articulation 关节属性参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pxr import UsdPhysics, Gf

# 获取关节并配置
joint = UsdPhysics.RevoluteJoint.Define(stage, "/robot/joint_1")
joint_api = UsdPhysics.JointAPI(joint)

# 关节限位
joint.CreateLowerLimitAttr(0.0) # float - 弧度
joint.CreateUpperLimitAttr(3.14) # float - 弧度

# 关节驱动 (PhysicsDriveAPI)
drive = UsdPhysics.DriveAPI.Apply(joint, "angular") # 或 "linear"
drive.CreateTypeAttr("force") # token: "force" | "acceleration"
drive.CreateMaxForceAttr(1000.0) # float
drive.CreateDampingAttr(100.0) # float
drive.CreateStiffnessAttr(10000.0) # float
drive.CreateTargetPositionAttr(0.0) # float - 目标位置(弧度)
drive.CreateTargetVelocityAttr(0.0) # float - 目标速度

# 关节摩擦力
joint.CreateBreakForceAttr(1e10) # float
joint.CreateBreakTorqueAttr(1e10) # float

# 碰撞体对 (CollisionGroups)
from pxr import PhysxSchema
collision_group = PhysxSchema.PhysxCollisionAPI.Apply(prim)
# 或使用 PhysicsCollisionGroup

2.3 Isaac Sim Robotics API 核心类

1
2
3
4
5
6
7
# Python API 导入路径概览
from isaacsim.core.prims import SingleArticulation # 单臂/单机器人控制
from isaacsim.core.prims import ArticulationView # 多机器人批处理
from isaacsim.core.api import World # 仿真世界主类
from isaacsim.core.utils.stage import add_reference_to_stage # 加载 USD 资产
from isaacsim.core.utils.extensions import enable_extension # 启用扩展
from isaacsim.core.utils.viewports import set_camera_view # 设置相机视角

SingleArticulation API 参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from isaacsim.core.prims import SingleArticulation
from isaacsim.core.utils.stage import add_reference_to_stage
import numpy as np

# 1. 加载机器人 USD 场景
add_reference_to_stage("robot.usd", "/World/robot")

# 2. 创建 Articulation 控制对象
robot = SingleArticulation(prim_path="/World/robot")

# 3. 初始化(必须在 world.reset() 之后)
robot.initialize()

# 4. 核心控制方法
dof_positions = robot.get_joint_positions() # -> np.ndarray [n_dof]
dof_velocities = robot.get_joint_velocities() # -> np.ndarray [n_dof]
dof_names = robot.get_dof_names() # -> list[str]

robot.set_joint_positions(positions=np.array([0.0, 0.5, -1.0, 0.0, 0.0, 0.0]))
robot.set_joint_velocities(velocities=np.zeros(6))
robot.set_joint_efforts(efforts=np.array([0.0, 10.0, 0.0, 0.0, 0.0, 0.0]))

# 获取/设置关节驱动模式
robot.get_joint_drive_modes() # -> list[int] (0=force, 1=acceleration, 2=velocity)
robot.set_joint_drive_modes([0]*6)

robot.get_articulation_controller().set_drive_targets(targets=np.array([0.5, 0.0, 0.0, 0.0, 0.0, 0.0]))
robot.get_articulation_controller().set_drive_velocity_targets(targets=np.zeros(6))

# 获取末端执行器变换
ee_path = "/World/robot/end_effector"
ee_transform = robot.get_object_local_pose(ee_path)

# 获取整个机器人的世界位姿
root_pose = robot.get_world_pose()

2.4 完整的 URDF → USD → Isaac Sim 工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
"""完整的 URDF 导入和机器人仿真流水线"""
import argparse
from pathlib import Path

def run_robot_simulation(usd_path: str, headless: bool = False):
from isaacsim import SimulationApp

config = {"headless": headless}
simulation_app = SimulationApp(config)

from isaacsim.core.utils.stage import add_reference_to_stage
from isaacsim.core.api import World
from isaacsim.core.prims import SingleArticulation

# 创建仿真世界
world = World(stage_units_in_meters=1.0)
world.scene.add_default_ground_plane()

# 加载机器人
robot_prim_path = "/World/robot"
add_reference_to_stage(usd_path, robot_prim_path)

# 创建控制对象
articulation = SingleArticulation(prim_path=robot_prim_path)

# 重置并初始化
world.reset()
articulation.initialize()

dof_count = len(articulation.get_dof_names())
print(f"Robot DOF: {dof_count}, Joints: {articulation.get_dof_names()}")

# 仿真循环
step = 0
while simulation_app.is_running():
world.step(render=True)

if not world.is_playing():
continue

# 正弦关节运动
import numpy as np
positions = np.zeros(dof_count)
if dof_count >= 1:
positions[0] = 0.5 * np.sin(step * 0.02)
if dof_count >= 2:
positions[1] = 0.3 * np.sin(step * 0.02 + 1.0)

articulation.set_joint_positions(positions)
step += 1

simulation_app.close()


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("usd_path", type=Path)
parser.add_argument("--headless", action="store_true")
args = parser.parse_args()
run_robot_simulation(str(args.usd_path.resolve()), args.headless)

2.5 机器人资产目录结构(最佳实践)

1
2
3
4
5
6
7
8
9
10
11
12
13
robot_name_v1/
├── robot_name.urdf # 源 URDF 文件
├── robot_name/ # USD 资产目录
│ ├── robot_name.usd # 主 USD 文件(二进制/文本)
│ └── configuration/ # Isaac Sim 配置
│ ├── robot_name_base.usd # 安装配置(基座固定方式)
│ ├── robot_name_physics.usd # 物理参数微调
│ └── robot_name_sensor.usd # 传感器挂载配置
├── meshes/ # STL/OBJ 网格文件
│ ├── base_link.stl
│ ├── link_1.stl
│ └── ...
└── limits.xml # 关节限位参数

2.6 Robot Inspector WIndow

Isaac Sim 的 Robot Inspector 窗口可用于调试 USD 机器人结构:

  • 位置: Window → Robot Inspector
  • 功能: 查看关节树、齿轮传动关系、碰撞体层级
  • 输出: 导出 USD 关节/碰撞体分析报告

三、NVIDIA Robot Description Schema

3.1 官方 Robot Schema 概述

NVIDIA 定义了以下 USD Schema 用于机器人描述:

Schema 文件 扩展 ID 用途
omni.isaac.robot_schema Robot Schema 定义机器人组件类型(关节、连杆、传感器)
omni.isaac.sensor_schema Sensor Schema 定义传感器类型(相机、Lidar、IMU)
omni.isaac.articulation_schema Articulation Schema 定义运动链关节类型和驱动模式

3.2 Robot Schema API 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# USD 场景中的机器人 prim 通过 apiSchemas 元数据标记
# 在 USD 文件中,这表现为:
"""
def Xform "robot" (
kind = "component"
prepend apiSchemas = [
"PhysicsArticulationRootAPI",
"PhysxArticulationAPI"
]
)
"""

# Python 中通过以下方式访问:
from pxr import UsdPhysics, PhysxSchema

# 检查 prim 是否为 Articulation 根
prim = stage.GetPrimAtPath("/World/robot")
if UsdPhysics.ArticulationRootAPI(prim):
print("Is an articulation root")

# 应用 PhysX Articulation API
PhysxSchema.PhysxArticulationAPI.Apply(prim)
articulation_api = PhysxSchema.PhysxArticulationAPI(prim)
articulation_api.GetArticulationEnabledAttr().Set(True)

# 设置求解器迭代次数
articulation_api.GetSolverPositionIterationCountAttr().Set(8)
articulation_api.GetSolverVelocityIterationCountAttr().Set(4)

3.3 Joint Drive 类型与参数

驱动类型 token 值 控制方式 适用场景
Force "force" 直接施加力/力矩 一般力矩控制
Acceleration "acceleration" 设定加速度 末端执行器控制
Velocity "velocity" 设定速度 PD 控制 轨迹跟踪

驱动参数 XML(URDF 导入配置等价):

1
2
3
4
5
6
7
8
9
10
{
"joint_drive": {
"type": "force",
"stiffness": 10000.0,
"damping": 100.0,
"max_force": 1000.0,
"target_position": 0.0,
"target_velocity": 0.0
}
}

3.4 PhysX Articulation 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pxr import PhysxSchema

# PhysX Articulation 属性
articulation = PhysxSchema.PhysxArticulationAPI.Apply(root_prim)

# 收缩因子 (shrink factor) - 碰撞体收缩以避免被卡住
articulation.GetShrinkFactorAttr().Set(0.1) # 默认 0.0

# 最大关节速度/加速度
articulation.GetMaxJointVelocityAttr().Set(100.0)
articulation.GetMaxJointAccelerationAttr().Set(1000.0)

# 睡眠阈值 - 关节停止运动后进入睡眠状态的阈值
articulation.GetSleepThresholdAttr().Set(0.001)

# 分离线性/角度阻尼
articulation.GetLinearDampingAttr().Set(0.0)
articulation.GetAngularDampingAttr().Set(0.0)

# 启用/禁用自我碰撞
articulation.GetSelfCollisionAttr().Set(False)

四、USD Composer 工作流

4.1 USD Composer(原 Omniverse Composer)

USD Composer 是 NVIDIA Omniverse 平台中用于场景组装和 USD 编辑的核心应用。在机器人工作流中:

关键扩展:

扩展 ID 功能
omni.kit.window.stage Stage 窗口 - 场景图层次浏览
omni.kit.window.viewport 视口 - 3D 场景实时预览
omni.kit.window.property 属性编辑器 - 编辑 prim 属性
omni.kit.window.content_browser 内容浏览器 - 资产面板
omni.usd.schema USD Schema 管理
omni.graph OmniGraph 可视化编程

4.2 机器人场景组装流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
┌─────────────────────────────────────────────────────────┐
│ USD Composer 工作流 │
├─────────────────────────────────────────────────────────┤
│ │
│ ① URDF Import │
│ ├─ File → Import → URDF │
│ ├─ 配置 fix_base / merge_fixed_joints / self_collision │
│ └─ 生成 .usd + .usdc (二进制几何缓存) │
│ │
│ ② Robot Setup │
│ ├─ PhysicsArticulationRootAPI 应用到根节点 │
│ ├─ 检查关节类型 (Revolute / Prismatic / Fixed) │
│ ├─ 配置 Joint Drive (Stiffness / Damping) │
│ └─ 配置 CollisionPair (自碰撞排除) │
│ │
│ ③ Scene Assembly │
│ ├─ 加载环境 USD (workspace, table, conveyor) │
│ ├─ 放置机器人 (Transform / Xform) │
│ ├─ 添加传感器 (Camera / Lidar / IMU) │
│ └─ 配置材质 (Material prim + MDL Shader) │
│ │
│ ④ OmniGraph 配置 │
│ ├─ 创建 ROS2 通信图 (Publisher / Subscriber) │
│ ├─ 配置关节命令订阅 (/joint_commands) │
│ ├─ 配置关节状态发布 (/joint_states) │
│ └─ 配置传感器数据流 │
│ │
│ ⑤ Simulation & Verification │
│ ├─ Physics Play → 验证运动链 │
│ ├─ ROS2 Bridge → 测试外部控制 │
│ └─ Export → 发布 USD 资产包 │
│ │
└─────────────────────────────────────────────────────────┘

4.3 USD Composer Python 脚本自动化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 通过 Python 脚本在 USD Composer 中操作
# 需要 isaacsim 或 omniverse-kit 环境

from pxr import Usd, UsdGeom, Sdf, Gf
import carb

# 获取当前 Stage
stage = Usd.Stage.Open("/path/to/scene.usd")

# 创建 Xform 节点放置机器人
xform = UsdGeom.Xform.Define(stage, "/World/robot_1")
translate_op = xform.AddTranslateOp()
translate_op.Set(Gf.Vec3d(1.0, 0.0, 0.0))

# 添加引用到机器人 USD 文件
prim = stage.GetPrimAtPath("/World/robot_1")
ref_list = prim.GetReferences()
ref_list.AddReference("./robot.usd")

# 添加 PhysicsArticulationRootAPI
from pxr import UsdPhysics
UsdPhysics.ArticulationRootAPI.Apply(prim)

# 保存
stage.GetRootLayer().Save()

五、USD 与 ROS 2 的集成

5.1 isaac_ros_essentials 概述

仓库地址: https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_common
ROS 2 包: 包含在 isaac_ros_essentials

ROS 2 包 功能 依赖
isaac_ros_visual_slam 视觉 SLAM Isaac Elbrus GPU 加速
isaac_ros_dnn_encoders DNN 视觉编码器 TensorRT
isaac_ros_apex 点云处理加速 CUDA
isaac_ros_nitros 类型桥接 (NITROS) NVIDIA 传输优化
isaac_ros_occupancy_grid_localizer 占据网格定位 GPU 加速
isaac_ros_bi3d 立体深度估计 双目相机

5.2 Isaac Sim ROS 2 Bridge

扩展名称: omni.isaac.ros2_bridge
扩展 ID: omni.isaac.ros2_bridge
导入路径: omni.isaac.ros2_bridge

支持的 ROS 2 话题

ROS 2 话题类型 方向 Isaac Sim 配置方式
/joint_states (sensor_msgs/JointState) 发布 ArticulationController 自动发布
/joint_commands (std_msgs/Float64MultiArray) 订阅 ROS2JointControl Omnigraph 节点
/tf / /tf_static (tf2_msgs/TFMessage) 发布 ROS2TransformTree 节点
/odom (nav_msgs/Odometry) 发布 ROS2Odometry 节点
/scan (sensor_msgs/LaserScan) 发布 ROS2LidarScan 节点
/camera_info / /image_raw (sensor_msgs) 发布 ROS2Camera 节点
/clock (rosgraph_msgs/Clock) 发布 ROS2Clock 节点
/cmd_vel (geometry_msgs/Twist) 订阅 ROS2AckermannControl / ROS2DiffDriveControl

ROS 2 Bridge 配置 YAML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ros2_bridge_config.yaml
bridge:
enabled: True
namespace: "" # 命名空间
qos_profile: "DEFAULT" # QOS 配置文件
publish_frequency: 100.0 # 发布频率 Hz

# 话题映射
topics:
joint_states:
type: sensor_msgs/JointState
publish: True
rate: 100
joint_commands:
type: std_msgs/Float64MultiArray
subscribe: True
clock:
type: rosgraph_msgs/Clock
publish: True
rate: 100
tf:
type: tf2_msgs/TFMessage
publish: True
rate: 50

5.3 OmniGraph ROS 2 节点

OmniGraph 是 Isaac Sim 的可视化编程系统。ROS 2 集成通过 ROS 2 OmniGraph 节点实现:

核心 ROS 2 OmniGraph 节点:

1
2
3
4
5
6
7
8
9
10
11
ROS2SubscribeJointState      # 订阅关节状态
ROS2PublishJointState # 发布关节状态
ROS2PublishTransformTree # 发布 TF 树
ROS2SubscribeTwist # 订阅速度指令
ROS2PublishOdometry # 发布里程计
ROS2PublishLaserScan # 发布激光扫描
ROS2PublishCameraInfo # 发布相机信息
ROS2PublishImage # 发布图像话题
ROS2PublishClock # 发布时钟话题
ROS2SimulationControlAction # 仿真控制 Action 服务
ROS2WorldControlAction # 世界控制 Action 服务

Python 方式配置 ROS 2 OmniGraph

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from omni.isaac.ros2_bridge import omni_graph_ros2

# 创建 ROS 2 发布节点
publisher = omni_graph_ros2.ROS2PublishJointState(
node_name="isaac_sim_joint_state_publisher",
topic_name="/joint_states",
queue_size=10,
publish_rate=100.0,
)

# 创建 ROS 2 订阅节点
subscriber = omni_graph_ros2.ROS2SubscribeJointState(
node_name="isaac_sim_joint_command_subscriber",
topic_name="/joint_commands",
queue_size=10,
)

# 连接图
# (使用 OmniGraph 的 Graph 和 Node API)
from omni.graph.core import create_graph, create_node

5.4 ROS 2 + Isaac Sim 仿真控制架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌────────────────────────────────────────────────────────────┐
│ ISAAC SIM (仿真环境) │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ PhysX │───▶│ ROS2 Bridge │───▶│ ROS 2 Master │ │
│ │ Physics │ │ (OmniGraph) │ │ / DDS │ │
│ └──────────┘ └──────────────────┘ └───────┬───────┘ │
│ │ │ │
│ ┌────▼──────┐ │ │
│ │ Renderer │ │ │
│ └───────────┘ │ │
│ ▼ │
└────────────────────────────────────────────────────┬─────────┘

┌─────────────────────────────────────┐
│ ROS 2 机器人栈 (外部) │
│ │
│ ┌───────────┐ ┌──────────────┐ │
│ │ MoveIt 2 │ │ Nav2 / SLAM │ │
│ └───────────┘ └──────────────┘ │
│ ┌───────────┐ ┌──────────────┐ │
│ │ RViz2 │ │ RQT / 调试 │ │
│ └───────────┘ └──────────────┘ │
└─────────────────────────────────────┘

5.5 URDF TurtleBot 导入 + ROS 2 控制示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
"""
导入 TurtleBot URDF 并通过 ROS 2 控制
参考: Isaac Sim Tutorial "URDF Import: Turtlebot"
"""

def setup_turtlebot_ros2():
from isaacsim import SimulationApp
simulation_app = SimulationApp({"headless": False})

from omni.isaac.core import World
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.ros2_bridge import omni_graph_ros2

world = World()
world.scene.add_default_ground_plane()

# 加载 TurtleBot
add_reference_to_stage("/Isaac/Assets/robots/turtlebot/turtlebot.usd", "/World/turtlebot")

# 初始化 ROS 2 桥接
from omni.isaac.ros2_bridge import acquire_ros2_bridge
ros2_bridge = acquire_ros2_bridge()

# 配置 OmniGraph
# ... (创建 ROS2PublishJointState, ROS2SubscribeTwist 等)

world.reset()

while simulation_app.is_running():
world.step(render=True)

# ROS 2 spin 由桥接自动处理

simulation_app.close()

六、USD 机器人大规模仿真架构

6.1 多机器人场景 USD 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 加载多个机器人的推荐方式
robot_usd_paths = {
"robot_1": "/Assets/Robots/robot_a.usd",
"robot_2": "/Assets/Robots/robot_b.usd",
"robot_3": "/Assets/Robots/robot_c.usd",
}

for name, path in robot_usd_paths.items():
prim_path = f"/World/{name}"
add_reference_to_stage(path, prim_path)

# 设置位姿
xform = UsdGeom.Xform.Get(stage, prim_path)
if xform:
translate_op = xform.AddTranslateOp()
translate_op.Set(Gf.Vec3d(x_positions[idx], 0.0, 0.0))

6.2 ArticulationView(批量控制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from isaacsim.core.prims import ArticulationView

# 批量管理多个 Articulation
# 用于 Isaac Lab 风格的并行环境
articulation_view = ArticulationView(
prim_paths_expr="/World/robot_*", # glob 匹配
positions=None,
rotations=None,
)

# 批量化控制
articulation_view.set_joint_positions(joint_positions_batch) # [n_envs, n_dof]
articulation_view.set_joint_velocities(joint_velocities_batch)
articulation_view.reset()

6.3 USD Payload 延迟加载

1
2
3
4
5
6
7
8
9
10
11
# 使用 Payload 实现大规模场景的按需加载
prim = stage.DefinePrim("/World/RobotLibrary")
payload_list = prim.GetPayloads()
payload_list.AddPayload(
assetPath="./robot.usd",
primPath="/World/RobotLibrary/robot_instance_1"
)

# 只有当遍历到这个 prim 时才会实际加载
# 后续可以通过以下方式强制加载:
prim.Load()

七、USD Composer 与开发者工具链

7.1 关键扩展与工具

工具 扩展 ID 用途
USD Composer omni.create 主场景编辑器
USD View usdview USD 快速预览(命令行)
Kit SDK omni.kit 自定义扩展 SDK
Script Editor omni.kit.window.script_editor 内嵌 Python 编辑器
USD Inspector omni.kit.window.inspector USD 场景图调试器

7.2 命令行工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# usdview - 预览 USD 文件
usdview /path/to/robot.usd

# usdcat - 转换 USD 格式
usdcat /path/to/robot.usdc -o /path/to/robot.usda # 二进制→文本
usdcat /path/to/robot.usda -o /path/to/robot.usdc # 文本→二进制

# usdchecker - 验证 USD 完整性
usdchecker /path/to/scene.usd

# usddiff - 比较两个 USD 文件
usddiff /path/to/file_a.usd /path/to/file_b.usd

# usdresolve - 解析资产路径
usdresolve @robot.usd@

# usdrecord - 录制 USD 视口到图像/视频
usdrecord /path/to/scene.usd output.png

# sdfdump - 导出 SDF Layer 分析
sdfdump /path/to/layer.usda

7.3 USD 格式转换工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
            ┌──────────┐
│ CAD File │
│ (STEP/SLDPRT/IGS)
└────┬─────┘

┌────▼─────┐
│ CAD Converter │ (omni.kit.converter or Onshape Importer)
└────┬─────┘

┌────▼─────┐
│ .usd │ ← 中间 USD (带精细网格)
└────┬─────┘

┌─────────┴──────────┐
▼ ▼
┌─────────────┐ ┌──────────────┐
│ USDC (binary) │ │ USDA (text) │
│ 运行时加载 │ │ 版本控制/编辑 │
└─────────────┘ └──────────────┘

┌────▼─────┐
│ Isaac Sim │ ← USD + USD Physics
│ + ROS 2 │
└───────────┘

7.4 USD Physics / PhysX Debugging

1
2
3
4
5
6
7
8
9
10
# 在 Isaac Sim 中查看物理调试可视化
# Window → Physics Inspector → 显示碰撞体、关节、约束

# Python 调试接口
from omni.physx import get_physx_interface
physx_interface = get_physx_interface()
physx_interface.set_debug_visualization_flags(["COLLISION_SHAPES", "JOINTS", "CONTACT_POINTS"])

# 导出 PhysX 场景快照
physx_interface.export_scene_snapshot("/tmp/physx_snapshot.json")

八、参数汇总表

8.1 机器人 USD Schema 核心参数表

参数 类型 默认值 Schema 单位/说明
physics:mass float 0.0 PhysicsMassAPI kg
physics:density float 0.0 PhysicsMassAPI kg/m³
physics:centerOfMass point3f (0,0,0) PhysicsCenterOfMassAPI 局部坐标系
physics:inertiaTensor matrix3d diag(1) PhysicsMassAPI 惯性张量
physics:lowerLimit float -1e10 PhysicsJointAPI rad (旋转) / m (滑动)
physics:upperLimit float 1e10 PhysicsJointAPI rad / m
physics:axis float3 (0,0,0) PhysicsJointAPI 归一化轴方向
physics:localPos0 point3f (0,0,0) PhysicsJointAPI 父体局部位置
physics:localPos1 point3f (0,0,0) PhysicsJointAPI 子体局部位置
physics:breakForce float FLT_MAX PhysicsJointAPI 断裂力阈值
physics:breakTorque float FLT_MAX PhysicsJointAPI 断裂力矩阈值
drive:stiffness float 0.0 PhysicsDriveAPI 弹簧刚度
drive:damping float 0.0 PhysicsDriveAPI 阻尼系数
drive:maxForce float FLT_MAX PhysicsDriveAPI 最大驱动力
drive:targetPosition float 0.0 PhysicsDriveAPI 目标位置
drive:targetVelocity float 0.0 PhysicsDriveAPI 目标速度

8.2 URDF 与 USD 关节类型映射

URDF 关节类型 USD Joint Schema drive token 关节自由度 备注
revolute PhysicsRevoluteJoint angular 1 (旋转) +/-限位
continuous PhysicsRevoluteJoint angular 1 (旋转) 无限制旋转
prismatic PhysicsPrismaticJoint linear 1 (滑动) +/-限位
fixed PhysicsFixedJoint - 0 刚连接
floating PhysicsD6Joint 6 entries 6 自由6DOF
planar PhysicsPrismaticJoint x2 linear x2 2 XY平面运动
spherical PhysicsSphericalJoint angular x3 3 球旋转

8.3 ROS 2 Isaac Sim Bridge 包

仓库 描述 ROS 2 版本
isaac_ros_common 基础工具和启动文件 Humble / Iron / Rolling
isaac_ros_nitros NVIDIA 传输优化系统 Humble
isaac_ros_visual_slam GPU 加速视觉 SLAM Humble
isaac_ros_dnn_encoders DNN 视觉编码器 Humble
isaac_ros_apex 点云处理 Humble
isaac_ros_occupancy_grid_localizer 占据网格定位 Humble

九、故障排查指南

9.1 常见问题

问题 可能原因 排查方法
URDF 导入后机器人不动 未应用 ArticulationRootAPI 检查 prim metadata
关节反向旋转 轴方向错误 检查 physics:axis 符号
关节超限碎裂 碰撞体参数错误 检查碰撞体包围盒 + shrink factor
机器人飘在空中 质心错误 / 重力未启用 检查 physics:centerOfMass + physics:mass
ROS 2 无数据 Bridge 未启动 / 命名空间不匹配 检查 ros2_bridge 扩展状态
性能卡顿 碰撞体过于精细 使用凸包近似 (physics:approximation = "convexHull")

9.2 调试命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 打印完整的 USD 场景图
stage = Usd.Stage.Open("robot.usd")
for prim in stage.Traverse():
print(f"{prim.GetPath()}: {prim.GetTypeName()}")
for attr in prim.GetAttributes():
print(f" {attr.GetName()} = {attr.Get()}")

# 检查物理属性
from pxr import UsdPhysics, PhysxSchema
for prim in stage.Traverse():
if UsdPhysics.JointAPI(prim):
print(f"Joint: {prim.GetPath()}")
print(f" Body0: {UsdPhysics.JointAPI(prim).GetBody0Attr().Get()}")
print(f" Body1: {UsdPhysics.JointAPI(prim).GetBody1Attr().Get()}")

十、参考资源

10.1 官方文档

10.2 开源项目

10.3 关键 Python 包导入路径

1
2
3
4
5
6
7
8
9
10
11
12
# 核心 USD
from pxr import Usd, UsdGeom, Sdf, Gf, Vt, Tf
from pxr import UsdPhysics, PhysxSchema
from pxr import UsdShade, UsdLux # 材质和灯光

# Isaac Sim Robotics
from isaacsim.core.prims import SingleArticulation, ArticulationView
from isaacsim.core.utils.stage import add_reference_to_stage
from isaacsim.urdf import URDFImporter

# ROS 2 Bridge
from omni.isaac.ros2_bridge import acquire_ros2_bridge