本节摘要:静态环境 = 地形 + 建筑 + 障碍物,建模核心是"视觉精细、碰撞简化、静态冻结"三条原则。地形可用程序生成(噪声参数即可得起伏场景),建筑与货架用基本体拼装,大批障碍物靠程序化生成而非手工摆放。本节给出可运行的地形配置与仓库场景实例。
原则一:静态物体要声明静态。 <static>true</static> 告诉物理引擎"这个模型永不移动"——引擎可以把它从动力学求解中摘出去,每步省掉它的积分成本。一栋楼 200 个 link,忘了声明 static,物理引擎每步都在"确认楼没动",纯属浪费。
原则二:视觉与碰撞分离。 第 3 章说过碰撞体用基本几何体,这里给场景级的完整版本:货架视觉可以是精美网格,碰撞就是几个盒子的组合;地形视觉可以百万面,碰撞用低分辨率高度图。
原则三:规模靠生成,不靠手摆。 一个仓库场景 300 个货箱,手摆要一天、参数还不可复现;写 20 行脚本生成,改个种子就能换一批布局——这也是做泛化训练的前提。
<model name="terrain"> <static>true</static> <link name="link"> <collision name="collision"> <geometry> <heightmap> <uri>model://my_terrain/materials/textures/terrain_heightmap.png</uri> <size>100 100 10</size> <!-- 长宽100m, 高度尺度10m --> </heightmap> </geometry> </collision> <visual name="visual"> <geometry> <heightmap> <uri>model://my_terrain/materials/textures/terrain_heightmap.png</uri> <size>100 100 10</size> </heightmap> </geometry> </visual> </link> </model>
高度图是灰度 PNG:亮的像素高、暗的低。<size> 把像素值映射到米。工程要点两条:
没有真实地形数据时,程序生成即可:任何分形噪声(如简单的一维值噪声叠加四五个倍频)输出的灰度图就能当丘陵地形用,脚本十几行。研究者做地形鲁棒性实验,通常就是"噪声种子 × 坡度系数"扫参数。
一个仓库的关键元素,全部用盒子和圆柱拼:
<!-- 一排货架: static + 盒子组合, 视觉可换成网格 --> <model name="shelf_row_1"> <static>true</static> <pose>5 0 0 0 0 0</pose> <link name="link"> <collision name="c1"> <pose>0 0 1.5 0 0 0</pose> <geometry><box><size>3 1 0.1</size></box></geometry> </collision> <collision name="c2"> <pose>0 0 0.5 0 0 0</pose> <geometry><box><size>3 1 0.1</size></box></geometry> </collision> <visual name="v1"> <pose>0 0 1.0 0 0 0</pose> <geometry><box><size>3.05 1.05 3</size></box></geometry> <!-- 材质决定雷达与相机的"观感"(第5章的伏笔) --> <material><ambient>0.4 0.4 0.4 1</ambient></material> </visual> </link> </model>
| 场景元素 | 视觉 | 碰撞 | 理由 |
|---|---|---|---|
| 墙体 | 单面或薄盒 | 加厚的盒 | 防 3.3 的穿透 |
| 货架 | 精细网格 | 2-3 个盒子 | 雷达只需正确反射轮廓 |
| 货箱群 | 网格/贴图 | 每箱一个盒 | 简单稳定 |
| 柱子 | 圆柱网格 | 圆柱基本体 | 形状简单无需简化 |
| 地面 | 平面/高分辨率高度图 | 平面或低分辨率高度图 | 最低成本 |
⚠️ 常见坑:墙体碰撞体用"零厚度单面"(直接拿视觉面当碰撞),高速物体一步跨过整面墙(3.3 的穿透病因在场景侧的镜像)。碰撞墙永远给厚度,或者开启引擎的连续碰撞检测并接受性能代价。
💡 关键直觉:静态环境的"真实性"预算应该花在你的实验关心的感知通道上。导航实验,墙体轮廓的碰撞精度值钱;视觉抓取实验,材质与光照的渲染质量值钱;两者都顶配是浪费。
把上文"程序生成地形"的承诺兑现成可跑的脚本——一维值噪声叠四个倍频,输出灰度 PNG 直接喂给上面的 heightmap 配置:
# gen_terrain.py:无依赖,输出 256x256 高度图 import math, random random.seed(7) W = H = 256 grad = [random.uniform(0, 1) for _ in range(512)] def vnoise(x): # 一维值噪声,样条插值 i = int(x) % 500; f = x - int(x) a, b = grad[i], grad[i+1] t = f*f*(3-2*f) return a*(1-t) + b*t rows = [] for r in range(H): row = [] for c in range(W): h = sum(vnoise(c*2**-k) * vnoise(r*2**-k) * 0.5**k for k in range(4)) # 四倍频叠加 row.append(int(h*255)) rows.append(row) with open("terrain_heightmap.pgm", "w") as f: f.write(f"P2\n{W} {H}\n255\n") f.write("\n".join(" ".join(map(str, r)) for r in rows))
换 random.seed 即换一片山;把 2**-k 的频率系数调大,山变得更碎。配合 4.1 的世界生成器,"地形种子 + 障碍密度"两个旋钮就能扫出整个训练分布。
<size> 映射;噪声种子是批量造地形的抓手。场景建好了,还差最后一环:这些模型文件放在哪、
model://协议按什么顺序找——4.3 解决仿真工程里最"低级"却最耗时的问题。