Unverified Commit 3476fb02 authored by Pascal Roth's avatar Pascal Roth Committed by GitHub

Allows for custom `TerrainGenerator` without modifications of the `TerrainImporter` (#2487)

# Description

Currently, the `TerrainGenerator` class is hardcoded in the
`TerrainImporter`. This class resolves this issue and allows for custom
implementations.

## Type of change

- New feature (non-breaking change which adds functionality)

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

---------
Signed-off-by: 's avatarKelly Guo <kellyg@nvidia.com>
Signed-off-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
Co-authored-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
parent 18c4718d
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.40.3" version = "0.40.4"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.40.4 (2025-06-03)
~~~~~~~~~~~~~~~~~~~
Changed
^^^^^^^
* Removes the hardcoding to :class:`~isaaclab.terrains.terrain_generator.TerrainGenerator` in
:class:`~isaaclab.terrains.terrain_generator.TerrainImporter` and instead the ``class_type`` is used which is
passed in the ``TerrainGeneratorCfg``.
0.40.3 (2025-03-20) 0.40.3 (2025-03-20)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
......
...@@ -24,10 +24,10 @@ There are two main components in this package: ...@@ -24,10 +24,10 @@ There are two main components in this package:
* :meth:`TerrainImporter.import_usd`: spawn a prim as reference to input USD file. * :meth:`TerrainImporter.import_usd`: spawn a prim as reference to input USD file.
""" """
from .height_field import * # noqa: F401, F403 from .height_field import * # noqa: F401, F403
from .sub_terrain_cfg import FlatPatchSamplingCfg, SubTerrainBaseCfg
from .terrain_generator import TerrainGenerator from .terrain_generator import TerrainGenerator
from .terrain_generator_cfg import FlatPatchSamplingCfg, SubTerrainBaseCfg, TerrainGeneratorCfg from .terrain_generator_cfg import TerrainGeneratorCfg
from .terrain_importer import TerrainImporter from .terrain_importer import TerrainImporter
from .terrain_importer_cfg import TerrainImporterCfg from .terrain_importer_cfg import TerrainImporterCfg
from .trimesh import * # noqa: F401, F403 from .trimesh import * # noqa: F401, F403
......
...@@ -12,7 +12,7 @@ from dataclasses import MISSING ...@@ -12,7 +12,7 @@ from dataclasses import MISSING
from isaaclab.utils import configclass from isaaclab.utils import configclass
from ..terrain_generator_cfg import SubTerrainBaseCfg from ..sub_terrain_cfg import SubTerrainBaseCfg
from . import hf_terrains from . import hf_terrains
......
# 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
from __future__ import annotations
import numpy as np
import trimesh
from collections.abc import Callable
from dataclasses import MISSING
from isaaclab.utils import configclass
@configclass
class FlatPatchSamplingCfg:
"""Configuration for sampling flat patches on the sub-terrain.
For a given sub-terrain, this configuration specifies how to sample flat patches on the terrain.
The sampled flat patches can be used for spawning robots, targets, etc.
Please check the function :meth:`~isaaclab.terrains.utils.find_flat_patches` for more details.
"""
num_patches: int = MISSING
"""Number of patches to sample."""
patch_radius: float | list[float] = MISSING
"""Radius of the patches.
A list of radii can be provided to check for patches of different sizes. This is useful to deal with
cases where the terrain may have holes or obstacles in some areas.
"""
x_range: tuple[float, float] = (-1e6, 1e6)
"""The range of x-coordinates to sample from. Defaults to (-1e6, 1e6).
This range is internally clamped to the size of the terrain mesh.
"""
y_range: tuple[float, float] = (-1e6, 1e6)
"""The range of y-coordinates to sample from. Defaults to (-1e6, 1e6).
This range is internally clamped to the size of the terrain mesh.
"""
z_range: tuple[float, float] = (-1e6, 1e6)
"""Allowed range of z-coordinates for the sampled patch. Defaults to (-1e6, 1e6)."""
max_height_diff: float = MISSING
"""Maximum allowed height difference between the highest and lowest points on the patch."""
@configclass
class SubTerrainBaseCfg:
"""Base class for terrain configurations.
All the sub-terrain configurations must inherit from this class.
The :attr:`size` attribute is the size of the generated sub-terrain. Based on this, the terrain must
extend from :math:`(0, 0)` to :math:`(size[0], size[1])`.
"""
function: Callable[[float, SubTerrainBaseCfg], tuple[list[trimesh.Trimesh], np.ndarray]] = MISSING
"""Function to generate the terrain.
This function must take as input the terrain difficulty and the configuration parameters and
return a tuple with a list of ``trimesh`` mesh objects and the terrain origin.
"""
proportion: float = 1.0
"""Proportion of the terrain to generate. Defaults to 1.0.
This is used to generate a mix of terrains. The proportion corresponds to the probability of sampling
the particular terrain. For example, if there are two terrains, A and B, with proportions 0.3 and 0.7,
respectively, then the probability of sampling terrain A is 0.3 and the probability of sampling terrain B
is 0.7.
"""
size: tuple[float, float] = (10.0, 10.0)
"""The width (along x) and length (along y) of the terrain (in m). Defaults to (10.0, 10.0).
In case the :class:`~isaaclab.terrains.TerrainImporterCfg` is used, this parameter gets overridden by
:attr:`isaaclab.scene.TerrainImporterCfg.size` attribute.
"""
flat_patch_sampling: dict[str, FlatPatchSamplingCfg] | None = None
"""Dictionary of configurations for sampling flat patches on the sub-terrain. Defaults to None,
in which case no flat patch sampling is performed.
The keys correspond to the name of the flat patch sampling configuration and the values are the
corresponding configurations.
"""
...@@ -8,10 +8,13 @@ ...@@ -8,10 +8,13 @@
# #
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
import numpy as np import numpy as np
import os import os
import torch import torch
import trimesh import trimesh
from typing import TYPE_CHECKING
import omni.log import omni.log
...@@ -20,11 +23,13 @@ from isaaclab.utils.io import dump_yaml ...@@ -20,11 +23,13 @@ from isaaclab.utils.io import dump_yaml
from isaaclab.utils.timer import Timer from isaaclab.utils.timer import Timer
from isaaclab.utils.warp import convert_to_warp_mesh from isaaclab.utils.warp import convert_to_warp_mesh
from .height_field import HfTerrainBaseCfg
from .terrain_generator_cfg import FlatPatchSamplingCfg, SubTerrainBaseCfg, TerrainGeneratorCfg
from .trimesh.utils import make_border from .trimesh.utils import make_border
from .utils import color_meshes_by_height, find_flat_patches from .utils import color_meshes_by_height, find_flat_patches
if TYPE_CHECKING:
from .sub_terrain_cfg import FlatPatchSamplingCfg, SubTerrainBaseCfg
from .terrain_generator_cfg import TerrainGeneratorCfg
class TerrainGenerator: class TerrainGenerator:
r"""Terrain generator to handle different terrain generation functions. r"""Terrain generator to handle different terrain generation functions.
...@@ -113,6 +118,8 @@ class TerrainGenerator: ...@@ -113,6 +118,8 @@ class TerrainGenerator:
self.device = device self.device = device
# set common values to all sub-terrains config # set common values to all sub-terrains config
from .height_field import HfTerrainBaseCfg # prevent circular import
for sub_cfg in self.cfg.sub_terrains.values(): for sub_cfg in self.cfg.sub_terrains.values():
# size of all terrains # size of all terrains
sub_cfg.size = self.cfg.size sub_cfg.size = self.cfg.size
......
...@@ -19,100 +19,25 @@ inherit from ``isaaclab.terrains.terrains_cfg.TerrainConfig`` and define the fol ...@@ -19,100 +19,25 @@ inherit from ``isaaclab.terrains.terrains_cfg.TerrainConfig`` and define the fol
from __future__ import annotations from __future__ import annotations
import numpy as np
import trimesh
from collections.abc import Callable
from dataclasses import MISSING from dataclasses import MISSING
from typing import Literal from typing import Literal
from isaaclab.utils import configclass from isaaclab.utils import configclass
from .sub_terrain_cfg import SubTerrainBaseCfg
@configclass from .terrain_generator import TerrainGenerator
class FlatPatchSamplingCfg:
"""Configuration for sampling flat patches on the sub-terrain.
For a given sub-terrain, this configuration specifies how to sample flat patches on the terrain.
The sampled flat patches can be used for spawning robots, targets, etc.
Please check the function :meth:`~isaaclab.terrains.utils.find_flat_patches` for more details.
"""
num_patches: int = MISSING
"""Number of patches to sample."""
patch_radius: float | list[float] = MISSING
"""Radius of the patches.
A list of radii can be provided to check for patches of different sizes. This is useful to deal with
cases where the terrain may have holes or obstacles in some areas.
"""
x_range: tuple[float, float] = (-1e6, 1e6)
"""The range of x-coordinates to sample from. Defaults to (-1e6, 1e6).
This range is internally clamped to the size of the terrain mesh.
"""
y_range: tuple[float, float] = (-1e6, 1e6)
"""The range of y-coordinates to sample from. Defaults to (-1e6, 1e6).
This range is internally clamped to the size of the terrain mesh.
"""
z_range: tuple[float, float] = (-1e6, 1e6)
"""Allowed range of z-coordinates for the sampled patch. Defaults to (-1e6, 1e6)."""
max_height_diff: float = MISSING
"""Maximum allowed height difference between the highest and lowest points on the patch."""
@configclass @configclass
class SubTerrainBaseCfg: class TerrainGeneratorCfg:
"""Base class for terrain configurations. """Configuration for the terrain generator."""
All the sub-terrain configurations must inherit from this class.
The :attr:`size` attribute is the size of the generated sub-terrain. Based on this, the terrain must
extend from :math:`(0, 0)` to :math:`(size[0], size[1])`.
"""
function: Callable[[float, SubTerrainBaseCfg], tuple[list[trimesh.Trimesh], np.ndarray]] = MISSING
"""Function to generate the terrain.
This function must take as input the terrain difficulty and the configuration parameters and
return a tuple with a list of ``trimesh`` mesh objects and the terrain origin.
"""
proportion: float = 1.0
"""Proportion of the terrain to generate. Defaults to 1.0.
This is used to generate a mix of terrains. The proportion corresponds to the probability of sampling
the particular terrain. For example, if there are two terrains, A and B, with proportions 0.3 and 0.7,
respectively, then the probability of sampling terrain A is 0.3 and the probability of sampling terrain B
is 0.7.
"""
size: tuple[float, float] = (10.0, 10.0)
"""The width (along x) and length (along y) of the terrain (in m). Defaults to (10.0, 10.0).
In case the :class:`~isaaclab.terrains.TerrainImporterCfg` is used, this parameter gets overridden by
:attr:`isaaclab.scene.TerrainImporterCfg.size` attribute.
"""
flat_patch_sampling: dict[str, FlatPatchSamplingCfg] | None = None class_type: type = TerrainGenerator
"""Dictionary of configurations for sampling flat patches on the sub-terrain. Defaults to None, """The class to use for the terrain generator.
in which case no flat patch sampling is performed.
The keys correspond to the name of the flat patch sampling configuration and the values are the Defaults to :class:`isaaclab.terrains.terrain_generator.TerrainGenerator`.
corresponding configurations.
""" """
@configclass
class TerrainGeneratorCfg:
"""Configuration for the terrain generator."""
seed: int | None = None seed: int | None = None
"""The seed for the random number generator. Defaults to None, in which case the seed from the """The seed for the random number generator. Defaults to None, in which case the seed from the
current NumPy's random state is used. current NumPy's random state is used.
......
...@@ -21,7 +21,6 @@ import isaaclab.sim as sim_utils ...@@ -21,7 +21,6 @@ import isaaclab.sim as sim_utils
from isaaclab.markers import VisualizationMarkers from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import FRAME_MARKER_CFG from isaaclab.markers.config import FRAME_MARKER_CFG
from .terrain_generator import TerrainGenerator
from .utils import create_prim_from_mesh from .utils import create_prim_from_mesh
if TYPE_CHECKING: if TYPE_CHECKING:
...@@ -89,7 +88,9 @@ class TerrainImporter: ...@@ -89,7 +88,9 @@ class TerrainImporter:
if self.cfg.terrain_generator is None: if self.cfg.terrain_generator is None:
raise ValueError("Input terrain type is 'generator' but no value provided for 'terrain_generator'.") raise ValueError("Input terrain type is 'generator' but no value provided for 'terrain_generator'.")
# generate the terrain # generate the terrain
terrain_generator = TerrainGenerator(cfg=self.cfg.terrain_generator, device=self.device) terrain_generator = self.cfg.terrain_generator.class_type(
cfg=self.cfg.terrain_generator, device=self.device
)
self.import_mesh("terrain", terrain_generator.terrain_mesh) self.import_mesh("terrain", terrain_generator.terrain_mesh)
# configure the terrain origins based on the terrain generator # configure the terrain origins based on the terrain generator
self.configure_env_origins(terrain_generator.terrain_origins) self.configure_env_origins(terrain_generator.terrain_origins)
......
...@@ -15,7 +15,7 @@ import isaaclab.terrains.trimesh.mesh_terrains as mesh_terrains ...@@ -15,7 +15,7 @@ import isaaclab.terrains.trimesh.mesh_terrains as mesh_terrains
import isaaclab.terrains.trimesh.utils as mesh_utils_terrains import isaaclab.terrains.trimesh.utils as mesh_utils_terrains
from isaaclab.utils import configclass from isaaclab.utils import configclass
from ..terrain_generator_cfg import SubTerrainBaseCfg from ..sub_terrain_cfg import SubTerrainBaseCfg
""" """
Different trimesh terrain configurations. Different trimesh terrain configurations.
......
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