Skip to content

Commit 2ca11b4

Browse files
committed
implement corridor with turns terrain config
1 parent d23d01d commit 2ca11b4

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

exts/nav_tasks/nav_tasks/terrains/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# SPDX-License-Identifier: Apache-2.0
55

66
from .corridor_cfg import CorridorTerrainCfg
7+
from .corridor_turn_cfg import CorridorTurnTerrainCfg
78
from .maze_terrain_cfg import MazeTerrainCfg
89
from .pillar_terrain_cfg import MeshPillarPlannerTestTerrainCfg, MeshPillarTerrainCfg, MeshPillarTerrainDeterministicCfg
910
from .quad_stairs_terrain_cfg import MeshQuadPyramidStairsCfg
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Copyright (c) 2025, The Nav-Suite Project Developers (https://github.com/leggedrobotics/nav-suite/blob/main/CONTRIBUTORS.md).
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
"""Functions to generate different terrains using the ``trimesh`` library."""
7+
8+
from __future__ import annotations
9+
10+
import numpy as np
11+
import trimesh
12+
from typing import TYPE_CHECKING
13+
14+
from isaaclab.terrains.trimesh.utils import * # noqa: F401, F403
15+
from isaaclab.terrains.trimesh.utils import make_border, make_plane
16+
17+
if TYPE_CHECKING:
18+
from . import corridor_turn_cfg
19+
20+
21+
# Helper for wall creation
22+
def wall(orientation, start, end, height, thickness):
23+
24+
center = ((np.array(start) + np.array(end)) / 2).tolist()
25+
if orientation == "vertical":
26+
length = np.abs(start[0] - end[0])
27+
extents = [length, thickness, height]
28+
elif orientation == "horizontal":
29+
length = np.abs(start[1] - end[1])
30+
extents = [thickness, length, height]
31+
32+
center[2] = height / 2
33+
return trimesh.creation.box(extents=extents, transform=trimesh.transformations.translation_matrix(center))
34+
35+
36+
def corridor_turn_terrain(difficulty: float, cfg: corridor_turn_cfg.CorridorTurnTerrainCfg):
37+
"""Generate a corridor with a 90 or 180 degree turn."""
38+
# get the width of the corridor
39+
width = np.random.uniform(cfg.width_range[0], cfg.width_range[1])
40+
# get the terrain center
41+
terrain_center = [0.5 * cfg.size[0], 0.5 * cfg.size[1], 0.0]
42+
# initialize list of meshes
43+
meshes_list = list()
44+
# generate a ground plane for the terrain
45+
ground_plane = make_plane(cfg.size, height=0.0, center_zero=False)
46+
meshes_list.append(ground_plane)
47+
48+
# generate the border if needed
49+
if cfg.border_width > 0.0:
50+
# obtain a list of meshes for the border
51+
border_center = [0.5 * cfg.size[0], 0.5 * cfg.size[1], 0]
52+
border_inner_size = (cfg.size[0] - 2 * cfg.border_width, cfg.size[1] - 2 * cfg.border_width)
53+
make_borders = make_border(cfg.size, border_inner_size, 0.1, border_center)
54+
# add the border meshes to the list of meshes
55+
meshes_list += make_borders
56+
57+
t = cfg.wall_thickness
58+
h = cfg.wall_height
59+
60+
if cfg.turn_type == "90":
61+
# Ensure the size of the terrain is sufficient for the corridor and turn
62+
if cfg.size[0] / 2 < width / 2 or cfg.size[1] / 2 < width / 2:
63+
print(
64+
"Terrain size is too small for the specified corridor width and a 90 degree turn. Adjusting parameters"
65+
" (terrain may not be useful)."
66+
)
67+
if cfg.size[0] / 2 < width / 2:
68+
width = cfg.size[0] - 0.1
69+
if cfg.size[1] / 2 < width / 2:
70+
width = cfg.size[1] - 0.1
71+
# L-shaped corridor: horizontal then vertical
72+
# Horizontal segments
73+
meshes_list.append(
74+
wall(
75+
"horizontal",
76+
[terrain_center[0] + width / 2, terrain_center[1] - width / 2, 0],
77+
[terrain_center[1] + width / 2, cfg.size[1], 0],
78+
h,
79+
t,
80+
)
81+
)
82+
meshes_list.append(
83+
wall(
84+
"horizontal",
85+
[terrain_center[0] - width / 2, terrain_center[1] + width / 2, 0],
86+
[terrain_center[0] - width / 2, cfg.size[1], 0],
87+
h,
88+
t,
89+
)
90+
)
91+
# Vertical segments
92+
meshes_list.append(
93+
wall(
94+
"vertical",
95+
[terrain_center[0] + width / 2, terrain_center[1] - width / 2, 0],
96+
[0, terrain_center[1] - width / 2, 0],
97+
h,
98+
t,
99+
)
100+
)
101+
meshes_list.append(
102+
wall(
103+
"vertical",
104+
[terrain_center[0] - width / 2, terrain_center[1] + width / 2, 0],
105+
[0, terrain_center[1] + width / 2, 0],
106+
h,
107+
t,
108+
)
109+
)
110+
elif cfg.turn_type == "180":
111+
# U-shaped corridor: two verticals and a horizontal at the top
112+
u_top_width = np.random.uniform(cfg.u_top_width_range[0], cfg.u_top_width_range[1])
113+
# Ensure the size of the terrain is sufficient for the corridor and turn
114+
if cfg.size[0] / 2 < width or cfg.size[1] / 2 < width + u_top_width / 2:
115+
print(
116+
"Terrain size is too small for the specified corridor width and a 180 degree turn. Adjusting parameters"
117+
" (terrain may not be useful)."
118+
)
119+
if cfg.size[0] / 2 < width:
120+
width = cfg.size[0] / 2 - 0.1
121+
if cfg.size[1] / 2 < width + u_top_width / 2:
122+
u_top_width = 2 * (cfg.size[1] / 2 - width) - 0.1
123+
# U top segments
124+
meshes_list.append(
125+
wall(
126+
"horizontal",
127+
[terrain_center[0], terrain_center[1] - u_top_width / 2, 0],
128+
[terrain_center[0], terrain_center[1] + u_top_width / 2, 0],
129+
h,
130+
t,
131+
)
132+
)
133+
meshes_list.append(
134+
wall(
135+
"horizontal",
136+
[terrain_center[0] + width, terrain_center[1] - u_top_width / 2 - width, 0],
137+
[terrain_center[0] + width, terrain_center[1] + u_top_width / 2 + width, 0],
138+
h,
139+
t,
140+
)
141+
)
142+
# Right segments
143+
meshes_list.append(
144+
wall(
145+
"vertical",
146+
[terrain_center[0], terrain_center[1] - u_top_width / 2, 0],
147+
[0, terrain_center[1] - u_top_width / 2, 0],
148+
h,
149+
t,
150+
)
151+
)
152+
meshes_list.append(
153+
wall(
154+
"vertical",
155+
[terrain_center[0] + width, terrain_center[1] - u_top_width / 2 - width, 0],
156+
[0, terrain_center[1] - u_top_width / 2 - width, 0],
157+
h,
158+
t,
159+
)
160+
)
161+
# Vertical segments
162+
meshes_list.append(
163+
wall(
164+
"vertical",
165+
[terrain_center[0], terrain_center[1] + u_top_width / 2, 0],
166+
[0, terrain_center[1] + u_top_width / 2, 0],
167+
h,
168+
t,
169+
)
170+
)
171+
meshes_list.append(
172+
wall(
173+
"vertical",
174+
[terrain_center[0] + width, terrain_center[1] + u_top_width / 2 + width, 0],
175+
[0, terrain_center[1] + u_top_width / 2 + width, 0],
176+
h,
177+
t,
178+
)
179+
)
180+
181+
# Center wall between the two inner vertical segments
182+
meshes_list.append(
183+
wall("vertical", [terrain_center[0] - width, terrain_center[1], 0], [0, terrain_center[1], 0], h, t)
184+
)
185+
else:
186+
raise ValueError("turn_type must be '90' or '180'")
187+
188+
# compute the origin of the terrain
189+
origin = np.array([terrain_center[0], terrain_center[1], terrain_center[2]])
190+
return meshes_list, origin
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright (c) 2025, The Nav-Suite Project Developers (https://github.com/leggedrobotics/nav-suite/blob/main/CONTRIBUTORS.md).
2+
# All rights reserved.
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
from isaaclab.terrains import SubTerrainBaseCfg
7+
from isaaclab.utils import configclass
8+
9+
from .corridor_turn import corridor_turn_terrain
10+
11+
12+
@configclass
13+
class CorridorTurnTerrainCfg(SubTerrainBaseCfg):
14+
"""Configuration for a terrain with corridors and 90/180 degree turns."""
15+
16+
function = corridor_turn_terrain
17+
"""Function to generate the terrain."""
18+
19+
size: tuple[float, float] = (10.0, 10.0)
20+
"""Size of the terrain in meters."""
21+
22+
wall_height: float = 2.5
23+
"""Height of the corridor walls."""
24+
25+
wall_thickness: float = 0.2
26+
"""Thickness of the corridor walls."""
27+
28+
width_range: tuple[float, float] = (2.5, 5.0)
29+
"""The minimum and maximum width of the corridor (in m)."""
30+
31+
u_top_width_range: tuple[float, float] = (1.5, 3.0)
32+
"""The minimum and maximum width of the top part of the U-shape corridor (in m)."""
33+
34+
turn_type: str = "90"
35+
"""Type of turn: '90' for L-shape, '180' for U-shape."""
36+
37+
border_width: float = 0.0
38+
"""The width of the border around the terrain (in m). Defaults to 0.0.
39+
40+
The border is a flat terrain with the same height as the terrain.
41+
"""
42+
43+
# Optionally, add more parameters as needed

0 commit comments

Comments
 (0)