Height Fields (HField)#
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.
Height Field Features#
Height fields offer the following advantages:
Efficient Storage: Uses regular grids to store elevation data with low memory footprint
Fast Collision Detection: Grid-based spatial partitioning algorithms provide efficient collision queries
Realistic Terrain Simulation: Supports complex terrain features such as hillsides, ravines, plains, etc.
Large-scale Scenes: Suitable for representing large terrain areas, commonly used in outdoor robot simulation
MJCF Configuration#
In MJCF files, height fields are defined using the <hfield> tag, supporting three data source methods:
1. Inline Elevation Data#
Directly specify elevation data matrix in XML:
<asset>
<hfield name="terrain1"
nrow="15"
ncol="15"
elevation="0.0 0.43 0.78 0.97 ..."
size="5 5 2 0.1"/>
</asset>
2. PNG Image Files#
Use PNG images as elevation data source (recommended for visual terrain):
<asset>
<hfield name="png_terrain"
file="terrain.png"
content_type="image/png"
size="10 10 3 0.2"/>
</asset>
Notes:
PNG images are automatically converted to grayscale
White pixels correspond to high elevation, black pixels to low elevation
Intensity values are used as elevation data and normalized to [0, 1] range
3. Custom Binary Files#
Use MuJoCo custom binary format:
<asset>
<hfield name="binary_terrain"
file="terrain.hfield"
content_type="image/vnd.mujoco.hfield"
size="8 8 2.5 0.15"/>
</asset>
Binary file format:
File size:
4 ร (2 + nrow ร ncol)bytesStructure:
int32 nrow: Number of rowsint32 ncol: Number of columnsfloat32 data[nrowรncol]: Elevation data (row-major order)
MJCF Attribute Description#
Attribute |
Type |
Default |
Description |
|---|---|---|---|
|
string |
optional |
Height field name for reference. If omitted and file name is specified, uses filename (without path and extension) |
|
string |
optional |
Currently ignored by MotrixSim |
|
string |
optional |
External file path. |
|
integer |
โ0โ |
Number of rows in elevation data matrix. Default 0 means load from file and automatically infer matrix size |
|
integer |
โ0โ |
Number of columns in elevation data matrix |
|
float array (nrowรncol) |
optional |
Inline elevation data matrix. Data automatically normalized to [0, 1] range. Defaults to 0 if not provided |
|
float array (4 elements) |
required |
Physical dimensions: |
Detailed size Attribute Description#
The size attribute contains four floats: [radius_x, radius_y, elevation_z, base_z], with each parameter meaning:
radius_x: Radius in X direction (half-width). Total width of height field on X axis is
2 ร radius_xradius_y: Radius in Y direction (half-length). Total length of height field on Y axis is
2 ร radius_yelevation_z: Maximum elevation. This value scales normalized [0-1] elevation data, so lowest point is at Z=0, highest point at Z=elevation_z
base_z: Base depth. Currently ignored by MotrixSim
Important Notes:
Height fields are centered in the local coordinate system of the referencing geometry
Elevation direction is +Z direction
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
Therefore, base parts must have non-zero thickness to avoid penetration issues
Example:
<!-- Create a 10ร10 unit height field with max elevation 2 units and base thickness 0.1 units -->
<hfield name="terrain" size="5 5 2 0.1" nrow="50" ncol="50"/>
Collision Detection Features#
Height field collision detection has the following important characteristics:
Collision Model:
Height fields are treated as unions of triangular prisms
First select potentially colliding sub-grid prisms based on geometry bounding boxes
Then use general convex colliders for precise collision calculation
Supported Collision Types:
โ Height field with sphere, capsule, cylinder, cube, polyhedron
โ Height field with plane collision (not supported)
โ Height field with other height field collision (not supported)
For more detailed hfield field descriptions, please refer to the MJCF official documentation.
Geometry Reference#
Use height fields in <worldbody>:
<worldbody>
<geom name="terrain" type="hfield" hfield="terrain1" material="ground_material"/>
<geom pos="10 0 0" name="terrain2" type="hfield" hfield="file_terrain" material="ground_material"/>
</worldbody>
Main Interfaces#
In MotrixSim, you can access HField objects through the following methods:
model.num_hfields: Get the number of height fields in the current scene.model.get_hfield(name_or_index): Get a specific height field object by name or index.
HField Object Properties#
After obtaining an HField object, you can access the following properties:
hfield = model.get_hfield("terrain1")
# Basic properties
name = hfield.name # Height field name
nrow = hfield.nrow # Number of grid rows
ncol = hfield.ncol # Number of grid columns
bound = hfield.bound # Bounding box [-x, -y, 0, x, y, z]
# Elevation data
height_matrix = hfield.height_matrix # Complete elevation matrix (nrow ร ncol)
# Query elevation at specific point
height = hfield.get(row=5, col=10) # Get elevation at specified row and column
Usage Examples#
Basic Height Field Operations#
# Get number of height fields
num_hfields = model.num_hfields
print(f"Scene contains {num_hfields} height fields")
# Get height field by name
hfield1 = model.get_hfield("terrain1")
print(f"Height field name: {hfield1.name}")
print(f"Grid size: {hfield1.nrow} ร {hfield1.ncol}")
print(f"Bounding box: {hfield1.bound}")
# Get height field by index
hfield2 = model.get_hfield(1)
print(f"Second height field name: {hfield2.name}")
# Get complete height matrix
height_matrix = hfield1.height_matrix
print(f"Height matrix shape: {height_matrix.shape}")
# Query specific height value
sample_height = hfield1.get(row=7, col=7)
print(f"Center point height: {sample_height:.3f}")
Elevation Data Analysis#
# Height data analysis
heights = hfield1.height_matrix
# Calculate statistics
min_height = np.min(heights)
max_height = np.max(heights)
mean_height = np.mean(heights)
std_height = np.std(heights)
print("\nHeight statistics:")
print(f" Min height: {min_height:.3f}")
print(f" Max height: {max_height:.3f}")
print(f" Mean height: {mean_height:.3f}")
print(f" Std deviation: {std_height:.3f}")
# Find specific elevation ranges
high_points = np.where(heights > 0.5)
print(f"\nPoints with height > 0.5: {len(high_points[0])}")
# Calculate terrain slope (simple approximation)
if heights.shape[0] > 1 and heights.shape[1] > 1:
# Calculate elevation differences between adjacent points
grad_y = np.gradient(heights, axis=0)
grad_x = np.gradient(heights, axis=1)
gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
max_gradient = np.max(gradient_magnitude)
print(f"Max slope gradient: {max_gradient:.3f}")
Complete Simulation Example#
# Copyright (C) 2020-2025 Motphys Technology Co., Ltd. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
import numpy as np
from motrixsim import SceneData, load_model, run, step
from motrixsim.render import RenderApp
def demonstrate_hfield_api(model):
"""Demonstrate height field API usage"""
print("=== Height Field API Demo ===")
# tag::basic_access
# Get number of height fields
num_hfields = model.num_hfields
print(f"Scene contains {num_hfields} height fields")
# Get height field by name
hfield1 = model.get_hfield("terrain1")
print(f"Height field name: {hfield1.name}")
print(f"Grid size: {hfield1.nrow} ร {hfield1.ncol}")
print(f"Bounding box: {hfield1.bound}")
# Get height field by index
hfield2 = model.get_hfield(1)
print(f"Second height field name: {hfield2.name}")
# Get complete height matrix
height_matrix = hfield1.height_matrix
print(f"Height matrix shape: {height_matrix.shape}")
# Query specific height value
sample_height = hfield1.get(row=7, col=7)
print(f"Center point height: {sample_height:.3f}")
# end::basic_access
# tag::height_analysis
# Height data analysis
heights = hfield1.height_matrix
# Calculate statistics
min_height = np.min(heights)
max_height = np.max(heights)
mean_height = np.mean(heights)
std_height = np.std(heights)
print("\nHeight statistics:")
print(f" Min height: {min_height:.3f}")
print(f" Max height: {max_height:.3f}")
print(f" Mean height: {mean_height:.3f}")
print(f" Std deviation: {std_height:.3f}")
# Find specific elevation ranges
high_points = np.where(heights > 0.5)
print(f"\nPoints with height > 0.5: {len(high_points[0])}")
# Calculate terrain slope (simple approximation)
if heights.shape[0] > 1 and heights.shape[1] > 1:
# Calculate elevation differences between adjacent points
grad_y = np.gradient(heights, axis=0)
grad_x = np.gradient(heights, axis=1)
gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
max_gradient = np.max(gradient_magnitude)
print(f"Max slope gradient: {max_gradient:.3f}")
# end::height_analysis
print("\n" + "=" * 50 + "\n")
def main():
# Create render window for visualization
with RenderApp() as render:
# The scene description file
path = "examples/assets/hfield.xml"
# Load the scene model
model = load_model(path)
# Demonstrate height field API
demonstrate_hfield_api(model)
# Create the render instance of the model
render.launch(model)
# Create the physics data of the model
data = SceneData(model)
# Add object to demonstrate collision with height field
# Add a test object below existing sphere
print("Starting simulation demo...")
print("Spheres will collide with height field and roll along terrain")
print("Use mouse to control view:")
print(" - Left click drag: Rotate view")
print(" - Right click drag: Pan view")
run.render_loop(model.options.timestep, 60, lambda: step(model, data), lambda: render.sync(data))
if __name__ == "__main__":
main()
Run the height field simulation example:
uv run examples/hfield.py
File Format#
MotrixSim supports binary height field file format (.hfield):
File Structure#
Header: First 8 bytes
nrow(int32): Number of grid rowsncol(int32): Number of grid columns
Data section: Remaining bytes
Elevation data array (float32), length
nrow ร ncol
Generating Height Field Files#
You can create custom height field files using the following method:
import numpy as np
def create_hfield_file(filename, height_data):
"""Create binary height field file"""
nrow, ncol = height_data.shape
with open(filename, 'wb') as f:
# Write header information
f.write(np.array([nrow, ncol], dtype=np.int32).tobytes())
# Write elevation data
f.write(height_data.astype(np.float32).tobytes())
# Example: Create simple terrain
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = 0.5 * np.sin(X) * np.cos(Y) # Sine wave terrain
create_hfield_file("custom_terrain.hfield", Z)
API Reference#
For more HField-related APIs, please refer to:
HField API: Complete height field class interface documentationSceneModel.get_hfield(): Method to get height field objectsSceneModel.num_hfields: Get number of height fields