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]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.40.3"
version = "0.40.4"
# Description
title = "Isaac Lab framework for Robot Learning"
......
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)
~~~~~~~~~~~~~~~~~~~
......
......@@ -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.
"""
from .height_field import * # noqa: F401, F403
from .sub_terrain_cfg import FlatPatchSamplingCfg, SubTerrainBaseCfg
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_cfg import TerrainImporterCfg
from .trimesh import * # noqa: F401, F403
......
......@@ -12,7 +12,7 @@ from dataclasses import MISSING
from isaaclab.utils import configclass
from ..terrain_generator_cfg import SubTerrainBaseCfg
from ..sub_terrain_cfg import SubTerrainBaseCfg
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 @@
#
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
import numpy as np
import os
import torch
import trimesh
from typing import TYPE_CHECKING
import omni.log
......@@ -20,11 +23,13 @@ from isaaclab.utils.io import dump_yaml
from isaaclab.utils.timer import Timer
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 .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:
r"""Terrain generator to handle different terrain generation functions.
......@@ -113,6 +118,8 @@ class TerrainGenerator:
self.device = device
# 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():
# size of all terrains
sub_cfg.size = self.cfg.size
......
......@@ -19,100 +19,25 @@ inherit from ``isaaclab.terrains.terrains_cfg.TerrainConfig`` and define the fol
from __future__ import annotations
import numpy as np
import trimesh
from collections.abc import Callable
from dataclasses import MISSING
from typing import Literal
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."""
from .sub_terrain_cfg import SubTerrainBaseCfg
from .terrain_generator import TerrainGenerator
@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.
"""
class TerrainGeneratorCfg:
"""Configuration for the terrain generator."""
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.
class_type: type = TerrainGenerator
"""The class to use for the terrain generator.
The keys correspond to the name of the flat patch sampling configuration and the values are the
corresponding configurations.
Defaults to :class:`isaaclab.terrains.terrain_generator.TerrainGenerator`.
"""
@configclass
class TerrainGeneratorCfg:
"""Configuration for the terrain generator."""
seed: int | None = None
"""The seed for the random number generator. Defaults to None, in which case the seed from the
current NumPy's random state is used.
......
......@@ -21,7 +21,6 @@ import isaaclab.sim as sim_utils
from isaaclab.markers import VisualizationMarkers
from isaaclab.markers.config import FRAME_MARKER_CFG
from .terrain_generator import TerrainGenerator
from .utils import create_prim_from_mesh
if TYPE_CHECKING:
......@@ -89,7 +88,9 @@ class TerrainImporter:
if self.cfg.terrain_generator is None:
raise ValueError("Input terrain type is 'generator' but no value provided for 'terrain_generator'.")
# 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)
# configure the terrain origins based on the terrain generator
self.configure_env_origins(terrain_generator.terrain_origins)
......
......@@ -15,7 +15,7 @@ import isaaclab.terrains.trimesh.mesh_terrains as mesh_terrains
import isaaclab.terrains.trimesh.utils as mesh_utils_terrains
from isaaclab.utils import configclass
from ..terrain_generator_cfg import SubTerrainBaseCfg
from ..sub_terrain_cfg import SubTerrainBaseCfg
"""
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