diff --git a/exts/nav_tasks/config/extension.toml b/exts/nav_tasks/config/extension.toml index 4d24f16..583aa68 100644 --- a/exts/nav_tasks/config/extension.toml +++ b/exts/nav_tasks/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.3.10" +version = "0.3.11" # Description title = "IsaacLab Navigation RL Tasks" diff --git a/exts/nav_tasks/docs/CHANGELOG.rst b/exts/nav_tasks/docs/CHANGELOG.rst index 069ba33..4f0f94d 100644 --- a/exts/nav_tasks/docs/CHANGELOG.rst +++ b/exts/nav_tasks/docs/CHANGELOG.rst @@ -1,6 +1,22 @@ Changelog --------- + +0.3.11 (2025-08-13) +~~~~~~~~~~~~~~~~~~ + +Added +^^^^^ + +- Added ``freeze_low_level_policy`` option to :class:`nav_tasks.mdp.actions.NavigationSE2ActionCfg` to allow to not freeze the low level policy. +- Added ``nan_fill_value`` option to :func:`nav_tasks.mdp.observations.camera_observations:camera_image` to allow to fill NaNs with a specific value. + +Changed +^^^^^^^ + +- Updated :func:`nav_tasks.mdp.observations.height_scan_observations:height_scan_clipped` to make clipping optional. + + 0.3.10 (2025-08-08) ~~~~~~~~~~~~~~~~~~ diff --git a/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions.py b/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions.py index 5757b06..f77ccc0 100644 --- a/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions.py +++ b/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions.py @@ -30,7 +30,9 @@ def __init__(self, cfg: NavigationSE2ActionCfg, env: ManagerBasedRLEnv): # load policies file_bytes = read_file(self.cfg.low_level_policy_file) self.low_level_policy = torch.jit.load(file_bytes, map_location=self.device) - self.low_level_policy = torch.jit.freeze(self.low_level_policy.eval()) + self.low_level_policy.eval() + if self.cfg.freeze_low_level_policy: + self.low_level_policy = torch.jit.freeze(self.low_level_policy) # prepare joint position actions if not isinstance(self.cfg.low_level_action, list): diff --git a/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions_cfg.py b/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions_cfg.py index c1a43e7..b781e3a 100644 --- a/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions_cfg.py +++ b/exts/nav_tasks/nav_tasks/mdp/actions/navigation_actions_cfg.py @@ -28,6 +28,11 @@ class NavigationSE2ActionCfg(ActionTermCfg): low_level_policy_file: str = MISSING """Path to the low level policy file.""" + freeze_low_level_policy: bool = True + """Whether to freeze the low level policy. + + Can improve performance but will also eliminate possible functions such as `reset`.""" + low_level_obs_group: str = "low_level_policy" """Observation group of the low level policy.""" diff --git a/exts/nav_tasks/nav_tasks/mdp/observations/camera_observations.py b/exts/nav_tasks/nav_tasks/mdp/observations/camera_observations.py index 387f236..ba2ce4c 100644 --- a/exts/nav_tasks/nav_tasks/mdp/observations/camera_observations.py +++ b/exts/nav_tasks/nav_tasks/mdp/observations/camera_observations.py @@ -22,7 +22,11 @@ def camera_image( - env: ManagerBasedEnv, sensor_cfg: SceneEntityCfg, data_type: str = "distance_to_image_plane", flatten: bool = False + env: ManagerBasedEnv, + sensor_cfg: SceneEntityCfg, + data_type: str = "distance_to_image_plane", + flatten: bool = False, + nan_fill_value: float | None = None, ) -> torch.Tensor: """Camera image Observations. @@ -34,6 +38,7 @@ def camera_image( sensor_cfg: The name of the sensor. data_type: The type of data to extract from the sensor. Default is "distance_to_image_plane". flatten: If True, the image will be flattened to 1D. Default is False. + nan_fill_value: The value to fill nan/inf values with. If None, the maximum distance of the sensor will be used. Returns: The image data.""" @@ -43,8 +48,9 @@ def camera_image( img = sensor.data.output[data_type].clone() if data_type == "distance_to_image_plane": - img[torch.isnan(img)] = sensor.cfg.max_distance - img[torch.isinf(img)] = sensor.cfg.max_distance + if nan_fill_value is None: + nan_fill_value = sensor.cfg.max_distance + img = torch.nan_to_num(img, nan=nan_fill_value, posinf=nan_fill_value, neginf=0.0) # if type torch.uint8, convert to float and scale between 0 and 1 if img.dtype == torch.uint8: diff --git a/exts/nav_tasks/nav_tasks/mdp/observations/height_scan_observations.py b/exts/nav_tasks/nav_tasks/mdp/observations/height_scan_observations.py index 71111a8..08cb906 100644 --- a/exts/nav_tasks/nav_tasks/mdp/observations/height_scan_observations.py +++ b/exts/nav_tasks/nav_tasks/mdp/observations/height_scan_observations.py @@ -49,16 +49,15 @@ def height_scan_clipped( env: ManagerBasedRLEnv, sensor_cfg: SceneEntityCfg, offset: float = 0.5, - clip_height: tuple[float, float] = (-1.0, 0.5), + clip_height: tuple[float, float] | None = (-1.0, 0.5), ) -> torch.Tensor: - """Height scan from the given sensor w.r.t. the sensor's frame. - - The provided offset (Defaults to 0.5) is subtracted from the returned values. - """ + """Height scan from the given sensor w.r.t. the sensor's frame.""" # get the bounded height scan height = height_scan_bounded(env, sensor_cfg, offset) - # clip to max observable height - return torch.clip(height, clip_height[0], clip_height[1]) + if clip_height is not None: + # clip to max observable height + height = torch.clip(height, clip_height[0], clip_height[1]) + return height def height_scan_square(