<< All versions

Skill v1.0.1

currentAutomated scan100/100
nvidia-omniverse/physx/tensor-bindings-cpu

+3 new

──Details
PublishedOctober 27, 2026 at 03:39 AM
Content Hashsha256:141235a53d335c32...
Git SHAb97365c6ddb3
Bump Typepatch
Compare with v1.0.0
──Files
Files (1 file, 7.3 KB)
SKILL.md7.3 KBactive
SKILL.md · 168 lines · 7.3 KB

version: "1.0.1" name: tensor-bindings-cpu description: Exchange CPU simulation state — poses, velocities, joint targets — as caller-owned NumPy arrays through tensor bindings. The tensor-binding CODE API is deprecated in ovphysx 0.6 in favor of the session read/write API (Python PhysX.read / PhysX.write, C ovphysx_read / ovphysx_write); use ovphysx-session-write for control inputs and state, and the ovphysx-output-read skill (ovstage-native identity-preserving reads and write-back) to read output back. Tensor bindings are kept only for maintaining existing caller-owned bulk-NumPy binding code during the deprecation period. license: Apache-2.0 compatibility: "ovphysx >=0.6.0 wheel or SDK; Python examples require NumPy, and C examples require SDK headers and libraries." allowed-tools: Read Shell metadata: version: "0.1.7" author: NVIDIA Omniverse Physics tags: "ovphysx, physics, tensor-bindings, cpu"


Tensor Bindings: CPU Read and Write

Tensor-binding code API deprecated (ovphysx 0.6). The tensor-binding CODE API is deprecated in
favor of the session read/write API — Python PhysX.read / PhysX.write, C ovphysx_read /
ovphysx_write. Prefer the session API where it fits (ovphysx-output-read covers ovstage-native
identity-preserving reads and write-back). For writing control inputs and state, use the
ovphysx-session-write skill; tensor bindings are kept only for maintaining existing
caller-owned bulk-NumPy binding code.

Tensor bindings map physics-object path patterns to typed tensor views, including authored USD objects and runtime-only clones. This enables bulk data exchange with NumPy, PyTorch, Warp, or any other DLPack-compatible framework.

When to Use

Use this skill when a caller needs bulk CPU tensor reads or writes for simulation state, such as poses, velocities, or joint targets, through the public TensorBindingsAPI.

Instructions

  1. Read docs/tutorials/tensor_bindings.md and the sample for the caller's language before changing code.
  2. Populate an ovstage, advance and wait for its write floor, attach it at that

ordinal, create bindings once from stable physics-object path patterns, then reuse them to read or write tensors with the binding shape and dtype. Inspect binding.native_device (or the C getter) before allocating when the process is not forced to CPU mode.

  1. Use Shell to run the Python sample or compile the C sample after adapting the scene path and tensor type.

For Python, install physx-usd-schemas and newton-usd-schemas alongside ovphysx. Register both PhysX and Newton schemas before the first population call so authored newton:* values are preserved.

Import the PhysX schema recipe from ovphysx.samples.python_samples.schema_support; for native samples, set PHYSX_USD_SCHEMAS_ROOT to the extracted wheel. See Physics Schemas for setup.

Python

python
from pathlib import Path
import ovphysx
from ovphysx.samples.python_samples.schema_support import physx_schema_paths
from ovphysx import PhysX
from ovphysx.types import TensorType
import numpy as np
import ovstage
PhysX.set_cpu_mode(True)
physx = PhysX()
# Register external PhysX and Newton schemas before the first population.
ovstage.population.register_usd_schemas(
[*[str(p) for p in physx_schema_paths()], str(ovphysx.newton_schema_root())]
)
stage = ovstage.Stage("ovphysx-tensors")
usd_path = Path(ovphysx.__file__).resolve().parent / "samples" / "data" / "links_chain_sample.usda"
ovstage.population.open_usd(stage, str(usd_path), ordinal=1, domains=ovstage.PopulationDomain.ALL)
# attach_ovstage() reads at a sealed ordinal.
stage.advance_write_floor(ordinal=1).wait()
physx.wait_task(physx.attach_ovstage(stage, read_ordinal=1))
# Write a control-input binding for targets you set.
velocity_target_binding = physx.create_tensor_binding(
pattern="/World/articulation/articulationLink*",
tensor_type=TensorType.ARTICULATION_DOF_VELOCITY_TARGET,
raise_if_empty=True,
)
# Use a separate binding for the simulated state you read back.
link_pose_binding = physx.create_tensor_binding(
pattern="/World/articulation/articulationLink*",
tensor_type=TensorType.ARTICULATION_LINK_POSE,
raise_if_empty=True,
)
# Write control inputs
targets = np.zeros(velocity_target_binding.shape, dtype=np.float32)
targets[0, 0] = 25.0 # set first DOF velocity target
velocity_target_binding.write(targets)
# step_sync steps and waits in one call
physx.step_sync(0.01)
# Read simulated state from the pose binding (not the target binding)
link_poses = np.zeros(link_pose_binding.shape, dtype=np.float32)
link_pose_binding.read(link_poses)
# Clean up
velocity_target_binding.destroy()
link_pose_binding.destroy()
physx.wait_task(physx.detach_ovstage())
stage.destroy()
physx.destroy()

Read simulated results from a state binding (poses, positions), not from a target binding: a velocity-target binding reads back the control inputs you wrote, not the physics outcome.

Pass raise_if_empty=True when a zero-count binding is a configuration error. The default False accepts empty bindings; check binding.count before reading.

Use ALL when populating USD so nested world transforms are resolved by the rendering domain. PHYSICS alone seeds local world matrices in OVStage 0.3; see Population domains.

Full sample:

  • samples/python_samples/tensor_bindings.py (wheel)
  • Source checkout: tests/python_samples/tensor_bindings.py

C

Full sample:

  • samples/c_samples/tensor_bindings_c/main.c (SDK)
  • Source checkout: tests/c_samples/tensor_bindings_c/main.c

Common tensor types

ConstantData
TensorType.RIGID_BODY_POSERigid body positions + quaternions
TensorType.ARTICULATION_DOF_POSITIONJoint positions
TensorType.ARTICULATION_DOF_VELOCITY_TARGETJoint velocity drive targets
TensorType.ARTICULATION_LINK_POSEArticulation link poses

See include/ovphysx/ovphysx_types.h for the full list. In C the same types use the OVPHYSX_TENSOR_*_F32 enum spelling (for example Python TensorType.RIGID_BODY_POSE is C OVPHYSX_TENSOR_RIGID_BODY_POSE_F32).

Key APIs

PythonC
physx.create_tensor_binding(pattern, tensor_type, *, raise_if_empty=False)ovphysx_create_tensor_binding()
binding.native_deviceovphysx_get_tensor_binding_native_device()
binding.read(output)ovphysx_read_tensor_binding()
binding.write(input)ovphysx_write_tensor_binding()
binding.destroy()ovphysx_destroy_tensor_binding()

Partial updates (RL-style)

TensorBindings supports selectively applying actions without changing the binding:

  • Masked write: pass a bool/uint8 mask of shape [N] (1 = update, 0 = keep old value).
  • Python: binding.write(tensor, mask=mask)
  • C: ovphysx_write_tensor_binding_masked()
  • Indexed write: pass an int32 index tensor of shape [K] (rows to update).
  • Python: binding.write(tensor, indices=indices)
  • C: ovphysx_write_tensor_binding(handle, binding_handle, &src_tensor, &index_tensor)

References

  • Docs: docs/tutorials/tensor_bindings.md
  • Python sample: samples/python_samples/tensor_bindings.py (wheel; source: tests/python_samples/tensor_bindings.py)
  • C sample: samples/c_samples/tensor_bindings_c/main.c (SDK; source: tests/c_samples/tensor_bindings_c/main.c)
← v1.0.0All versions