Source code for isaaclab.envs.common

# 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

from __future__ import annotations

import warnings
from dataclasses import MISSING, fields
from typing import Dict, Literal, TypeVar  # noqa: UP035

import gymnasium as gym
import torch

from isaaclab.utils.configclass import configclass

##
# Deprecated: ViewerCfg
##


def _viewer_cfg_field_matches_default(value, default) -> bool:
    """Return True when *value* equals *default* (element-wise for tuples/lists)."""
    if isinstance(value, (tuple, list)):
        return type(value) is type(default) and tuple(value) == tuple(default)
    return value == default


[docs] @configclass class ViewerCfg: """Configuration of the scene viewport camera. .. deprecated:: :class:`ViewerCfg` is deprecated and will be removed in a future release. Configure the viewport camera via :class:`~isaaclab_visualizers.kit.KitVisualizerCfg` and add it to :attr:`~isaaclab.sim.SimulationCfg.visualizer_cfgs` instead:: from isaaclab.sim import SimulationCfg from isaaclab_visualizers.kit import KitVisualizerCfg sim_cfg = SimulationCfg(visualizer_cfgs=[KitVisualizerCfg(eye=(7.5, 7.5, 7.5), lookat=(0.0, 0.0, 0.0))]) """ eye: tuple[float, float, float] = (7.5, 7.5, 7.5) """Initial camera position (in m). Default is (7.5, 7.5, 7.5).""" lookat: tuple[float, float, float] = (0.0, 0.0, 0.0) """Initial camera target position (in m). Default is (0.0, 0.0, 0.0).""" cam_prim_path: str = "/OmniverseKit_Persp" """The camera prim path to record images from. Default is "/OmniverseKit_Persp".""" resolution: tuple[int, int] = (1280, 720) """The resolution (width, height) of the camera. Default is (1280, 720).""" origin_type: Literal["world", "env", "asset_root", "asset_body"] = "world" """The frame in which the camera position (eye) and target (lookat) are defined. Default is "world".""" env_index: int = 0 """The environment index for frame origin. Default is 0.""" asset_name: str | None = None """The asset name in the interactive scene for the frame origin. Default is None.""" body_name: str | None = None """The name of the body in :attr:`asset_name` for the frame origin. Default is None.""" def __post_init__(self) -> None: # Warn only when the user configured a non-default field so that bare ``ViewerCfg()`` # usage (e.g. in task configs that haven't been migrated yet) stays silent. # # @configclass stores mutable defaults (tuples, lists) via default_factory rather than # default, so we must check both to obtain the canonical default value. differing = [] for f in fields(self): if not f.init: continue if f.default is not MISSING: default = f.default elif f.default_factory is not MISSING: # type: ignore[misc] default = f.default_factory() # type: ignore[misc] else: continue if not _viewer_cfg_field_matches_default(getattr(self, f.name), default): differing.append(f.name) if differing: warnings.warn( "ViewerCfg is deprecated and will be removed in a future release. " "Use KitVisualizerCfg added to SimulationCfg.visualizer_cfgs instead.", DeprecationWarning, stacklevel=2, )
def _apply_deprecated_viewer_cfg(env_cfg: object) -> None: """Apply the deprecated ``viewer`` field to ``sim.default_visualizer_cfg`` if it was set. Detects any non-default value on ``env_cfg.viewer`` (eye, lookat, origin_type, asset tracking), emits a log warning, and writes the settings into ``env_cfg.sim.default_visualizer_cfg`` so the Kit visualizer still picks them up. Mapping from deprecated :class:`ViewerCfg` fields to :class:`~isaaclab_visualizers.kit.KitVisualizerCfg`: * ``eye`` / ``lookat`` → ``eye`` / ``lookat`` * ``env_index`` → ``origin_env_index`` * ``origin_type="asset_root"`` → ``origin_type="asset"``, ``origin_track_path="<asset_name>"`` * ``origin_type="asset_body"`` → ``origin_type="asset"``, ``origin_track_path="<asset_name>/<body_name>"`` Must be called before :class:`~isaaclab.sim.SimulationContext` is constructed so that the translated cfg is visible to the context. """ import logging as _logging viewer = getattr(env_cfg, "viewer", None) if viewer is None: return _defaults = ViewerCfg() eye_changed = not _viewer_cfg_field_matches_default(viewer.eye, _defaults.eye) lookat_changed = not _viewer_cfg_field_matches_default(viewer.lookat, _defaults.lookat) origin_changed = viewer.origin_type != _defaults.origin_type resolution_changed = not _viewer_cfg_field_matches_default( getattr(viewer, "resolution", _defaults.resolution), _defaults.resolution ) cam_prim_path_changed = getattr(viewer, "cam_prim_path", _defaults.cam_prim_path) != _defaults.cam_prim_path if not (eye_changed or lookat_changed or origin_changed or resolution_changed): return if cam_prim_path_changed: _logging.getLogger(__name__).warning( "env_cfg.viewer.cam_prim_path=%r cannot be automatically forwarded to KitVisualizerCfg " "(no equivalent field). Set the camera prim path via the Kit viewport UI or configure " "a custom KitVisualizerCfg in env_cfg.sim.visualizer_cfgs.", viewer.cam_prim_path, ) _logging.getLogger(__name__).warning( "env_cfg.viewer is deprecated. Set env_cfg.sim.default_visualizer_cfg = " "KitVisualizerCfg(eye=..., lookat=...) instead. The viewer values have been " "automatically forwarded for this run." ) sim_cfg = getattr(env_cfg, "sim", None) if sim_cfg is None: return if getattr(sim_cfg, "default_visualizer_cfg", None) is not None: _logging.getLogger(__name__).warning( "env_cfg.viewer is deprecated, but its non-default values (eye, lookat, origin_type) " "could NOT be forwarded automatically because env_cfg.sim.default_visualizer_cfg is " "already set. To silence this warning and preserve your camera settings, migrate to " "KitVisualizerCfg: env_cfg.sim.default_visualizer_cfg = KitVisualizerCfg(eye=..., lookat=...)." ) return # Map deprecated origin_type values to the new KitVisualizerCfg fields. new_origin_type = viewer.origin_type origin_track_path = None if viewer.origin_type in ("asset_root", "asset_body"): new_origin_type = "asset" if getattr(viewer, "asset_name", None) is not None: body_name = getattr(viewer, "body_name", None) if viewer.origin_type == "asset_body" and body_name is not None: origin_track_path = f"{viewer.asset_name}/{body_name}" else: origin_track_path = viewer.asset_name try: from isaaclab_visualizers.kit import KitVisualizerCfg resolution = getattr(viewer, "resolution", None) sim_cfg.default_visualizer_cfg = KitVisualizerCfg( eye=tuple(viewer.eye), lookat=tuple(viewer.lookat), origin_type=new_origin_type, origin_env_index=getattr(viewer, "env_index", 0), origin_track_path=origin_track_path, **({"window_width": resolution[0], "window_height": resolution[1]} if resolution is not None else {}), ) except ImportError: from isaaclab.visualizers import VisualizerCfg sim_cfg.default_visualizer_cfg = VisualizerCfg( eye=tuple(viewer.eye), lookat=tuple(viewer.lookat), ) ## # Types. ## SpaceType = TypeVar("SpaceType", gym.spaces.Space, int, set, tuple, list, dict) """A sentinel object to indicate a valid space type to specify states, observations and actions.""" VecEnvObs = Dict[str, torch.Tensor | Dict[str, torch.Tensor]] """Observation returned by the environment. The observations are stored in a dictionary. The keys are the group to which the observations belong. This is useful for various setups such as reinforcement learning with asymmetric actor-critic or multi-agent learning. For non-learning paradigms, this may include observations for different components of a system. Within each group, the observations can be stored either as a dictionary with keys as the names of each observation term in the group, or a single tensor obtained from concatenating all the observation terms. For example, for asymmetric actor-critic, the observation for the actor and the critic can be accessed using the keys ``"policy"`` and ``"critic"`` respectively. Note: By default, most learning frameworks deal with default and privileged observations in different ways. This handling must be taken care of by the wrapper around the :class:`ManagerBasedRLEnv` instance. For included frameworks (RSL-RL, RL-Games, skrl), the observations must have the key "policy". In case, the key "critic" is also present, then the critic observations are taken from the "critic" group. Otherwise, they are the same as the "policy" group. """ VecEnvStepReturn = tuple[VecEnvObs, torch.Tensor, torch.Tensor, torch.Tensor, dict] """The environment signals processed at the end of each step. The tuple contains batched information for each sub-environment. The information is stored in the following order: 1. **Observations**: The observations from the environment. 2. **Rewards**: The rewards from the environment. 3. **Terminated Dones**: Whether the environment reached a terminal state, such as task success or robot falling etc. 4. **Timeout Dones**: Whether the environment reached a timeout state, such as end of max episode length. 5. **Extras**: A dictionary containing additional information from the environment. """ AgentID = TypeVar("AgentID") """Unique identifier for an agent within a multi-agent environment. The identifier has to be an immutable object, typically a string (e.g.: ``"agent_0"``). """ ObsType = TypeVar("ObsType", torch.Tensor, Dict[str, torch.Tensor]) """A sentinel object to indicate the data type of the observation. """ ActionType = TypeVar("ActionType", torch.Tensor, Dict[str, torch.Tensor]) """A sentinel object to indicate the data type of the action. """ StateType = TypeVar("StateType", torch.Tensor, dict) """A sentinel object to indicate the data type of the state. """ EnvStepReturn = tuple[ Dict[AgentID, ObsType], Dict[AgentID, torch.Tensor], Dict[AgentID, torch.Tensor], Dict[AgentID, torch.Tensor], Dict[AgentID, dict], ] """The environment signals processed at the end of each step. The tuple contains batched information for each sub-environment (keyed by the agent ID). The information is stored in the following order: 1. **Observations**: The observations from the environment. 2. **Rewards**: The rewards from the environment. 3. **Terminated Dones**: Whether the environment reached a terminal state, such as task success or robot falling etc. 4. **Timeout Dones**: Whether the environment reached a timeout state, such as end of max episode length. 5. **Extras**: A dictionary containing additional information from the environment. """