Commit 50446e76 authored by matthewtrepte's avatar matthewtrepte Committed by Kelly Guo

Fixes for stage in memory and Isaac Sim 4.5 compatibility (#503)

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link:
https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html
-->

- Adds 4.5 backwards compatibility (thanks @ooctipus)
- Adds unit test coverage for stage in mem for all envs
- Fixes an issue caused by a missing on play callback when stage in mem
is enabled (thanks @ossamaAhmed)
- Add stage in mem support for callbacks which require the stage in mem
(in headless mode)
- Add more clean-up steps to envs to avoid hanging issue with stage in
mem
- Fixes attach stage to context fn to include attaching to physx
- Sets stage in mem to False by default
- Cleans up warning messages

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- This change requires a documentation update

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------
Signed-off-by: 's avatarrwiltz <165190220+rwiltz@users.noreply.github.com>
Signed-off-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
Signed-off-by: 's avatarAshwin Varghese Kuruttukulam <123109010+ashwinvkNV@users.noreply.github.com>
Signed-off-by: 's avatarKelly Guo <kellyg@nvidia.com>
Signed-off-by: 's avatarMichael Gussert <michael@gussert.com>
Signed-off-by: 's avatarsamibouziri <79418773+samibouziri@users.noreply.github.com>
Signed-off-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
Signed-off-by: 's avatarKyle Morgenstein <34984693+KyleM73@users.noreply.github.com>
Signed-off-by: 's avatarHongyu Li <lihongyu0807@icloud.com>
Signed-off-by: 's avatarToni-SM <toni.semu@gmail.com>
Signed-off-by: 's avatarJames Tigue <166445701+jtigue-bdai@users.noreply.github.com>
Signed-off-by: 's avatarPascal Roth <57946385+pascal-roth@users.noreply.github.com>
Signed-off-by: 's avatarVictor Khaustov <3192677+vi3itor@users.noreply.github.com>
Signed-off-by: 's avatarAlvinC <alvincny529@gmail.com>
Signed-off-by: 's avatarTyler Lum <tylergwlum@gmail.com>
Signed-off-by: 's avatarMiguel Alonso Jr. <76960110+miguelalonsojr@users.noreply.github.com>
Signed-off-by: 's avatarrenaudponcelet <renaud.poncelet@gmail.com>
Co-authored-by: 's avatarlotusl-code <lotusl@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarjaczhangnv <jaczhang@nvidia.com>
Co-authored-by: 's avatarrwiltz <165190220+rwiltz@users.noreply.github.com>
Co-authored-by: 's avatarYanzi Zhu <yanziz@nvidia.com>
Co-authored-by: 's avatarnv-mhaselton <mhaselton@nvidia.com>
Co-authored-by: 's avatarcosmith-nvidia <141183495+cosmith-nvidia@users.noreply.github.com>
Co-authored-by: 's avatarMichael Gussert <michael@gussert.com>
Co-authored-by: 's avatarCY Chen <cyc@nvidia.com>
Co-authored-by: 's avataroahmednv <oahmed@Nvidia.com>
Co-authored-by: 's avatarAshwin Varghese Kuruttukulam <123109010+ashwinvkNV@users.noreply.github.com>
Co-authored-by: 's avatarRafael Wiltz <rwiltz@nvidia.com>
Co-authored-by: 's avatarPeter Du <peterd@nvidia.com>
Co-authored-by: 's avatarchengronglai <chengrongl@nvidia.com>
Co-authored-by: 's avatarpulkitg01 <pulkitg@nvidia.com>
Co-authored-by: 's avatarConnor Smith <cosmith@nvidia.com>
Co-authored-by: 's avatarAshwin Varghese Kuruttukulam <ashwinvk@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
Co-authored-by: 's avatarshauryadNv <shauryad@nvidia.com>
Co-authored-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
Co-authored-by: 's avatarsamibouziri <79418773+samibouziri@users.noreply.github.com>
Co-authored-by: 's avatarJames Smith <142246516+jsmith-bdai@users.noreply.github.com>
Co-authored-by: 's avatarShundo Kishi <syundo0730@gmail.com>
Co-authored-by: 's avatarSheikh Dawood <sabdulajees@nvidia.com>
Co-authored-by: 's avatarToni-SM <aserranomuno@nvidia.com>
Co-authored-by: 's avatarGonglitian <70052908+Gonglitian@users.noreply.github.com>
Co-authored-by: 's avatarJames Tigue <166445701+jtigue-bdai@users.noreply.github.com>
Co-authored-by: 's avatarMayank Mittal <mittalma@leggedrobotics.com>
Co-authored-by: 's avatarKyle Morgenstein <34984693+KyleM73@users.noreply.github.com>
Co-authored-by: 's avatarJohnson Sun <20457146+j3soon@users.noreply.github.com>
Co-authored-by: 's avatarPascal Roth <57946385+pascal-roth@users.noreply.github.com>
Co-authored-by: 's avatarHongyu Li <lihongyu0807@icloud.com>
Co-authored-by: 's avatarJean-Francois-Lafleche <57650687+Jean-Francois-Lafleche@users.noreply.github.com>
Co-authored-by: 's avatarWei Jinqi <changshanshi@outlook.com>
Co-authored-by: 's avatarLouis LE LAY <le.lay.louis@gmail.com>
Co-authored-by: 's avatarHarsh Patel <hapatel@theaiinstitute.com>
Co-authored-by: 's avatarKousheek Chakraborty <kousheekc@gmail.com>
Co-authored-by: 's avatarVictor Khaustov <3192677+vi3itor@users.noreply.github.com>
Co-authored-by: 's avatarAlvinC <alvincny529@gmail.com>
Co-authored-by: 's avatarFelipe Mohr <50018670+felipemohr@users.noreply.github.com>
Co-authored-by: 's avatarAdAstra7 <87345760+likecanyon@users.noreply.github.com>
Co-authored-by: 's avatargao <ziqi.gao@iff-extern.fraunhofer.de>
Co-authored-by: 's avatarTyler Lum <tylergwlum@gmail.com>
Co-authored-by: 's avatar-T.K.- <t_k_233@outlook.com>
Co-authored-by: 's avatarClemens Schwarke <96480707+ClemensSchwarke@users.noreply.github.com>
Co-authored-by: 's avatarMiguel Alonso Jr. <76960110+miguelalonsojr@users.noreply.github.com>
Co-authored-by: 's avatarMiguel Alonso Jr. <miguel.alonso@nfinite.app>
Co-authored-by: 's avatarrenaudponcelet <renaud.poncelet@gmail.com>
Co-authored-by: 's avatarAles Borovicka <aborovicka@nvidia.com>
Co-authored-by: 's avatarnv-mm <mmagdics@nvidia.com>
Co-authored-by: 's avatarAntoine RICHARD <antoiner@nvidia.com>
Co-authored-by: 's avatardvangelder-nvidia <dvangelder@nvidia.com>
Co-authored-by: 's avatarOcti Zhang <zhengyuz@nvidia.com>
parent dc3fb797
......@@ -163,7 +163,7 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
[settings.ngx]
......
......@@ -130,7 +130,7 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
# Asset path
......
......@@ -274,7 +274,7 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
[settings.physics]
......
......@@ -128,7 +128,7 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
# Asset path
......
......@@ -37,5 +37,5 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
......@@ -59,7 +59,7 @@ folders = [
"${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
"${app}/../../source", # needed to find extensions in Isaac Lab
]
# Asset path
......
......@@ -38,6 +38,33 @@ if not exist "%isaac_path%" (
)
goto :eof
rem -----------------------------------------------------------------------
rem Returns success (exit code 0) if Isaac Sim's version starts with "4.5"
rem -----------------------------------------------------------------------
:is_isaacsim_version_4_5
rem make sure we have %python_exe%
call :extract_python_exe
rem 1) try to locate the VERSION file via the kit install
for /f "delims=" %%V in ('!python_exe! -c "import isaacsim,os;print(os.path.abspath(os.path.join(os.path.dirname(isaacsim.__file__), os.pardir, os.pardir, 'VERSION')))"') do set "VERSION_PATH=%%V"
if exist "!VERSION_PATH!" (
for /f "usebackq delims=" %%L in ("!VERSION_PATH!") do set "ISAACSIM_VER=%%L"
) else (
rem 2) fallback to importlib.metadata if no VERSION file
for /f "delims=" %%L in ('!python_exe! -c "from importlib.metadata import version;print(version(''isaacsim''))"') do set "ISAACSIM_VER=%%L"
)
rem Clean up the version string (remove any trailing whitespace or newlines)
set "ISAACSIM_VER=!ISAACSIM_VER: =!"
rem Use string comparison instead of findstr for more reliable matching
if "!ISAACSIM_VER:~0,3!"=="4.5" (
exit /b 0
) else (
exit /b 1
)
goto :eof
rem extract the python from isaacsim
:extract_python_exe
rem check if using conda
......@@ -132,6 +159,30 @@ if %errorlevel% equ 0 (
) else (
echo [INFO] Creating conda environment named '%env_name%'...
echo [INFO] Installing dependencies from %ISAACLAB_PATH%\environment.yml
rem ————————————————————————————————
rem patch Python version if needed, but back up first
rem ————————————————————————————————
copy "%ISAACLAB_PATH%environment.yml" "%ISAACLAB_PATH%environment.yml.bak" >nul
call :is_isaacsim_version_4_5
if !ERRORLEVEL! EQU 0 (
echo [INFO] Detected Isaac Sim 4.5 --^> forcing python=3.10
rem Use findstr to replace the python version line
(
for /f "delims=" %%L in ('type "%ISAACLAB_PATH%environment.yml"') do (
set "line=%%L"
set "line=!line: =!"
if "!line:~0,15!"=="-python=3.11" (
echo - python=3.10
) else (
echo %%L
)
)
) > "%ISAACLAB_PATH%environment.yml.tmp"
rem Replace the original file with the modified version
move /y "%ISAACLAB_PATH%environment.yml.tmp" "%ISAACLAB_PATH%environment.yml" >nul
) else (
echo [INFO] Isaac Sim ^>=5.0, installing python=3.11
)
call conda env create -y --file %ISAACLAB_PATH%\environment.yml -n %env_name%
)
rem cache current paths for later
......
......@@ -43,6 +43,35 @@ install_system_deps() {
fi
}
is_isaacsim_version_4_5() {
local python_exe
python_exe=$(extract_python_exe)
# 1) Try the VERSION file
local sim_file
sim_file=$("${python_exe}" -c "import isaacsim; print(isaacsim.__file__)" 2>/dev/null) || return 1
local version_path
version_path=$(dirname "${sim_file}")/../../VERSION
if [[ -f "${version_path}" ]]; then
local ver
ver=$(head -n1 "${version_path}")
[[ "${ver}" == 4.5* ]] && return 0
fi
# 2) Fallback to importlib.metadata via a here-doc
local ver
ver=$("${python_exe}" <<'PYCODE' 2>/dev/null
from importlib.metadata import version, PackageNotFoundError
try:
print(version("isaacsim"))
except PackageNotFoundError:
import sys; sys.exit(1)
PYCODE
) || return 1
[[ "${ver}" == 4.5* ]]
}
# check if running in docker
is_docker() {
[ -f /.dockerenv ] || \
......@@ -171,8 +200,20 @@ setup_conda_env() {
echo -e "[INFO] Creating conda environment named '${env_name}'..."
echo -e "[INFO] Installing dependencies from ${ISAACLAB_PATH}/environment.yml"
# Create environment from YAML file with specified name
# patch Python version if needed, but back up first
cp "${ISAACLAB_PATH}/environment.yml"{,.bak}
if is_isaacsim_version_4_5; then
echo "[INFO] Detected Isaac Sim 4.5 → forcing python=3.10"
sed -i 's/^ - python=3\.11/ - python=3.10/' "${ISAACLAB_PATH}/environment.yml"
else
echo "[INFO] Isaac Sim 5.0, installing python=3.11"
fi
conda env create -y --file ${ISAACLAB_PATH}/environment.yml -n ${env_name}
# (optional) restore original environment.yml:
if [[ -f "${ISAACLAB_PATH}/environment.yml.bak" ]]; then
mv "${ISAACLAB_PATH}/environment.yml.bak" "${ISAACLAB_PATH}/environment.yml"
fi
fi
# cache current paths for later
......
......@@ -96,10 +96,8 @@ def main():
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.0, 0.0, 2.5], [-0.5, 0.0, 0.5])
# Design scene by adding assets to it
# Design scene
design_scene()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -24,14 +24,11 @@ simulation_app = app_launcher.app
import numpy as np
import torch
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.actuators import ImplicitActuatorCfg
from isaaclab.assets import AssetBaseCfg
from isaaclab.assets.articulation import ArticulationCfg
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
JETBOT_CONFIG = ArticulationCfg(
......@@ -163,16 +160,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
def main():
"""Main function."""
# Initialize the simulation context
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
sim.set_camera_view([3.5, 0.0, 3.2], [0.0, 0.0, 0.5])
# Design scene
scene_cfg = NewRobotsSceneCfg(args_cli.num_envs, env_spacing=2.0)
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene = InteractiveScene(scene_cfg)
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -35,12 +35,10 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import Articulation
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
##
# Pre-defined configs
......@@ -123,14 +121,12 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities, scene_origins = design_scene()
attach_stage_to_usd_context()
scene_origins = torch.tensor(scene_origins, device=sim.device)
# Play the simulator
sim.reset()
......
......@@ -36,13 +36,11 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
import isaaclab.utils.math as math_utils
from isaaclab.assets import DeformableObject, DeformableObjectCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
def design_scene():
......@@ -148,14 +146,12 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Deformab
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view(eye=[3.0, 0.0, 1.0], target=[0.0, 0.0, 0.5])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities, scene_origins = design_scene()
attach_stage_to_usd_context()
scene_origins = torch.tensor(scene_origins, device=sim.device)
# Play the simulator
sim.reset()
......
......@@ -36,13 +36,11 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
import isaaclab.utils.math as math_utils
from isaaclab.assets import RigidObject, RigidObjectCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
def design_scene():
......@@ -128,14 +126,12 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, RigidObj
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view(eye=[1.5, 0.0, 1.0], target=[0.0, 0.0, 0.0])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities, scene_origins = design_scene()
attach_stage_to_usd_context()
scene_origins = torch.tensor(scene_origins, device=sim.device)
# Play the simulator
sim.reset()
......
......@@ -36,12 +36,10 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import Articulation, SurfaceGripper, SurfaceGripperCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
##
# Pre-defined configs
......@@ -168,10 +166,7 @@ def main():
# Set main camera
sim.set_camera_view([2.75, 7.5, 10.0], [2.75, 0.0, 0.0])
# Design scene
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene_entities, scene_origins = design_scene()
attach_stage_to_usd_context()
scene_origins = torch.tensor(scene_origins, device=sim.device)
# Play the simulator
sim.reset()
......
......@@ -35,13 +35,10 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import ArticulationCfg, AssetBaseCfg
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import configclass
##
......@@ -113,16 +110,13 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
# Design scene
scene_cfg = CartpoleSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene = InteractiveScene(scene_cfg)
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -141,7 +141,6 @@ def main():
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
env_cfg.sim.device = args_cli.device
env_cfg.sim.create_stage_in_memory = True
# setup base environment
env = ManagerBasedEnv(cfg=env_cfg)
......
......@@ -315,7 +315,6 @@ def main():
# setup base environment
env_cfg = CubeEnvCfg()
env_cfg.sim.create_stage_in_memory = True
env = ManagerBasedEnv(cfg=env_cfg)
# setup target position commands
......
......@@ -205,7 +205,6 @@ def main():
"""Main function."""
# setup base environment
env_cfg = QuadrupedEnvCfg()
env_cfg.sim.create_stage_in_memory = True
env = ManagerBasedEnv(cfg=env_cfg)
# load level policy
......
......@@ -70,7 +70,6 @@ def main():
env_cfg.sim.device = args_cli.device
if args_cli.device == "cpu":
env_cfg.sim.use_fabric = False
env_cfg.sim.create_stage_in_memory = True
# create environment
env = ManagerBasedRLEnv(cfg=env_cfg)
......
......@@ -46,7 +46,6 @@ def main():
env_cfg = CartpoleEnvCfg()
env_cfg.scene.num_envs = args_cli.num_envs
env_cfg.sim.device = args_cli.device
env_cfg.sim.create_stage_in_memory = True
# setup RL environment
env = ManagerBasedRLEnv(cfg=env_cfg)
......
......@@ -41,13 +41,10 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import ArticulationCfg, AssetBaseCfg
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.sensors import CameraCfg, ContactSensorCfg, RayCasterCfg, patterns
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import configclass
##
......@@ -160,16 +157,13 @@ def main():
"""Main function."""
# Initialize the simulation context
sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view(eye=[3.5, 3.5, 3.5], target=[0.0, 0.0, 0.0])
# Design scene
scene_cfg = SensorsSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene = InteractiveScene(scene_cfg)
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -35,7 +35,6 @@ simulation_app = app_launcher.app
import math
import torch
import isaacsim.core.utils.stage as stage_utils
import isaacsim.util.debug_draw._debug_draw as omni_debug_draw
import isaaclab.sim as sim_utils
......@@ -45,7 +44,6 @@ from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import FRAME_MARKER_CFG
from isaaclab.sensors import FrameTransformer, FrameTransformerCfg, OffsetCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
##
# Pre-defined configs
......@@ -166,14 +164,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene_entities: dict):
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view(eye=[2.5, 2.5, 2.5], target=[0.0, 0.0, 0.0])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities = design_scene()
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -34,12 +34,10 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import RigidObject, RigidObjectCfg
from isaaclab.sensors.ray_caster import RayCaster, RayCasterCfg, patterns
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
from isaaclab.utils.timer import Timer
......@@ -132,14 +130,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene_entities: dict):
def main():
"""Main function."""
# Load simulation context
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([0.0, 15.0, 15.0], [0.0, 0.0, -2.5])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities = design_scene()
attach_stage_to_usd_context()
# Play simulator
sim.reset()
# Now we are ready!
......
......@@ -39,12 +39,10 @@ import os
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import omni.replicator.core as rep
import isaaclab.sim as sim_utils
from isaaclab.sensors.ray_caster import RayCasterCamera, RayCasterCameraCfg, patterns
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import convert_dict_to_backend
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
from isaaclab.utils.math import project_points, unproject_depth
......@@ -165,14 +163,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene_entities: dict):
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg()
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 2.5, 3.5], [0.0, 0.0, 0.0])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities = design_scene()
attach_stage_to_usd_context()
# Play simulator
sim.reset()
# Now we are ready!
......
......@@ -66,7 +66,6 @@ import random
import torch
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import omni.replicator.core as rep
import isaaclab.sim as sim_utils
......@@ -75,7 +74,6 @@ from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import RAY_CASTER_MARKER_CFG
from isaaclab.sensors.camera import Camera, CameraCfg
from isaaclab.sensors.camera.utils import create_pointcloud_from_depth
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import convert_dict_to_backend
......@@ -270,14 +268,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene_entities: dict):
def main():
"""Main function."""
# Load simulation context
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
# Design scene
scene_entities = design_scene()
attach_stage_to_usd_context()
# Play simulator
sim.reset()
# Now we are ready!
......
......@@ -39,8 +39,6 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import AssetBaseCfg
from isaaclab.controllers import DifferentialIKController, DifferentialIKControllerCfg
......@@ -48,7 +46,6 @@ from isaaclab.managers import SceneEntityCfg
from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import FRAME_MARKER_CFG
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import configclass
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
from isaaclab.utils.math import subtract_frame_transforms
......@@ -193,16 +190,13 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])
# Design scene
scene_cfg = TableTopSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene = InteractiveScene(scene_cfg)
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -38,8 +38,6 @@ simulation_app = app_launcher.app
import torch
import isaacsim.core.utils.stage as stage_utils
import isaaclab.sim as sim_utils
from isaaclab.assets import Articulation, AssetBaseCfg
from isaaclab.controllers import OperationalSpaceController, OperationalSpaceControllerCfg
......@@ -47,7 +45,6 @@ from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import FRAME_MARKER_CFG
from isaaclab.scene import InteractiveScene, InteractiveSceneCfg
from isaaclab.sensors import ContactSensorCfg
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils import configclass
from isaaclab.utils.math import (
combine_frame_transforms,
......@@ -465,16 +462,13 @@ def convert_to_task_frame(osc: OperationalSpaceController, command: torch.tensor
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, create_stage_in_memory=True)
sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device)
sim = sim_utils.SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])
# Design scene
scene_cfg = SceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0)
# Create scene with stage in memory and then attach to USD context
with stage_utils.use_stage(sim.get_initial_stage()):
scene = InteractiveScene(scene_cfg)
attach_stage_to_usd_context()
# Play the simulator
sim.reset()
# Now we are ready!
......
......@@ -362,16 +362,6 @@ class AppLauncher:
),
)
# special flag for backwards compatibility
arg_group.add_argument(
"--use_isaacsim_45",
type=bool,
default=False,
help=(
"Uses previously version of Isaac Sim 4.5. This will reference the Isaac Sim 4.5 compatible app files"
" and will result in some features being unavailable. For full feature set, please update to Isaac Sim"
" 5.0."
),
)
# Corresponding to the beginning of the function,
# if we have removed -h/--help handling, we add it back.
......@@ -714,7 +704,8 @@ class AppLauncher:
kit_app_exp_path = os.environ["EXP_PATH"]
isaaclab_app_exp_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), *[".."] * 4, "apps")
# For Isaac Sim 4.5 compatibility, we use the 4.5 app files in a different folder
if launcher_args.get("use_isaacsim_45", False):
# if launcher_args.get("use_isaacsim_45", False):
if self.is_isaac_sim_version_4_5():
isaaclab_app_exp_path = os.path.join(isaaclab_app_exp_path, "isaacsim_4_5")
if self._sim_experience_file == "":
......@@ -770,7 +761,7 @@ class AppLauncher:
if recording_enabled:
if self._headless:
raise ValueError("Animation recording is not supported in headless mode.")
if launcher_args.get("use_isaacsim_45", False):
if self.is_isaac_sim_version_4_5():
raise RuntimeError(
"Animation recording is not supported in Isaac Sim 4.5. Please update to Isaac Sim 5.0."
)
......@@ -890,6 +881,8 @@ class AppLauncher:
# parse preset file
repo_path = os.path.join(carb.tokens.get_tokens_interface().resolve("${app}"), "..")
if self.is_isaac_sim_version_4_5():
repo_path = os.path.join(repo_path, "..")
preset_filename = os.path.join(repo_path, f"apps/rendering_modes/{rendering_mode}.kit")
with open(preset_filename) as file:
preset_dict = toml.load(file)
......@@ -935,6 +928,30 @@ class AppLauncher:
# raise the error for keyboard interrupt
raise KeyboardInterrupt
def is_isaac_sim_version_4_5(self) -> bool:
if not hasattr(self, "_is_sim_ver_4_5"):
# 1) Try to read the VERSION file (for manual / binary installs)
version_path = os.path.abspath(os.path.join(os.path.dirname(isaacsim.__file__), "../../VERSION"))
if os.path.isfile(version_path):
with open(version_path) as f:
ver = f.readline().strip()
if ver.startswith("4.5"):
self._is_sim_ver_4_5 = True
return True
# 2) Fall back to metadata (for pip installs)
from importlib.metadata import version as pkg_version
try:
ver = pkg_version("isaacsim")
if ver.startswith("4.5"):
self._is_sim_ver_4_5 = True
else:
self._is_sim_ver_4_5 = False
except Exception:
self._is_sim_ver_4_5 = False
return self._is_sim_ver_4_5
def _hide_play_button(self, flag):
"""Hide/Unhide the play button in the toolbar.
......
......@@ -1361,6 +1361,21 @@ class Articulation(AssetBase):
if self._root_physx_view._backend is None:
raise RuntimeError(f"Failed to create articulation at: {root_prim_path_expr}. Please check PhysX logs.")
if int(get_version()[2]) < 5:
omni.log.warn(
"Spatial tendons are not supported in Isaac Sim < 5.0: patching spatial-tendon getter"
" and setter to use dummy value"
)
self._root_physx_view.max_spatial_tendons = 0
self._root_physx_view.get_spatial_tendon_stiffnesses = lambda: torch.empty(0, device=self.device)
self._root_physx_view.get_spatial_tendon_dampings = lambda: torch.empty(0, device=self.device)
self._root_physx_view.get_spatial_tendon_limit_stiffnesses = lambda: torch.empty(0, device=self.device)
self._root_physx_view.get_spatial_tendon_offsets = lambda: torch.empty(0, device=self.device)
self._root_physx_view.set_spatial_tendon_properties = lambda *args, **kwargs: omni.log.warn(
"Spatial tendons are not supported in Isaac Sim < 5.0: Calling"
" set_spatial_tendon_properties has no effect"
)
# log information about the articulation
omni.log.info(f"Articulation initialized at: {self.cfg.prim_path} with root '{root_prim_path_expr}'.")
omni.log.info(f"Is fixed root: {self.is_fixed_base}")
......@@ -1578,14 +1593,6 @@ class Articulation(AssetBase):
self._spatial_tendon_names = list()
# parse fixed tendons properties if they exist
if self.num_fixed_tendons > 0 or self.num_spatial_tendons > 0:
# for spatial tendons, check if we are using isaac sim 5.0
if self.num_spatial_tendons > 0:
isaac_sim_version = get_version()
# checks for Isaac Sim v5.0 as spatial tendons are only available since 5.0
if int(isaac_sim_version[2]) < 5:
raise RuntimeError(
"Spatial tendons are not available in Isaac Sim 4.5. Please update to Isaac Sim 5.0."
)
joint_paths = self.root_physx_view.dof_paths[0]
......
......@@ -21,6 +21,7 @@ from isaacsim.core.simulation_manager import IsaacEvents, SimulationManager
from isaacsim.core.utils.stage import get_current_stage
import isaaclab.sim as sim_utils
from isaaclab.sim import SimulationContext
if TYPE_CHECKING:
from .asset_base_cfg import AssetBaseCfg
......@@ -92,23 +93,40 @@ class AssetBase(ABC):
if len(matching_prims) == 0:
raise RuntimeError(f"Could not find prim with path {self.cfg.prim_path}.")
# note: Use weakref on all callbacks to ensure that this object can be deleted when its destructor is called.
# register simulator callbacks (with weakref safety to avoid crashes on deletion)
def safe_callback(callback_name, event, obj_ref):
"""Safely invoke a callback on a weakly-referenced object, ignoring ReferenceError if deleted."""
try:
obj = obj_ref
getattr(obj, callback_name)(event)
except ReferenceError:
# Object has been deleted; ignore.
pass
# note: use weakref on callbacks to ensure that this object can be deleted when its destructor is called.
# add callbacks for stage play/stop
# The order is set to 10 which is arbitrary but should be lower priority than the default order of 0
obj_ref = weakref.proxy(self)
timeline_event_stream = omni.timeline.get_timeline_interface().get_timeline_event_stream()
# the order is set to 10 which is arbitrary but should be lower priority than the default order of 0
# register timeline PLAY event callback (lower priority with order=10)
self._initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY),
lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_initialize_callback", event, obj_ref),
order=10,
)
# register timeline STOP event callback (lower priority with order=10)
self._invalidate_initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP),
lambda event, obj=weakref.proxy(self): obj._invalidate_initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_invalidate_initialize_callback", event, obj_ref),
order=10,
)
# register prim deletion callback
self._prim_deletion_callback_id = SimulationManager.register_callback(
self._on_prim_deletion, event=IsaacEvents.PRIM_DELETION
lambda event, obj_ref=obj_ref: safe_callback("_on_prim_deletion", event, obj_ref),
event=IsaacEvents.PRIM_DELETION,
)
# add handle for debug visualization (this is set to a valid handle inside set_debug_vis)
self._debug_vis_handle = None
# set initial state of debug visualization
......@@ -314,6 +332,9 @@ class AssetBase(ABC):
Note:
This function is called when the prim is deleted.
"""
# skip callback if required
if getattr(SimulationContext.instance(), "_skip_next_prim_deletion_callback_fn", False):
return
if prim_path == "/":
self._clear_callbacks()
return
......
......@@ -7,9 +7,9 @@ import torch
import weakref
import omni.physics.tensors.impl.api as physx
from isaacsim.core.utils.stage import get_current_stage_id
import isaaclab.utils.math as math_utils
from isaaclab.sim.utils import get_current_stage_id
from isaaclab.utils.buffers import TimestampedBuffer
......
......@@ -21,6 +21,7 @@ from pxr import UsdPhysics
import isaaclab.sim as sim_utils
import isaaclab.utils.math as math_utils
import isaaclab.utils.string as string_utils
from isaaclab.sim import SimulationContext
from ..asset_base import AssetBase
from .rigid_object_collection_data import RigidObjectCollectionData
......@@ -92,23 +93,40 @@ class RigidObjectCollection(AssetBase):
# stores object names
self._object_names_list = []
# note: Use weakref on all callbacks to ensure that this object can be deleted when its destructor is called.
# register simulator callbacks (with weakref safety to avoid crashes on deletion)
def safe_callback(callback_name, event, obj_ref):
"""Safely invoke a callback on a weakly-referenced object, ignoring ReferenceError if deleted."""
try:
obj = obj_ref
getattr(obj, callback_name)(event)
except ReferenceError:
# Object has been deleted; ignore.
pass
# note: use weakref on callbacks to ensure that this object can be deleted when its destructor is called.
# add callbacks for stage play/stop
# The order is set to 10 which is arbitrary but should be lower priority than the default order of 0
obj_ref = weakref.proxy(self)
timeline_event_stream = omni.timeline.get_timeline_interface().get_timeline_event_stream()
# the order is set to 10 which is arbitrary but should be lower priority than the default order of 0
# register timeline PLAY event callback (lower priority with order=10)
self._initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY),
lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_initialize_callback", event, obj_ref),
order=10,
)
# register timeline STOP event callback (lower priority with order=10)
self._invalidate_initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP),
lambda event, obj=weakref.proxy(self): obj._invalidate_initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_invalidate_initialize_callback", event, obj_ref),
order=10,
)
# register prim deletion callback
self._prim_deletion_callback_id = SimulationManager.register_callback(
self._on_prim_deletion, event=IsaacEvents.PRIM_DELETION
lambda event, obj_ref=obj_ref: safe_callback("_on_prim_deletion", event, obj_ref),
event=IsaacEvents.PRIM_DELETION,
)
self._debug_vis_handle = None
"""
......@@ -711,6 +729,9 @@ class RigidObjectCollection(AssetBase):
Note:
This function is called when the prim is deleted.
"""
# skip callback if required
if getattr(SimulationContext.instance(), "_skip_next_prim_deletion_callback_fn", False):
return
if prim_path == "/":
self._clear_callbacks()
return
......
......@@ -7,9 +7,9 @@ import torch
import weakref
import omni.physics.tensors.impl.api as physx
from isaacsim.core.utils.stage import get_current_stage_id
import isaaclab.utils.math as math_utils
from isaaclab.sim.utils import get_current_stage_id
from isaaclab.utils.buffers import TimestampedBuffer
......
......@@ -68,22 +68,39 @@ class SurfaceGripper(AssetBase):
# flag for whether the sensor is initialized
self._is_initialized = False
self._debug_vis_handle = None
# Register simulator callbacks (with weakref safety to avoid crashes on deletion)
def safe_callback(callback_name, event, obj_ref):
"""Safely invoke a callback on a weakly-referenced object, ignoring ReferenceError if deleted."""
try:
obj = obj_ref
getattr(obj, callback_name)(event)
except ReferenceError:
# Object has been deleted; ignore.
pass
# note: Use weakref on callbacks to ensure that this object can be deleted when its destructor is called.
# add callbacks for stage play/stop
# The order is set to 10 which is arbitrary but should be lower priority than the default order of 0
obj_ref = weakref.proxy(self)
timeline_event_stream = omni.timeline.get_timeline_interface().get_timeline_event_stream()
# The order is set to 10 which is arbitrary but should be lower priority than the default order of 0
# Register timeline PLAY event callback (lower priority with order=10)
self._initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY),
lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_initialize_callback", event, obj_ref),
order=10,
)
# Register timeline STOP event callback (lower priority with order=10)
self._invalidate_initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP),
lambda event, obj=weakref.proxy(self): obj._invalidate_initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_invalidate_initialize_callback", event, obj_ref),
order=10,
)
# Register prim deletion callback
self._prim_deletion_callback_id = SimulationManager.register_callback(
self._on_prim_deletion, event=IsaacEvents.PRIM_DELETION
lambda event, obj_ref=obj_ref: safe_callback("_on_prim_deletion", event, obj_ref),
event=IsaacEvents.PRIM_DELETION,
)
"""
......
......@@ -17,16 +17,16 @@ from collections.abc import Sequence
from dataclasses import MISSING
from typing import Any, ClassVar
import isaacsim.core.utils.stage as stage_utils
import isaacsim.core.utils.torch as torch_utils
import omni.kit.app
import omni.log
import omni.physx
from isaacsim.core.version import get_version
from isaaclab.managers import EventManager
from isaaclab.scene import InteractiveScene
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.sim.utils import attach_stage_to_usd_context, use_stage
from isaaclab.utils.noise import NoiseModel
from isaaclab.utils.timer import Timer
......@@ -119,7 +119,8 @@ class DirectMARLEnv(gym.Env):
# generate scene
with Timer("[INFO]: Time taken for scene creation", "scene_creation"):
with stage_utils.use_stage(self.sim.get_initial_stage()):
# set the stage context for scene creation steps which use the stage
with use_stage(self.sim.get_initial_stage()):
self.scene = InteractiveScene(self.cfg.scene)
self._setup_scene()
attach_stage_to_usd_context()
......@@ -150,6 +151,9 @@ class DirectMARLEnv(gym.Env):
if builtins.ISAAC_LAUNCHED_FROM_TERMINAL is False:
print("[INFO]: Starting the simulation. This may take a few seconds. Please wait...")
with Timer("[INFO]: Time taken for simulation start", "simulation_start"):
# since the reset can trigger callbacks which use the stage,
# we need to set the stage context here
with use_stage(self.sim.get_initial_stage()):
self.sim.reset()
# update scene to pre populate data buffers for assets and sensors.
# this is needed for the observation manager to get valid tensors for initialization.
......@@ -534,9 +538,18 @@ class DirectMARLEnv(gym.Env):
del self.scene
if self.viewport_camera_controller is not None:
del self.viewport_camera_controller
# clear callbacks and instance
if float(".".join(get_version()[2])) >= 5:
if self.cfg.sim.create_stage_in_memory:
# detach physx stage
omni.physx.get_physx_simulation_interface().detach_stage()
self.sim.stop()
self.sim.clear()
self.sim.clear_all_callbacks()
self.sim.clear_instance()
# destroy the window
if self._window is not None:
self._window = None
......
......@@ -17,17 +17,17 @@ from collections.abc import Sequence
from dataclasses import MISSING
from typing import Any, ClassVar
import isaacsim.core.utils.stage as stage_utils
import isaacsim.core.utils.torch as torch_utils
import omni.kit.app
import omni.log
import omni.physx
from isaacsim.core.simulation_manager import SimulationManager
from isaacsim.core.version import get_version
from isaaclab.managers import EventManager
from isaaclab.scene import InteractiveScene
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.sim.utils import attach_stage_to_usd_context, use_stage
from isaaclab.utils.noise import NoiseModel
from isaaclab.utils.timer import Timer
......@@ -125,7 +125,8 @@ class DirectRLEnv(gym.Env):
# generate scene
with Timer("[INFO]: Time taken for scene creation", "scene_creation"):
with stage_utils.use_stage(self.sim.get_initial_stage()):
# set the stage context for scene creation steps which use the stage
with use_stage(self.sim.get_initial_stage()):
self.scene = InteractiveScene(self.cfg.scene)
self._setup_scene()
attach_stage_to_usd_context()
......@@ -156,6 +157,9 @@ class DirectRLEnv(gym.Env):
if builtins.ISAAC_LAUNCHED_FROM_TERMINAL is False:
print("[INFO]: Starting the simulation. This may take a few seconds. Please wait...")
with Timer("[INFO]: Time taken for simulation start", "simulation_start"):
# since the reset can trigger callbacks which use the stage,
# we need to set the stage context here
with use_stage(self.sim.get_initial_stage()):
self.sim.reset()
# update scene to pre populate data buffers for assets and sensors.
# this is needed for the observation manager to get valid tensors for initialization.
......@@ -489,9 +493,18 @@ class DirectRLEnv(gym.Env):
del self.scene
if self.viewport_camera_controller is not None:
del self.viewport_camera_controller
# clear callbacks and instance
if float(".".join(get_version()[2])) >= 5:
if self.cfg.sim.create_stage_in_memory:
# detach physx stage
omni.physx.get_physx_simulation_interface().detach_stage()
self.sim.stop()
self.sim.clear()
self.sim.clear_all_callbacks()
self.sim.clear_instance()
# destroy the window
if self._window is not None:
self._window = None
......
......@@ -8,15 +8,16 @@ import torch
from collections.abc import Sequence
from typing import Any
import isaacsim.core.utils.stage as stage_utils
import isaacsim.core.utils.torch as torch_utils
import omni.log
import omni.physx
from isaacsim.core.simulation_manager import SimulationManager
from isaacsim.core.version import get_version
from isaaclab.managers import ActionManager, EventManager, ObservationManager, RecorderManager
from isaaclab.scene import InteractiveScene
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.sim.utils import attach_stage_to_usd_context, use_stage
from isaaclab.ui.widgets import ManagerLiveVisualizer
from isaaclab.utils.timer import Timer
......@@ -129,8 +130,8 @@ class ManagerBasedEnv:
# generate scene
with Timer("[INFO]: Time taken for scene creation", "scene_creation"):
# get stage handle and set stage context
with stage_utils.use_stage(self.sim.get_initial_stage()):
# set the stage context for scene creation steps which use the stage
with use_stage(self.sim.get_initial_stage()):
self.scene = InteractiveScene(self.cfg.scene)
attach_stage_to_usd_context()
print("[INFO]: Scene manager: ", self.scene)
......@@ -159,6 +160,9 @@ class ManagerBasedEnv:
if builtins.ISAAC_LAUNCHED_FROM_TERMINAL is False:
print("[INFO]: Starting the simulation. This may take a few seconds. Please wait...")
with Timer("[INFO]: Time taken for simulation start", "simulation_start"):
# since the reset can trigger callbacks which use the stage,
# we need to set the stage context here
with use_stage(self.sim.get_initial_stage()):
self.sim.reset()
# update scene to pre populate data buffers for assets and sensors.
# this is needed for the observation manager to get valid tensors for initialization.
......@@ -457,9 +461,18 @@ class ManagerBasedEnv:
del self.event_manager
del self.recorder_manager
del self.scene
# clear callbacks and instance
if float(".".join(get_version()[2])) >= 5:
if self.cfg.sim.create_stage_in_memory:
# detach physx stage
omni.physx.get_physx_simulation_interface().detach_stage()
self.sim.stop()
self.sim.clear()
self.sim.clear_all_callbacks()
self.sim.clear_instance()
# destroy the window
if self._window is not None:
self._window = None
......
......@@ -32,7 +32,7 @@ from pxr import Gf, PhysxSchema, Sdf, Usd, UsdGeom, UsdPhysics, Vt
import isaaclab.sim as sim_utils
from isaaclab.sim.spawners import SpawnerCfg
from isaaclab.sim.utils import attach_stage_to_usd_context, is_current_stage_in_memory
from isaaclab.sim.utils import attach_stage_to_usd_context
from isaaclab.utils.configclass import configclass
from isaaclab.utils.math import convert_quat
......@@ -400,12 +400,7 @@ class VisualizationMarkers:
if child_prim.IsA(UsdGeom.Gprim):
# early attach stage to usd context if stage is in memory
# since stage in memory is not supported by the "ChangePropertyCommand" kit command
if is_current_stage_in_memory():
omni.log.warn(
"Attaching stage in memory to USD context early to support omni kit command during stage"
" creation."
)
attach_stage_to_usd_context()
attach_stage_to_usd_context(attaching_early=True)
# invisible to secondary rays such as depth images
omni.kit.commands.execute(
......
......@@ -12,7 +12,7 @@ import omni.log
import omni.usd
from isaacsim.core.cloner import GridCloner
from isaacsim.core.prims import XFormPrim
from isaacsim.core.utils.stage import get_current_stage, get_current_stage_id
from isaacsim.core.utils.stage import get_current_stage
from pxr import PhysxSchema
import isaaclab.sim as sim_utils
......@@ -31,6 +31,7 @@ from isaaclab.assets import (
)
from isaaclab.sensors import ContactSensorCfg, FrameTransformerCfg, SensorBase, SensorBaseCfg
from isaaclab.sim import SimulationContext
from isaaclab.sim.utils import get_current_stage_id
from isaaclab.terrains import TerrainImporter, TerrainImporterCfg
from .interactive_scene_cfg import InteractiveSceneCfg
......
......@@ -26,6 +26,7 @@ from isaacsim.core.simulation_manager import IsaacEvents, SimulationManager
from isaacsim.core.utils.stage import get_current_stage
import isaaclab.sim as sim_utils
from isaaclab.sim import SimulationContext
if TYPE_CHECKING:
from .sensor_base_cfg import SensorBaseCfg
......@@ -63,23 +64,40 @@ class SensorBase(ABC):
# get stage handle
self.stage = get_current_stage()
# note: Use weakref on callbacks to ensure that this object can be deleted when its destructor is called.
# register simulator callbacks (with weakref safety to avoid crashes on deletion)
def safe_callback(callback_name, event, obj_ref):
"""Safely invoke a callback on a weakly-referenced object, ignoring ReferenceError if deleted."""
try:
obj = obj_ref
getattr(obj, callback_name)(event)
except ReferenceError:
# Object has been deleted; ignore.
pass
# note: use weakref on callbacks to ensure that this object can be deleted when its destructor is called.
# add callbacks for stage play/stop
# The order is set to 10 which is arbitrary but should be lower priority than the default order of 0
obj_ref = weakref.proxy(self)
timeline_event_stream = omni.timeline.get_timeline_interface().get_timeline_event_stream()
# the order is set to 10 which is arbitrary but should be lower priority than the default order of 0
# register timeline PLAY event callback (lower priority with order=10)
self._initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.PLAY),
lambda event, obj=weakref.proxy(self): obj._initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_initialize_callback", event, obj_ref),
order=10,
)
# register timeline STOP event callback (lower priority with order=10)
self._invalidate_initialize_handle = timeline_event_stream.create_subscription_to_pop_by_type(
int(omni.timeline.TimelineEventType.STOP),
lambda event, obj=weakref.proxy(self): obj._invalidate_initialize_callback(event),
lambda event, obj_ref=obj_ref: safe_callback("_invalidate_initialize_callback", event, obj_ref),
order=10,
)
# register prim deletion callback
self._prim_deletion_callback_id = SimulationManager.register_callback(
self._on_prim_deletion, event=IsaacEvents.PRIM_DELETION
lambda event, obj_ref=obj_ref: safe_callback("_on_prim_deletion", event, obj_ref),
event=IsaacEvents.PRIM_DELETION,
)
# add handle for debug visualization (this is set to a valid handle inside set_debug_vis)
self._debug_vis_handle = None
# set initial state of debug visualization
......@@ -293,6 +311,9 @@ class SensorBase(ABC):
Note:
This function is called when the prim is deleted.
"""
# skip callback if required
if getattr(SimulationContext.instance(), "_skip_next_prim_deletion_callback_fn", False):
return
if prim_path == "/":
self._clear_callbacks()
return
......
......@@ -341,7 +341,7 @@ class SimulationCfg:
"""Render settings. Default is RenderCfg()."""
create_stage_in_memory: bool = False
"""If stage is first created in memory and then attached to usd context for simulation and rendering.
"""If stage is first created in memory. Default is False.
Creating the stage in memory can reduce start-up time.
"""
......@@ -31,6 +31,8 @@ from isaacsim.core.utils.viewports import set_camera_view
from isaacsim.core.version import get_version
from pxr import Gf, PhysxSchema, Usd, UsdPhysics
from isaaclab.sim.utils import create_new_stage_in_memory, use_stage
from .simulation_cfg import SimulationCfg
from .spawners import DomeLightCfg, GroundPlaneCfg
from .utils import bind_physics_material
......@@ -131,7 +133,7 @@ class SimulationContext(_SimulationContext):
# create stage in memory if requested
if self.cfg.create_stage_in_memory:
self._initial_stage = stage_utils.create_new_stage_in_memory()
self._initial_stage = create_new_stage_in_memory()
else:
self._initial_stage = omni.usd.get_context().get_stage()
......@@ -241,14 +243,18 @@ class SimulationContext(_SimulationContext):
self._app_control_on_stop_handle = None
self._disable_app_control_on_stop_handle = False
# flag for skipping prim deletion callback
# when stage in memory is attached
self._skip_next_prim_deletion_callback_fn = False
# flatten out the simulation dictionary
sim_params = self.cfg.to_dict()
if sim_params is not None:
if "physx" in sim_params:
physx_params = sim_params.pop("physx")
sim_params.update(physx_params)
# create a simulation context to control the simulator
# add warning about enabling stabilization for large step sizes
if not self.cfg.physx.enable_stabilization and (self.cfg.dt > 0.0333):
omni.log.warn(
"Large simulation step size (> 0.0333 seconds) is not recommended without enabling stabilization."
......@@ -256,6 +262,19 @@ class SimulationContext(_SimulationContext):
" simulation step size if you run into physics issues."
)
# create a simulation context to control the simulator
if float(".".join(self._isaacsim_version[2])) < 5:
# stage arg is not supported before isaac sim 5.0
super().__init__(
stage_units_in_meters=1.0,
physics_dt=self.cfg.dt,
rendering_dt=self.cfg.dt * self.cfg.render_interval,
backend="torch",
sim_params=sim_params,
physics_prim_path=self.cfg.physics_prim_path,
device=self.cfg.device,
)
else:
super().__init__(
stage_units_in_meters=1.0,
physics_dt=self.cfg.dt,
......@@ -676,7 +695,7 @@ class SimulationContext(_SimulationContext):
def _init_stage(self, *args, **kwargs) -> Usd.Stage:
_ = super()._init_stage(*args, **kwargs)
with stage_utils.use_stage(self.get_initial_stage()):
with use_stage(self.get_initial_stage()):
# a stage update here is needed for the case when physics_dt != rendering_dt, otherwise the app crashes
# when in headless mode
self.set_setting("/app/player/playSimulations", False)
......
......@@ -253,7 +253,15 @@ def _spawn_from_usd_file(
# check file path exists
if not stage.ResolveIdentifierToEditTarget(usd_path):
raise FileNotFoundError(f"USD file not found at path: '{usd_path}'.")
if "4.5" in usd_path:
usd_5_0_path = (
usd_path.replace("http", "https").replace("-production.", "-staging.").replace("/4.5", "/5.0")
)
if not stage.ResolveIdentifierToEditTarget(usd_5_0_path):
raise FileNotFoundError(f"USD file not found at path at either: '{usd_path}' or '{usd_5_0_path}'.")
usd_path = usd_5_0_path
else:
raise FileNotFoundError(f"USD file not found at path at: '{usd_path}'.")
# spawn asset if it doesn't exist.
if not prim_utils.is_prim_path_valid(prim_path):
# add prim as reference to stage
......
......@@ -12,12 +12,7 @@ import omni.kit.commands
import omni.log
from pxr import Usd
from isaaclab.sim.utils import (
attach_stage_to_usd_context,
clone,
is_current_stage_in_memory,
safe_set_attribute_on_usd_prim,
)
from isaaclab.sim.utils import attach_stage_to_usd_context, clone, safe_set_attribute_on_usd_prim
from isaaclab.utils.assets import NVIDIA_NUCLEUS_DIR
if TYPE_CHECKING:
......@@ -56,12 +51,7 @@ def spawn_preview_surface(prim_path: str, cfg: visual_materials_cfg.PreviewSurfa
if not prim_utils.is_prim_path_valid(prim_path):
# early attach stage to usd context if stage is in memory
# since stage in memory is not supported by the "CreatePreviewSurfaceMaterialPrim" kit command
if is_current_stage_in_memory():
omni.log.warn(
"Attaching stage in memory to USD context early to support an operation which doesn't support stage in"
" memory."
)
attach_stage_to_usd_context()
attach_stage_to_usd_context(attaching_early=True)
omni.kit.commands.execute("CreatePreviewSurfaceMaterialPrim", mtl_path=prim_path, select_new_prim=False)
else:
......@@ -111,12 +101,7 @@ def spawn_from_mdl_file(prim_path: str, cfg: visual_materials_cfg.MdlMaterialCfg
if not prim_utils.is_prim_path_valid(prim_path):
# early attach stage to usd context if stage is in memory
# since stage in memory is not supported by the "CreateMdlMaterialPrim" kit command
if is_current_stage_in_memory():
omni.log.warn(
"Attaching stage in memory to USD context early to support an operation which doesn't support stage in"
" memory."
)
attach_stage_to_usd_context()
attach_stage_to_usd_context(attaching_early=True)
# extract material name from path
material_name = cfg.mdl_path.split("/")[-1].split(".")[0]
......
......@@ -12,7 +12,7 @@ import omni.kit.commands
import omni.log
from pxr import Sdf, Usd
from isaaclab.sim.utils import attach_stage_to_usd_context, clone, is_current_stage_in_memory
from isaaclab.sim.utils import attach_stage_to_usd_context, clone
from isaaclab.utils import to_camel_case
if TYPE_CHECKING:
......@@ -90,12 +90,7 @@ def spawn_camera(
if cfg.lock_camera:
# early attach stage to usd context if stage is in memory
# since stage in memory is not supported by the "ChangePropertyCommand" kit command
if is_current_stage_in_memory():
omni.log.warn(
"Attaching stage in memory to USD context early to support an operation which doesn't support stage in"
" memory."
)
attach_stage_to_usd_context()
attach_stage_to_usd_context(attaching_early=True)
omni.kit.commands.execute(
"ChangePropertyCommand",
......
......@@ -7,6 +7,7 @@
from __future__ import annotations
import contextlib
import functools
import inspect
import re
......@@ -20,8 +21,9 @@ import omni.kit.commands
import omni.log
from isaacsim.core.cloner import Cloner
from isaacsim.core.utils.carb import get_carb_setting
from isaacsim.core.utils.stage import get_current_stage, get_current_stage_id
from pxr import PhysxSchema, Sdf, Usd, UsdGeom, UsdPhysics, UsdShade
from isaacsim.core.utils.stage import get_current_stage
from isaacsim.core.version import get_version
from pxr import PhysxSchema, Sdf, Usd, UsdGeom, UsdPhysics, UsdShade, UsdUtils
# from Isaac Sim 4.2 onwards, pxr.Semantics is deprecated
try:
......@@ -115,12 +117,7 @@ def safe_set_attribute_on_usd_prim(prim: Usd.Prim, attr_name: str, value: Any, c
# early attach stage to usd context if stage is in memory
# since stage in memory is not supported by the "ChangePropertyCommand" kit command
if is_current_stage_in_memory():
omni.log.warn(
"Attaching stage in memory to USD context early to support an operation which doesn't support stage in"
" memory."
)
attach_stage_to_usd_context()
attach_stage_to_usd_context(attaching_early=True)
# change property
omni.kit.commands.execute(
......@@ -819,32 +816,55 @@ Stage management.
"""
def attach_stage_to_usd_context():
def attach_stage_to_usd_context(attaching_early: bool = False):
"""Attaches stage in memory to usd context.
This function should be called during or after scene is created and before stage is simulated or rendered.
Note:
If the stage is not in memory or rendering is not enabled, this function will return without attaching.
"""
import omni.physxfabric
Args:
attaching_early: Whether to attach the stage to the usd context before stage is created. Defaults to False.
"""
from isaaclab.sim.simulation_context import SimulationContext
# if Isaac Sim version is less than 5.0, stage in memory is not supported
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
return
# if stage is not in memory, we can return early
if not is_current_stage_in_memory():
return
# attach stage to physx
stage_id = get_current_stage_id()
physx_sim_interface = omni.physx.get_physx_simulation_interface()
physx_sim_interface.attach_stage(stage_id)
# this carb flag is equivalent to if rendering is enabled
carb_setting = carb.settings.get_settings()
is_rendering_enabled = get_carb_setting(carb_setting, "/physics/fabricUpdateTransformations")
# if stage is not in memory or rendering is not enabled, we don't need to attach it
if not is_current_stage_in_memory() or not is_rendering_enabled:
# if rendering is not enabled, we don't need to attach it
if not is_rendering_enabled:
return
stage_id = get_current_stage_id()
# early attach warning msg
if attaching_early:
omni.log.warn(
"Attaching stage in memory to USD context early to support an operation which doesn't support stage in"
" memory."
)
# skip this callback to avoid wiping the stage after attachment
SimulationContext.instance().skip_next_stage_open_callback()
# skip this callback to avoid clearing the prims
SimulationContext.instance()._skip_next_prim_deletion_callback_fn = True
# enable physics fabric
SimulationContext.instance()._physics_context.enable_fabric(True)
......@@ -855,6 +875,8 @@ def attach_stage_to_usd_context():
physx_sim_interface = omni.physx.get_physx_simulation_interface()
physx_sim_interface.attach_stage(stage_id)
SimulationContext.instance()._skip_next_prim_deletion_callback_fn = False
def is_current_stage_in_memory() -> bool:
"""This function checks if the current stage is in memory.
......@@ -866,11 +888,11 @@ def is_current_stage_in_memory() -> bool:
"""
# grab current stage id
stage_id = stage_utils.get_current_stage_id()
stage_id = get_current_stage_id()
# grab context stage id
context_stage = omni.usd.get_context().get_stage()
with stage_utils.use_stage(context_stage):
with use_stage(context_stage):
context_stage_id = get_current_stage_id()
# check if stage ids are the same
......@@ -955,3 +977,59 @@ def select_usd_variants(prim_path: str, variants: object | dict[str, str], stage
f"Setting variant selection '{variant_selection}' for variant set '{variant_set_name}' on"
f" prim '{prim_path}'."
)
"""
Isaac Sim stage utils wrappers to enable backwards compatibility to Isaac Sim 4.5
"""
@contextlib.contextmanager
def use_stage(stage: Usd.Stage) -> None:
"""Context manager that sets a thread-local stage, if supported.
In Isaac Sim < 5.0, this is a no-op to maintain compatibility.
Args:
stage (Usd.Stage): The stage to set temporarily.
"""
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
omni.log.warn("[Compat] Isaac Sim < 5.0 does not support thread-local stage contexts. Skipping use_stage().")
yield # no-op
else:
with stage_utils.use_stage(stage):
yield
def create_new_stage_in_memory() -> Usd.Stage:
"""Create a new stage in memory, if supported.
Returns:
The new stage.
"""
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
omni.log.warn(
"[Compat] Isaac Sim < 5.0 does not support creating a new stage in memory. Falling back to creating a new"
" stage attached to USD context."
)
return stage_utils.create_new_stage()
else:
return stage_utils.create_new_stage_in_memory()
def get_current_stage_id() -> int:
"""Get the current open stage id.
Reimplementation of stage_utils.get_current_stage_id() for Isaac Sim < 5.0.
Returns:
int: The stage id.
"""
stage = get_current_stage()
stage_cache = UsdUtils.StageCache.Get()
stage_id = stage_cache.GetId(stage).ToLongInt()
if stage_id < 0:
stage_id = stage_cache.Insert(stage).ToLongInt()
return stage_id
......@@ -20,6 +20,7 @@ import torch
import isaacsim.core.utils.prims as prim_utils
import pytest
from isaacsim.core.version import get_version
import isaaclab.sim as sim_utils
from isaaclab.actuators import ImplicitActuatorCfg
......@@ -171,6 +172,9 @@ def test_initialization(sim, num_articulations, device, add_ground_plane) -> Non
device: The device to run the test on.
add_ground_plane: Whether to add a ground plane to the simulation.
"""
isaac_sim_version = get_version()
if int(isaac_sim_version[2]) < 5:
return
surface_gripper_cfg, articulation_cfg = generate_surface_gripper_cfgs(kinematic_enabled=False)
surface_gripper, articulation, _ = generate_surface_gripper(
surface_gripper_cfg, articulation_cfg, num_articulations, device
......@@ -202,6 +206,9 @@ def test_initialization(sim, num_articulations, device, add_ground_plane) -> Non
@pytest.mark.parametrize("add_ground_plane", [True])
def test_raise_error_if_not_cpu(sim, device, add_ground_plane) -> None:
"""Test that the SurfaceGripper raises an error if the device is not CPU."""
isaac_sim_version = get_version()
if int(isaac_sim_version[2]) < 5:
return
num_articulations = 1
surface_gripper_cfg, articulation_cfg = generate_surface_gripper_cfgs(kinematic_enabled=False)
surface_gripper, articulation, translations = generate_surface_gripper(
......
......@@ -12,6 +12,7 @@ simulation_app = AppLauncher(headless=True).app
"""Rest everything follows."""
import importlib
import torch
import pytest
......@@ -104,9 +105,10 @@ def test_se2keyboard_constructors(mock_environment, mocker):
v_y_sensitivity=0.5,
omega_z_sensitivity=1.2,
)
device_mod = importlib.import_module("isaaclab.devices.keyboard.se2_keyboard")
mocker.patch.dict("sys.modules", {"carb": mock_environment["carb"], "omni": mock_environment["omni"]})
mocker.patch("isaaclab.devices.keyboard.se2_keyboard.carb", mock_environment["carb"])
mocker.patch("isaaclab.devices.keyboard.se2_keyboard.omni", mock_environment["omni"])
mocker.patch.object(device_mod, "carb", mock_environment["carb"])
mocker.patch.object(device_mod, "omni", mock_environment["omni"])
keyboard = Se2Keyboard(config)
......@@ -128,9 +130,10 @@ def test_se3keyboard_constructors(mock_environment, mocker):
pos_sensitivity=0.5,
rot_sensitivity=0.9,
)
device_mod = importlib.import_module("isaaclab.devices.keyboard.se3_keyboard")
mocker.patch.dict("sys.modules", {"carb": mock_environment["carb"], "omni": mock_environment["omni"]})
mocker.patch("isaaclab.devices.keyboard.se3_keyboard.carb", mock_environment["carb"])
mocker.patch("isaaclab.devices.keyboard.se3_keyboard.omni", mock_environment["omni"])
mocker.patch.object(device_mod, "carb", mock_environment["carb"])
mocker.patch.object(device_mod, "omni", mock_environment["omni"])
keyboard = Se3Keyboard(config)
......@@ -158,9 +161,10 @@ def test_se2gamepad_constructors(mock_environment, mocker):
omega_z_sensitivity=1.2,
dead_zone=0.02,
)
device_mod = importlib.import_module("isaaclab.devices.gamepad.se2_gamepad")
mocker.patch.dict("sys.modules", {"carb": mock_environment["carb"], "omni": mock_environment["omni"]})
mocker.patch("isaaclab.devices.gamepad.se2_gamepad.carb", mock_environment["carb"])
mocker.patch("isaaclab.devices.gamepad.se2_gamepad.omni", mock_environment["omni"])
mocker.patch.object(device_mod, "carb", mock_environment["carb"])
mocker.patch.object(device_mod, "omni", mock_environment["omni"])
gamepad = Se2Gamepad(config)
......@@ -184,9 +188,10 @@ def test_se3gamepad_constructors(mock_environment, mocker):
rot_sensitivity=1.7,
dead_zone=0.02,
)
device_mod = importlib.import_module("isaaclab.devices.gamepad.se3_gamepad")
mocker.patch.dict("sys.modules", {"carb": mock_environment["carb"], "omni": mock_environment["omni"]})
mocker.patch("isaaclab.devices.gamepad.se3_gamepad.carb", mock_environment["carb"])
mocker.patch("isaaclab.devices.gamepad.se3_gamepad.omni", mock_environment["omni"])
mocker.patch.object(device_mod, "carb", mock_environment["carb"])
mocker.patch.object(device_mod, "omni", mock_environment["omni"])
gamepad = Se3Gamepad(config)
......@@ -214,8 +219,9 @@ def test_se2spacemouse_constructors(mock_environment, mocker):
v_y_sensitivity=0.5,
omega_z_sensitivity=1.2,
)
device_mod = importlib.import_module("isaaclab.devices.spacemouse.se2_spacemouse")
mocker.patch.dict("sys.modules", {"hid": mock_environment["hid"]})
mocker.patch("isaaclab.devices.spacemouse.se2_spacemouse.hid", mock_environment["hid"])
mocker.patch.object(device_mod, "hid", mock_environment["hid"])
spacemouse = Se2SpaceMouse(config)
......@@ -238,8 +244,9 @@ def test_se3spacemouse_constructors(mock_environment, mocker):
pos_sensitivity=0.5,
rot_sensitivity=0.9,
)
device_mod = importlib.import_module("isaaclab.devices.spacemouse.se3_spacemouse")
mocker.patch.dict("sys.modules", {"hid": mock_environment["hid"]})
mocker.patch("isaaclab.devices.spacemouse.se3_spacemouse.hid", mock_environment["hid"])
mocker.patch.object(device_mod, "hid", mock_environment["hid"])
spacemouse = Se3SpaceMouse(config)
......@@ -274,6 +281,7 @@ def test_openxr_constructors(mock_environment, mocker):
mock_head_retargeter = mocker.MagicMock()
retargeters = [mock_controller_retargeter, mock_head_retargeter]
device_mod = importlib.import_module("isaaclab.devices.openxr.openxr_device")
mocker.patch.dict(
"sys.modules",
{
......@@ -282,12 +290,9 @@ def test_openxr_constructors(mock_environment, mocker):
"isaacsim.core.prims": mocker.MagicMock(),
},
)
mocker.patch("isaaclab.devices.openxr.openxr_device.XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch(
"isaaclab.devices.openxr.openxr_device.XRPoseValidityFlags",
mock_environment["omni"].kit.xr.core.XRPoseValidityFlags,
)
mock_single_xform = mocker.patch("isaaclab.devices.openxr.openxr_device.SingleXFormPrim")
mocker.patch.object(device_mod, "XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch.object(device_mod, "XRPoseValidityFlags", mock_environment["omni"].kit.xr.core.XRPoseValidityFlags)
mock_single_xform = mocker.patch.object(device_mod, "SingleXFormPrim")
# Configure the mock to return a string for prim_path
mock_instance = mock_single_xform.return_value
......@@ -330,9 +335,10 @@ def test_create_teleop_device_basic(mock_environment, mocker):
devices_cfg = {"test_keyboard": keyboard_cfg}
# Mock Se3Keyboard class
device_mod = importlib.import_module("isaaclab.devices.keyboard.se3_keyboard")
mocker.patch.dict("sys.modules", {"carb": mock_environment["carb"], "omni": mock_environment["omni"]})
mocker.patch("isaaclab.devices.keyboard.se3_keyboard.carb", mock_environment["carb"])
mocker.patch("isaaclab.devices.keyboard.se3_keyboard.omni", mock_environment["omni"])
mocker.patch.object(device_mod, "carb", mock_environment["carb"])
mocker.patch.object(device_mod, "omni", mock_environment["omni"])
# Create the device using the factory
device = create_teleop_device("test_keyboard", devices_cfg)
......@@ -358,6 +364,7 @@ def test_create_teleop_device_with_callbacks(mock_environment, mocker):
callbacks = {"button_a": button_a_callback, "button_b": button_b_callback}
# Mock OpenXRDevice class and dependencies
device_mod = importlib.import_module("isaaclab.devices.openxr.openxr_device")
mocker.patch.dict(
"sys.modules",
{
......@@ -366,12 +373,9 @@ def test_create_teleop_device_with_callbacks(mock_environment, mocker):
"isaacsim.core.prims": mocker.MagicMock(),
},
)
mocker.patch("isaaclab.devices.openxr.openxr_device.XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch(
"isaaclab.devices.openxr.openxr_device.XRPoseValidityFlags",
mock_environment["omni"].kit.xr.core.XRPoseValidityFlags,
)
mock_single_xform = mocker.patch("isaaclab.devices.openxr.openxr_device.SingleXFormPrim")
mocker.patch.object(device_mod, "XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch.object(device_mod, "XRPoseValidityFlags", mock_environment["omni"].kit.xr.core.XRPoseValidityFlags)
mock_single_xform = mocker.patch.object(device_mod, "SingleXFormPrim")
# Configure the mock to return a string for prim_path
mock_instance = mock_single_xform.return_value
......@@ -403,6 +407,7 @@ def test_create_teleop_device_with_retargeters(mock_environment, mocker):
devices_cfg = {"test_xr": device_cfg}
# Mock OpenXRDevice class and dependencies
device_mod = importlib.import_module("isaaclab.devices.openxr.openxr_device")
mocker.patch.dict(
"sys.modules",
{
......@@ -411,20 +416,18 @@ def test_create_teleop_device_with_retargeters(mock_environment, mocker):
"isaacsim.core.prims": mocker.MagicMock(),
},
)
mocker.patch("isaaclab.devices.openxr.openxr_device.XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch(
"isaaclab.devices.openxr.openxr_device.XRPoseValidityFlags",
mock_environment["omni"].kit.xr.core.XRPoseValidityFlags,
)
mock_single_xform = mocker.patch("isaaclab.devices.openxr.openxr_device.SingleXFormPrim")
mocker.patch.object(device_mod, "XRCore", mock_environment["omni"].kit.xr.core.XRCore)
mocker.patch.object(device_mod, "XRPoseValidityFlags", mock_environment["omni"].kit.xr.core.XRPoseValidityFlags)
mock_single_xform = mocker.patch.object(device_mod, "SingleXFormPrim")
# Configure the mock to return a string for prim_path
mock_instance = mock_single_xform.return_value
mock_instance.prim_path = "/XRAnchor"
# Mock retargeter classes
mocker.patch("isaaclab.devices.openxr.retargeters.Se3AbsRetargeter")
mocker.patch("isaaclab.devices.openxr.retargeters.GripperRetargeter")
retargeter_mod = importlib.import_module("isaaclab.devices.openxr.retargeters")
mocker.patch.object(retargeter_mod, "Se3AbsRetargeter")
mocker.patch.object(retargeter_mod, "GripperRetargeter")
# Create the device using the factory
device = create_teleop_device("test_xr", devices_cfg)
......
......@@ -17,6 +17,7 @@ HEADLESS = True
app_launcher = AppLauncher(headless=HEADLESS)
simulation_app = app_launcher.app
import importlib
import numpy as np
import carb
......@@ -119,8 +120,9 @@ def mock_xrcore(mocker):
head_mock.get_virtual_world_pose.return_value = pose_matrix_mock
# Patch the modules
mocker.patch("isaaclab.devices.openxr.openxr_device.XRCore", xr_core_mock)
mocker.patch("isaaclab.devices.openxr.openxr_device.XRPoseValidityFlags", xr_pose_validity_flags_mock)
device_mod = importlib.import_module("isaaclab.devices.openxr.openxr_device")
mocker.patch.object(device_mod, "XRCore", xr_core_mock)
mocker.patch.object(device_mod, "XRPoseValidityFlags", xr_pose_validity_flags_mock)
return {
"XRCore": xr_core_mock,
......
......@@ -1593,10 +1593,15 @@ def test_frame_offset_small_resolution(setup_camera):
camera_cfg = copy.deepcopy(camera_cfg)
camera_cfg.height = 80
camera_cfg.width = 80
camera_cfg.offset.pos = (0.0, 0.0, 0.5)
tiled_camera = TiledCamera(camera_cfg)
# play sim
sim.reset()
# simulate some steps first to make sure objects are settled
stage = stage_utils.get_current_stage()
for i in range(10):
prim = stage.GetPrimAtPath(f"/World/Objects/Obj_{i:02d}")
UsdGeom.Gprim(prim).GetOrderedXformOps()[2].Set(Gf.Vec3d(1.0, 1.0, 1.0))
for i in range(100):
# step simulation
sim.step()
......@@ -1606,7 +1611,6 @@ def test_frame_offset_small_resolution(setup_camera):
image_before = tiled_camera.data.output["rgb"].clone() / 255.0
# update scene
stage = stage_utils.get_current_stage()
for i in range(10):
prim = stage.GetPrimAtPath(f"/World/Objects/Obj_{i:02d}")
color = Gf.Vec3f(0, 0, 0)
......@@ -1621,7 +1625,7 @@ def test_frame_offset_small_resolution(setup_camera):
image_after = tiled_camera.data.output["rgb"].clone() / 255.0
# check difference is above threshold
assert torch.abs(image_after - image_before).mean() > 0.01 # images of same color should be below 0.001
assert torch.abs(image_after - image_before).mean() > 0.1 # images of same color should be below 0.01
def test_frame_offset_large_resolution(setup_camera):
......@@ -1677,16 +1681,17 @@ Helper functions.
@staticmethod
def _populate_scene():
"""Add prims to the scene."""
# TODO: why does this cause hanging in Isaac Sim 5.0?
# # Ground-plane
# cfg = sim_utils.GroundPlaneCfg()
# cfg.func("/World/defaultGroundPlane", cfg)
# Ground-plane
cfg = sim_utils.GroundPlaneCfg()
cfg.func("/World/defaultGroundPlane", cfg)
# Lights
cfg = sim_utils.SphereLightCfg()
cfg.func("/World/Light/GreySphere", cfg, translation=(4.5, 3.5, 10.0))
cfg.func("/World/Light/WhiteSphere", cfg, translation=(-4.5, 3.5, 10.0))
# Random objects
random.seed(0)
np.random.seed(0)
torch.manual_seed(0)
for i in range(10):
# sample random position
position = np.random.rand(3) - np.asarray([0.05, 0.05, -1.0])
......
......@@ -109,6 +109,8 @@ def test_modify_properties_on_articulation_instanced_usd(setup_simulation):
sim, arti_cfg, rigid_cfg, collision_cfg, mass_cfg, joint_cfg = setup_simulation
# spawn asset to the stage
asset_usd_file = f"{ISAAC_NUCLEUS_DIR}/Robots/ANYbotics/anymal_c/anymal_c.usd"
if "4.5" in ISAAC_NUCLEUS_DIR:
asset_usd_file = asset_usd_file.replace("http", "https").replace("production", "staging").replace("4.5", "5.0")
prim_utils.create_prim("/World/asset_instanced", usd_path=asset_usd_file, translation=(0.0, 0.0, 0.62))
# set properties on the asset and check all properties are set
......@@ -132,6 +134,8 @@ def test_modify_properties_on_articulation_usd(setup_simulation):
sim, arti_cfg, rigid_cfg, collision_cfg, mass_cfg, joint_cfg = setup_simulation
# spawn asset to the stage
asset_usd_file = f"{ISAAC_NUCLEUS_DIR}/Robots/FrankaRobotics/FrankaPanda/franka.usd"
if "4.5" in ISAAC_NUCLEUS_DIR:
asset_usd_file = asset_usd_file.replace("http", "https").replace("production", "staging").replace("4.5", "5.0")
prim_utils.create_prim("/World/asset", usd_path=asset_usd_file, translation=(0.0, 0.0, 0.62))
# set properties on the asset and check all properties are set
......
......@@ -20,6 +20,7 @@ import omni.usd
import pytest
import usdrt
from isaacsim.core.cloner import GridCloner
from isaacsim.core.version import get_version
import isaaclab.sim as sim_utils
from isaaclab.sim.simulation_context import SimulationCfg, SimulationContext
......@@ -48,12 +49,17 @@ Tests
def test_stage_in_memory_with_shapes(sim):
"""Test spawning of shapes with stage in memory."""
# skip test if stage in memory is not supported
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
pytest.skip("Stage in memory is not supported in this version of Isaac Sim")
# define parameters
num_clones = 10
# grab stage in memory and set as current stage via the with statement
stage_in_memory = sim.get_initial_stage()
with stage_utils.use_stage(stage_in_memory):
with sim_utils.use_stage(stage_in_memory):
# create cloned cone stage
for i in range(num_clones):
prim_utils.create_prim(f"/World/env_{i}", "Xform", translation=(i, i, 0))
......@@ -90,7 +96,7 @@ def test_stage_in_memory_with_shapes(sim):
# verify prims do not exist in context stage
context_stage = omni.usd.get_context().get_stage()
with stage_utils.use_stage(context_stage):
with sim_utils.use_stage(context_stage):
prims = prim_utils.find_matching_prim_paths(prim_path_regex)
assert len(prims) != num_clones
......@@ -108,6 +114,11 @@ def test_stage_in_memory_with_shapes(sim):
def test_stage_in_memory_with_usds(sim):
"""Test spawning of USDs with stage in memory."""
# skip test if stage in memory is not supported
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
pytest.skip("Stage in memory is not supported in this version of Isaac Sim")
# define parameters
num_clones = 10
usd_paths = [
......@@ -117,7 +128,7 @@ def test_stage_in_memory_with_usds(sim):
# grab stage in memory and set as current stage via the with statement
stage_in_memory = sim.get_initial_stage()
with stage_utils.use_stage(stage_in_memory):
with sim_utils.use_stage(stage_in_memory):
# create cloned robot stage
for i in range(num_clones):
prim_utils.create_prim(f"/World/env_{i}", "Xform", translation=(i, i, 0))
......@@ -151,7 +162,7 @@ def test_stage_in_memory_with_usds(sim):
# verify prims do not exist in context stage
context_stage = omni.usd.get_context().get_stage()
with stage_utils.use_stage(context_stage):
with sim_utils.use_stage(context_stage):
prims = prim_utils.find_matching_prim_paths(prim_path_regex)
assert len(prims) != num_clones
......@@ -169,13 +180,18 @@ def test_stage_in_memory_with_usds(sim):
def test_stage_in_memory_with_clone_in_fabric(sim):
"""Test cloning in fabric with stage in memory."""
# skip test if stage in memory is not supported
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5:
pytest.skip("Stage in memory is not supported in this version of Isaac Sim")
# define parameters
usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd"
num_clones = 100
# grab stage in memory and set as current stage via the with statement
stage_in_memory = sim.get_initial_stage()
with stage_utils.use_stage(stage_in_memory):
with sim_utils.use_stage(stage_in_memory):
# set up paths
base_env_path = "/World/envs"
source_prim_path = f"{base_env_path}/env_0"
......@@ -202,7 +218,7 @@ def test_stage_in_memory_with_clone_in_fabric(sim):
# verify prims do not exist in context stage
context_stage = omni.usd.get_context().get_stage()
with stage_utils.use_stage(context_stage):
with sim_utils.use_stage(context_stage):
prims = prim_utils.find_matching_prim_paths(prim_path_regex)
assert len(prims) != num_clones
......@@ -213,7 +229,7 @@ def test_stage_in_memory_with_clone_in_fabric(sim):
assert not sim_utils.is_current_stage_in_memory()
# verify prims now exist in fabric stage using usdrt apis
stage_id = stage_utils.get_current_stage_id()
stage_id = sim_utils.get_current_stage_id()
usdrt_stage = usdrt.Usd.Stage.Attach(stage_id)
for i in range(num_clones):
prim = usdrt_stage.GetPrimAtPath(f"/World/envs/env_{i}/Robot")
......
......@@ -94,9 +94,11 @@ def test_find_global_fixed_joint_prim():
prim_utils.create_prim(
"/World/Franka", usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/panda_instanceable.usd"
)
prim_utils.create_prim(
"/World/Franka_Isaac", usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/FrankaRobotics/FrankaPanda/franka.usd"
)
if "4.5" in ISAAC_NUCLEUS_DIR:
franka_usd = f"{ISAAC_NUCLEUS_DIR}/Robots/Franka/franka.usd"
else:
franka_usd = f"{ISAAC_NUCLEUS_DIR}/Robots/FrankaRobotics/FrankaPanda/franka.usd"
prim_utils.create_prim("/World/Franka_Isaac", usd_path=franka_usd)
# test
assert sim_utils.find_global_fixed_joint_prim("/World/ANYmal") is None
......
......@@ -92,7 +92,7 @@ def test_train_environments(workflow, task_spec, config_path, mode, num_gpus, kp
env_config = utils.get_env_config(env_configs, mode, workflow, task)
# Skip if config not found
if not env_config:
if env_config is None:
pytest.skip(f"No config found for task {task} in {mode} mode")
job_name = f"{workflow}:{task}"
......
......@@ -67,7 +67,7 @@ def evaluate_job(workflow, task, env_config, duration):
# evaluate all thresholds from the config
for threshold_name, threshold_val in thresholds.items():
uses_lower_threshold = threshold_name in env_config["lower_thresholds"]
uses_lower_threshold = threshold_name in env_config.get("lower_thresholds", {})
if threshold_name == "duration":
val = duration
else:
......@@ -150,6 +150,10 @@ def _retrieve_logs(workflow, task):
"""Retrieve training logs."""
# first grab all log files
repo_path = os.path.join(carb.tokens.get_tokens_interface().resolve("${app}"), "..")
from isaacsim.core.version import get_version
if int(get_version()[2]) < 5:
repo_path = os.path.join(repo_path, "..")
if workflow == "rl_games":
log_files_path = os.path.join(repo_path, f"logs/{workflow}/{task}/*/summaries/*")
else:
......
......@@ -31,6 +31,7 @@ import torch
import carb
import omni.usd
import pytest
from isaacsim.core.version import get_version
from isaaclab.envs import ManagerBasedRLEnvCfg
from isaaclab.envs.utils.spaces import sample_space
......@@ -59,10 +60,34 @@ def setup_environment():
return registered_tasks
# note, running an env test without stage in memory then
# running an env test with stage in memory causes IsaacLab to hang.
# so, here we run all envs with stage in memory first, then run
# all envs without stage in memory.
@pytest.mark.order(1)
@pytest.mark.parametrize("num_envs, device", [(32, "cuda"), (1, "cuda")])
@pytest.mark.parametrize("task_name", setup_environment())
def test_environments_with_stage_in_memory(task_name, num_envs, device):
# run environments with stage in memory
_run_environments(task_name, device, num_envs, num_steps=100, create_stage_in_memory=True)
@pytest.mark.order(2)
@pytest.mark.parametrize("num_envs, device", [(32, "cuda"), (1, "cuda")])
@pytest.mark.parametrize("task_name", setup_environment())
def test_environments(task_name, num_envs, device):
# run environments without stage in memory
_run_environments(task_name, device, num_envs, num_steps=100, create_stage_in_memory=False)
def _run_environments(task_name, device, num_envs, num_steps, create_stage_in_memory):
"""Run all environments and check environments return valid signals."""
# skip test if stage in memory is not supported
isaac_sim_version = float(".".join(get_version()[2]))
if isaac_sim_version < 5 and create_stage_in_memory:
pytest.skip("Stage in memory is not supported in this version of Isaac Sim")
# skip these environments as they cannot be run with 32 environments within reasonable VRAM
if num_envs == 32 and task_name in [
"Isaac-Stack-Cube-Franka-IK-Rel-Blueprint-v0",
......@@ -76,26 +101,33 @@ def test_environments(task_name, num_envs, device):
if task_name in ["Isaac-AutoMate-Assembly-Direct-v0", "Isaac-AutoMate-Disassembly-Direct-v0"]:
return
# skipping this test for now as it requires torch 2.6 or newer
if task_name == "Isaac-Cartpole-RGB-TheiaTiny-v0":
return
# TODO: why is this failing in Isaac Sim 5.0??? but the environment itself can run.
if task_name == "Isaac-Lift-Teddy-Bear-Franka-IK-Abs-v0":
return
print(f">>> Running test for environment: {task_name}")
_check_random_actions(task_name, device, num_envs, num_steps=100)
_check_random_actions(task_name, device, num_envs, num_steps=100, create_stage_in_memory=create_stage_in_memory)
print(f">>> Closing environment: {task_name}")
print("-" * 80)
def _check_random_actions(task_name: str, device: str, num_envs: int, num_steps: int = 1000):
def _check_random_actions(
task_name: str, device: str, num_envs: int, num_steps: int = 1000, create_stage_in_memory: bool = False
):
"""Run random actions and check environments returned signals are valid."""
# create a new stage
if not create_stage_in_memory:
# create a new context stage
omni.usd.get_context().new_stage()
# reset the rtx sensors carb setting to False
carb.settings.get_settings().set_bool("/isaaclab/render/rtx_sensors", False)
try:
# parse configuration
env_cfg: ManagerBasedRLEnvCfg = parse_env_cfg(task_name, device=device, num_envs=num_envs)
env_cfg.sim.create_stage_in_memory = create_stage_in_memory
# skip test if the environment is a multi-agent task
if hasattr(env_cfg, "possible_agents"):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment