后端架构#

概述#

Isaac Lab 支持多种物理后端,同时为环境代码提供统一的资源、传感器和场景接口。工厂在对象构造时选用当前后端的实现,因此代码可以使用相同的公共 API,无需直接导入特定后端模块。环境中后端与预设的选择方法见 后端与预设 。

工厂分派#

所有工厂均继承 FactoryBase ,先通过核心后端键选择器,再按照软件包和模块路径约定,定位受支持的后端实现:

  1. SimulationContext.physics_manager 的名称会映射到 FactoryBase._get_backend() 识别的某个后端键。添加新的物理后端时,需要扩展这个核心选择器。

  2. 工厂模块路径决定后端模块路径。例如, isaaclab.assets.articulation 映射到 isaaclab_physx.assets.articulation 、 isaaclab_newton.assets.articulation 或 isaaclab_ov.assets.articulation 。OvPhysX 后端键使用共享的 isaaclab_ov 集成软件包。

  3. 工厂按需导入后端模块,并将实现类缓存到注册表中。

User code: Articulation(cfg)
    │
    ▼
FactoryBase.__new__()
    │
    ├─ _get_backend()       → "physx", "newton", or "ovphysx"
    │    (reads SimulationContext.physics_manager)
    │
    ├─ _get_module_name()   → "isaaclab_physx.assets.articulation"
    │    (OvPhysX maps to the shared isaaclab_ov package)
    │
    ├─ importlib.import_module()
    │    (lazy load — only on first use)
    │
    └─ Return backend-specific instance

渲染器和可视化器则通过配置中的 class_type 选择实现。它们的选择独立于物理后端;渲染器实例通过下文介绍的仿真注册表共享。

物理管理器生命周期#

各后端均实现 PhysicsManager 。这一抽象基类负责管理仿真生命周期。具体实现根据 SimulationContext 初始化引擎,通过 forward() 更新运动学、 step() 推进仿真、 reset() 重置状态,并通过 close() 释放资源。

管理器通过 PhysicsEvent 回调处理跨后端的生命周期操作。 MODEL_INIT 在场景构建期间触发, PHYSICS_READY 在物理初始化完成后触发, STOP 在原生资源被替换或关闭之前触发。具体的 close() 实现负责派发 STOP 事件。

SimulationContext 在同一个注册表中持有原生资源和渲染器实例。对于具体类型相同、配置相等的对象, get_or_create_backend(backend_cfg) 会复用同一资源;缓存未命中时,通过 instantiate(backend_cfg) 创建资源。 BackendCfg 描述资源设置和身份, RendererCfg 在此基础上描述渲染器实例, PhysicsCfg 则选择物理管理器。注册前应完成配置,注册后将配置及其嵌套值视为只读;需要不同设置时,应创建新配置。所有使用方释放绑定后, close_backend(backend) 会关闭那个确切的已注册对象,不会比较配置或计算其哈希。通过 BackendCfg 声明的资源必须实现 close() 。普通构造配置共享由 Python 管理的数据,没有显式关闭操作;移除注册表条目即可释放其引用。如果关闭失败,条目会保留以便重试。物理关闭使相机渲染数据失效后,仿真清理会依次关闭材质写入器、渲染器实例、可视化器和其余原生资源,最后关闭 USD 舞台。

管理器和原生渲染器通过 backend 暴露借用的资源。例如, NewtonManager.backend.model 可访问最终构建完成的原生模型。关闭渲染器只会释放其绑定,不会关闭共享原生资源。暴露原生句柄并不替代 SDP 数据传输。

克隆上下文在执行计划分派前,单独以 sim.clone_contexts[Context] = Context(...) 注册。它们负责应用计划,不持有原生运行时资源。

Newton 有两类生命周期不同的资源,它们并非两个可互换的后端:

  • ModelBuilder 持有可修改的构建数据。克隆时填充这些数据,传感器在模型最终构建完成之前声明需求;之后构建器仍保留,用于硬重置。 NewtonBuilderCfg 是普通构造配置,而不是 BackendCfg ,构建器无需原生 close() 操作。

  • NewtonBackend 持有最终构建完成的模型和原生缓冲区,物理与渲染使用方借用这些句柄。关闭它会释放运行时分配的资源,但不会关闭构建器。

两类资源使用相同的注册表:

builder_cfg = NewtonBuilderCfg(physics_cfg=sim.cfg.physics)
builder = sim.get_or_create_backend(builder_cfg)
# Clone/import populates this builder before model allocation.
model_cfg = NewtonBackendCfg(physics_cfg=sim.cfg.physics, device=sim.device)
backend = sim.get_or_create_backend(model_cfg)

两种配置均使用选定的物理配置;物理后端不是 Newton 时,则选用仅用于渲染的表示。 SimulationContext 没有特定后端专用的配置字段,使用方也不访问克隆上下文,而是直接通过 SDP 请求刚体变换和可视点。查询共享原生资源的 BVH,但各使用方捕获的计算工作相互独立。

可跨后端使用的资源与传感器接口#

资源和传感器采用与工厂相同的分层方式:

  1. isaaclab 中的基类定义公共接口约定,例如 BaseArticulation 或 BaseContactSensor 。

  2. 工厂类同时继承 FactoryBase 和相应基类。

  3. 后端软件包提供受支持的具体实现。

数据类也采用相同模式,例如 ArticulationData(FactoryBase, BaseArticulationData) 。实现通过公共资源和传感器数据属性暴露 ProxyArray 。每个代理包装底层 wp.array ,通过 .warp 显式访问该数组,并通过 .torch 以缓存、零拷贝方式访问 torch.Tensor 视图。当某个 API 明确要求特定表示时,请使用对应访问器。将 ProxyArray 传给 wp.to_torch() 仅由已弃用的兼容层支持,新代码应使用 proxy_array.torch 。后端原生数据和内部存储仍可能使用原始 Warp 数组。用法与缓冲区生命周期说明见 使用 ProxyArray 。

可跨后端使用的渲染器与场景数据接口#

渲染器的选择独立于物理后端。通过 sim.get_or_create_backend(renderer_cfg) 获取符合 BaseRenderer 接口约定的实现。 RenderContext 通过注册表的过滤视图协调渲染生命周期,不另设渲染器缓存,也不持有渲染器所有权。它验证全局设置和注册时机,在物理就绪后初始化渲染器,并协调 USD 舞台准备、场景更新和材质写入器。渲染器选择与用法见 渲染器 。

物理管理器通过 SceneDataBackend 暴露实时仿真数据。仿真上下文持有的 SceneDataProvider 负责转换和重新映射数据,供不依赖具体后端的使用方消费:

physics manager -> SceneDataBackend -> SceneDataProvider -> renderer or visualizer

通过这一接口边界,渲染器和可视化器可以共用 Warp 原生数据通路,无需了解状态由哪个物理引擎管理。完整的数据流模型见 场景数据提供器 。

原生引擎访问边界#

可跨后端使用的接口构成稳定的 API 边界。高级代码可以使用各引擎的原生底层数据 API,但这些 API 保留各自的所有权和同步语义。PhysX 类型化视图、Newton 实时模型与状态数组及通用选择,以及 OvPhysX 张量绑定,见 访问原生物理 API 。

设计原则#

  • 按需加载: 后端模块仅在首次实例化时导入,既加快启动,也避免依赖未使用的后端。

  • 已识别的后端键与路径约定: 核心选择器识别后端键后,模块路径遵循 isaaclab.X.Y 的结构。OvPhysX 映射为 isaaclab_ov.X.Y ;其他已识别后端默认使用 isaaclab_<backend>.X.Y 。

  • 独立选择: 物理后端、渲染器和可视化器分别选择。

  • 显式数据互操作: 公共资源和传感器数据属性返回 ProxyArray ;通过 .warp 和 .torch 访问所需的数组表示,无需复制。

  • 无运行时额外开销: 实现在实例化时选定,不会在仿真频繁执行的路径中增加分派逻辑。