|
| 1 | +# Height Fields (HField) |
| 2 | + |
| 3 | +Height Fields (HField) are an efficient geometry type for representing terrain surfaces. They store elevation information through 2D grid data, providing realistic physical collision responses for robot simulation, terrain following, and surface interaction. |
| 4 | + |
| 5 | +## Height Field Features |
| 6 | + |
| 7 | +Height fields offer the following advantages: |
| 8 | + |
| 9 | +- **Efficient Storage**: Uses regular grids to store elevation data with low memory footprint |
| 10 | +- **Fast Collision Detection**: Grid-based spatial partitioning algorithms provide efficient collision queries |
| 11 | +- **Realistic Terrain Simulation**: Supports complex terrain features such as hillsides, ravines, plains, etc. |
| 12 | +- **Large-scale Scenes**: Suitable for representing large terrain areas, commonly used in outdoor robot simulation |
| 13 | + |
| 14 | +## MJCF Configuration |
| 15 | + |
| 16 | +In MJCF files, height fields are defined using the `<hfield>` tag, supporting three data source methods: |
| 17 | + |
| 18 | +### 1. Inline Elevation Data |
| 19 | + |
| 20 | +Directly specify elevation data matrix in XML: |
| 21 | + |
| 22 | +```xml |
| 23 | +<asset> |
| 24 | + <hfield name="terrain1" |
| 25 | + nrow="15" |
| 26 | + ncol="15" |
| 27 | + elevation="0.0 0.43 0.78 0.97 ..." |
| 28 | + size="5 5 2 0.1"/> |
| 29 | +</asset> |
| 30 | +``` |
| 31 | + |
| 32 | +### 2. PNG Image Files |
| 33 | + |
| 34 | +Use PNG images as elevation data source (recommended for visual terrain): |
| 35 | + |
| 36 | +```xml |
| 37 | +<asset> |
| 38 | + <hfield name="png_terrain" |
| 39 | + file="terrain.png" |
| 40 | + content_type="image/png" |
| 41 | + size="10 10 3 0.2"/> |
| 42 | +</asset> |
| 43 | +``` |
| 44 | + |
| 45 | +**Notes:** |
| 46 | + |
| 47 | +- PNG images are automatically converted to grayscale |
| 48 | +- White pixels correspond to high elevation, black pixels to low elevation |
| 49 | +- Intensity values are used as elevation data and normalized to [0, 1] range |
| 50 | + |
| 51 | +### 3. Custom Binary Files |
| 52 | + |
| 53 | +Use MuJoCo custom binary format: |
| 54 | + |
| 55 | +```xml |
| 56 | +<asset> |
| 57 | + <hfield name="binary_terrain" |
| 58 | + file="terrain.hfield" |
| 59 | + content_type="image/vnd.mujoco.hfield" |
| 60 | + size="8 8 2.5 0.15"/> |
| 61 | +</asset> |
| 62 | +``` |
| 63 | + |
| 64 | +**Binary file format:** |
| 65 | + |
| 66 | +- File size: `4 × (2 + nrow × ncol)` bytes |
| 67 | +- Structure: |
| 68 | + - `int32 nrow`: Number of rows |
| 69 | + - `int32 ncol`: Number of columns |
| 70 | + - `float32 data[nrow×ncol]`: Elevation data (row-major order) |
| 71 | + |
| 72 | +### MJCF Attribute Description |
| 73 | + |
| 74 | +| Attribute | Type | Default | Description | |
| 75 | +| -------------- | ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 76 | +| `name` | string | optional | Height field name for reference. If omitted and file name is specified, uses filename (without path and extension) | |
| 77 | +| `content_type` | string | optional | **Currently ignored by MotrixSim** | |
| 78 | +| `file` | string | optional | External file path. `.png` files are converted to grayscale, intensity values used as elevation data; other formats treated as custom binary format | |
| 79 | +| `nrow` | integer | "0" | Number of rows in elevation data matrix. Default 0 means load from file and automatically infer matrix size | |
| 80 | +| `ncol` | integer | "0" | Number of columns in elevation data matrix | |
| 81 | +| `elevation` | float array (nrow×ncol) | optional | Inline elevation data matrix. Data automatically normalized to [0, 1] range. Defaults to 0 if not provided | |
| 82 | +| `size` | float array (4 elements) | required | Physical dimensions: `[radius_x, radius_y, elevation_z, base_z]` | |
| 83 | + |
| 84 | +### Detailed size Attribute Description |
| 85 | + |
| 86 | +The `size` attribute contains four floats: `[radius_x, radius_y, elevation_z, base_z]`, with each parameter meaning: |
| 87 | + |
| 88 | +- **radius_x**: Radius in X direction (half-width). Total width of height field on X axis is `2 × radius_x` |
| 89 | +- **radius_y**: Radius in Y direction (half-length). Total length of height field on Y axis is `2 × radius_y` |
| 90 | +- **elevation_z**: Maximum elevation. This value scales normalized [0-1] elevation data, so lowest point is at Z=0, highest point at Z=elevation_z |
| 91 | +- **base_z**: Base depth. **Currently ignored by MotrixSim** |
| 92 | + |
| 93 | +**Important Notes:** |
| 94 | + |
| 95 | +- Height fields are centered in the local coordinate system of the referencing geometry |
| 96 | +- Elevation direction is +Z direction |
| 97 | +- Unlike planes, height fields are treated as unions of regular geometry, with no concept of "below height field" - geometry is either inside or outside the height field |
| 98 | +- Therefore, base parts must have non-zero thickness to avoid penetration issues |
| 99 | + |
| 100 | +**Example:** |
| 101 | + |
| 102 | +```xml |
| 103 | +<!-- Create a 10×10 unit height field with max elevation 2 units and base thickness 0.1 units --> |
| 104 | +<hfield name="terrain" size="5 5 2 0.1" nrow="50" ncol="50"/> |
| 105 | +``` |
| 106 | + |
| 107 | +### Collision Detection Features |
| 108 | + |
| 109 | +Height field collision detection has the following important characteristics: |
| 110 | + |
| 111 | +**Collision Model:** |
| 112 | + |
| 113 | +- Height fields are treated as unions of triangular prisms |
| 114 | +- First select potentially colliding sub-grid prisms based on geometry bounding boxes |
| 115 | +- Then use general convex colliders for precise collision calculation |
| 116 | + |
| 117 | +**Supported Collision Types:** |
| 118 | + |
| 119 | +- ✅ Height field with sphere, capsule, cylinder, cube, polyhedron |
| 120 | +- ❌ Height field with plane collision (not supported) |
| 121 | +- ❌ Height field with other height field collision (not supported) |
| 122 | + |
| 123 | +For more detailed hfield field descriptions, please refer to the [MJCF official documentation](https://mujoco.readthedocs.io/en/3.3.7/XMLreference.html#asset-hfield). |
| 124 | + |
| 125 | +### Geometry Reference |
| 126 | + |
| 127 | +Use height fields in `<worldbody>`: |
| 128 | + |
| 129 | +```xml |
| 130 | +<worldbody> |
| 131 | + <geom name="terrain" type="hfield" hfield="terrain1" material="ground_material"/> |
| 132 | + <geom pos="10 0 0" name="terrain2" type="hfield" hfield="file_terrain" material="ground_material"/> |
| 133 | +</worldbody> |
| 134 | +``` |
| 135 | + |
| 136 | +## Main Interfaces |
| 137 | + |
| 138 | +In MotrixSim, you can access HField objects through the following methods: |
| 139 | + |
| 140 | +- [`model.num_hfields`]: Get the number of height fields in the current scene. |
| 141 | +- [`model.get_hfield(name_or_index)`]: Get a specific height field object by name or index. |
| 142 | + |
| 143 | +### HField Object Properties |
| 144 | + |
| 145 | +After obtaining an HField object, you can access the following properties: |
| 146 | + |
| 147 | +```python |
| 148 | +hfield = model.get_hfield("terrain1") |
| 149 | + |
| 150 | +# Basic properties |
| 151 | +name = hfield.name # Height field name |
| 152 | +nrow = hfield.nrow # Number of grid rows |
| 153 | +ncol = hfield.ncol # Number of grid columns |
| 154 | +bound = hfield.bound # Bounding box [-x, -y, 0, x, y, z] |
| 155 | + |
| 156 | +# Elevation data |
| 157 | +height_matrix = hfield.height_matrix # Complete elevation matrix (nrow × ncol) |
| 158 | + |
| 159 | +# Query elevation at specific point |
| 160 | +height = hfield.get(row=5, col=10) # Get elevation at specified row and column |
| 161 | +``` |
| 162 | + |
| 163 | +## Usage Examples |
| 164 | + |
| 165 | +### Basic Height Field Operations |
| 166 | + |
| 167 | +```{literalinclude} ../../../../../examples/hfield.py |
| 168 | +:language: python |
| 169 | +:dedent: |
| 170 | +:start-after: "# tag::basic_access" |
| 171 | +:end-before: "# end::basic_access" |
| 172 | +``` |
| 173 | + |
| 174 | +### Elevation Data Analysis |
| 175 | + |
| 176 | +```{literalinclude} ../../../../../examples/hfield.py |
| 177 | +:language: python |
| 178 | +:dedent: |
| 179 | +:start-after: "# tag::height_analysis" |
| 180 | +:end-before: "# end::height_analysis" |
| 181 | +``` |
| 182 | + |
| 183 | +### Complete Simulation Example |
| 184 | + |
| 185 | +```{literalinclude} ../../../../../examples/hfield.py |
| 186 | +:language: python |
| 187 | +:dedent: |
| 188 | +``` |
| 189 | + |
| 190 | +Run the height field simulation example: |
| 191 | + |
| 192 | +```bash |
| 193 | +uv run examples/hfield.py |
| 194 | +``` |
| 195 | + |
| 196 | +## File Format |
| 197 | + |
| 198 | +MotrixSim supports binary height field file format (`.hfield`): |
| 199 | + |
| 200 | +### File Structure |
| 201 | + |
| 202 | +- **Header**: First 8 bytes |
| 203 | + - `nrow` (int32): Number of grid rows |
| 204 | + - `ncol` (int32): Number of grid columns |
| 205 | +- **Data section**: Remaining bytes |
| 206 | + - Elevation data array (float32), length `nrow × ncol` |
| 207 | + |
| 208 | +### Generating Height Field Files |
| 209 | + |
| 210 | +You can create custom height field files using the following method: |
| 211 | + |
| 212 | +```python |
| 213 | +import numpy as np |
| 214 | + |
| 215 | +def create_hfield_file(filename, height_data): |
| 216 | + """Create binary height field file""" |
| 217 | + nrow, ncol = height_data.shape |
| 218 | + |
| 219 | + with open(filename, 'wb') as f: |
| 220 | + # Write header information |
| 221 | + f.write(np.array([nrow, ncol], dtype=np.int32).tobytes()) |
| 222 | + # Write elevation data |
| 223 | + f.write(height_data.astype(np.float32).tobytes()) |
| 224 | + |
| 225 | +# Example: Create simple terrain |
| 226 | +x = np.linspace(-5, 5, 50) |
| 227 | +y = np.linspace(-5, 5, 50) |
| 228 | +X, Y = np.meshgrid(x, y) |
| 229 | +Z = 0.5 * np.sin(X) * np.cos(Y) # Sine wave terrain |
| 230 | + |
| 231 | +create_hfield_file("custom_terrain.hfield", Z) |
| 232 | +``` |
| 233 | + |
| 234 | +## API Reference |
| 235 | + |
| 236 | +For more HField-related APIs, please refer to: |
| 237 | + |
| 238 | +- [`HField API`]: Complete height field class interface documentation |
| 239 | +- [`SceneModel.get_hfield()`]: Method to get height field objects |
| 240 | +- [`SceneModel.num_hfields`]: Get number of height fields |
| 241 | + |
| 242 | +[`model.num_hfields`]: motrixsim.SceneModel.num_hfields |
| 243 | +[`model.get_hfield(name_or_index)`]: motrixsim.SceneModel.get_hfield |
| 244 | +[`HField API`]: motrixsim.HField |
| 245 | +[`SceneModel.get_hfield()`]: motrixsim.SceneModel.get_hfield |
| 246 | +[`SceneModel.num_hfields`]: motrixsim.SceneModel.num_hfields |
0 commit comments