Windows crash during Standalone Application in Isaac Sim

Isaac Sim version

5.1.0

Operating System

Windows 10

GPU Information

  • Model: NVIDIA RTX 2000 Ada Generation Laptop GPU (8GB)
  • Driver Version: 581.60
  • CUDA Version: 13.0

Topic Description

I am running a standalone Isaac Sim script (Replicator SDG pipeline) to generate a synthetic dataset of stacked RAM modules (CAD converted to USD using Hoops Core Converter). The script spawns ~20 stacks, each containing 2 RAM USD references (≈40 RAM prims total), then randomizes:

  1. Camera pose (stereo camera)

  2. Visibility of a random subset of RAM modules per frame (to simulate different filling rate of a ram tray)

  3. Lighting via Replicator custom event

I then capture per-frame outputs using Replicator annotators:

  • pointcloud

  • distance_to_image_plane

  • DepthSensorPointCloudColor

Isaac Sim crashes (almost everytime) during rep.orchestrator.step() after a few frames with a native access violation / heap corruption. The number of frames after which crashes can change everytime.

This is my code:

from omni.isaac.kit import SimulationApp

# -------------------------
# Config
# -------------------------

config = {
    "launch_config": {
        "headless": False
},
"cad_folder": "path_to_my_CAD_folder",
"num_frames": 30,
"resolution": (1024,1024),
"output_dir": "./out_ram_sdg"
}

launch_config = config.get("launch_config", {})
simulation_app = SimulationApp(launch_config=launch_config)

import os
import random
import numpy as np
import asyncio
from pathlib import Path
import carb
import omni.usd
import omni.replicator.core as rep
from pxr import Gf, Sdf, Usd, UsdGeom, UsdShade
from isaacsim.core.utils.stage import add_reference_to_stage
from isaacsim.core.utils.semantics import add_labels
from isaacsim.core.utils.extensions import enable_extension
enable_extension("isaacsim.test.utils")
enable_extension("omni.kit.converter.hoops_core")
from isaacsim.storage.native import get_assets_root_path
from isaacsim.test.utils import save_depth_image, save_rgb_image

# Isaac nucleus assets root path
assets_root_path = get_assets_root_path()

# -------------------------
# Utils
# -------------------------
def set_transform(prim, location=None, orientation=None, scale=None):
    if not prim or not prim.IsValid():
        raise RuntimeError("Invalid prim passed to set_transform()")
    xform = UsdGeom.Xformable(prim)
    xform.ClearXformOpOrder()
    if location is not None:
        xform.AddTranslateOp().Set(location)
    if orientation is not None:
        xform.AddOrientOp().Set(orientation)
    if scale is not None:
        xform.AddScaleOp().Set(scale)

# -------------------------
# CAD → USD + Wood Material
# -------------------------

def load_ram(cad_path, usd_out, prim_path):
    async def convert(cad_path, usd_out):
        """Convert CAD file to USD using Hoops Core Converter Extension"""  
        from omni.kit.converter.hoops_core import get_instance
        hoops_converter = get_instance()
        convert_options = {}
        convert_options["instancing"] = "false"
        convert_options["instancingStyle"] = "0"
        convert_options["convertMetadata"] = "true"
        convert_options["convertCurves"] = "true"
        convert_options["globalXforms"] = "false"
        convert_options["materialType"] = "2"
        convert_options["dMetersPerUnit"] = "0.001"
        await hoops_converter.create_converter_task(cad_path, usd_out, convert_options)
    
    asyncio.get_event_loop().run_until_complete(convert(cad_path, usd_out))

def spawn_two_ram_stack(stack_path, ram_usd_paths, local_offsets, local_orientations=None):
    """
    Creates a stack prim containing 2 RAM references.
    ram_usd_paths: list of 2 usd paths
    local_offsets: list of 2 (x,y,z) offsets (stack-local)
    local_orientations: list of 2 quats or None
    Returns stack prim path.
    """
    stage = omni.usd.get_context().get_stage()
    stage.DefinePrim(stack_path, "Xform")

    for j in range(2):
        prim_path = f"{stack_path}/ram_{j}"
        prim = add_reference_to_stage(ram_usd_paths[j], prim_path)

        if local_orientations is None:
            set_transform(prim, location=local_offsets[j])
        else:
            set_transform(prim, location=local_offsets[j], orientation=local_orientations[j])

        add_labels(prim, labels=["ram"], instance_name="class")

    return stack_path

def spawn_stack_grid(parent_path, ram_usd_paths,
                     stacks_x=4, stacks_y=2, stacks_z=1,
                     stack_spacing=(0.01, 0.05, 0),
                     stack_origin=(0, 0, 0),
                     local_offsets=None,
                     local_orientations=None):
    """
    Creates many stacks of 3 RAMs.
    Returns list of ALL ram prim paths (not just stacks).
    """
    stage = omni.usd.get_context().get_stage()
    stage.DefinePrim(parent_path, "Xform")

    sx, sy, sz = stack_spacing
    ox, oy, oz = stack_origin

    all_ram_paths = []

    idx = 0
    for k in range(stacks_z):
        for iy in range(stacks_y):
            for ix in range(stacks_x):
                idx += 1
                stack_path = f"{parent_path}/stack_{idx:03d}"
                stage.DefinePrim(stack_path, "Xform")

                # place the whole stack
                stack_pos = (ox + ix * sx, oy + iy * sy, oz + k * sz)
                stack_prim = stage.GetPrimAtPath(stack_path)
                set_transform(stack_prim, location=stack_pos)

                # spawn the 2 RAMs inside this stack
                spawn_two_ram_stack(
                    stack_path=stack_path,
                    ram_usd_paths=ram_usd_paths,
                    local_offsets=local_offsets,
                    local_orientations=local_orientations
                )

                # collect paths to each ram prim
                for j in range(2):
                    all_ram_paths.append(f"{stack_path}/ram_{j}")

    return all_ram_paths

# -------------------------
# Main SDG Pipeline
# -------------------------

# Create a new stage 
omni.usd.get_context().new_stage()
stage = omni.usd.get_context().get_stage()
carb.settings.get_settings().set("/rtx/post/aa/op", "off")
carb.settings.get_settings().set("rtx/post/dlss/execMode", 2)
# Set the renderer to Path Traced
rep.settings.set_render_pathtraced(samples_per_pixel=16)

# Add world prim to the empty stage
distant_light = stage.DefinePrim("/World/Lights/DistantLight", "DistantLight")
distant_light.CreateAttribute("inputs:intensity", Sdf.ValueTypeNames.Float).Set(300.0)

# Create a randomizer for lights, manually triggered at custom events
with rep.trigger.on_custom_event(event_name="randomize_lights"):
    lights = rep.create.light(
        light_type="Sphere",
        parent="/World/Lights",
        color=rep.distribution.uniform((0, 0, 0), (1, 1, 1)),
        temperature=rep.distribution.normal(6500, 500),
        intensity=rep.distribution.normal(35000, 5000),
        position=rep.distribution.uniform(-10, 10),
        rotation=rep.distribution.uniform(0, 180),
        scale=rep.distribution.uniform(0.1, 1),
        count=3,
    )

# Create a plastic grund plane
plane = rep.functional.create.plane(position=(0, 0, 0), scale=(10, 10, 1), semantics={"class": "plane"}, parent="/World", name="Plane")
material_path="https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/Base/Plastics/Plastic.mdl"
material_prim = stage.GetPrimAtPath(material_path)
try:
    success, result = omni.kit.commands.execute(
        "CreateMdlMaterialPrimCommand",
        mtl_url=material_path,
        mtl_name="Plastic",
        mtl_path="/World/Plane/Looks/Plastic"
    )
    stage =omni.usd.get_context().get_stage()
    plastic_material = UsdShade.Material(stage.GetPrimAtPath("/World/Plane/Looks/Plastic"))
except Exception as e:
        print(f"Error Occurred, {e}")
# Bind the specific material object
UsdShade.MaterialBindingAPI.Apply(plane).Bind(plastic_material, UsdShade.Tokens.strongerThanDescendants)
simulation_app.update()

# -------------------------
# Load RAM from CAD
# -------------------------
ram_folder = Path(config["cad_folder"])

# CAD extensions to process
cad_exts = {".step", ".stp", ".stl", ".igs"}
ram_usd_paths = []  # list of generated USD paths for the RAMs, to be used for instancing later
for cad_path in ram_folder.iterdir():
    if not cad_path.is_file():
        continue
    if cad_path.suffix.lower() not in cad_exts:
        continue

    usd_path = cad_path.with_suffix(".usd")
    prim_name = cad_path.stem.replace(" ", "_").replace("-", "_")
    prim_path = f"/World/RAM_{prim_name}"
    print(f"Converting: {cad_path.name} -> {usd_path.name}")
    ram_usd_paths.append(str(usd_path))
    load_ram(
        cad_path=str(cad_path),
        usd_out=str(usd_path),
        prim_path=prim_path)

local_offsets = [(0.00,  0.00,  0.00), (-0.007, 0.0005, 0.015)]
# extra rotations to make the RAMs upright (depends on how they were modeled)
qx90 = Gf.Quatf(Gf.Rotation(Gf.Vec3d(1, 0, 0), 90).GetQuat())
qy90 = Gf.Quatf(Gf.Rotation(Gf.Vec3d(0, 1, 0), 90).GetQuat())
local_orientations = [qx90, qx90 * qy90]

# Static object → no physics

all_ram_paths = spawn_stack_grid(
    parent_path="/World/RAM_STACKS",
    ram_usd_paths=ram_usd_paths,
    stacks_x=10, stacks_y=2, stacks_z=1,   # 40 stacks
    stack_spacing=(0.015, 0.15, 0),
    stack_origin=(0,0,0),
    local_offsets=local_offsets,
    local_orientations=local_orientations
)

# Group asset for easy referencing
ram_group = rep.create.group(all_ram_paths, semantics=[('class', 'ram_group')])

# -------------------------
# Stereo-Camera
# -------------------------
camera_positions = [(-1.3, 0, 0.6), (-0.8, -0.2, 0.65), (0, 0, 1.5), (0.15, -1.0, 0.7)] 

# Adjust camera parameters to ensure the whole stack is visible, and good depth quality on the RAMs (not too far, not too close)
camera = rep.create.stereo_camera(parent="/World", 
                                  name="StereoCam",
                                  stereo_baseline=0.1, 
                                  clipping_range=(0.6, 10),
                                  focal_length=60,
                                  focus_distance=1
                                  )
render_product = rep.create.render_product(camera, config["resolution"])

# -------------------------
# Annotators
# -------------------------

pc_anno = rep.annotators.get("pointcloud")
pc_anno.attach(render_product[0])

dist_anno = rep.annotators.get("distance_to_image_plane")
dist_anno.attach(render_product[0])

depth_anno = rep.AnnotatorRegistry.get_annotator("DepthSensorPointCloudColor")
depth_anno.attach(render_product[0])

# -------------------------
# Capture Loop
# -------------------------

N_FRAMES = config["num_frames"]
output_dir = config["output_dir"]
os.makedirs(output_dir, exist_ok=True)

rep.orchestrator.preview()
# Disable capture on play (data generation will be triggered manually)
rep.orchestrator.set_capture_on_play(False)

points_L, rgb_L = [], []

with rep.trigger.on_frame(max_execs=N_FRAMES):
        # Make the entire stack visible at the start of each frame
        ram_prims = rep.get.prim_at_path(all_ram_paths)
        with ram_prims:
            rep.modify.visibility(True)

        for i in range(N_FRAMES):
            simulation_app.update()

        # Randomize camera poses every frame, looking at the RAM stack
        pos = rep.distribution.sequence(camera_positions)
        with camera:
            rep.modify.pose(
                position=pos,
                look_at=ram_group
                )
        # Randomly select how many ram units hide within the stack to increase variety in the dataset
        n_hidden = rep.distribution.choice(choices=list(range(0, len(all_ram_paths)//2)))
        # Randomly select which many ram units hide within the stack
        hidden = rep.distribution.choice(all_ram_paths,
                                         num_samples=n_hidden,
                                         with_replacements=False)
        with hidden:
            # Hide the selected ram units
            rep.modify.visibility(False)

for i in range(N_FRAMES):
    stage = omni.usd.get_context().get_stage()
    prim = stage.GetPrimAtPath("/World/StereoCam")
    matrix= omni.usd.get_world_transform_matrix(prim)
    translate= matrix.ExtractTranslation()
    print("Prim world translation: ", translate)
    if i % 3 == 0:
        print(f"\t Randomizing lights")
        rep.utils.send_og_event(event_name="randomize_lights")

    # Trigger any on_frame registered randomizers
    rep.orchestrator.step(rt_subframes=16)

    save_depth_image(dist_anno.get_data(), output_dir, f"dist_{i}.png", normalize=True)
    save_rgb_image(depth_anno.get_data(), output_dir, f"depth_color_{i}.png")
    pc_data = pc_anno.get_data()
    pc = pc_data["data"]

    # Robust sanitize
    if pc is None:
        print("[WARN] None pointcloud")
        continue

    pc = np.asarray(pc)

    # Case 1: already Nx3 → OK
    if pc.ndim == 2 and pc.shape[1] == 3 and pc.shape[0] > 0:
        points_L.append(pc)

    # Case 2: flattened → reshape
    elif pc.ndim == 1 and pc.size % 3 == 0 and pc.size > 0:
        pc = pc.reshape(-1, 3)
        points_L.append(pc)

    else:
        print(f"[WARN] Skipped malformed pc: shape={pc.shape}")
        continue

    # RGB always matches pc length
    rgb = pc_data["info"]["pointRgb"]
    rgb = np.asarray(rgb)

    if rgb.ndim == 1 and rgb.size % 4 == 0:
        rgb = rgb.reshape(-1,4)[:, :3]

    rgb_L.append(rgb)

    print(f"[SDG] Frame {i}")


# Merge all views → single pointcloud
pc = np.concatenate(points_L) if len(points_L) else np.empty((0,3))
rgb = np.concatenate(rgb_L) 

import open3d as o3d
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(pc)
#pcd.colors = o3d.utility.Vector3dVector(rgb)
o3d.visualization.draw_geometries([pcd])

o3d.io.write_point_cloud(os.path.join(output_dir,"ram_scene.ply"), pcd)

pc_anno.detach()
dist_anno.detach()
depth_anno.detach()
for rp in render_product:
    rp.destroy()

rep.orchestrator.wait_until_complete()
print("\n[SDG] Dataset complete")

for _ in range(50):
    simulation_app.update()

simulation_app.close()

Error message

Windows fatal exception: access violation

Thread 0x00002614 (most recent call first):
File “C:\isaac-sim\kit\python\Lib\concurrent\futures\thread.py”, line 81 in _worker
File “C:\isaac-sim\kit\python\Lib\threading.py”, line 982 in run
File “C:\isaac-sim\kit\python\Lib\threading.py”, line 1045 in _bootstrap_inner
File “C:\isaac-sim\kit\python\Lib\threading.py”, line 1002 in _bootstrap

The same version of the script also produced:

Windows fatal exception: code 0xc0000374

I am upoloading also the whole crash message

crash.txt (77.0 KB)

Thanks for this report. Do you happen to have an example cad file (or two) you can share to use with this repro?

Thanks for the reply! Yes, but I can’t manage to load them here directly. These are the links from where I have downloaded them :

Quick update. This doesn’t look like a typical log for an out of memory crash, however I did notice your card in this log is 8GB VRAM, with 28 MiB free at time of crash.

Isaac Sim’s min specs are 16GB VRAM. Do you have access to a machine that meets min specs to test on?

I have tried also on Linux with the following settings:

  • Driver Version: 570.172.08
  • GPU: NVIDIA GeForce RTX 4090
  • CUDA Version: 12.8
  • VRAM: 24564MB
  • Ubuntu Version: 22.04.5
  • Processor: Intel(R) Core™ Ultra 7 265K
  • Cores: 20 | Logical Cores: 20

It still crashes with different errors, for example:

double free or corruption (!prev)
2026-02-26T17:00:39Z [0ms] [Warning] [carb.crashreporter-breakpad.plugin] [crash] A crash has occurred.

or

malloc(): mismatching next->prev_size (unsorted)
2026-02-26T17:05:23Z [0ms] [Warning] [carb.crashreporter-breakpad.plugin] [crash] A crash has occurred.

or

corrupted size vs. prev_size while consolidating
2026-02-26T17:02:18Z [0ms] [Warning] [carb.crashreporter-breakpad.plugin] [crash] A crash has occurred.

RAM_files.zip (7.5 MB)
I have managed to load here the files for repro. In the zip there are both the CAD models and the USD files already converted

I hate to say, but you are WELL below the minimal specs for using Omniverse in general, let along SDG. Our absolute MIN specs is 12GB, really 16GB. 8GB is just way too low. And an A2000 is very low power. It’s a 75W card.

As I have said, I have also the same problem with another setup that meet the specs:

  • Driver Version: 570.172.08
  • GPU: NVIDIA GeForce RTX 4090
  • CUDA Version: 12.8
  • VRAM: 24564MB
  • Ubuntu Version: 22.04.5
  • Processor: Intel(R) Core™ Ultra 7 265K
  • Cores: 20 | Logical Cores: 20

I understand what you are saying but this is not the main problem

Your crash logs are pretty generic, but the other big issue is you have two GPUs in your laptop, which always cause issues. The hard crash could be because the laptop is talking to the Intel GPU, and not the Nvidia GPU. You need to disable that secondary card. For the 4090, maybe update to the very latest drivers. Your drivers are old. We would need totally separate crash logs from one machine to the other. If you want to use the 4090, then we would need fresh crash logs from that machine.

Thank you.
I have updated the drivers in the 4090 as follows:

  • NVIDIA-SMI 590.48.01
  • Driver Version: 590.48.01
  • CUDA Version: 13.1

It still crashes with this log file. I hope it helps more
log_forum_report.txt (114.2 KB)

And does the laptop with the 4090 only have the ONE GPU, the Nvidia 4090 or does it have a secondary card?

Looking at the logs, something is definitely wrong with your Isaac Sim. It is saying all of your code and your extensions are out of date. Literally all of your extensions.

Can you do a HARD wipe of Omniverse from your system and start with USD Composer. Delete c:/packman-repo, delete the Isaac Sim folder. Delete the c:/users/temp/ov folder. Just do a full purge.

Then test with USD Composer
GitHub - NVIDIA-Omniverse/kit-app-template: Omniverse Kit App Template · GitHub

Also these warnings.
2026-03-03T13:56:57Z [841ms] [Warning] [gpu.foundation.plugin] CPU performance profile is set to powersave. This profile sets the CPU to the lowest frequency reducing performance.
2026-03-03T13:56:57Z [849ms] [Warning] [gpu.foundation.plugin] IOMMU is enabled.

Please disable IOMMU if possible.

Disabling IOMMU (Input-Output Memory Management Unit) is usually done through your computer’s BIOS/UEFI settings, though it can also be disabled at the operating system level in Linux via kernel parameters.

Here are the standard ways to disable it depending on your needs:

Method 1: Through the BIOS/UEFI (Recommended)

This is the most direct way to disable IOMMU at the hardware level.

  1. Restart your computer.
  2. Enter the BIOS/UEFI: As your computer boots up, repeatedly press the BIOS key for your specific motherboard (commonly F2, Del, F12, F10, or Esc).
  3. Locate the Virtualization/Chipset settings: The exact location varies by motherboard manufacturer, but look for tabs labeled Advanced, Chipset Configuration, CPU Configuration, or Overclocking.
  4. Find the IOMMU setting: It will likely be named one of the following depending on your processor:
  • AMD Processors: Look for IOMMU or AMD-Vi.
  • Intel Processors: Look for Intel VT-d (Virtualization Technology for Directed I/O).
  1. Change the setting: Select the option and change it from “Enabled” or “Auto” to Disabled.
  2. Save and Exit: Press F10 (or the designated key shown on your screen) to save your changes and restart your computer.

Method 2: Through Linux (GRUB Bootloader)

If you are using Linux and only want to disable IOMMU at the OS level (or if your BIOS lacks the toggle), you can edit your GRUB boot parameters.

  1. Open your terminal.
  2. Open the GRUB configuration file as root using a text editor (like nano):
sudo nano /etc/default/grub

  1. Locate the line that starts with GRUB_CMDLINE_LINUX_DEFAULT.
  2. Modify the line to turn off IOMMU. Depending on your CPU, add the following inside the quotes (and remove iommu=on or iommu=pt if they are there):
  • For Intel: Add intel_iommu=off
  • For AMD: Add amd_iommu=off
  • Example: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=off"
  1. Save the file (Ctrl+O, Enter) and exit the editor (Ctrl+X).
  2. Update GRUB to apply the changes:
  • Ubuntu/Debian: sudo update-grub
  • Fedora/Arch (BIOS): sudo grub2-mkconfig -o /boot/grub2/grub.cfg
  • Fedora/Arch (UEFI): sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg (path varies by distro)
  1. Reboot your system.

Note: Disabling IOMMU will prevent you from using PCI passthrough (like passing a physical GPU directly to a virtual machine) and may disable certain hardware-level security features.

Would you like help finding the specific BIOS key or menu path for your exact motherboard model?

It has just this GPU.
Thanks very much for all the suggestions, I will follow them and update you soon

Hi, I have an update.
I have disabled the IOMMU (using method 2) but the problem still occurred.
I have tried with this version of the code and the crash has not happened ever since:


launch_config = config.get("launch_config", {})
simulation_app = SimulationApp(launch_config=launch_config)

import os
import random
import numpy as np
import asyncio
from pathlib import Path
import carb
import omni.usd
import omni.replicator.core as rep
from pxr import Gf, Sdf, Usd, UsdGeom, UsdShade
from isaacsim.core.utils.stage import add_reference_to_stage
from isaacsim.core.utils.semantics import add_labels
from isaacsim.core.utils.extensions import enable_extension
enable_extension("isaacsim.test.utils")
enable_extension("omni.kit.converter.hoops_core")
from isaacsim.storage.native import get_assets_root_path
from isaacsim.test.utils import save_depth_image, save_rgb_image

# Isaac nucleus assets root path
assets_root_path = get_assets_root_path()

# -------------------------
# Utils
# -------------------------
def set_transform(prim, location=None, orientation=None, scale=None):
    if not prim or not prim.IsValid():
        raise RuntimeError("Invalid prim passed to set_transform()")
    xform = UsdGeom.Xformable(prim)
    xform.ClearXformOpOrder()
    if location is not None:
        xform.AddTranslateOp().Set(location)
    if orientation is not None:
        xform.AddOrientOp().Set(orientation)
    if scale is not None:
        xform.AddScaleOp().Set(scale)


def set_prim_visibility(stage, prim_paths, visible=True):
    for path in prim_paths:
        prim = stage.GetPrimAtPath(path)
        if not prim.IsValid():
            continue
        imageable = UsdGeom.Imageable(prim)
        if visible:
            imageable.MakeVisible()
        else:
            imageable.MakeInvisible()

# -------------------------
# CAD → USD + Wood Material
# -------------------------

def load_ram(cad_path, usd_out, prim_path):
    async def convert(cad_path, usd_out):
        """Convert CAD file to USD using Hoops Core Converter Extension"""  
        from omni.kit.converter.hoops_core import get_instance
        hoops_converter = get_instance()
        convert_options = {}
        convert_options["instancing"] = "false"
        convert_options["instancingStyle"] = "0"
        convert_options["convertMetadata"] = "true"
        convert_options["convertCurves"] = "true"
        convert_options["globalXforms"] = "false"
        convert_options["materialType"] = "2"
        convert_options["dMetersPerUnit"] = "0.001"
        await hoops_converter.create_converter_task(cad_path, usd_out, convert_options)
    
    asyncio.get_event_loop().run_until_complete(convert(cad_path, usd_out))

    # ram = add_reference_to_stage(usd_out, prim_path)

    # add_labels(ram, labels=[f"ram_{prim_name}"], instance_name="class")
    # print("RAM prim: ", ram.GetAllChildren())
    # return ram

def spawn_two_ram_stack(stack_path, ram_usd_paths, local_offsets, local_orientations=None):
    """
    Creates a stack prim containing 2 RAM references.
    ram_usd_paths: list of 2 usd paths
    local_offsets: list of 2 (x,y,z) offsets (stack-local)
    local_orientations: list of 2 quats or None
    Returns stack prim path.
    """
    stage = omni.usd.get_context().get_stage()
    stage.DefinePrim(stack_path, "Xform")

    for j in range(2):
        prim_path = f"{stack_path}/ram_{j}"
        prim = add_reference_to_stage(ram_usd_paths[j], prim_path)

        if local_orientations is None:
            set_transform(prim, location=local_offsets[j])
        else:
            set_transform(prim, location=local_offsets[j], orientation=local_orientations[j])

        add_labels(prim, labels=["ram"], instance_name="class")

    return stack_path

def spawn_stack_grid(parent_path, ram_usd_paths,
                     stacks_x=4, stacks_y=2, stacks_z=1,
                     stack_spacing=(0.01, 0.05, 0),
                     stack_origin=(0, 0, 0),
                     local_offsets=None,
                     local_orientations=None):
    """
    Creates many stacks of 3 RAMs.
    Returns list of ALL ram prim paths (not just stacks).
    """
    stage = omni.usd.get_context().get_stage()
    stage.DefinePrim(parent_path, "Xform")

    sx, sy, sz = stack_spacing
    ox, oy, oz = stack_origin

    all_ram_paths = []

    idx = 0
    for k in range(stacks_z):
        for iy in range(stacks_y):
            for ix in range(stacks_x):
                idx += 1
                stack_path = f"{parent_path}/stack_{idx:03d}"
                stage.DefinePrim(stack_path, "Xform")

                # place the whole stack
                stack_pos = (ox + ix * sx, oy + iy * sy, oz + k * sz)
                stack_prim = stage.GetPrimAtPath(stack_path)
                set_transform(stack_prim, location=stack_pos)

                # spawn the 2 RAMs inside this stack
                spawn_two_ram_stack(
                    stack_path=stack_path,
                    ram_usd_paths=ram_usd_paths,
                    local_offsets=local_offsets,
                    local_orientations=local_orientations
                )

                # collect paths to each ram prim
                for j in range(2):
                    all_ram_paths.append(f"{stack_path}/ram_{j}")

    return all_ram_paths

# -------------------------
# Main SDG Pipeline
# -------------------------

# Create a new stage 
output_dir = config["output_dir"]
os.makedirs(output_dir, exist_ok=True)

omni.usd.get_context().new_stage()
stage = omni.usd.get_context().get_stage()
carb.settings.get_settings().set("/rtx/post/aa/op", "off")
carb.settings.get_settings().set("rtx/post/dlss/execMode", 2)
carb.settings.get_settings().set("/omni/replicator/backends/disk/root_dir", os.path.abspath(output_dir))
carb.settings.get_settings().set("/renderer/multiGPU/enabled", False)
carb.settings.get_settings().set("/renderer/activeGpu", 0)
# Set the renderer to Path Traced
rep.settings.set_render_pathtraced(samples_per_pixel=16)

# Add world prim to the empty stage
distant_light = stage.DefinePrim("/World/Lights/DistantLight", "DistantLight")
distant_light.CreateAttribute("inputs:intensity", Sdf.ValueTypeNames.Float).Set(300.0)

# Create a plastic grund plane
plane = rep.functional.create.plane(position=(0, 0, 0), scale=(10, 10, 1), semantics={"class": "plane"}, parent="/World", name="Plane")
material_path="https://omniverse-content-production.s3.us-west-2.amazonaws.com/Materials/Base/Plastics/Plastic.mdl"
material_prim = stage.GetPrimAtPath(material_path)
try:
    success, result = omni.kit.commands.execute(
        "CreateMdlMaterialPrimCommand",
        mtl_url=material_path,
        mtl_name="Plastic",
        mtl_path="/World/Plane/Looks/Plastic"
    )
    stage =omni.usd.get_context().get_stage()
    plastic_material = UsdShade.Material(stage.GetPrimAtPath("/World/Plane/Looks/Plastic"))
except Exception as e:
        print(f"Error Occurred, {e}")
# Bind the specific material object
UsdShade.MaterialBindingAPI.Apply(plane).Bind(plastic_material, UsdShade.Tokens.strongerThanDescendants)
simulation_app.update()

# -------------------------
# Load RAM from CAD
# -------------------------
ram_folder = Path(config["cad_folder"])

# CAD extensions to process
cad_exts = {".step", ".stp", ".stl", ".igs"}
ram_usd_paths = []  # list of generated USD paths for the RAMs, to be used for instancing later
for cad_path in ram_folder.iterdir():
    if not cad_path.is_file():
        continue
    if cad_path.suffix.lower() not in cad_exts:
        continue

    usd_path = cad_path.with_suffix(".usd")
    prim_name = cad_path.stem.replace(" ", "_").replace("-", "_")
    prim_path = f"/World/RAM_{prim_name}"
    print(f"Converting: {cad_path.name} -> {usd_path.name}")
    ram_usd_paths.append(str(usd_path))
    load_ram(
        cad_path=str(cad_path),
        usd_out=str(usd_path),
        prim_path=prim_path)

local_offsets = [(0.00,  0.00,  0.00), (-0.007, 0.0005, 0.015)]
# extra rotations to make the RAMs upright (depends on how they were modeled)
qx90 = Gf.Quatf(Gf.Rotation(Gf.Vec3d(1, 0, 0), 90).GetQuat())
qy90 = Gf.Quatf(Gf.Rotation(Gf.Vec3d(0, 1, 0), 90).GetQuat())
local_orientations = [qx90, qx90 * qy90]

# Static object → no physics

all_ram_paths = spawn_stack_grid(
    parent_path="/World/RAM_STACKS",
    ram_usd_paths=ram_usd_paths,
    stacks_x=10, stacks_y=2, stacks_z=1,   # 40 stacks
    stack_spacing=(0.015, 0.15, 0),
    stack_origin=(0,0,0),
    local_offsets=local_offsets,
    local_orientations=local_orientations
)

# Group asset for easy referencing
ram_group = rep.create.group(all_ram_paths, semantics=[('class', 'ram_group')])

# -------------------------
# Stereo-Camera
# -------------------------
camera_positions = [(-1.3, 0, 0.6), (-0.75, -0.2, 0.65), (0, 0, 1.5), (0.15, -1.0, 0.7)] 

# Adjust camera parameters to ensure the whole stack is visible, and good depth quality on the RAMs (not too far, not too close)
camera = rep.create.stereo_camera(parent="/World", 
                                  name="StereoCam",
                                  stereo_baseline=0.1, 
                                  clipping_range=(0.6, 10),
                                  focal_length=60,
                                  focus_distance=1
                                  )
render_product = rep.create.render_product(camera, config["resolution"])

# -------------------------
# Annotators
# -------------------------

depth_anno = rep.AnnotatorRegistry.get_annotator("DepthSensorPointCloudColor")
depth_anno.attach(render_product[0])

# -------------------------
# Capture Loop
# -------------------------

N_FRAMES = config["num_frames"]

rep.orchestrator.preview()
# Disable capture on play (data generation will be triggered manually)
rep.orchestrator.set_capture_on_play(False)

points_L, rgb_L = [], []

writer = None
if config.get("use_basic_writer", False):
    writer = rep.WriterRegistry.get("BasicWriter")
    writer.initialize(
        output_dir=output_dir,
        rgb=True,
        distance_to_image_plane=True,
        colorize_depth=True,
        pointcloud=True,
    )
    writer.attach(render_product)

# Create a randomizer for lights, manually triggered at custom events
with rep.trigger.on_custom_event(event_name="randomize_lights"):
    lights = rep.create.light(
        light_type="Sphere",
        parent="/World/Lights",
        color=rep.distribution.uniform((0, 0, 0), (1, 1, 1)),
        temperature=rep.distribution.normal(6500, 500),
        intensity=rep.distribution.normal(35000, 5000),
        position=rep.distribution.uniform(-10, 10),
        rotation=rep.distribution.uniform(0, 180),
        scale=rep.distribution.uniform(0.1, 1),
        count=3,
    )
            
try:
    for i in range(N_FRAMES):
        stage = omni.usd.get_context().get_stage()

        # Keep all RAMs visible, then hide a random subset for variability.
        set_prim_visibility(stage, all_ram_paths, visible=True)
        n_hidden = random.randint(0, len(all_ram_paths))
        if n_hidden > 0:
            hidden_paths = random.sample(all_ram_paths, k=n_hidden)
            set_prim_visibility(stage, hidden_paths, visible=False)

        with camera:
            rep.modify.pose(
                position=camera_positions[i % len(camera_positions)],
                look_at=ram_group
            )

        prim = stage.GetPrimAtPath("/World/StereoCam")
        matrix = omni.usd.get_world_transform_matrix(prim)
        translate = matrix.ExtractTranslation()
        print("Prim world translation: ", translate)

        if i % 3 == 0:
            print("\t Randomizing lights")
            rep.utils.send_og_event(event_name="randomize_lights")

        rep.orchestrator.step(rt_subframes=8)
        save_rgb_image(depth_anno.get_data(), output_dir, f"depth_color_{i}.png")

        simulation_app.update()
        print(f"[SDG] Frame {i}")
finally:
    if writer is not None:
        writer.detach()
    depth_anno.detach()
    for rp in render_product:
        rp.destroy()
    rep.orchestrator.wait_until_complete()
    print("\n[SDG] Dataset complete")
    simulation_app.close()

The main difference is that I do no use rep.modify.visibility(True) and rep.modify.visibility(False) but UsdGeom.Imageable(prim).MakeVisible() and UsdGeom.Imageable(prim).MakeInvisible() .
I hope this insight helps.

1 Like