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 配置。 场景创建工具可直接在
PhysicsSceneprim 上设置求解器参数。
当前可用功能#
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-DirectIsaac-Humanoid-DirectIsaacContrib-Velocity-Rough-AnymalBIsaacContrib-Velocity-Rough-AnymalCIsaac-Velocity-Flat-AnymalDIsaac-Velocity-Rough-AnymalDIsaacContrib-Velocity-Rough-UnitreeA1IsaacContrib-Velocity-Rough-UnitreeGo1Isaac-Velocity-Rough-UnitreeGo2Isaac-Velocity-Rough-CassieIsaac-Velocity-Rough-G1Isaac-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 实例,再释放运行时;显式清理可以避免在长时间运行的仿真中一直保留这些绑定。