From f44fa0169eca1e9e2d862f528532e157c75b78a3 Mon Sep 17 00:00:00 2001 From: Pascal Roth Date: Mon, 28 Apr 2025 11:10:00 +0200 Subject: [PATCH 1/7] add concat dim feature --- source/isaaclab/config/extension.toml | 2 +- source/isaaclab/docs/CHANGELOG.rst | 17 +++++ .../isaaclab/managers/command_manager.py | 4 +- .../isaaclab/managers/manager_term_cfg.py | 11 +++- .../isaaclab/managers/observation_manager.py | 29 ++++++-- .../test/managers/test_observation_manager.py | 66 +++++++++++++++++++ 6 files changed, 121 insertions(+), 8 deletions(-) diff --git a/source/isaaclab/config/extension.toml b/source/isaaclab/config/extension.toml index 3f09063efa85..1eaea7fb1bfa 100644 --- a/source/isaaclab/config/extension.toml +++ b/source/isaaclab/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.36.23" +version = "0.36.24" # Description title = "Isaac Lab framework for Robot Learning" diff --git a/source/isaaclab/docs/CHANGELOG.rst b/source/isaaclab/docs/CHANGELOG.rst index dd7855a2f160..40e318aa63dd 100644 --- a/source/isaaclab/docs/CHANGELOG.rst +++ b/source/isaaclab/docs/CHANGELOG.rst @@ -1,6 +1,23 @@ Changelog --------- + +0.36.24 (2025-04-28) +~~~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +* Added support for concatenation of observations along different dimensions in :class:`~isaaclab.managers.observation_manager.ObservationManager`. + +Changed +^^^^^^^ + +* Updated the :class:`~isaaclab.managers.command_manager.CommandManager` to update the command counter after the + resampling call. + + + 0.36.23 (2025-04-24) ~~~~~~~~~~~~~~~~~~~~ diff --git a/source/isaaclab/isaaclab/managers/command_manager.py b/source/isaaclab/isaaclab/managers/command_manager.py index 3821a5c8bbda..33e54e7f4da9 100644 --- a/source/isaaclab/isaaclab/managers/command_manager.py +++ b/source/isaaclab/isaaclab/managers/command_manager.py @@ -181,10 +181,10 @@ def _resample(self, env_ids: Sequence[int]): if len(env_ids) != 0: # resample the time left before resampling self.time_left[env_ids] = self.time_left[env_ids].uniform_(*self.cfg.resampling_time_range) - # increment the command counter - self.command_counter[env_ids] += 1 # resample the command self._resample_command(env_ids) + # increment the command counter + self.command_counter[env_ids] += 1 """ Implementation specific functions. diff --git a/source/isaaclab/isaaclab/managers/manager_term_cfg.py b/source/isaaclab/isaaclab/managers/manager_term_cfg.py index 199409c2e222..219c3b69d872 100644 --- a/source/isaaclab/isaaclab/managers/manager_term_cfg.py +++ b/source/isaaclab/isaaclab/managers/manager_term_cfg.py @@ -201,12 +201,21 @@ class ObservationGroupCfg: concatenate_terms: bool = True """Whether to concatenate the observation terms in the group. Defaults to True. - If true, the observation terms in the group are concatenated along the last dimension. + If true, the observation terms in the group are concatenated along the dimension specified in concatenate_dim. Otherwise, they are kept separate and returned as a dictionary. If the observation group contains terms of different dimensions, it must be set to False. """ + concatenate_dim: int = -1 + """Dimension along to concatenate the different oberservation terms. Defaults to -1. + + If concatenate_terms is True, this specifies the dimension along which the observation terms are concatenated. + The indicated dimension is the one of the observations, i.e. for a 2D RGB image (H, W, C), the dimension + 0 means concatenating along the height, 1 along the width and 2 along the channels. The offset due + to the batched environment is handled automatically. + """ + enable_corruption: bool = False """Whether to enable corruption for the observation group. Defaults to False. diff --git a/source/isaaclab/isaaclab/managers/observation_manager.py b/source/isaaclab/isaaclab/managers/observation_manager.py index 78b1e16940b8..9c7aec1fdbe4 100644 --- a/source/isaaclab/isaaclab/managers/observation_manager.py +++ b/source/isaaclab/isaaclab/managers/observation_manager.py @@ -88,8 +88,14 @@ def __init__(self, cfg: object, env: ManagerBasedEnv): # otherwise, keep the list of shapes as is if self._group_obs_concatenate[group_name]: try: - term_dims = [torch.tensor(dims, device="cpu") for dims in group_term_dims] - self._group_obs_dim[group_name] = tuple(torch.sum(torch.stack(term_dims, dim=0), dim=0).tolist()) + term_dims = torch.stack([torch.tensor(dims, device="cpu") for dims in group_term_dims], dim=0) + if len(term_dims.shape) > 1: + dim_sum = torch.sum(term_dims[:, self._group_obs_concatenate_dim[group_name]], dim=0) + term_dims[0, self._group_obs_concatenate_dim[group_name]] = dim_sum + term_dims = term_dims[0] + else: + term_dims = torch.sum(term_dims, dim=0) + self._group_obs_dim[group_name] = tuple(term_dims.tolist()) except RuntimeError: raise RuntimeError( f"Unable to concatenate observation terms in group '{group_name}'." @@ -330,7 +336,13 @@ def compute_group(self, group_name: str) -> torch.Tensor | dict[str, torch.Tenso # concatenate all observations in the group together if self._group_obs_concatenate[group_name]: - return torch.cat(list(group_obs.values()), dim=-1) + # set the concatenate dimension, account for the batch dimension if positive dimension is given + dim = ( + self._group_obs_concatenate_dim[group_name] + 1 + if self._group_obs_concatenate_dim[group_name] >= 0 + else self._group_obs_concatenate_dim[group_name] + ) + return torch.cat(list(group_obs.values()), dim=dim) else: return group_obs @@ -347,6 +359,8 @@ def _prepare_terms(self): self._group_obs_term_cfgs: dict[str, list[ObservationTermCfg]] = dict() self._group_obs_class_term_cfgs: dict[str, list[ObservationTermCfg]] = dict() self._group_obs_concatenate: dict[str, bool] = dict() + self._group_obs_concatenate_dim: dict[str, int] = dict() + self._group_obs_term_history_buffer: dict[str, dict] = dict() # create a list to store modifiers that are classes # we store it as a separate list to only call reset on them and prevent unnecessary calls @@ -384,6 +398,7 @@ def _prepare_terms(self): group_entry_history_buffer: dict[str, CircularBuffer] = dict() # read common config for the group self._group_obs_concatenate[group_name] = group_cfg.concatenate_terms + self._group_obs_concatenate_dim[group_name] = group_cfg.concatenate_dim # check if config is dict already if isinstance(group_cfg, dict): group_cfg_items = group_cfg.items() @@ -392,7 +407,13 @@ def _prepare_terms(self): # iterate over all the terms in each group for term_name, term_cfg in group_cfg_items: # skip non-obs settings - if term_name in ["enable_corruption", "concatenate_terms", "history_length", "flatten_history_dim"]: + if term_name in [ + "enable_corruption", + "concatenate_terms", + "history_length", + "flatten_history_dim", + "concatenate_dim", + ]: continue # check for non config if term_cfg is None: diff --git a/source/isaaclab/test/managers/test_observation_manager.py b/source/isaaclab/test/managers/test_observation_manager.py index 1df4c27dd9ff..1498673f9220 100644 --- a/source/isaaclab/test/managers/test_observation_manager.py +++ b/source/isaaclab/test/managers/test_observation_manager.py @@ -662,6 +662,72 @@ class PolicyCfg(ObservationGroupCfg): with self.assertRaises(ValueError): self.obs_man = ObservationManager(cfg, self.env) + def test_concatenate_dim(self): + """Test concatenation of observations along different dimensions.""" + + @configclass + class MyObservationManagerCfg: + """Test config class for observation manager.""" + + @configclass + class PolicyCfg(ObservationGroupCfg): + """Test config class for policy observation group.""" + + concatenate_terms = True + concatenate_dim = 1 # Concatenate along dimension 1 + term_1 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + term_2 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + + @configclass + class CriticCfg(ObservationGroupCfg): + """Test config class for critic observation group.""" + + concatenate_terms = True + concatenate_dim = 2 # Concatenate along dimension 2 + term_1 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + term_2 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + + @configclass + class CriticCfg_neg_dim(ObservationGroupCfg): + """Test config class for critic observation group.""" + + concatenate_terms = True + concatenate_dim = -1 # Concatenate along last dimension + term_1 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + term_2 = ObservationTermCfg(func=grilled_chicken_image, scale=1.0, params={"bland": 1.0, "channel": 1}) + + policy: ObservationGroupCfg = PolicyCfg() + critic: ObservationGroupCfg = CriticCfg() + critic_neg_dim: ObservationGroupCfg = CriticCfg_neg_dim() + + # create observation manager + cfg = MyObservationManagerCfg() + self.obs_man = ObservationManager(cfg, self.env) + # compute observation using manager + observations = self.obs_man.compute() + + # obtain the group observations + obs_policy: torch.Tensor = observations["policy"] + obs_critic: torch.Tensor = observations["critic"] + obs_critic_neg_dim: torch.Tensor = observations["critic_neg_dim"] + + # check the observation shapes + # For policy: concatenated along dim 1, so width should be doubled + self.assertEqual((self.env.num_envs, 128, 512, 1), obs_policy.shape) + # For critic: concatenated along last dim, so channels should be doubled + self.assertEqual((self.env.num_envs, 128, 256, 2), obs_critic.shape) + # For critic_neg_dim: concatenated along last dim, so channels should be doubled + self.assertEqual((self.env.num_envs, 128, 256, 2), obs_critic_neg_dim.shape) + + # verify the data is concatenated correctly + # For policy: check that the second half matches the first half + torch.testing.assert_close(obs_policy[:, :, :256, :], obs_policy[:, :, 256:, :]) + # For critic: check that the second channel matches the first channel + torch.testing.assert_close(obs_critic[:, :, :, 0], obs_critic[:, :, :, 1]) + + # For critic_neg_dim: check that it is the same as critic + torch.testing.assert_close(obs_critic_neg_dim, obs_critic) + if __name__ == "__main__": run_tests() From 13f15ea0070f656a523295c2441436c1356b3817 Mon Sep 17 00:00:00 2001 From: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> Date: Wed, 14 May 2025 10:54:47 +0200 Subject: [PATCH 2/7] Update source/isaaclab/isaaclab/managers/manager_term_cfg.py Co-authored-by: Mayank Mittal <12863862+Mayankm96@users.noreply.github.com> Signed-off-by: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> --- source/isaaclab/isaaclab/managers/manager_term_cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/isaaclab/isaaclab/managers/manager_term_cfg.py b/source/isaaclab/isaaclab/managers/manager_term_cfg.py index 219c3b69d872..c501c6f06ebe 100644 --- a/source/isaaclab/isaaclab/managers/manager_term_cfg.py +++ b/source/isaaclab/isaaclab/managers/manager_term_cfg.py @@ -201,7 +201,7 @@ class ObservationGroupCfg: concatenate_terms: bool = True """Whether to concatenate the observation terms in the group. Defaults to True. - If true, the observation terms in the group are concatenated along the dimension specified in concatenate_dim. + If true, the observation terms in the group are concatenated along the dimension specified through :attr:`concatenate_dim`. Otherwise, they are kept separate and returned as a dictionary. If the observation group contains terms of different dimensions, it must be set to False. From 34be231022a2965bef9f272b3e47431c696e8af1 Mon Sep 17 00:00:00 2001 From: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> Date: Wed, 14 May 2025 10:54:58 +0200 Subject: [PATCH 3/7] Update source/isaaclab/isaaclab/managers/manager_term_cfg.py Co-authored-by: Mayank Mittal <12863862+Mayankm96@users.noreply.github.com> Signed-off-by: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> --- source/isaaclab/isaaclab/managers/manager_term_cfg.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/isaaclab/isaaclab/managers/manager_term_cfg.py b/source/isaaclab/isaaclab/managers/manager_term_cfg.py index c501c6f06ebe..a300716815b3 100644 --- a/source/isaaclab/isaaclab/managers/manager_term_cfg.py +++ b/source/isaaclab/isaaclab/managers/manager_term_cfg.py @@ -208,7 +208,8 @@ class ObservationGroupCfg: """ concatenate_dim: int = -1 - """Dimension along to concatenate the different oberservation terms. Defaults to -1. + """Dimension along to concatenate the different observation terms. Defaults to -1, which + means the last dimension of the observation terms. If concatenate_terms is True, this specifies the dimension along which the observation terms are concatenated. The indicated dimension is the one of the observations, i.e. for a 2D RGB image (H, W, C), the dimension From 34f279b559212f5347fa74d17f4b3ce222c6a4be Mon Sep 17 00:00:00 2001 From: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> Date: Wed, 14 May 2025 10:55:10 +0200 Subject: [PATCH 4/7] Update source/isaaclab/isaaclab/managers/manager_term_cfg.py Co-authored-by: Mayank Mittal <12863862+Mayankm96@users.noreply.github.com> Signed-off-by: Pascal Roth <57946385+pascal-roth@users.noreply.github.com> --- source/isaaclab/isaaclab/managers/manager_term_cfg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/isaaclab/isaaclab/managers/manager_term_cfg.py b/source/isaaclab/isaaclab/managers/manager_term_cfg.py index a300716815b3..08887fcf84cf 100644 --- a/source/isaaclab/isaaclab/managers/manager_term_cfg.py +++ b/source/isaaclab/isaaclab/managers/manager_term_cfg.py @@ -211,9 +211,9 @@ class ObservationGroupCfg: """Dimension along to concatenate the different observation terms. Defaults to -1, which means the last dimension of the observation terms. - If concatenate_terms is True, this specifies the dimension along which the observation terms are concatenated. - The indicated dimension is the one of the observations, i.e. for a 2D RGB image (H, W, C), the dimension - 0 means concatenating along the height, 1 along the width and 2 along the channels. The offset due + If :attr:`concatenate_terms` is True, this parameter specifies the dimension along which the observation terms are concatenated. + The indicated dimension depends on the shape of the observations. For instance, for a 2D RGB image of shape (H, W, C), the dimension + 0 means concatenating along the height, 1 along the width, and 2 along the channels. The offset due to the batched environment is handled automatically. """ From 18e8b38cbdbb4099e81ea0308f51b3b5bd87ef67 Mon Sep 17 00:00:00 2001 From: Pascal Roth Date: Wed, 14 May 2025 11:24:33 +0200 Subject: [PATCH 5/7] remove if statement during runtime --- .../isaaclab/managers/observation_manager.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/source/isaaclab/isaaclab/managers/observation_manager.py b/source/isaaclab/isaaclab/managers/observation_manager.py index 9c7aec1fdbe4..57c0c001da5f 100644 --- a/source/isaaclab/isaaclab/managers/observation_manager.py +++ b/source/isaaclab/isaaclab/managers/observation_manager.py @@ -90,8 +90,12 @@ def __init__(self, cfg: object, env: ManagerBasedEnv): try: term_dims = torch.stack([torch.tensor(dims, device="cpu") for dims in group_term_dims], dim=0) if len(term_dims.shape) > 1: - dim_sum = torch.sum(term_dims[:, self._group_obs_concatenate_dim[group_name]], dim=0) - term_dims[0, self._group_obs_concatenate_dim[group_name]] = dim_sum + if self._group_obs_concatenate_dim[group_name] >= 0: + dim = self._group_obs_concatenate_dim[group_name] - 1 # account for the batch offset + else: + dim = self._group_obs_concatenate_dim[group_name] + dim_sum = torch.sum(term_dims[:, dim], dim=0) + term_dims[0, dim] = dim_sum term_dims = term_dims[0] else: term_dims = torch.sum(term_dims, dim=0) @@ -337,12 +341,7 @@ def compute_group(self, group_name: str) -> torch.Tensor | dict[str, torch.Tenso # concatenate all observations in the group together if self._group_obs_concatenate[group_name]: # set the concatenate dimension, account for the batch dimension if positive dimension is given - dim = ( - self._group_obs_concatenate_dim[group_name] + 1 - if self._group_obs_concatenate_dim[group_name] >= 0 - else self._group_obs_concatenate_dim[group_name] - ) - return torch.cat(list(group_obs.values()), dim=dim) + return torch.cat(list(group_obs.values()), dim=self._group_obs_concatenate_dim[group_name]) else: return group_obs @@ -398,7 +397,9 @@ def _prepare_terms(self): group_entry_history_buffer: dict[str, CircularBuffer] = dict() # read common config for the group self._group_obs_concatenate[group_name] = group_cfg.concatenate_terms - self._group_obs_concatenate_dim[group_name] = group_cfg.concatenate_dim + self._group_obs_concatenate_dim[group_name] = ( + group_cfg.concatenate_dim + 1 if group_cfg.concatenate_dim >= 0 else group_cfg.concatenate_dim + ) # check if config is dict already if isinstance(group_cfg, dict): group_cfg_items = group_cfg.items() From 014b73354f08a1858bd42fa3dfadc18a340fcc35 Mon Sep 17 00:00:00 2001 From: Kelly Guo Date: Fri, 16 May 2025 21:22:14 -0700 Subject: [PATCH 6/7] Update extension.toml Signed-off-by: Kelly Guo --- source/isaaclab/config/extension.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/isaaclab/config/extension.toml b/source/isaaclab/config/extension.toml index 0068d568064f..9d0a19dd8896 100644 --- a/source/isaaclab/config/extension.toml +++ b/source/isaaclab/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.39.3" +version = "0.39.4" # Description title = "Isaac Lab framework for Robot Learning" From 05cad50dc6a37c498cc7ee262922a11be7aa25ee Mon Sep 17 00:00:00 2001 From: Kelly Guo Date: Fri, 16 May 2025 22:52:55 -0700 Subject: [PATCH 7/7] Update extension.toml Signed-off-by: Kelly Guo --- source/isaaclab/config/extension.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/isaaclab/config/extension.toml b/source/isaaclab/config/extension.toml index 9d0a19dd8896..0424a20f5021 100644 --- a/source/isaaclab/config/extension.toml +++ b/source/isaaclab/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.39.4" +version = "0.39.5" # Description title = "Isaac Lab framework for Robot Learning"