-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Draft: deformables #6202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Draft: deformables #6202
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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" | ||||||||||||||||||||||
|
Comment on lines
+870
to
+875
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||||||||||||||||||
| 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 | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@configclassclasses defined inside a function called once per parametrized case_make_franka_cloth_camera_env_cfgis invoked once for each of the 7 parametrizeddata_typevalues in a test session. Each call re-executes the@configclassdecorator on three classes (TestFrankaClothCameraSceneCfg,TestFrankaClothCameraObservationsCfg,TestFrankaClothCameraEnvCfg) whose names are identical across invocations. If@configclassregisters class names in any module-level registry (as some configclass frameworks do for serialisation/deserialization), repeated registration under the same name can silently overwrite entries or raise an error. Other rendering helpers in this file use pre-defined imported cfg classes (e.g.CartpoleCameraEnvCfg,ShadowHandCameraEnvCfg) to avoid this. Extracting the three inner classes to module level — parameterising them at construction time — would remove the risk.