高度场(Height Field)#
高度场(Height Field,简称 HField)是一种用于表示地形表面的高效几何体类型。它通过二维网格数据存储高程信息,为机器人仿真、地形跟随和表面交互提供真实的物理碰撞响应。
高度场特点#
高度场具有以下优势:
高效存储:使用规则网格存储高程数据,内存占用较小
快速碰撞检测:基于网格的空间分割算法,提供高效的碰撞查询
真实地形模拟:支持复杂的地形特征,如山坡、沟壑、平原等
大规模场景:适合表示大面积地形,常用于户外机器人仿真
MJCF 配置#
在 MJCF 文件中,高度场通过 <hfield> 标签定义,支持三种数据来源方式:
1. 内联高程数据#
直接在 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 图像文件#
使用 PNG 图像作为高程数据源(推荐用于可视化地形):
<asset>
<hfield name="png_terrain"
file="terrain.png"
content_type="image/png"
size="10 10 3 0.2"/>
</asset>
说明:
PNG 图像会被自动转换为灰度图像
白色像素对应高程,黑色像素对应低程
强度值用作高程数据并标准化到 [0, 1] 范围
3. 自定义二进制文件#
使用 MuJoCo 自定义二进制格式:
<asset>
<hfield name="binary_terrain"
file="terrain.hfield"
content_type="image/vnd.mujoco.hfield"
size="8 8 2.5 0.15"/>
</asset>
二进制文件格式:
文件大小:
4 × (2 + nrow × ncol)字节结构:
int32 nrow:行数int32 ncol:列数float32 data[nrow×ncol]:高程数据(行主序)
MJCF 属性说明#
属性 |
类型 |
默认值 |
说明 |
|---|---|---|---|
|
字符串 |
可选 |
高度场的名称,用于引用。如果省略且指定了文件名,则使用文件名(不含路径和扩展名) |
|
字符串 |
可选 |
MotrixSim 目前忽略此属性 |
|
字符串 |
可选 |
外部文件路径。 |
|
整数 |
"0" |
高程数据矩阵的行数。默认值 0 表示从文件加载数据并自动推断矩阵大小 |
|
整数 |
"0" |
高程数据矩阵的列数 |
|
浮点数组 (nrow×ncol) |
可选 |
内联高程数据矩阵。数据会自动标准化到 [0, 1] 范围。未提供时默认为 0 |
|
浮点数组 (4 个元素) |
必需 |
物理尺寸: |
size 属性详细说明#
size 属性包含四个浮点数:[radius_x, radius_y, elevation_z, base_z],每个参数的含义如下:
radius_x:X 方向的半径(半宽)。高度场在 X 轴上的总宽度为
2 × radius_xradius_y:Y 方向的半径(半长)。高度场在 Y 轴上的总长度为
2 × radius_yelevation_z:最大高程。这个值缩放标准化到 [0-1] 的高程数据,因此最低点在 Z=0,最高点在 Z=elevation_z
base_z:基础深度。MotrixSim目前忽略此属性
重要说明:
高度场以引用几何体的局部坐标系为中心
高程方向为 +Z 方向
与平面不同,高度场被视为常规几何体的联合,没有"在高度场下方"的概念,几何体要么在高度场内部,要么在外部
因此基础部分必须有非零厚度以避免穿透问题
示例:
<!-- 创建一个 10×10 单位、最大高程 2 单位、基础厚度 0.1 单位的高度场 -->
<hfield name="terrain" size="5 5 2 0.1" nrow="50" ncol="50"/>
碰撞检测特性#
高度场的碰撞检测具有以下重要特性:
碰撞模型:
高度场被视为三角棱柱的联合体
首先根据几何体边界框选择可能碰撞的子网格棱柱
然后使用通用凸碰撞器进行精确碰撞计算
支持的碰撞类型:
✅ 高度场与球体、胶囊体、圆柱体、立方体、多面体
❌ 高度场与平面的碰撞(不支持)
❌ 高度场与其他高度场的碰撞(不支持)
更详细的 hfield 字段说明,请参考MJCF 官方文档。
几何体引用#
在 <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>
主要接口#
在 MotrixSim 中,您可以通过以下方式访问 HField 对象:
model.num_hfields: 获取当前场景中的高度场数量。model.get_hfield(name_or_index): 根据名称或索引获取特定的高度场对象。
HField 对象属性#
获取到 HField 对象后,可以访问以下属性:
hfield = model.get_hfield("terrain1")
# 基本属性
name = hfield.name # 高度场名称
nrow = hfield.nrow # 网格行数
ncol = hfield.ncol # 网格列数
bound = hfield.bound # 边界框 [-x, -y, 0, x, y, z]
# 高程数据
height_matrix = hfield.height_matrix # 完整的高程矩阵 (nrow × ncol)
# 查询特定点的高程值
height = hfield.get(row=5, col=10) # 获取指定行列的高程
使用示例#
基本高度场操作#
# 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}")
高程数据分析#
# 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}")
完整仿真示例#
# 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 time
import numpy as np
from motrixsim import SceneData, load_model, 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
render = RenderApp()
# 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")
while True:
# Control the step interval to prevent too fast simulation
time.sleep(0.02)
# Physics world step
step(model, data)
# Sync render objects from physic world
render.sync(data)
if __name__ == "__main__":
main()
运行高度场仿真示例:
uv run examples/hfield.py
文件格式#
MotrixSim 支持二进制高度场文件格式(.hfield):
文件结构#
头部:前 8 字节
nrow(int32):网格行数ncol(int32):网格列数
数据部分:剩余字节
高程数据数组 (float32),长度为
nrow × ncol
生成高度场文件#
您可以使用以下方式创建自定义高度场文件:
import numpy as np
def create_hfield_file(filename, height_data):
"""创建二进制高度场文件"""
nrow, ncol = height_data.shape
with open(filename, 'wb') as f:
# 写入头部信息
f.write(np.array([nrow, ncol], dtype=np.int32).tobytes())
# 写入高程数据
f.write(height_data.astype(np.float32).tobytes())
# 示例:创建简单的地形
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) # 正弦波形地形
create_hfield_file("custom_terrain.hfield", Z)
API Reference#
更多与 HField 相关的 API,请参考:
HField API: 完整的高度场类接口文档SceneModel.get_hfield(): 获取高度场对象的方法SceneModel.num_hfields: 获取高度场数量