|
18 | 18 |
|
19 | 19 | import isaacsim.core.utils.prims as prim_utils |
20 | 20 | import isaacsim.core.utils.stage as stage_utils |
| 21 | +from isaacsim.core.simulation_manager import SimulationManager |
21 | 22 | import pytest |
22 | | -from isaacsim.core.api.simulation_context import SimulationContext |
| 23 | +from isaaclab.sim import build_simulation_context |
23 | 24 | from pxr import UsdGeom |
24 | | - |
25 | 25 | import isaaclab.sim as sim_utils |
26 | 26 | from isaaclab.scene.cloner import TemplateCloneCfg, clone_from_template, physx_replicate, usd_replicate |
27 | 27 |
|
28 | 28 |
|
29 | | -@pytest.fixture |
30 | | -def sim(): |
31 | | - """Create a fresh simulation context and stage for each test.""" |
32 | | - stage_utils.create_new_stage() |
33 | | - dt = 0.01 |
34 | | - sim = SimulationContext(physics_dt=dt, rendering_dt=dt, backend="numpy") |
35 | | - stage_utils.update_stage() |
36 | | - yield sim |
37 | | - sim.stop() |
38 | | - sim.clear() |
39 | | - sim.clear_all_callbacks() |
40 | | - sim.clear_instance() |
| 29 | +@pytest.fixture(params=["cpu", "cuda"]) |
| 30 | +def sim(request): |
| 31 | + """Provide a fresh simulation context for each test on CPU and CUDA.""" |
| 32 | + with build_simulation_context(device=request.param, dt=0.01, add_lighting=False) as sim: |
| 33 | + yield sim |
41 | 34 |
|
42 | 35 |
|
43 | 36 | def test_usd_replicate_with_positions_and_mask(sim): |
@@ -137,13 +130,11 @@ def test_clone_from_template(sim): |
137 | 130 | Steps: |
138 | 131 | - Create /World/template and /World/envs/env_0..env_31 |
139 | 132 | - Spawn three prototypes under /World/template/Object/proto_asset_.* |
140 | | - - Clone using TemplateCloneCfg with random_heterogenous_cloning=False (modulo mapping) |
| 133 | + - Clone using TemplateCloneCfg with random_heterogeneous_cloning=False (modulo mapping) |
141 | 134 | - Verify modulo placement exists; then call sim.reset(), and create PhysX view |
142 | 135 | """ |
143 | | - from isaacsim.core.simulation_manager import SimulationManager |
144 | | - |
145 | 136 | num_clones = 32 |
146 | | - clone_cfg = TemplateCloneCfg(random_heterogenous_cloning=False, device="cpu") |
| 137 | + clone_cfg = TemplateCloneCfg(random_heterogeneous_cloning=False, device=sim.cfg.device) |
147 | 138 | prim_utils.create_prim(clone_cfg.template_root, "Xform") |
148 | 139 | prim_utils.create_prim("/World/envs", "Xform") |
149 | 140 | for i in range(num_clones): |
@@ -197,3 +188,97 @@ def test_clone_from_template(sim): |
197 | 188 | object_view_regex = f"{clone_cfg.clone_regex}/Object".replace(".*", "*") |
198 | 189 | physx_view = SimulationManager.get_physics_sim_view().create_rigid_body_view(object_view_regex) |
199 | 190 | assert physx_view._backend is not None |
| 191 | + |
| 192 | + |
| 193 | +def _run_colocation_collision_filter(sim, asset_cfg, expected_types, assert_count=False): |
| 194 | + """Shared harness for colocated collision filter checks across devices.""" |
| 195 | + num_clones = 32 |
| 196 | + clone_cfg = TemplateCloneCfg(random_heterogeneous_cloning=False, device=sim.cfg.device) |
| 197 | + prim_utils.create_prim(clone_cfg.template_root, "Xform") |
| 198 | + prim_utils.create_prim("/World/envs", "Xform") |
| 199 | + for i in range(num_clones): |
| 200 | + prim_utils.create_prim(f"/World/envs/env_{i}", "Xform", translation=(0, 0, 0)) |
| 201 | + |
| 202 | + prim = asset_cfg.func(f"{clone_cfg.template_root}/Object/{clone_cfg.template_prototype_identifier}_.*", asset_cfg) |
| 203 | + assert prim.IsValid() |
| 204 | + |
| 205 | + stage = stage_utils.get_current_stage() |
| 206 | + clone_from_template(stage, num_clones=num_clones, template_clone_cfg=clone_cfg) |
| 207 | + |
| 208 | + primitive_prims = sim_utils.get_all_matching_child_prims( |
| 209 | + "/World/envs", predicate=lambda prim: prim.GetTypeName() in expected_types |
| 210 | + ) |
| 211 | + |
| 212 | + if assert_count: |
| 213 | + assert len(primitive_prims) == num_clones |
| 214 | + |
| 215 | + for i, primitive_prim in enumerate(primitive_prims): |
| 216 | + assert primitive_prim.GetTypeName() == expected_types[i % len(expected_types)] |
| 217 | + |
| 218 | + sim.reset() |
| 219 | + object_view_regex = f"{clone_cfg.clone_regex}/Object".replace(".*", "*") |
| 220 | + physx_view = SimulationManager.get_physics_sim_view().create_rigid_body_view(object_view_regex) |
| 221 | + for _ in range(100): |
| 222 | + sim.step() |
| 223 | + distance_from_origin = torch.norm(physx_view.get_transforms()[:, :2], dim=-1) |
| 224 | + assert torch.all(distance_from_origin < 0.1) |
| 225 | + |
| 226 | + |
| 227 | +def test_colocation_collision_filter_homogeneous(sim): |
| 228 | + """Verify colocated clones of a single prototype stay stable after PhysX cloning. |
| 229 | +
|
| 230 | + All clones are spawned at exactly the same pose; if the collision filter is wrong the pile |
| 231 | + explodes on reset. This asserts the filter keeps the colocated objects stable while stepping |
| 232 | + across CPU and CUDA backends. |
| 233 | + """ |
| 234 | + _run_colocation_collision_filter( |
| 235 | + sim, |
| 236 | + sim_utils.ConeCfg( |
| 237 | + radius=0.3, |
| 238 | + height=0.6, |
| 239 | + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), |
| 240 | + mass_props=sim_utils.MassPropertiesCfg(mass=100.0), |
| 241 | + rigid_props=sim_utils.RigidBodyPropertiesCfg( |
| 242 | + solver_position_iteration_count=4, solver_velocity_iteration_count=0 |
| 243 | + ), |
| 244 | + collision_props=sim_utils.CollisionPropertiesCfg(), |
| 245 | + ), |
| 246 | + expected_types=["Cone"], |
| 247 | + assert_count=True, |
| 248 | + ) |
| 249 | + |
| 250 | + |
| 251 | +def test_colocation_collision_filter_heterogeneous(sim): |
| 252 | + """Verify colocated clones of multiple prototypes retain modulo ordering and remain stable. |
| 253 | +
|
| 254 | + The cone, cube, and sphere are all spawned in the identical pose for every clone; an incorrect |
| 255 | + collision filter would blow up the simulation on reset. This guards both modulo ordering and |
| 256 | + that the colocated set stays stable through PhysX steps across CPU and CUDA. |
| 257 | + """ |
| 258 | + _run_colocation_collision_filter( |
| 259 | + sim, |
| 260 | + sim_utils.MultiAssetSpawnerCfg( |
| 261 | + assets_cfg=[ |
| 262 | + sim_utils.ConeCfg( |
| 263 | + radius=0.3, |
| 264 | + height=0.6, |
| 265 | + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), |
| 266 | + mass_props=sim_utils.MassPropertiesCfg(mass=100.0), |
| 267 | + ), |
| 268 | + sim_utils.CuboidCfg( |
| 269 | + size=(0.3, 0.3, 0.3), |
| 270 | + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2), |
| 271 | + ), |
| 272 | + sim_utils.SphereCfg( |
| 273 | + radius=0.3, |
| 274 | + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0), metallic=0.2), |
| 275 | + ), |
| 276 | + ], |
| 277 | + rigid_props=sim_utils.RigidBodyPropertiesCfg( |
| 278 | + solver_position_iteration_count=4, solver_velocity_iteration_count=0 |
| 279 | + ), |
| 280 | + mass_props=sim_utils.MassPropertiesCfg(mass=1.0), |
| 281 | + collision_props=sim_utils.CollisionPropertiesCfg(), |
| 282 | + ), |
| 283 | + expected_types=["Cone", "Cube", "Sphere"], |
| 284 | + ) |
0 commit comments