Hi there,
As a few people have noticed, there seems to be some problem when using particles in a standalone python script, especially when the simulation context is using “cuda”. Specifically, the particles “freeze” at the initial positions and does not move. I have found the solution to be using:
world = World(backend="torch", device="cpu")
physx_interface = acquire_physx_interface()
physx_interface.overwrite_gpu_setting(1)
I read somewhere on this forum that this is a GPU rendering issue. Through some testing, i believe this is the case, i.e. place a ball below many particles, and play the simulation, the ball moves while the particles seem to be frozen in the GUI. So potentially that physx is simulating correctly but somehow the USD stage is not updated properly. This would be acceptable if there’s a way to access the states of the particles, but the only way right now is to use UsdGeom.Points.GetPointsAttr() in the code below, which reads the USD prim’s attributes (that doesn’t get updated with GPU context).
So I have two questions:
-
Does using CPU context introduce performance overheads? Since the particle dynamics are handled by the GPU, does this mean performance will potentially be bottlenecked by the GPU-CPU data transfer?
-
What’s the best way to get the position of all the particles, and to release them? So far I’m using UsdGeom.Points.GetPointsAttr() for this purpose, but i noticed that it’s quite slow when there are multiple particle sets present. What’s the recommended method for this? (i.e. something similar to ArticulationView in omni/physics/tensors/impl/api, maybe?)
For reference, i’m using the following script:
#launch Isaac Sim before any other imports
#default first two lines in any standalone application
from omni.isaac.kit import SimulationApp
simulation_app = SimulationApp({"headless": False}) # we can also run as headless.
from omni.isaac.core import World
from omni.isaac.core.objects import DynamicCuboid
import numpy as np
from pxr import Usd, UsdLux, UsdGeom, Sdf, Gf, Vt, UsdPhysics, PhysxSchema
import omni.kit.commands
from omni.physx.scripts import particleUtils
import omni.timeline
from omni.isaac.kit.utils import set_carb_setting
from omni.physx import acquire_physx_interface
from pxr import UsdGeom, Vt
world = World(backend="torch", device="cpu")
physx_interface = acquire_physx_interface()
physx_interface.overwrite_gpu_setting(1)
world.scene.add_default_ground_plane()
fancy_cube = world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0, 0, 1.0]),
scale=np.array([0.5015, 0.5015, 0.5015]),
color=np.array([0, 0, 1.0]),
))
# spawn particle stuff here
scene = world.scene
_stage = scene.stage
_defaultPrimPath = Sdf.Path("/World")
numParticlesZ = 35
_rng_seed = 42
_rng = np.random.default_rng(_rng_seed)
# TODO solve type check? TGS
_solverPositionIterations = 4
# physxAPI = PhysxSchema.PhysxSceneAPI.Apply(scene.GetPrim())
# hysxAPI.CreateSolverTypeAttr("TGS")
# Particle system
particleSystemPath = _defaultPrimPath.AppendChild("particleSystem0")
# particle params
particleSpacing = 0.05
restOffset = particleSpacing * 0.9
fluidRestOffset = restOffset * 0.6
particleContactOffset = restOffset + 0.001
particle_system = particleUtils.add_physx_particle_system(
stage=_stage,
particle_system_path=particleSystemPath,
simulation_owner="physicsScene",
contact_offset=restOffset * 1.5 + 0.01,
rest_offset=restOffset * 1.5,
particle_contact_offset=particleContactOffset,
solid_rest_offset=0.0,
fluid_rest_offset=fluidRestOffset,
solver_position_iterations=_solverPositionIterations,
)
particle_system.CreateMaxVelocityAttr().Set(200)
particlesPath = _defaultPrimPath.AppendChild("particles")
_stage.SetInterpolationType(Usd.InterpolationTypeHeld)
lower = Gf.Vec3f(0, 0, 2.5)
positions, velocities = particleUtils.create_particles_grid(
lower, particleSpacing + 0.01, 110, 65, numParticlesZ
)
uniform_range = particleSpacing * 0.2
for i in range(len(positions)):
positions[i][0] += _rng.uniform(-uniform_range, uniform_range)
positions[i][1] += _rng.uniform(-uniform_range, uniform_range)
positions[i][2] += _rng.uniform(-uniform_range, uniform_range)
widths = [particleSpacing] * len(positions)
particles = particleUtils.add_physx_particleset_points(
stage=_stage,
path=particlesPath,
positions_list=Vt.Vec3fArray(positions),
velocities_list=Vt.Vec3fArray(velocities),
widths_list=widths,
particle_system_path=particleSystemPath,
self_collision=True,
fluid=False,
particle_group=0,
particle_mass=0.001,
density=0.0,
)
world.reset()
pset_prim = _stage.GetPrimAtPath("/World/particles")
geom_points = UsdGeom.Points.Get(pset_prim)
while simulation_app.is_running():
position, orientation = fancy_cube.get_world_pose()
linear_velocity = fancy_cube.get_linear_velocity()
particle_poss = geom_points.GetPointsAttr().Get()
world.step(render=True) # execute one physics step and one rendering step
simulation_app.close() # close Isaac Sim```