mcpField
P
physics-simulation

physics-simulation

github.com/isaac-simOther4.4k58 installsUpdated 6d ago
Repository

About physics-simulation

A practical skill for configuring and diagnosing rigid-body physics in Isaac Sim 6 and Kit 110. It covers scene settings, per-prim setup, materials, drives, sensors, and backend selection, with scripts and worked guidance.

Key features

  1. 01Physics scene and solver configuration
  2. 02Rigid body and collision setup
  3. 03Contact materials and joint drives
  4. 04Newton and PhysX backend selection
  5. 05Prim setup helper script

Use cases

  1. 01Set up rigid bodies and colliders
  2. 02Tune contact-rich or high-speed scenes
  3. 03Choose a physics backend for simulation

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/isaac-sim/isaacsim --skill physics-simulation

After installing, type/physics-simulationin Claude Code to confirm it loaded.

Bundled filesexamples.mdscripts/prim_physics_setup.py

Details

From the project README

Physics Simulation in Isaac Sim

Purpose

Configure PhysicsScene and per-prim rigid bodies, collisions, materials, joint drives, solver selection, and physics sensors with worked examples.

Prerequisites

  • For standalone physics: a completed source/libraries build or matching installed wheels/SDKs; most unit and headless physics tests need no GPU.
  • For Kit settings, extension auto-switching, timeline, or viewport work: built Isaac Sim and the shell environment from isaac-sim-orchestrator.

Limitations

  • Targets Isaac Sim 6 / Kit 110 unless a section states otherwise.
  • Does not replace official NVIDIA documentation for unsupported edge cases.

Troubleshooting

Error / symptomCauseSolution
Black or empty framesMissing lights or non-RTX render modeAdd dome/key light; confirm RTX / PathTracing settings
Hang on stage load or first renderMDL compile or oversized stageFollow isolation steps in isaac-sim-troubleshooting

Standalone library versus Kit adapter

Use source/libraries/isaacsim/physics/{manager,registration,entities,ovsim} with the source/libraries/isaacsim/physics_engines/{ovnewton,ovphysx} engines for standalone physics implementation, backend registration, entities, and runtime control. isaacsim.physics_engines.ovstage is the process-level setup and native-handle bridge for the private OVStage runtime, not a physics engine.

The isaacsim.core.simulation_manager settings, extension enablement and auto-switch behavior, Kit timeline, viewport, and SimulationApp stay on the Kit adapter route. Do not apply their launch flags to standalone module tests, and do not treat the library PhysicsManager as a drop-in spelling change for the Kit SimulationManager without verifying the consumer workflow.

For a standalone engine whose public package documents import-time registration, import that package once and select it through PhysicsManager; normal examples must not add separate activate() or shutdown() calls. Drive setup, initialization, stepping, and invalidation through the manager, passing the stage identifier returned by the Stage object rather than a magic value. Prefer RigidBodyEntity and ArticulationEntity over raw simulation views and manual tensor conversion when the entity facade supports the operation.

Tests should execute the example's normal public entry point without pre-activating its backend, so they detect missing registration and lifecycle ownership. Use the checked-out engine API documentation and the matching source/examples series as the canonical syntax; do not copy an older example's compatibility lifecycle.

Targets Isaac Sim 6.0+ / Kit 110. Both backends share UsdPhysics.*; backend-specific behavior is called out per section.

Available Scripts

ScriptPurposeArguments
scripts/prim_physics_setup.pyPer-prim physics setup helpers for Isaac Sim / USD (Kit 110)see script --help

Running scripts

From agent runtimes that expose skill execution helpers, invoke helpers with run_script():

run_script("scripts/prim_physics_setup.py", args=["--help"])

From a built Isaac Sim tree, run the same file with ./python.sh (Linux) or python.bat (Windows) from _build/*/release, or execute shell helpers directly when they do not require the simulator.

Backend selection (Kit 110)

isaacsim.core.simulation_manager registers physics engines and picks the active one. default_engine in its extension.toml is "physx", but the isaacsim.physics.newton extension defaults auto_switch_on_startup = true, so any app that enables isaacsim.physics.newton (the standard isaacsim.exp.full.kit does) ends up with Newton active at startup.

from isaacsim.core.simulation_manager import SimulationManager

SimulationManager.switch_physics_engine("newton")   # or "physx"
print(SimulationManager.get_active_physics_engine())

# Inspect what is available
from isaacsim.physics.newton import get_available_physics_engines, get_active_physics_engine
print(get_available_physics_engines())

Force the engine explicitly when launching:

--/exts/isaacsim.core.simulation_manager/default_engine=newton     # or =physx
--/exts/isaacsim.physics.newton/auto_switch_on_startup=false        # opt out of auto-switch

Newton config classes (extension Python API; not surfaced in the user-guide RST yet — see the extension's API docs page or docs/isaacsim/physics/newton_physics.rst):

ClassRole
isaacsim.physics.newton.NewtonConfigper-sim settings (CUDA graph capture, fabric sync, contact/joint defaults)
XPBDSolverConfigXPBD solver (rigid + soft)
MuJoCoSolverConfigMuJoCo Warp solver
isaacsim.physics.newton.tensorsNumPy / PyTorch / Warp frontends

Both Newton and PhysX consume the standard UsdPhysics.Scene + PhysxSchema.PhysxSceneAPI; many PhysxSchema.* attributes are still honored under Newton, plus Newton reads its solver config via omni.usd.schema.newton.

Stack & reading order

  1. This skill: scene config, per-prim setup, contact materials, drives, sensors, readback, backend selection.
  2. usd-articulation: multi-link articulations + Robot Schema overlay.
  3. urdf-mjcf-to-usd-conversion: importer config (RL vs teleop drives).
  4. isaac-sim-troubleshooting: when physics misbehaves on Kit 110.

Mechanism recipes (impact, feeders, dominoes, tops, cradles, pendulum waves, escapements) live in Worked Examples.


Part 1 — Scene-Level Configuration

PhysicsScene Setup
from pxr import Usd, UsdGeom, UsdPhysics, PhysxSchema, Gf

ps = UsdPhysics.Scene.Define(stage, "/World/PhysicsScene")
ps.CreateGravityDirectionAttr().Set(Gf.Vec3f(0, 0, -1))
ps.CreateGravityMagnitudeAttr().Set(9.81)

px = PhysxSchema.PhysxSceneAPI.Apply(ps.GetPrim())
px.CreateTimeStepsPerSecondAttr().Set(240)   # see Hz table below
px.CreateEnableCCDAttr().Set(True)
px.CreateEnableStabilizationAttr().Set(True)
px.CreateSolverTypeAttr().Set("TGS")          # TGS or PGS; TGS preferred for articulations
Physics Hz Selection
ScenarioHzNotes
Standard rigid-body scenes60–120Default for warehouse, general sim
Stacking / contact-rich240Tight contact resolution
High-velocity impacts120 with 2–4 substepsPair with CCD
Small-part vibration (feeders)≥ 4× vibration freq, typically 480Resolve oscillation correctly
Spinning bodies / gyros480Numerical precision for angular momentum
Stiff contact chains (cradles, escapements)480Solver needs many sub-iterations

Rule of thumb: physics timestep must be > 4× the highest frequency in the system (vibration, spin, contact-stiffness mode).

Solver Iteration Counts (per-body)

Set on PhysxRigidBodyAPI per body that needs it. Higher = more accurate, slower.

ScenarioPosition itersVelocity iters
Simple rigid bodies, tumbling164
Stacking328
Complex joints / articulations6416
Stiff contact chains (cradle, escapement)6432
pxrb = PhysxSchema.PhysxRigidBodyAPI.Apply(prim)
pxrb.CreateSolverPositionIterationCountAttr().Set(32)
pxrb.CreateSolverVelocityIterationCountAttr().Set(8)
pxrb.CreateEnableCCDAttr().Set(True)
When to Disable Stabilization

EnableStabilizationAttr is on by default and helps stacks settle. It destroys angular momentum on free-spinning bodies. Disable it for:

  • Spinning tops, gyros, flywheels
  • Pendulum mechanisms (clock escapements, pendulum waves)
  • Anything whose correctness depends on conserved angular velocity
px.CreateEnableStabilizationAttr().Set(False)

Part 2 — Per-Prim Physics Setup

RigidBody / Collision / Static / Kinematic

Use scripts/prim_physics_setup.py for dynamic, static, and kinematic body setup. It keeps the RigidBodyAPI, MassAPI, and CollisionAPI application sequence in one executable implementation.

Rule: RigidBodyAPI + CollisionAPI on the same prim. Splitting them across parent/child causes intermittent collision failures.

Static Colliders with Scale — Translate-First Pattern

Scaling a Cube prim with CollisionAPI applied directly causes PhysX to use the wrong collision bounds (objects fall through ground). Use a parent xform for position, a child mesh for scale:

# CORRECT
xf = UsdGeom.Xform.Define(stage, "/World/Ground")
UsdGeom.Xformable(xf.GetPrim()).AddTranslateOp().Set(Gf.Vec3d(0, 0, -0.05))
mesh = UsdGeom.Cube.Define(stage, "/World/Ground/Mesh")
mesh.CreateSizeAttr().Set(1.0)
UsdGeom.Xformable(mesh.GetPrim()).AddScaleOp().Set(Gf.Vec3f(50.0, 50.0, 0.1))
UsdPhysics.CollisionAPI.Apply(mesh.GetPrim())

Cube.size=1.0 means the cube has half-extents of 0.5, not 1.0. Use size=2.0 when you want "the scale op equals the half-extent."

Mass & Inertia
mass_api = UsdPhysics.MassAPI.Apply(prim)
mass_api.CreateMassAttr().Set(0.25)                                    # kg
mass_api.CreateCenterOfMassAttr().Set(Gf.Vec3f(0, 0, 0.05))            # local
mass_api.CreateDiagonalInertiaAttr().Set(Gf.Vec3f(1e-4, 1e-4, 2e-4))   # kg·m²

For URDF-imported robots, prefer import_inertia_tensor: true in config.yaml over auto-computed geometric inertia (see urdf-mjcf-to-usd-conversion).


Part 3 — Contact Materials

def create_contact_material(stage, mat_path, static_friction=0.5,
                            dynamic_friction=0.4, restitution=0.1):
    prim = stage.DefinePrim(mat_path)
    mat = UsdPhysics.MaterialAPI.Apply(prim)
    mat.CreateStaticFrictionAttr().Set(static_friction)
    mat.CreateDynamicFrictionAttr().Set(dynamic_friction)
    mat.CreateRestitutionAttr().Set(restitution)
    return mat
Reference Values
Material pairingStatic μDynamic μRestitution
Concrete on concrete0.60.50.05
Steel on steel0.740.570.6
Rubber on rubber0.80.70.5
Rubber on concrete1.00.80.3
Wood on wood0.50.30.2
Metal generic0.40.30.2
Plastic (dice)0.40.30.3
Felt (casino)0.50.40.2
Cardboard on steel0.40.30.1

For chains of stiff contacts (Newton's cradle, escapements), set restitutionCombineMode=max on PhysxMaterialAPI so the highest restitution wins at each contact.


Part 4 — Joint Drives

joint = stage.GetPrimAtPath("/World/Robot/joint_arm")
drive = UsdPhysics.DriveAPI.Apply(joint, "angular")    # "angular" | "linear"
drive.CreateTypeAttr().Set("force")                    # "force" | "acceleration"
drive.CreateStiffnessAttr().Set(1000.0)                # Kp (Nm/rad for angular)
drive.CreateDampingAttr().Set(100.0)                   # Kd (Nm·s/rad)
drive.CreateMaxForceAttr().Set(500.0)                  # torque/force limit
drive.CreateTargetPositionAttr().Set(0.0)              # target (deg or m)

For RL training, the agent commands torques directly. Set drive_type to none and stiffness/damping to 0 in config.yaml (see urdf-mjcf-to-usd-conversion). Active PD drives fight the RL agent.

For revolute pendulum joints (clock escapements, pendulum waves), set joint friction to 0:

joint_api = PhysxSchema.PhysxJointAPI.Apply(joint)
joint_api.CreateJointFrictionAttr().Set(0.0)

Part 5 — Backend Selection (Newton vs PhysX)

Quick Choice
You wantUse
RL training with thousands of envsNewton (Featherstone or MuJoCo)
Differentiable simulationNewton
Legacy PhysX scene from Isaac Sim 5.xPhysX
Soft bodies, cloth, deformablesNewton (VBD or XPBD)
Validated against MuJoCo baselinesNewton SolverMuJoCo
Newton Solvers
SolverCoordinatesDifferentiableBest For
SolverFeatherstoneGeneralizedYes (Warp)Articulated robots (default for manipulators, legged)
SolverMuJoCoGeneralizedYes (mujoco-warp)Validated locomotion, MuJoCo policy ports
SolverXPBDMaximalPartialSoft constraints, cables, ropes
SolverSemiImplicitMaximalYes (Warp)Fast prototyping, simple rigid bodies
SolverVBD(deformable)YesSoft bodies, deformables

For an existing PhysX-only USD asset that must also run under Newton, see physx-to-newton-conversion.

Newton vs PhysX Differences
AspectPhysXNewton
BackendClosed C++/CUDAWarp/CUDA (open, JIT)
CoordinatesMaximal (6DoF per body)Generalized (Featherstone) or maximal
DifferentiableNoYes (native Warp autodiff)
Multi-GPULimitedYes (Warp device abstraction)
USD integrationSchema extensionsNative USD loader
Performance ceilingGood < 4096 envsDesigned for 10K+ envs
Newton + Torch — Critical Init Order

Never import torch before Newton physics settles — CUDA context conflict hangs Kit. Defer torch imports until after timeline.play() + settle loop. Use map_location="cpu" for policy inference if VRAM is tight.

Newton-Specific Configuration (Isaac Lab)
# config.yaml for URDF→USD conversion (Isaac Lab)
make_instanceable: true    # CRITICAL for RL parallel envs
fix_base: false            # true for fixed-base arm; false for mobile/legged

See urdf-mjcf-to-usd-conversion for the full schema.


Part 6 — Physics Sensors

The current namespace is isaacsim.sensors.experimental.physics (authoring + runtime classes paired). The legacy isaacsim.sensors.physics import path still works but is deprecated for new code.

Migration: see Migrating from isaacsim.sensors.physics to isaacsim.sensors.experimental.physics for the concept mapping and updated scripts.

Contact
from isaacsim.sensors.experimental.physics import Contact, ContactSensor
import isaacsim.core.experimental.utils.app as app_utils

contact = Contact.create(
    path="/World/Robot/foot/contact",
    min_threshold=0.0, max_threshold=1e6, radius=-1,   # -1 = use collision shape
)
sensor = ContactSensor(contact)
app_utils.play(commit=True)            # required before get_data()
reading = sensor.get_data()            # ContactSensorReading
IMU
from isaacsim.sensors.experimental.physics import IMU, IMUSensor

imu = IMU.create(path="/World/Robot/imu", tick_rate=200.0)
sensor = IMUSensor(imu, annotators=["linear_acceleration", "angular_velocity", "orientation"])
app_utils.play(commit=True)
frame = sensor.get_data()                # returns IMUSensorReading
Effort / joint state

Runtime-only classes from the same module; no separate authoring type — they wrap an existing joint by path.

from isaacsim.sensors.experimental.physics import EffortSensor, JointStateSensor

effort = EffortSensor("/World/Robot/joint_arm_1")
joint  = JointStateSensor("/World/Robot/joint_arm_1")
app_utils.play(commit=True)
reading = effort.get_data()              # EffortSensorReading
state   = joint.get_data()               # JointStateSensorReading
Raycast (scene query)
import omni.physics.tensors as physics_tensors

sim_view = physics_tensors.create_simulation_view("cuda:0")
hit = sim_view.raycast_closest(origin, direction, max_dist)

For higher-fidelity sensor simulation (LiDAR scan patterns, multi-ray, vendor sensor models, depth/radar/acoustic), see isaac-sim-sensor and isaac-camera. They use the modern isaacsim.sensors.experimental.rtx and .physics namespaces.


Part 7 — Physics-to-USD Readback (CRITICAL)

The most common silent bug: reading authored USD transforms instead of simulated state.

SourceReturnsWhen to use
UsdGeom.XformCache.GetLocalToWorldTransform()Authored USD transform (initial pose)Editor-time queries, before play
RigidPrim.get_world_pose()Simulated stateAlways during simulation
Articulation.get_world_poses() (Kit 110)Simulated state for articulated bodiesArticulated robots
Dynamic Control (DC) dc.get_rigid_body_pose()Simulated stateLegacy / quick scripts
Why XformCache Is Wrong During Sim

updateToUsd=True writes physics state to Fabric, not the USD stage layer. XformCache reads the USD layer. Result: it always returns initial poses.

RigidPrim / GeomPrim pattern (Kit 110)
from isaacsim.core.experimental.prims import RigidPrim
import isaacsim.core.experimental.utils.app as app_utils

rp = RigidPrim(paths="/World/Dice/Die_*")
app_utils.play(commit=True)
pos_wp, quat_wp = rp.get_world_poses()   # warp arrays
positions  = pos_wp.numpy()              # (N, 3)
quaternions = quat_wp.numpy()            # (N, 4) [w, x, y, z]
Articulation pattern (Kit 110)
from isaacsim.core.experimental.prims import Articulation
import isaacsim.core.experimental.utils.app as app_utils

robot = Articulation("/World/Robot")
app_utils.play(commit=True)        # required for tensor data
pos_wp, quat_wp = robot.get_world_poses()
dof_positions  = robot.get_dof_positions().numpy()    # (N, num_dofs)
dof_velocities = robot.get_dof_velocities().numpy()
J = robot.get_jacobian_matrices().numpy()             # for IK

Legacy isaacsim.core.api.articulations.Articulation / isaacsim.core.api.prims.RigidPrim still load but are superseded by the isaacsim.core.experimental.* stack.

Migration: for the broader omni.isaac.* → isaacsim.* renaming map, see Renaming Extensions. For the experimental Articulation / RigidPrim APIs see the Python scripting index.

Quaternion Convention

USD/Isaac uses [w, x, y, z]; scipy uses [x, y, z, w]. Convert:

from scipy.spatial.transform import Rotation
r = Rotation.from_quat([quat[1], quat[2], quat[3], quat[0]])
euler = r.as_euler('xyz', degrees=True)

Part 8 — Common Gotchas

  1. CollisionAPI alone = static collider; RigidBodyAPI + CollisionAPI = dynamic.
  2. Same-prim requirement: both APIs must be on the same prim.
  3. Kinematic bodies: use CreateKinematicEnabledAttr().Set(True), not enable/disable on RigidBodyAPI.
  4. Cube.size=1.0 = half-extent 0.5. Use size=2.0 if you want scale ops to equal half-extents.
  5. physics:velocity USD attributes are ignored by PhysX at runtime. Use RigidPrim.set_linear_velocities() after timeline.play().
  6. physics:angularVelocity is in DEGREES/second, not rad/s. Convert with math.degrees().
  7. SimulationContext.step(render=True) is the only reliable physics-with-render advance. app.update() does not sync physics.
  8. Experimental sensors need app_utils.play(commit=True) (or timeline.play()) before get_data(); do not call initialize() from the legacy World flow.
  9. get_rigid_body_state() does not exist in Isaac Sim 5.1+; use RigidPrim.get_world_poses() from isaacsim.core.experimental.prims.
  10. PhysX cannot resolve sequential momentum transfer in same-island contact chains (see Worked Example 4: Newton's Cradle).
  11. Tunneling at high spin rates: compound colliders fail above ~50 rad/s after 5–6s. Use simpler convex hulls or higher physics Hz.
  12. Before controller diagnosis: verify a PhysicsScene, active timeline, and expected collision APIs are present.

Worked Examples (impact, vibratory feeder, gyro, cradle, escapement)

See examples.md for details.

Comments

Markdown supported