Genesis 如何决定你机器人的形状:URDF 凸分解管线实战指南
2026/9/13 23:26:48 网站建设 项目流程

Genesis 如何决定你机器人的形状:URDF 凸分解管线实战指南

【免费下载链接】genesis-worldSimulation platform for general-purpose robotics & embodied AI learning.项目地址: https://gitcode.com/GitHub_Trending/genesi/genesis-world

Genesis 是一个面向通用机器人与具身智能的仿真平台,而它处理非凸网格的第一道工序,就是凸分解:把 URDF、MJCF 或 OBJ 里那些带凹槽、带镂空、带细长结构的碰撞网格,自动拆成一组凸块。这个过程发生在scene.add_entity()之后、scene.build()之前,你在代码里几乎感觉不到它的存在——但它决定了你的机器人到底用几个凸块参与碰撞,也决定了第一次加载资产要等几秒钟还是几十秒。

好消息是:整条管线都是自动决策的。你不需要手写碰撞几何,只需要在几个参数上做出正确选择。下面沿着管线实际走过的顺序,把每个决策点讲清楚。

先建立心智模型:一条网格的三种命运

在 genesis/utils/mesh.py 的postprocess_collision_geoms()中,每个网格文件被添加进场景后都会经过同一组检查,最终落到三条路径之一:

  1. 放行:网格本身水密(watertight)、凸且绕向一致,被判定为 authored(作者手工提供的干净几何),跳过一切处理;
  2. 取壳:只包一个凸包(convex hull),用体积误差判断这个壳"够不够像"原模型;
  3. 分解:误差超阈值时,调用 CoACD 算法把网格切成多个凸块。

另有一条岔路:convexify=False时完全不碰凸性,转而做水密包裹并走 SDF 距离场路线。下面逐个检查站说明。

检查站一:它已经"干净"了吗

管线第一件事是廉价地验证三个属性:is_winding_consistent(面片绕向一致)、is_watertight(封闭)、is_convex(凸)。三者全过,直接视为 authored 几何,后续取壳、分解、减面全部跳过。

这也解释了 examples/rigid/authored_decomp.py 这个样例的意图:它用 genesis/assets/tower/ 里手工制作的立柱、圆环、球体 URDF 搭一座塔,每个碰撞网格都满足上述条件,所以整座塔的构建几乎零开销。你可以用vis_mode="collision"参数确认看到的碰撞网格就是原始网格本身。

检查站二:单个凸包能否通过体积检验

不是 authored 的网格,在convexify=True(刚体实体的默认值)下,管线会先试最便宜的办法——取整个网格的凸包,然后做一次体积对账:

cmesh = trimesh.convex.convex_hull(tmesh) volume_err = abs(cmesh.volume / abs(tmesh.volume) - 1.0) if volume_err > decompose_error_threshold: # 需要凸分解

逻辑很直白:甜甜圈(torus)的凸包体积远大于本体,带凹槽的零件同样如此,volume_err大就意味着"一个壳"严重失真。阈值来自两处参数(见 genesis/options/morphs.py 的FileMorph):

  • decompose_object_error_threshold:普通刚体对象,默认0.15(15%);
  • decompose_robot_error_threshold:多关节机器人,默认inf,即默认跳过分解、只取壳。

这个默认值差异值得记住:一把 URDF 机械臂的每个连杆默认只会得到一个凸包,而不是多个凸块。想让某个连杆被精细分解,需要显式调低机器人阈值;反过来,给普通物体设float("inf")也能强制跳过分解。

检查站三:CoACD 负责切块

真正触发分解后,convex_decompose()会调用第三方coacd库,参数由gs.options.CoacdOptions控制(定义在 genesis/options/misc.py):

result = coacd.run_coacd( mesh, threshold=args.threshold, # 默认 0.1 max_convex_hull=args.max_convex_hull, mcts_nodes=args.mcts_nodes, # 默认 20 mcts_iterations=args.mcts_iterations, # 默认 100 mcts_max_depth=args.mcts_max_depth, # 默认 3 merge=args.merge, # 默认 True )

源码注释里给了唯一一条量化经验:threshold 减半,凸包数量约翻倍。这是调精度/数量平衡时最直接的旋钮。其余几个要点:

  • mcts_*一组是蒙特卡洛树搜索(MCTS)的参数,控制切分搜索的节点数、迭代次数与最大深度;
  • max_convex_hull=-1表示不限制凸块总数;
  • pca=True会被直接拒绝,因为上游 CoACD 存在相关 bug,Genesis 在选项校验层就把这条路封死了。

切完的结果不是用一次就扔:convex_decompose()会把凸块序列化为.cvx文件写入磁盘缓存,并且缓存键用的是归一化到单位尺度的顶点——同一个资产以不同scale添加时共享同一条缓存记录,取出时再按比例放大。

另一条路:convexify=False 时发生什么

如果你希望保留网格的真实非凸轮廓(代价是走网格 SDF 路线),把convexify设为False,管线改走另一套流程:

  • 先对非凸碰撞网格做顶点焊接(weld)与重复面修复,专门处理 STL 这类不共享顶点的"顶点汤"文件;
  • 多个碰撞几何若共享contype/conaffinity与接触参数,会被融合成单一封闭曲面,减少后续扫描的顶点量;
  • 最后按watertighten(0–8,默认 5)做水密包裹,把开口或自交的表面补成 SDF 能可靠工作的封闭体。

examples/rigid/nonconvex_mesh.py 演示了这条路径:一辆低配坦克网格(meshes/tank.obj)保持非凸,一颗小球从 1 米高度落下,观察碰撞表现。

动手:用示例脚本复现分解结果

examples/rigid/convex_decomposition.py 是最直观的观测入口——把固定网格和四个 MJCF 资产(甜甜圈、马克杯、杯子、苹果)都设成vis_mode="collision",启动后直接看到每个物体的碰撞表现:哪些只剩一个壳,哪些被切成了多个凸块。

gs.init(backend=gs.gpu if args.gpu else gs.cpu, precision="32", seed=0) scene = gs.Scene(rigid_options=gs.options.RigidOptions(dt=0.01), show_viewer=args.vis) scene.add_entity( gs.morphs.Mesh(file="meshes/tank.obj", scale=5.0, fixed=True, euler=(80, 10, 90), pos=(0.05, -0.1, 0.0)), vis_mode="collision", ) scene.build() for i in range(2000): scene.step()

需要本地源码时:

git clone https://gitcode.com/GitHub_Trending/genesi/genesis-world

两层缓存:为什么第二次添加几乎免费

管线里最贵的两步(文件解析、取壳/分解)各有一层记忆化:

  • 磁盘层:解析结果按文件修改时间缓存(_load_trimesh_scene_cached,LRU 上限 32),分解结果按几何内容缓存为.cvx文件;
  • 内存层_COLLISION_GEOMS_CACHE是一个按实际占用封顶512 MiB的缓存,键覆盖了顶点/面数据、convexify、误差阈值、CoacdOptions全量配置以及摩擦、密度等每几何体字段。

实际含义:搭建 100 个相同马克杯的批量场景时,分解只在第一个杯子上发生,后面 99 个共享同一份处理结果,内存占用也被折叠。改资产文件后缓存会自动失效,不需要手动清理。

凸分解常用参数速查表

参数默认值作用
convexifyTrue(刚体)碰撞网格是否转成凸表示
decompose_object_error_threshold0.15普通对象:凸包体积偏差超过 15% 即分解
decompose_robot_error_thresholdinf关节机器人:默认不分解,只取凸包
decimate_face_num500减面目标面数
watertighten5非凸路径水密包裹强度(0–8)
CoacdOptions.threshold0.1近似容差;减半 → 凸块约翻倍
CoacdOptions.max_convex_hull-1凸块总数上限,-1 不限
CoacdOptions.mcts_iterations100MCTS 搜索迭代次数

下一步

  • 想改管线本身:从 genesis/utils/mesh.py 的postprocess_collision_geoms()读起,它是所有决策的分发点;
  • 想验证行为是否符合预期:跑 tests/rigid/test_collision_nonconvex.py 与 tests/rigid/test_collision.py,它们覆盖了非凸碰撞的各种情形;
  • 想做自己的 authored 资产:参考 genesis/assets/tower/generate_tower.py,看项目如何批量生成"水密 + 凸"的碰撞网格,让整座场景跳过分解。

【免费下载链接】genesis-worldSimulation platform for general-purpose robotics & embodied AI learning.项目地址: https://gitcode.com/GitHub_Trending/genesi/genesis-world

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询