Unverified Commit 91ad4944 authored by YunLiu's avatar YunLiu Committed by GitHub

Fixes visual prims handling during texture randomization. (#2476)

# Description

<!--
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
-->

Fixed potential issues in
:func:`~isaaclab.envs.mdp.events.randomize_visual_texture_material`
related to handling visual prims during texture randomization.

Fixes # (issue)

<!-- 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. -->

## Type of change

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

- Bug fix (non-breaking change which fixes an issue)

## Screenshots

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.
-->

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] 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 avatarYunLiu <55491388+KumoLiu@users.noreply.github.com>
parent 33e68659
[package]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.40.5"
version = "0.40.6"
# Description
title = "Isaac Lab framework for Robot Learning"
......
Changelog
---------
0.40.6 (2025-06-12)
~~~~~~~~~~~~~~~~~~~
Fixed
^^^^^
* Fixed potential issues in :func:`~isaaclab.envs.mdp.events.randomize_visual_texture_material` related to handling visual prims during texture randomization.
0.40.5 (2025-05-22)
~~~~~~~~~~~~~~~~~~~
......
......@@ -1229,8 +1229,25 @@ class randomize_visual_texture_material(ManagerTermBase):
body_names_regex = ".*"
# create the affected prim path
# TODO: Remove the hard-coded "/visuals" part.
prim_path = f"{asset.cfg.prim_path}/{body_names_regex}/visuals"
# Check if the pattern with '/visuals' yields results when matching `body_names_regex`.
# If not, fall back to a broader pattern without '/visuals'.
asset_main_prim_path = asset.cfg.prim_path
# Try the pattern with '/visuals' first for the generic case
pattern_with_visuals = f"{asset_main_prim_path}/{body_names_regex}/visuals"
# Use sim_utils to check if any prims currently match this pattern
matching_prims = sim_utils.find_matching_prim_paths(pattern_with_visuals)
if matching_prims:
# If matches are found, use the pattern with /visuals
prim_path = pattern_with_visuals
else:
# If no matches found, fall back to the broader pattern without /visuals
# This pattern (e.g., /World/envs/env_.*/Table/.*) should match visual prims
# whether they end in /visuals or have other structures.
prim_path = f"{asset_main_prim_path}/.*"
carb.log_info(
f"Pattern '{pattern_with_visuals}' found no prims. Falling back to '{prim_path}' for texture"
" randomization."
)
# Create the omni-graph node for the randomization term
def rep_texture_randomization():
......@@ -1240,7 +1257,6 @@ class randomize_visual_texture_material(ManagerTermBase):
rep.randomizer.texture(
textures=texture_paths, project_uvw=True, texture_rotate=rep.distribution.uniform(*texture_rotation)
)
return prims_group.node
# Register the event to the replicator
......
......@@ -20,6 +20,7 @@ simulation_app = app_launcher.app
import math
import torch
import unittest
from unittest.mock import patch
import omni.usd
......@@ -127,6 +128,36 @@ class EventCfg:
)
@configclass
class EventCfgFallback:
"""Configuration for events that tests the fallback mechanism."""
# Test fallback when /visuals pattern doesn't match
test_fallback_texture_randomizer = EventTerm(
func=mdp.randomize_visual_texture_material,
mode="reset",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=["slider"]),
"texture_paths": [
f"{NVIDIA_NUCLEUS_DIR}/Materials/Base/Wood/Bamboo_Planks/Bamboo_Planks_BaseColor.png",
f"{NVIDIA_NUCLEUS_DIR}/Materials/Base/Wood/Cherry/Cherry_BaseColor.png",
],
"event_name": "test_fallback_texture_randomizer",
"texture_rotation": (0.0, 0.0),
},
)
reset_cart_position = EventTerm(
func=mdp.reset_joints_by_offset,
mode="reset",
params={
"asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
"position_range": (-1.0, 1.0),
"velocity_range": (-0.1, 0.1),
},
)
@configclass
class CartpoleEnvCfg(ManagerBasedEnvCfg):
"""Configuration for the cartpole environment."""
......@@ -150,6 +181,29 @@ class CartpoleEnvCfg(ManagerBasedEnvCfg):
self.sim.dt = 0.005 # sim step every 5ms: 200Hz
@configclass
class CartpoleEnvCfgFallback(ManagerBasedEnvCfg):
"""Configuration for the cartpole environment that tests fallback mechanism."""
# Scene settings
scene = CartpoleSceneCfg(env_spacing=2.5)
# Basic settings
actions = ActionsCfg()
observations = ObservationsCfg()
events = EventCfgFallback()
def __post_init__(self):
"""Post initialization."""
# viewer settings
self.viewer.eye = (4.5, 0.0, 6.0)
self.viewer.lookat = (0.0, 0.0, 2.0)
# step settings
self.decimation = 4 # env step every 4 sim steps: 200Hz / 4 = 50Hz
# simulation settings
self.sim.dt = 0.005 # sim step every 5ms: 200Hz
class TestTextureRandomization(unittest.TestCase):
"""Test for texture randomization"""
......@@ -186,6 +240,46 @@ class TestTextureRandomization(unittest.TestCase):
env.close()
def test_texture_randomization_fallback(self):
"""Test texture randomization fallback mechanism when /visuals pattern doesn't match."""
def mock_find_matching_prim_paths(pattern):
"""Mock function that simulates a case where /visuals pattern doesn't match."""
# If the pattern contains '/visuals', return empty list to trigger fallback
if pattern.endswith("/visuals"):
return []
return None
for device in ["cpu", "cuda"]:
with self.subTest(device=device):
# create a new stage
omni.usd.get_context().new_stage()
# set the arguments - use fallback config
env_cfg = CartpoleEnvCfgFallback()
env_cfg.scene.num_envs = 16
env_cfg.scene.replicate_physics = False
env_cfg.sim.device = device
with patch.object(
mdp.events.sim_utils, "find_matching_prim_paths", side_effect=mock_find_matching_prim_paths
):
# This should trigger the fallback mechanism and log the fallback message
env = ManagerBasedEnv(cfg=env_cfg)
# simulate physics
with torch.inference_mode():
for count in range(20): # shorter test for fallback
# reset every few steps to check nothing breaks
if count % 10 == 0:
env.reset()
# sample random actions
joint_efforts = torch.randn_like(env.action_manager.action)
# step the environment
env.step(joint_efforts)
env.close()
def test_texture_randomization_failure_replicate_physics(self):
"""Test texture randomization failure when replicate physics is set to True."""
# create a new stage
......
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