Skip to content

Support composite observation space with proper min max in manager based env #2811

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/isaaclab/config/extension.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

# Note: Semantic Versioning is used: https://semver.org/
version = "0.40.11"
version = "0.X.0"

# Description
title = "Isaac Lab framework for Robot Learning"
Expand Down
11 changes: 11 additions & 0 deletions source/isaaclab/docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Changelog
---------

0.X.0 (2025-06-29)
~~~~~~~~~~~~~~~~~~~~

Added
^^^^^

* Added MangerBasedRLEnv support for composite gym observation spaces.
* A test for the composite gym observation spaces in ManagerBasedRLEnv is added to ensure that the observation spaces
are correctly configured base on the clip.


0.40.11 (2025-06-27)
~~~~~~~~~~~~~~~~~~~~

Expand Down
11 changes: 7 additions & 4 deletions source/isaaclab/isaaclab/envs/manager_based_rl_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,13 @@ def _configure_gym_env_spaces(self):
if has_concatenated_obs:
self.single_observation_space[group_name] = gym.spaces.Box(low=-np.inf, high=np.inf, shape=group_dim)
else:
self.single_observation_space[group_name] = gym.spaces.Dict({
term_name: gym.spaces.Box(low=-np.inf, high=np.inf, shape=term_dim)
for term_name, term_dim in zip(group_term_names, group_dim)
})
group_term_cfgs = self.observation_manager._group_obs_term_cfgs[group_name]
for term_name, term_dim, term_cfg in zip(group_term_names, group_dim, group_term_cfgs):
low = -np.inf if term_cfg.clip is None else term_cfg.clip[0]
high = np.inf if term_cfg.clip is None else term_cfg.clip[1]
self.single_observation_space[group_name] = gym.spaces.Dict(
{term_name: gym.spaces.Box(low=low, high=high, shape=term_dim)}
)
# action space (unbounded since we don't impose any limits)
action_dim = sum(self.action_manager.action_term_dim)
self.single_action_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(action_dim,))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Test texture randomization in the cartpole scene using pytest."""

from isaaclab.app import AppLauncher

# launch omniverse app
simulation_app = AppLauncher(headless=True, enable_cameras=True).app

import gymnasium as gym
import numpy as np

import omni.usd
import pytest

from isaaclab.envs import ManagerBasedRLEnv

from isaaclab_tasks.manager_based.classic.cartpole.cartpole_camera_env_cfg import (
CartpoleDepthCameraEnvCfg,
CartpoleRGBCameraEnvCfg,
)
from isaaclab_tasks.manager_based.locomotion.velocity.config.anymal_c.rough_env_cfg import AnymalCRoughEnvCfg


@pytest.mark.parametrize(
"env_cfg_cls",
[CartpoleRGBCameraEnvCfg, CartpoleDepthCameraEnvCfg, AnymalCRoughEnvCfg],
ids=["RGB", "Depth", "RayCaster"],
)
@pytest.mark.parametrize("device", ["cpu", "cuda"])
def test_obs_space_follows_clip_contraint(env_cfg_cls, device):
"""Ensure curriculum terms apply correctly after the fallback and replacement."""
# new USD stage
omni.usd.get_context().new_stage()

# configure the cartpole env
env_cfg = env_cfg_cls()
env_cfg.scene.num_envs = 2 # keep num_envs small for testing
env_cfg.observations.policy.concatenate_terms = False
env_cfg.sim.device = device

env = ManagerBasedRLEnv(cfg=env_cfg)
for group_name, group_space in env.observation_space.spaces.items():
for term_name, term_space in group_space.spaces.items():
term_cfg = getattr(getattr(env_cfg.observations, group_name), term_name)
low = -np.inf if term_cfg.clip is None else term_cfg.clip[0]
high = np.inf if term_cfg.clip is None else term_cfg.clip[1]
assert isinstance(
term_space, gym.spaces.Box
), f"Expected Box space for {term_name} in {group_name}, got {type(term_space)}"
assert np.all(term_space.low == low)
assert np.all(term_space.high == high)

env.close()