diff --git a/source/isaaclab_tasks/test/core/test_rendering_deformable.py b/source/isaaclab_tasks/test/core/test_rendering_deformable.py new file mode 100644 index 00000000000..321a65d87f7 --- /dev/null +++ b/source/isaaclab_tasks/test/core/test_rendering_deformable.py @@ -0,0 +1,37 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Rendering correctness tests for test-local Franka cloth deformable camera setup.""" + +# Launch Isaac Sim Simulator first for kit-based combinations. +from isaaclab.app import AppLauncher + +app_launcher = AppLauncher(headless=True, enable_cameras=True) +simulation_app = app_launcher.app + +from pathlib import Path # noqa: E402 + +import pytest # noqa: E402 +from rendering_test_utils import ( # noqa: E402 + PHYSICS_RENDERER_AOV_COMBINATIONS, + make_attach_comparison_properties_fixture, + make_determinism_fixture, + make_generate_html_report_fixture, + rendering_test_deformable, +) + +pytestmark = pytest.mark.isaacsim_ci + +_COMPARISON_SCORES: list[dict] = [] + +_determinism_fixture = make_determinism_fixture() +_generate_html_report_fixture = make_generate_html_report_fixture(_COMPARISON_SCORES, Path(__file__).stem + ".html") +_attach_comparison_properties_fixture = make_attach_comparison_properties_fixture(_COMPARISON_SCORES) + + +@pytest.mark.parametrize("physics_backend,renderer,data_type", PHYSICS_RENDERER_AOV_COMBINATIONS) +def test_rendering_deformable(physics_backend, renderer, data_type): + """Test Franka cloth deformable rendering correctness.""" + rendering_test_deformable(physics_backend, renderer, data_type, _COMPARISON_SCORES) diff --git a/source/isaaclab_tasks/test/core/test_rendering_deformable_kitless.py b/source/isaaclab_tasks/test/core/test_rendering_deformable_kitless.py new file mode 100644 index 00000000000..9aa2e4e6124 --- /dev/null +++ b/source/isaaclab_tasks/test/core/test_rendering_deformable_kitless.py @@ -0,0 +1,33 @@ +# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Kit-less rendering correctness tests for test-local Franka cloth deformable camera setup.""" + +from pathlib import Path + +import pytest +from rendering_test_utils import ( + KITLESS_PHYSICS_RENDERER_AOV_COMBINATIONS, + make_attach_comparison_properties_fixture, + make_determinism_fixture, + make_generate_html_report_fixture, + make_require_ovlibs_install_fixture, + rendering_test_deformable, +) + +pytestmark = pytest.mark.isaacsim_ci + +_COMPARISON_SCORES: list[dict] = [] + +_determinism_fixture = make_determinism_fixture() +_generate_html_report_fixture = make_generate_html_report_fixture(_COMPARISON_SCORES, Path(__file__).stem + ".html") +_attach_comparison_properties_fixture = make_attach_comparison_properties_fixture(_COMPARISON_SCORES) +_require_ovlibs_install_fixture = make_require_ovlibs_install_fixture() + + +@pytest.mark.parametrize("physics_backend,renderer,data_type", KITLESS_PHYSICS_RENDERER_AOV_COMBINATIONS) +def test_rendering_deformable_kitless(physics_backend, renderer, data_type): + """Camera output must match golden images for the Franka cloth deformable test setup.""" + rendering_test_deformable(physics_backend, renderer, data_type, _COMPARISON_SCORES) diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-albedo.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-albedo.png new file mode 100644 index 00000000000..d93c0fd4d83 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-albedo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cf8b6985106c77ea8c59e160cfffc1ddf0ee40e409d3f1e077b6ca0282a3e2b +size 3629 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-depth.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-depth.png new file mode 100644 index 00000000000..31c35b63a6d --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-depth.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24e28613d5c02790928e0da3e4bbb7fea8a2523bf9b73f7c77e2605ba703d9dc +size 5479 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-rgb.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-rgb.png new file mode 100644 index 00000000000..12bea11b0b2 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-rgb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:658e13897f4f4fc957e5d8ca585e9190a52195f214fb296658ae481017a1ab63 +size 54019 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-semantic_segmentation.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-semantic_segmentation.png new file mode 100644 index 00000000000..24c3f318065 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-semantic_segmentation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f372f30ddd2737c40ce663c9a6614ca9379557fb65ee8d7f97cc91d709caf94d +size 1844 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png new file mode 100644 index 00000000000..0d1c833fca1 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:098e5d450c6162bef4afebde4b5761b563079d1bdaf90687fe1e5dbed932a40b +size 3240 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png new file mode 100644 index 00000000000..09e86c05a8f --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:271356c4d28b126f5530b023e5ad6c7225a771b5a239594ae9e77347e1f96d63 +size 3959 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_full_mdl.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_full_mdl.png new file mode 100644 index 00000000000..b30d057ed61 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/newton-isaacsim_rtx_renderer-simple_shading_full_mdl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cef163377d7e99071a2ccab79ff4792b17d87291a8990d20765adf6d201c8870 +size 9467 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-albedo.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-albedo.png new file mode 100644 index 00000000000..09d0604b689 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-albedo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47b362a3833757e6ee46c15db11ebd21189e6bd7c30767b88775e7506f1bc883 +size 3421 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-depth.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-depth.png new file mode 100644 index 00000000000..219134a9883 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-depth.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07418291b07213ea6e9acab68d3ad8c0bb1daf4a651069c65177aed88724c59e +size 5470 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-rgb.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-rgb.png new file mode 100644 index 00000000000..a39753c0bc9 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-rgb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34a63a7c641e9a83db39c5cfb92bdb8121a846875c73e5a887b696154d497f79 +size 54084 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-semantic_segmentation.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-semantic_segmentation.png new file mode 100644 index 00000000000..371dc2d3b42 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-semantic_segmentation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1090e9f00d5accbc571e38542609442b642264908fec63338024a293f4063d0c +size 1457 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png new file mode 100644 index 00000000000..dd157c0c4ce --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_constant_diffuse.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b993ae6b0af314f559ce690f62b6ddf134d372d6cfb0b2323311275cf1f88b7 +size 3239 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png new file mode 100644 index 00000000000..2cec8ce5a4f --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_diffuse_mdl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42b0867eea4397d5d97b03586df6c112aae3021ae8f6fea35279c9e579b508e3 +size 3663 diff --git a/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_full_mdl.png b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_full_mdl.png new file mode 100644 index 00000000000..3091ac6d4c9 --- /dev/null +++ b/source/isaaclab_tasks/test/golden_images/franka_cloth_deformable/physx-isaacsim_rtx_renderer-simple_shading_full_mdl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33203fac250e34d766937b61a66c1742c1cda2f83ae061ae2d3d42c4f812a158 +size 9461 diff --git a/source/isaaclab_tasks/test/rendering_test_utils.py b/source/isaaclab_tasks/test/rendering_test_utils.py index 2e35bc1f0d5..a5fb09e4907 100644 --- a/source/isaaclab_tasks/test/rendering_test_utils.py +++ b/source/isaaclab_tasks/test/rendering_test_utils.py @@ -32,6 +32,8 @@ # needs to be large enough to tolerate minor rendering noise while small enough to catch unexpected changes. MAX_DIFFERENT_PIXELS_PERCENTAGE_BY_ENV_NAME = { "cartpole": 1.0, + # Cloth and soft-body edges show more anti-aliasing noise than rigid cartpole geometry. + "franka_cloth_deformable": 5.0, # Shadow-hand renderings (incl. ``Isaac-Reorient-Cube-Shadow-Camera-Direct``) show up to # ~3.28 % per-pixel diff from anti-aliasing noise along the many finger/cube edges. 5.0 gives # headroom above that without masking real regressions, which the SSIM gate still catches. @@ -87,17 +89,20 @@ def _make_sensor_data_type_params( - physics_backend: str, renderer: str, sensor_data_types: list[str] = None + physics_backend: str, + renderer: str, + sensor_data_types: list[str] | None = None, + extra_marks: tuple[Any, ...] = (), ) -> list[pytest.param]: """Create golden-image parameter entries for every supported output type.""" - sensor_data_types = sensor_data_types or _DEFAULT_SENSOR_DATA_TYPES + sensor_data_types = list(sensor_data_types or _DEFAULT_SENSOR_DATA_TYPES) return [ pytest.param( physics_backend, f"{renderer}_renderer", data_type, id=f"{physics_backend}-{renderer}-{data_type}", - marks=_FLAKY_MARK, + marks=(_FLAKY_MARK, *extra_marks), ) for data_type in sensor_data_types ] @@ -197,7 +202,7 @@ def _redirect_ovrtx_renderer_log_to_stdout(env_cfg: Any) -> None: # manager-based envs scene = getattr(env_cfg, "scene", None) if scene is not None: - for camera_name in ("base_camera", "wrist_camera"): + for camera_name in ("base_camera", "wrist_camera", "tiled_camera"): camera_cfg = getattr(scene, camera_name, None) if camera_cfg is not None: camera_cfgs.append(camera_cfg) @@ -217,6 +222,11 @@ def _physics_preset_name(physics_backend: str) -> str: return "newton_mjwarp" if physics_backend == "newton" else physics_backend +def _physics_preset_name_deformable(physics_backend: str) -> str: + """Map deformable-test physics labels to Hydra preset names.""" + return "newton_mjwarp_vbd" if physics_backend == "newton" else physics_backend + + def _normalize_tensor(tensor: torch.Tensor, data_type: str) -> torch.Tensor: """Convert camera output tensor to [0, 1] float32 for conversion to image.""" normalized = tensor.float() @@ -770,3 +780,122 @@ def rendering_test_dexsuite_kuka( # This invokes camera sensor and renderer cleanup explicitly before pytest teardown, otherwise OV # native code could probably complain about leaks and trigger segmentation fault. env = None + + +def _make_franka_cloth_camera_env_cfg(data_type: str): + """Create a test-local Franka cloth camera env cfg without exposing a production task.""" + import isaaclab.sim as sim_utils + from isaaclab.envs import mdp as env_mdp + from isaaclab.managers import ObservationGroupCfg as ObsGroup + from isaaclab.managers import ObservationTermCfg as ObsTerm + from isaaclab.managers import SceneEntityCfg + from isaaclab.sensors import CameraCfg + from isaaclab.utils.configclass import configclass + + from isaaclab_tasks.core.lift.config.franka_soft.franka_cloth_env_cfg import FrankaClothEnvCfg, FrankaClothSceneCfg + from isaaclab_tasks.utils.presets import MultiBackendRendererCfg + + @configclass + class TestFrankaClothCameraSceneCfg(FrankaClothSceneCfg): + """Franka cloth scene with a test-only camera sensor.""" + + tiled_camera: CameraCfg = CameraCfg( + prim_path="/World/envs/env_.*/Camera", + offset=CameraCfg.OffsetCfg( + pos=(0.85, -0.55, 0.42), + rot=(0.52, 0.18, 0.27, 0.79), + convention="opengl", + ), + data_types=[data_type], + spawn=sim_utils.PinholeCameraCfg(clipping_range=(0.01, 2.5)), + width=100, + height=100, + renderer_cfg=MultiBackendRendererCfg(), + ) + + @configclass + class TestFrankaClothCameraObservationsCfg: + """Image-only observations for the local rendering test env.""" + + @configclass + class PolicyCfg(ObsGroup): + image = ObsTerm( + func=env_mdp.image, + params={"sensor_cfg": SceneEntityCfg("tiled_camera"), "data_type": data_type, "permute": True}, + ) + + def __post_init__(self) -> None: + self.enable_corruption = False + self.concatenate_terms = True + + policy: ObsGroup = PolicyCfg() + + @configclass + class TestFrankaClothCameraEnvCfg(FrankaClothEnvCfg): + """Test-only camera variant of ``Isaac-Lift-Cloth-Franka``.""" + + scene: TestFrankaClothCameraSceneCfg = TestFrankaClothCameraSceneCfg( + num_envs=4, env_spacing=2.5, replicate_physics=True + ) + observations: TestFrankaClothCameraObservationsCfg = TestFrankaClothCameraObservationsCfg() + + def __post_init__(self) -> None: + super().__post_init__() + self.commands.deformable_pose.debug_vis = False + self.events.reset_deformable.params["position_range"] = { + "x": (0.0, 0.0), + "y": (0.0, 0.0), + "z": (0.0, 0.0), + } + + return TestFrankaClothCameraEnvCfg() + + +def rendering_test_deformable( + physics_backend: str, + renderer: str, + data_type: str, + comparison_scores: list[dict], +) -> None: + if physics_backend == "ovphysx": + pytest.skip("ovphysx is not supported yet.") + + from isaaclab.envs import ManagerBasedRLEnv + + env_cfg = _make_franka_cloth_camera_env_cfg(data_type) + env_cfg = _apply_overrides_to_env_cfg( + env_cfg, [f"presets={_physics_preset_name_deformable(physics_backend)},{renderer}"] + ) + + env_cfg.scene.num_envs = 4 + + if renderer == "ovrtx_renderer": + _redirect_ovrtx_renderer_log_to_stdout(env_cfg) + + test_name = "franka_cloth_deformable" + env = None + + try: + env = ManagerBasedRLEnv(env_cfg) + + # After 15 steps, the cloth should have fallen down on top of the cube and deformed. + zero_actions = torch.zeros(env.num_envs, env.action_manager.total_action_dim, device=env.device) + for _ in range(15): + env.step(zero_actions) + + maybe_save_stage(test_name, physics_backend, renderer, data_type) + validate_camera_outputs( + test_name, + physics_backend, + renderer, + {data_type: env.scene.sensors["tiled_camera"].data.output[data_type]}, + max_different_pixels_percentage=MAX_DIFFERENT_PIXELS_PERCENTAGE_BY_ENV_NAME[test_name], + comparison_scores=comparison_scores, + ) + finally: + if env is not None: + env.close() + + # This invokes camera sensor and renderer cleanup explicitly before pytest teardown, otherwise OV + # native code could probably complain about leaks and trigger segmentation fault. + env = None