4.2 静态环境构建


4.2 静态环境构建

本节摘要:静态环境 = 地形 + 建筑 + 障碍物,建模核心是"视觉精细、碰撞简化、静态冻结"三条原则。地形可用程序生成(噪声参数即可得起伏场景),建筑与货架用基本体拼装,大批障碍物靠程序化生成而非手工摆放。本节给出可运行的地形配置与仓库场景实例。

三条原则先立起来

原则一:静态物体要声明静态。 <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> 把像素值映射到米。工程要点两条:

  • 碰撞与视觉用同一张图但不同分辨率。Gazebo 的 heightmap 天然用低采样做碰撞、高采样做渲染,原则二自动满足。
  • 跨度与分辨率的账要算清。100m 跨度配 512×512 高度图,每格约 0.2m——轮式机器人在上面跑会有可感知的颠簸;若你的实验关注的是导航而非振动,把地形低通滤波一下再导出,别让轮子替高频噪声买单。

没有真实地形数据时,程序生成即可:任何分形噪声(如简单的一维值噪声叠加四五个倍频)输出的灰度图就能当丘陵地形用,脚本十几行。研究者做地形鲁棒性实验,通常就是"噪声种子 × 坡度系数"扫参数。

室内场景:基本体的拼装艺术

一个仓库的关键元素,全部用盒子和圆柱拼:

<!-- 一排货架: 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 的世界生成器,"地形种子 + 障碍密度"两个旋钮就能扫出整个训练分布。

本节要点回顾

  • 三原则:static 声明冻结、视觉/碰撞分离、批量靠程序生成。
  • 地形即高度图:灰度 PNG + <size> 映射;噪声种子是批量造地形的抓手。
  • 室内拼基本体:货架两三个盒子、墙体加厚度,碰撞轮廓对就够用。
  • 粒度跟实验走:导航关心碰撞轮廓,视觉任务关心材质光照,别两头都顶配。

场景建好了,还差最后一环:这些模型文件放在哪、model:// 协议按什么顺序找——4.3 解决仿真工程里最"低级"却最耗时的问题。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U