Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8fbf294
add settings to make training results deterministic
redfler Apr 30, 2026
b83b7ee
checks with ./isaaclab.sh --format
redfler Apr 30, 2026
2491117
added name to the CONTRIBUTORS.md
redfler Apr 30, 2026
bef85ea
improve title and description, add comment and newline
redfler Apr 30, 2026
980591d
document --experience and --determinism
redfler Apr 30, 2026
5beb625
add deterministic argument
redfler Apr 30, 2026
a345583
change dependencies
redfler May 7, 2026
4502b48
add flag to AppLauncher
redfler May 7, 2026
33bf92c
add test
redfler May 7, 2026
300d494
update docs
redfler May 7, 2026
04dba81
format
redfler May 7, 2026
fe579e7
Merge branch 'develop' into xul/determinism
redfler May 7, 2026
37bf404
use RendererCfg as default renderer_cfg in CameraCfg (#5521)
r-schmitt May 7, 2026
c87955e
Refactors schema cfgs to separate solver-common from PhysX-specific f…
vidurv-nvidia May 7, 2026
ce9432e
Updates docs for using nurec background in locomanipulation sdg (#5301)
dengyuchenkit May 7, 2026
425a473
[CI][Auto Version Bump] Compile changelog fragments (workflow_dispatch)
isaaclab-bot[bot] May 8, 2026
71e8bf1
[Newton] Bump Newton pin to v1.2.0rc2 (#5523)
hujc7 May 8, 2026
3fb3e2d
[CI][Auto Version Bump] Compile changelog fragments (schedule)
isaaclab-bot[bot] May 8, 2026
2e070e1
Fixes joint friction API docs (#5533)
AntoineRichard May 8, 2026
e08e75b
Deprecates state properties calls (#5423)
AntoineRichard May 8, 2026
18197ee
Merge branch 'develop' into xul/determinism
redfler May 8, 2026
2174553
Merge branch 'develop' into xul/determinism
redfler May 9, 2026
4b60e40
fix docs
redfler May 9, 2026
9c44458
fix docs
redfler May 9, 2026
82da515
resolve isaaclab-review-bot
redfler May 9, 2026
46fdce9
Merge branch 'develop' into xul/determinism
redfler May 9, 2026
ec23642
resolve isaaclab-review-bot
redfler May 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ Guidelines for modifications:
* Xiaodi Yuan
* Xinjie Yao
* Xinpeng Liu
* Xu Li
* Yang Jin
* Yanzi Zhu
* Yijie Guo
Expand Down
168 changes: 168 additions & 0 deletions apps/isaaclab.python.headless.determinism.kit
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
##
# Adapted from: https://github.com/NVIDIA-Omniverse/OmniIsaacGymEnvs/blob/main/apps/omni.isaac.sim.python.gym.camera.kit
#
# This app file designed specifically towards vision-based RL tasks. It provides necessary settings to enable
# multiple cameras to be rendered each frame. Additional settings are also applied to increase performance when
# rendering cameras across multiple environments.
##

[package]
title = "Isaac Lab Python Headless Deterministic Rendering"
description = "An app for running Isaac Lab headlessly with rendering enabled and deterministic settings"
version = "3.0.0"

# That makes it browsable in UI with "experience" filter
keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"]

[dependencies]
# Isaac Lab headless rendering app
"isaaclab.python.headless.rendering" = {}


[settings.isaaclab]
# This is used to check that this experience file is loaded when using cameras
cameras_enabled = true

[settings]
# Note: This path was adapted to be respective to the kit-exe file location
app.versionFile = "${exe-path}/VERSION"
app.folder = "${exe-path}/"
app.name = "IsaacLab"
app.version = "3.0.0"

### FSD
app.useFabricSceneDelegate = true
# Temporary, should be enabled by default in Kit soon
rtx.hydra.readTransformsFromFabricInRenderDelegate = true

# Disable print outs on extension startup information
# this only disables the app print_and_log function
app.enableStdoutOutput = false

# set the default ros bridge to disable on startup
isaac.startup.ros_bridge_extension = ""

# Flags for better rendering performance
# Disabling these settings reduces renderer VRAM usage and improves rendering performance, but at some quality cost
rtx.translucency.enabled = false
rtx.reflections.enabled = false
rtx.indirectDiffuse.enabled = false
rtx-transient.dlssg.enabled = false
rtx.directLighting.sampledLighting.enabled = true
rtx.directLighting.sampledLighting.samplesPerPixel = 1
rtx.sceneDb.ambientLightIntensity = 1.0
# rtx.shadows.enabled = false

# Avoids replicator warning
rtx.pathtracing.maxSamplesPerLaunch = 1000000
# Avoids silent trimming of tiles
rtx.viewTile.limit = 1000000

# Disable present thread to improve performance
exts."omni.renderer.core".present.enabled=false

# Disabling these settings reduces renderer VRAM usage and improves rendering performance, but at some quality cost
rtx.raytracing.cached.enabled = false
rtx.ambientOcclusion.enabled = false

# Set the DLSS model
rtx.post.dlss.execMode = 0 # can be 0 (Performance), 1 (Balanced), 2 (Quality), or 3 (Auto)

# Avoids unnecessary GPU context initialization
renderer.multiGpu.maxGpuCount=1

# Force synchronous rendering to improve training results
omni.replicator.asyncRendering = false

Comment thread
redfler marked this conversation as resolved.
# Avoids frame offset issue
app.updateOrder.checkForHydraRenderComplete = 1000
app.renderer.waitIdle=true
app.hydraEngine.waitIdle=true

# NuRec
omni.rtx.nre.compositing.rendererHints = 3

# Forces serial processing for omni graph to avoid NCCL timeout hangs in distributed training
app.execution.debug.forceSerial = true

app.audio.enabled = false

# Enable Vulkan - avoids torch+cu12 error on windows
app.vulkan = true

# Set profiler backend to NVTX by default
app.profilerBackend = "nvtx"

# Disables rate limit in runloop
app.runLoops.main.rateLimitEnabled=false

# disable replicator orchestrator for better runtime perf
exts."omni.replicator.core".Orchestrator.enabled = false

# disable the metrics assembler change listener, we don't want to do any runtime changes
metricsAssembler.changeListenerEnabled = false

# explicitly disable omni.kit.pip_archive to prevent conflicting dependencies
app.extensions.excluded = ["omni.kit.pip_archive", "omni.isaac.ml_archive", "isaacsim.pip.newton", "omni.warp.core"]

[settings.app.python]
# These disable the kit app from also printing out python output, which gets confusing
interceptSysStdOutput = false
logSysStdOutput = false

[settings.app.renderer]
skipWhileMinimized = false
sleepMsOnFocus = 0
sleepMsOutOfFocus = 0

[settings.physics]
updateToUsd = false
updateParticlesToUsd = false
updateVelocitiesToUsd = false
updateForceSensorsToUsd = false
outputVelocitiesLocalSpace = false
useFastCache = false
visualizationDisplayJoints = false
fabricUpdateTransformations = false
fabricUpdateVelocities = false
fabricUpdateForceSensors = false
fabricUpdateJointStates = false
### When Direct GPU mode is enabled (suppressReadback=true) use direct interop between PhysX GPU and Fabric
fabricUseGPUInterop = true

# Register extension folder from this repo in kit
[settings.app.exts]
folders = [
"${exe-path}/exts", # kit extensions
"${exe-path}/extscore", # kit core extensions
"${exe-path}/../exts", # isaac extensions
"${exe-path}/../extscache", # isaac cache extensions
"${exe-path}/../extsPhysics", # isaac physics extensions
"${exe-path}/../isaacsim/exts", # isaac extensions for pip
"${exe-path}/../isaacsim/extscache", # isaac cache extensions for pip
"${exe-path}/../isaacsim/extsPhysics", # isaac physics extensions for pip
"${app}", # needed to find other app files
"${app}/../source", # needed to find extensions in Isaac Lab
]

[settings.persistent]
UJITSO.geometry = true
UJITSO.enabled = true

# Asset path
# set the S3 directory manually to the latest published S3
# note: this is done to ensure prior versions of Isaac Sim still use the latest assets
[settings]
persistent.isaac.asset_root.default = "https://omniverse-content-staging.s3-us-west-2.amazonaws.com/Assets/Isaac/6.0"
persistent.isaac.asset_root.cloud = "https://omniverse-content-staging.s3-us-west-2.amazonaws.com/Assets/Isaac/6.0"
persistent.isaac.asset_root.nvidia = "https://omniverse-content-staging.s3-us-west-2.amazonaws.com/Assets/Isaac/6.0"

# ═══════════════════════════════════════════════════════════
# DETERMINISM-CRITICAL: changing these may break reproducibility
# ═══════════════════════════════════════════════════════════
rtx.rendermode = "RealTimePathTracing"
# Determinism: disable RTPT caches so each run recomputes lighting from the same
# explicit inputs. Warm/cold cache history can otherwise change accumulation paths
# and produce frame-to-frame drift across repeated experiments.
rtx.rtpt.cached.enabled = false
rtx.rtpt.lightcache.cached.enabled = false
77 changes: 77 additions & 0 deletions docs/source/features/reproducibility.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,83 @@ simulation results are reproducible across different runs. The seed is set into
parameters :attr:`isaaclab.envs.ManagerBasedEnvCfg.seed` or :attr:`isaaclab.envs.DirectRLEnvCfg.seed`
depending on the manager-based or direct environment implementation respectively.

App-level deterministic experience selection is exposed through ``AppLauncher``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``--deterministic`` flag is provided by :meth:`isaaclab.app.AppLauncher.add_app_launcher_args`.
When used with the default experience selection logic in a compatible headless launch, AppLauncher
automatically selects ``isaaclab.python.headless.determinism.kit``.

**Strict PyTorch determinism** (calling :meth:`~isaaclab.utils.seed.configure_seed` with
``torch_deterministic=True``) is wired into the **RL-Games** training script only. Other frameworks
still honor ``--seed`` / agent configuration for the environment; use
:meth:`~isaaclab.utils.seed.configure_seed` from your own entry point if you need the same PyTorch-wide
behavior elsewhere.

To enable deterministic rendering/app settings, launch workflows with ``--deterministic``
**and** ``--enable_cameras`` **and** ``--headless`` (without livestream/XR):

.. code-block:: bash

./isaaclab.sh -p scripts/reinforcement_learning/rl_games/train.py \
--task Isaac-Cartpole-v0 \
--enable_cameras --headless --deterministic

You can still pass ``--experience isaaclab.python.headless.determinism.kit`` explicitly if you prefer.

Gymnasium registry (``gym.register``) and training scripts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Isaac Lab tasks are registered with `Gymnasium <https://gymnasium.farama.org/>`_ using ``gym.register``.
Besides ``id`` and ``entry_point``, the ``kwargs`` dict lists **string entry points** that tell each
training script where to load configs from—for example:

* ``env_cfg_entry_point`` — environment configuration class (always present for Isaac tasks).
* ``rl_games_cfg_entry_point``, ``sb3_cfg_entry_point``, ``skrl_cfg_entry_point``, ``rsl_rl_cfg_entry_point`` —
optional; **only keys that appear in ``kwargs`` are valid** for that task id.

When you run ``scripts/reinforcement_learning/<framework>/train.py --task <TASK_ID>``, the script resolves
``<framework>_cfg_entry_point`` (or the name you pass with ``--agent``) against the registry. If the task
was registered **without** that key—for example ``Isaac-Cartpole-RGB-v0`` currently lists only
``rl_games_cfg_entry_point``—you will get a ``ValueError`` such as “Could not find configuration …
``sb3_cfg_entry_point``”. To use another framework you must either pick a task that registers that entry
point or extend ``gym.register(..., kwargs={...})`` for that task with matching agent YAML/Python configs.

Regression test for training scripts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The file ``source/isaaclab_tasks/test/test_train_scripts_deterministic.py`` checks that:

* ``AppLauncher`` exposes ``--deterministic``;
* the RL-Games training script calls :meth:`~isaaclab.utils.seed.configure_seed` after ``Runner`` construction;
* (optional, heavy) for RL-Games on ``Isaac-Cartpole-RGB-v0``, two runs **without** ``--deterministic`` diverge
in logged ``rewards/iter``, while two runs **with** ``--deterministic`` match.

Run all tests in that file from the repository root:

.. code-block:: bash

cd /path/to/isaaclab
./isaaclab.sh -p -m pytest source/isaaclab_tasks/test/test_train_scripts_deterministic.py

**Heavy reproducibility test (starts Kit and trains multiple times).** It is skipped unless you set
``ISAACLAB_RUN_DETERMINISM_TRAIN_TEST=1``:

.. code-block:: bash

cd /path/to/isaaclab
ISAACLAB_RUN_DETERMINISM_TRAIN_TEST=1 ./isaaclab.sh -p -m pytest \
source/isaaclab_tasks/test/test_train_scripts_deterministic.py -k reproducibility

**Pytest ``-k`` (keyword expression).** ``-k`` filters which tests run by matching their **names** (test
function name, class name, and parametrized case ids). It is **not** the same as ``-m`` (markers).

* ``-k reproducibility`` — runs only tests whose full name contains that substring (for example the
heavy RL-Games test ``test_rl_games_deterministic_flag_affects_rewards_reproducibility``).
* ``-k "not reproducibility"`` — runs the lighter checks and **skips** the heavy training comparison.
* Inspect names with ``./isaaclab.sh -p -m pytest ... --collect-only -q``; combine filters with
``and`` / ``or`` / ``not`` and parentheses as needed.

For results on our determinacy testing for RL training, please check the GitHub Pull Request `#940`_.

.. tip::
Expand Down
5 changes: 5 additions & 0 deletions scripts/reinforcement_learning/rl_games/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from isaaclab.utils.assets import retrieve_file_path
from isaaclab.utils.dict import print_dict
from isaaclab.utils.io import dump_yaml
from isaaclab.utils.seed import configure_seed

from isaaclab_rl.rl_games import MultiObserver, PbtAlgoObserver, RlGamesGpuEnv, RlGamesVecEnvWrapper

Expand Down Expand Up @@ -208,6 +209,10 @@ def main():
else:
runner = Runner(IsaacAlgoObserver())

# configure_seed must be called after Runner() so that PyTorch deterministic settings
# do not interfere with Runner's internal initialization.
configure_seed(env_cfg.seed, args_cli.deterministic)
Comment thread
redfler marked this conversation as resolved.

runner.load(agent_cfg)
runner.reset()

Expand Down
30 changes: 29 additions & 1 deletion source/isaaclab/isaaclab/app/app_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:

If provided as an empty string, the experience file is determined based on the command-line flags:

* If deterministic and headless mode are True (without livestream/XR), the experience file is set to
``isaaclab.python.headless.determinism.kit``.
* If headless and enable_cameras are True, the experience file is set to
``isaaclab.python.headless.rendering.kit``.
* If headless is False and enable_cameras is True, the experience file is set to
Expand All @@ -363,6 +365,9 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
* If headless is True and enable_cameras is False, the experience file is set to
``isaaclab.python.headless.kit``.

* ``deterministic`` (bool): Enable deterministic app settings by selecting
``isaaclab.python.headless.determinism.kit`` automatically for compatible default headless launches.

* ``kit_args`` (str): Optional command line arguments to be passed to Omniverse Kit directly.
Arguments should be combined into a single string separated by space.
Example usage: --kit_args "--ext-folder=/path/to/ext1 --ext-folder=/path/to/ext2"
Expand Down Expand Up @@ -488,6 +493,15 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
" it is resolved relative to the `apps` folder in Isaac Sim and Isaac Lab (in that order)."
),
)
arg_group.add_argument(
"--deterministic",
action="store_true",
default=AppLauncher._APPLAUNCHER_CFG_INFO["deterministic"][1],
help=(
"Enable deterministic app settings. If --experience is not set and the launch is headless"
" (without livestream/XR), AppLauncher auto-selects `isaaclab.python.headless.determinism.kit`."
),
)
arg_group.add_argument(
"--rendering_mode",
type=str,
Expand Down Expand Up @@ -556,6 +570,7 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
"xr": ([bool], False),
"device": ([str], "cuda:0"),
"experience": ([str], ""),
"deterministic": ([bool], False),
"rendering_mode": ([str], "balanced"),
"max_visible_envs": ([int, type(None)], None),
}
Expand Down Expand Up @@ -1003,6 +1018,9 @@ def _resolve_experience_file(self, launcher_args: dict):
# Check if input keywords contain an 'experience' file setting
# Note: since experience is taken as a separate argument by Simulation App, we store it separately
self._sim_experience_file = launcher_args.pop("experience", "")
deterministic_mode = bool(
launcher_args.get("deterministic", AppLauncher._APPLAUNCHER_CFG_INFO["deterministic"][1])
)

# If nothing is provided resolve the experience file based on the headless flag
kit_app_exp_path = os.environ["EXP_PATH"]
Expand All @@ -1015,7 +1033,17 @@ def _resolve_experience_file(self, launcher_args: dict):
if self._sim_experience_file == "":
# check if the headless flag is set
# xr rendering overrides camera rendering settings
if self._enable_cameras and not self._xr:
if deterministic_mode and (not self._enable_cameras or not self._headless or self._livestream or self._xr):
logger.warning(
"--deterministic has no effect when not in headless mode or "
"when cameras are disabled or when livestreaming or XR is enabled."
"Use --enable_cameras --headless --deterministic for deterministic rendering."
)
if deterministic_mode and self._enable_cameras and self._headless and not self._livestream and not self._xr:
self._sim_experience_file = os.path.join(
isaaclab_app_exp_path, "isaaclab.python.headless.determinism.kit"
)
elif self._enable_cameras and not self._xr:
if self._headless and not self._livestream:
self._sim_experience_file = os.path.join(
isaaclab_app_exp_path, "isaaclab.python.headless.rendering.kit"
Expand Down
Loading