OvPhysX#

警告

不要将 OvPhysX 与 Kit 可视化器组合使用。physics=ovphysx --visualizer kit 等命令不受支持:ovphysx 从自身软件包加载依赖 USD 的 PhysX 插件,而 Kit 在同一进程内管理另一套独立的 USD 和插件。请使用 --visualizer newton_gl、--visualizer rerun 或 --visualizer viser;也可以不传 --visualizer,以无界面方式运行。

OvPhysX 无需 Omniverse Kit 即可运行 PhysX。它从 USD 的 PhysicsScene prim 读取场景级求解器设置,USD 无法表示的 GPU 缓冲区设置则由 OvPhysxCfg 保存。

通过 OvPhysxCfg 选择 OvPhysX:

from isaaclab.sim import SimulationCfg
from isaaclab_ov.physics import OvPhysxCfg

sim_cfg = SimulationCfg(physics=OvPhysxCfg())

为什么使用 OvPhysX?#

  • 无需 Kit 运行时。 无需 Isaac Sim 即可运行无界面仿真。

  • USD 配置。 场景创建工具可直接在 PhysicsScene prim 上设置求解器参数。

当前可用功能#

OvPhysX 支持以下组件。配置任务时,请留意表中的要求和限制。

组件

支持范围与限制

刚体与关节系统

刚体、刚体集合和关节系统。

接触感知

合力与过滤后的接触力、接触点及接触时序。位姿跟踪要求每个环境仅包含一个传感器刚体。

运动与坐标系感知

IMU、PVA 和坐标变换。关节力/力矩读数包含根刚体,采用关节子刚体一侧的坐标系。

射线投射

使用 Warp 网格的射线投射器与射线投射相机。标准射线投射器要求目标网格保持静止。

渲染与场景数据

场景数据和帧视图实现支持 OVRTX 等不依赖 Kit 的使用者,不能与 Isaac Sim / Kit 共用进程。

可变形物体

CUDA 上的体积和表面可变形体。同一视图内的物体必须具有相同的节点数,见 可变形体限制。

异构克隆

支持刚体、关节和肌腱布局相同的几何变体,见 异构克隆。

传感器配置见 传感器,渲染器选择见 渲染器。

安装#

ovphysx 扩展依赖要求 OvPhysX 0.6.4.72118723。在仓库根目录执行以下命令安装:

uv sync --inexact --extra ovphysx

--inexact 保留通过其他 extra 安装的包。使用 --extra ov 可同时安装公开发布的 OvPhysX 和 OVRTX 运行时。该组合 extra 使用 OVRTX 0.5.1.385782 与 OVStage 0.2.1;OVRTX 0.4.1 与此运行时组合不兼容。

可选的 OVPhysX 0.7 支持#

默认依赖仍固定为 OVPhysX 0.6.4。后端也接受 OVPhysX 0.7,包括开发版本,并根据已安装的运行时版本选择任务完成、舞台销毁、场景读取和 Schema 查找方式。两个版本均继续使用张量和接触绑定。

OVPhysX 0.7 不再自带 PhysX USD Schema。启动前,请在仿真环境中安装 physx-usd-schemas>=25.11.2。对这类自行调整依赖的环境,请使用 uv run --no-sync,避免恢复项目默认的依赖版本。Isaac Lab 会在不导入提供方模块的情况下定位 PhysX 和可变形体 Schema 目录,并在填充 USD 场景前注册;已安装的 Newton Schema 也会注册。请勿忽略 Schema 注册错误:注册过晚时必须重启进程,而且无法恢复填充场景时丢失的设置。

旧版关节 USD#

OVPhysX 0.7 不再读取 PhysxJointAPI。在临时物理导出文件中,Isaac Lab 会将已有的 physxJoint:maxJointVelocity 和 physxJoint:armature 转换为 NewtonJointAPI 值。角速度仍使用 USD 的度每秒单位,线速度和转子惯量也保留原有 USD 单位。已写入的新格式值优先,现有的分轴 PhysX 覆盖设置保持不变。源资产和实时舞台不被修改,因此其他物理后端及旧版 OVPhysX 运行时仍保持原先行为。

若仍存在未迁移的非零 physxJoint:jointFriction,后端会拒绝加载并报告关节路径。该无量纲系数无法精确转换为基于力/力矩的新摩擦模型,需要移除它,并显式重新调整 newton:friction / newton:damping 或 PhysxJointAxisAPI 的摩擦力/力矩。不受支持的关节类型,以及无法表示的带时间采样的旧设置,同样需要迁移。此兼容层不保证不同 PhysX 引擎版本生成完全相同的轨迹。

若希望在加载场景时迁移而无需重新发布资产,可将 OvPhysxCfg.legacy_joint_friction_replacements 设为从关节名称正则表达式到 (static effort, dynamic effort, viscous coefficient) 的映射,数值采用 SI 制的力/力矩和黏性系数单位。在内部的 0.7 导出中,仅迁移匹配到且旧摩擦非零的旋转或移动关节:删除旧属性,写入新的 PhysxJointAxisAPI 值。已有的新格式摩擦设置优先。其他运行时、源 USD 文件和实时舞台均保持不变。

Allegro 重定向、Kuka-Allegro 抬升/重定向、Shadow 重定向和 Shadow 双手交接预设,为受影响的手部关节提供了替换值 (0.01, 0.0, 0.0):静摩擦力矩为 0.01 N*m,动摩擦和黏性系数为零。这些值与测得的 OVPhysX 0.6.4 运行时执行器设置一致,包括 Shadow 肌腱关节;它们不是将旧资产系数直接做数值转换得到的。执行器初始化和运行时摩擦随机化仍使用以力/力矩计的张量属性。将旧系数直接复制到新属性中,并不是正确的迁移方式。

测试安装#

先检查 Python 软件包和运行时 wheel 能否正常导入:

uv run --extra ovphysx --extra test python -c "import ovphysx.types; from isaaclab_ov.physics import OvPhysxCfg; print('OvPhysX runtime OK')"

然后运行一个小规模后端基本运行测试:

uv run --extra ovphysx --extra test python -m pytest source/isaaclab_ov/test/assets/test_rigid_object.py::test_rigid_object_real_ovphysx_seams -k cpu

要运行声明了 OvPhysX 物理预设的任务,使用与其他后端相同的预设 CLI 语法:

uv run --extra ovphysx isaaclab zero_agent --task Isaac-Cartpole-Direct \
    --num_envs 128 --max_steps 64 physics=ovphysx

此命令以无界面方式运行 64 步零动作轨迹,然后退出。

受支持的足式运动环境#

使用 环境浏览器 或任务帮助查看可用的 physics= 预设。列出可启动的组合:

uv run --extra ovphysx isaaclab list_envs --show_presets --keyword Velocity

策略播放与训练使用相同的 physics=ovphysx 选择器。

运动任务示例
  • Isaac-Ant-Direct

  • Isaac-Humanoid-Direct

  • IsaacContrib-Velocity-Rough-AnymalB

  • IsaacContrib-Velocity-Rough-AnymalC

  • Isaac-Velocity-Flat-AnymalD

  • Isaac-Velocity-Rough-AnymalD

  • IsaacContrib-Velocity-Rough-UnitreeA1

  • IsaacContrib-Velocity-Rough-UnitreeGo1

  • Isaac-Velocity-Rough-UnitreeGo2

  • Isaac-Velocity-Rough-Cassie

  • Isaac-Velocity-Rough-G1

  • Isaac-Velocity-Rough-H1

异构克隆#

异构运行时克隆要求 OvPhysX 0.6.3。张量绑定按环境的数字顺序排列,保证索引式重置、动作和观测对应正确变体。变体的几何可以不同,但刚体、关节和肌腱布局必须一致。Isaac Lab 检查原型旋转轴和肌腱布局;OVPhysX 在绑定关节体时检查刚体与关节元数据。

克隆上下文只编译一次计划中的原型和仿真世界分配。物理管理器挂接一个导出的 USD 舞台,重放原生副本,并在资源和传感器绑定之前预热运行时。硬重置复用相同声明,不另设一次性消费队列。绑定路径来自计划的世界布局和环境模板,而不是原生克隆顺序。

配置场景时,复制前只创建原型。环境位姿来自克隆计划:USD 复制为 Kit 创建坐标框架,OVPhysX 则在原生克隆前,将原始环境按位姿放入其内部导入场景。OVRTX 和 OVStage 在内部 USD 导出中填充所需的环境坐标框架,再应用计划中的位姿。SDK 的 CPU 层级计算要求这些坐标框架已填充到 USD。导出过滤仍会排除未路由的原型,以及已由 USD 复制创建的目标。

所有写入 USD 的源对象均获得原生环境 ID 零。因此,包含源对象或仅通过 USD 定义物理的每个仿真世界,都会将其声明的全部资源作为原始对象导入。其他世界从组成相同、完整的原始世界克隆资源,每个目标世界使用一个原生环境 ID。这样,机器人、桌子和物体可以相互接触,无需目标占位对象。只复制声明的资源子树,其他写入 USD 的资源保持不变。

请保持 filter_collisions 启用,利用 USD 碰撞组隔离原始世界。原生 GPU 物理副本还会使用原生环境 ID 过滤。启用 共享 OVStage 克隆 时,两个后端附加到同一个 OVStage,并且只克隆一次;这些副本使用场景中写入的 USD 碰撞组。由于 OVPhysX 0.6.3 中共享舞台首次查找绑定较慢,默认仍采用原生物理克隆。OvPhysX 0.6.3 的 CPU 不支持原生环境 ID 过滤,因此会导入所有声明的 USD 副本并使用碰撞组。大量原始环境若彼此重叠,仍可能耗尽碰撞粗检测的配对容量,因此应将环境在空间上分开。

可变形体限制#

目前 OvPhysX 要求同一个 DeformableObject 匹配到的所有物体具有相同数量的仿真节点。节点数量不同时,初始化会报错,而不会暴露填充后的状态,以免产生错误的归约结果。

可变形体场景还要求将整个 USD 舞台实例化展开,因此启动开销会随已定义环境数量增加。这条路径适合小规模验证场景,目前不支持数千个环境的训练规模工作负载。异构快速克隆器不支持可变形体。

访问原生接口#

当共享的资产和传感器 API 未提供所需数据或操作时,可使用原生访问接口。如果 Isaac Lab 持有的句柄已覆盖所需对象,请直接复用;只有需要额外功能时才创建底层访问对象。原生写入可能绕过 Isaac Lab 的缓存和校验,因此须遵循下述所有权与同步规则。

基本概念#

OvPhysX 暴露通用的 TensorBinding 对象。一个绑定将 prim 选择与已安装的一种 TensorType 结合,并不是某个资源专用的原生存储对象。调用方通过 read() 显式将值读入自己的缓冲区,再通过 write() 将缓冲区写回。Isaac Lab 可以选择使用 OvPhysxView 包装这些绑定,提供以字符串为键、带检查的便捷接口。

生命周期前提#

仅在 OvPhysX 仿真完成初始化和重置、原生运行时已就绪后创建绑定。重新加载 USD 舞台、重建舞台或运行时的重置流程,以及管理器清理,都会使所有已有绑定和视图失效,之后需要重新获取。

满足生命周期前提后,从管理器获取当前 OvPhysX 句柄:

physx = sim.backend_manager.get_physx_instance()
if physx is None:
    raise RuntimeError("OvPhysX has not been constructed; initialize and reset the simulation first.")

句柄非 None 只说明管理器已构建 OvPhysX 实例,并不表示当前仿真上下文已经就绪。管理器清理时会释放实例并清空句柄。访问前先初始化并重置当前上下文,创建新绑定或视图前始终重新获取句柄。

复用 Isaac Lab 持有的访问对象#

如果选择符合需要,复用 Isaac Lab 资源已经持有的便捷视图:

object_view = scene["object"].root_view
poses = object_view.get_attribute("rigid_body_pose")

创建底层访问#

所需选择尚未由 Isaac Lab 资源持有时,创建原始绑定。以下示例位姿绑定使用 float32 和仿真设备,并不是适用于所有张量类型的通用分配器:

import warp as wp
from ovphysx.types import TensorType

binding = physx.create_tensor_binding(
    pattern="/World/envs/env_*/Object",
    tensor_type=TensorType.RIGID_BODY_POSE,
)
try:
    poses = wp.empty(
        tuple(binding.shape),
        dtype=wp.float32,
        device=sim.backend_manager.get_device(),
    )
    binding.read(poses)
    binding.write(poses)
finally:
    binding.destroy()

分配缓冲区前,检查 binding.shape 、 binding.dtype 、绑定数量和路径元数据。必须精确匹配绑定形状与 DLPack 标量元数据,并遵守原生设备和访问模式要求。 RIGID_BODY_POSE 使用上方的 float32 布局,但其他张量类型的数据类型、形状、原生设备或写入权限可能不同。

对于新选择,使用原生运行时和 Tensor API 模式构建 OvPhysxView :

from isaaclab_ov.sim.views import OvPhysxView

view = OvPhysxView(
    physx,
    pattern="/World/envs/env_*/Object",
    device=sim.backend_manager.get_device(),
)
try:
    if view.try_binding_for("rigid_body_pose") is not None:
        poses = view.get_attribute("rigid_body_pose")
        view.set_attribute("rigid_body_pose", poses)
        view.read_into("rigid_body_pose", poses)
finally:
    view.close()

try_binding_for 尝试为该选择创建绑定,没有有效类型时返回 None 。 binding_for 返回原始绑定,会绕过视图的设备、数据类型、形状和只读检查。

读写语义#

原始 binding.read(buffer) 将值读入调用方持有的缓冲区, binding.write(buffer) 则将值写入仿真。仅修改本地缓冲区不会改变仿真。带检查的 get_attribute() 、 read_into() 和 set_attribute() 具有相同的读取与写回边界,同时验证绑定形状、标量数据类型、原生设备和写入权限。

所有权、同步与失效#

在绑定要求的设备上分配缓冲区,并匹配其形状与 DLPack 标量元数据。状态张量通常使用仿真设备,部分属性张量则仅支持 CPU。 OvPhysxView 验证这些要求,并可将受支持的平坦标量布局重新解释为结构化 Warp 值,但不会在 CPU 与 GPU 之间移动数据。

如果某个张量类型可能不适用于选定 prim,请使用 try_binding_for 。重置流程重建舞台或运行时之后,以及清理之后,应重新获取原始绑定和便捷视图。不再需要时,应销毁原始绑定并关闭自定义视图。管理器清理会先关闭已跟踪的 OvPhysxView 实例,再释放运行时;显式清理可以避免在长时间运行的仿真中一直保留这些绑定。

权威参考#

生成的 Isaac Lab OvPhysxView 参考文档介绍了便捷视图。仓库元数据固定了 OvPhysX 版本,但没有提供带版本的官方 OvPhysX 文档 URL,因此应在运行时检查已安装的 TensorType 与绑定元数据。

进一步阅读#