上周一位朋友发消息问我:手里有一个 leaphand.urdf,想丢进 Genesis Simulator 里 visualize 一下,看看这个机械手的结构和运动效果,结果折腾了一晚上都在报 "asset path" 相关的错误,连模型的影子都没看到。我听完就觉得这不是个例,凡是第一次在 Genesis 里加载自己的 URDF 模型的人,大概率都会在写路径这一步卡住。原因很简单:URDF 文件本身只是一张“零件清单”,真正的三维网格和材质全在外部文件里,仿真器按清单去取货,路径没写对就立刻翻车。
这篇内容我就以 leaphand.urdf 为例,把“在 Genesis Simulator 里可视化自己模型”的完整过程讲透,重点会放在 asset 路径错误的原理、定位方法和修复方案上。不管你是做机器人控制算法的,还是在搞具身智能数据采集,亦或是单纯想把一个开源机械手模型拿过来看看细节,这篇都可以直接照着操作。
1. 动手之前,先说清楚这次任务里到底会发生什么
1.1 leaphand.urdf 是什么,它为什么适合当例子
leaphand 是一个开源的五指灵巧手模型,在许多机械臂抓取、灵巧操作的研究里经常出现。它的 urdf 文件描述了这只手由多少个 link(刚体部件)和 joint(关节)组成,每个 link 长什么样则需要靠 meshes 目录下的 STL 或 OBJ 文件来描述。换句话说,leaphand.urdf不是孤立一个文件就能加载的,它身边必须有一个配套的资源目录,里面放着所有 link 对应的三角网格文件。
我个人很喜欢拿它举例子,是因为它的结构相对清晰:手指多、关节多、单个部件又是细长形状。只要这个模型能在 Genesis 里成功显示出来,你基本就能掌握所有常规机械臂、机械手模型的加载方式。再加上它来自开源社区,文件结构在大大小小的模型包里很典型,既有坑又有代表性,非常适合做路径问题的教学案例。
抛开这个具体模型,这次教程的方法可以平移到所有 URDF 模型上。核心思路其实就是两条:把文件目录放对,把 URDF 里的路径写对。听起来简单,但绝大多数人第一次都会死在第二步。
1.2 Genesis Simulator 是什么,和 MuJoCo、Isaac 是什么关系
如果你还没接触过 Genesis,简单说,它是一个面向机器人仿真和具身智能场景的物理仿真平台,核心代码基于 Python,开发管线很新,渲染效果也做得比较舒服。你可以在里面放入地面、障碍物、机器人模型,然后配置相机、传感器、控制器,跑完整的仿真循环。
很多人在刚开始时会把它和 MuJoCo、Isaac Sim 放在一起比较。从模型加载层面看,Genesis 虽然有自己的场景描述语言,但对机器人领域最通用的 URDF 格式做了完整支持。它在解析 URDF 时采用了一些和 MuJoCo 兼容的处理策略,默认会帮你把许多link、joint信息本地化成自研引擎里的表达。这带来一个直接后果:URDF 里的 mesh 路径成为加载成败的关键。因为 Genesis 不会像某些三维查看器那样随便找个代理位置去猜资源,如果路径解析失败,它就会直接报错或者让整个 link 在场景里消失。
对刚入门的读者来说,可以理解成:Genesis 是一个不惯着 URDF 的仿真器,路径规范做得越严谨,加载越顺利。这不是 Genesis 的缺点,反而能帮你养成模型管理的好习惯。
1.3 本篇文章面向哪些人、解决哪些问题
如果你是第一次在 Genesis 里加载自定义 URDF,这篇文章适合你。我会用小白也能直接复现的方式,从环境准备、文件目录设计、代码引入,一路讲到报错排查。你不需要提前掌握 URDF 语法,只需要有一台能跑 Python 的电脑,以及一个想加载的模型包。
文章中会出现几种很现实的报错情况,比如“模型文件就在当前目录,却总是报 not found”“URDF 明明写的是package://...,Genesis 根本不认”“模型加载后地面有了,但机器人死活看不到”。这些情况分别有不同的解决路径,我会在排障章节里单独拆开讲。读完以后你不仅能解决 leaphand 的问题,还能形成一套通用的“路径排障肌肉记忆”。
2. 为什么 asset 路径会成为一个绕不开的坑
2.1 URDF 的物理构成:link + joint + 外部资源描述
机器人 URDF 本质上是一个 XML 文件,里面反复出现的两个核心元素是link和joint。link表示刚体,比如手掌、手指的第一节、第二节;joint表示两个刚体之间的连接方式,比如转动关节、固定关节。
每个link下面可以配置三类视觉或物理数据:visual负责外观显示,collision负责碰撞检测,inertial负责惯性质量参数。其中visual和collision通常会引用一个外部网格文件,比如:
<visual> <geometry> <mesh filename="meshes/lp_link_0.stl" /> </geometry> </visual>这里meshes/lp_link_0.stl就是 asset 路径。Genesis 加载 URDF 时会逐行读取这些 XML 节点,根据filename属性去磁盘上加载三角网格。路径一旦对不上,最轻的表现是模型缺胳膊少腿,最严重的是整个加载流程直接中断。
因此,排查路径问题的第一步不是打开代码,而是打开这个 URDF 文件,理解它里面到底引用了哪些外部文件。只要这个环节理清了,后面所有报错都会变得很直白。
2.2 路径约定的差别:相对路径、绝对路径、package 协议
在 URDF 的 mesh 路径里,有三种典型写法。第一种是相对路径,比如meshes/lp_link_0.stl。它表示相对某个基准目录去寻找文件,这个基准目录在不同仿真器里的定义不完全一样,有的以 URDF 文件所在目录为基准,有的以当前运行脚本的工作目录为基准。第二种是绝对路径,比如/home/user/robot/meshes/lp_link_0.stl,在单一设备上基本有效,但一旦换了电脑或者把模型包发给别人,就很容易失效。第三种是 ROS 社区常见的package://协议写法,例如package://leaphand_description/meshes/lp_link_0.stl,这种写法有专门的 ROS 包解析器才能正确处理,Genesis 默认并不具备相同的包名查找机制,所以经常在这里报错。
把这个现象放到生活里,有点像快递单上的收货地址:相对路径相当于是“小区名字+楼栋号”,绝对路径相当于“省市县街道门牌号写全”,package://则是“某个只在特定快递系统里才能识别的小区暗号”。Genesis 只按照它自己的一套规则去解析地址,地址格式不对就派送失败。
2.3 到了 Genesis / MuJoCo 解析链路里,路径错误为什么立刻暴露
Genesis 在加载 URDF 时会对网格文件做真实读取,而不是仅仅记录一个“以后要显示的引用”。它需要把 STL/OBJ/Dae 里的几何信息转成引擎内部的碰撞几何或者渲染网格,这个过程在场景构建时同步发生。一旦读取不到文件,初始化阶段就会抛异常,或者更让人摸不着头脑的是,只给出一个模糊的警告后继续运行。
我复盘过许多朋友的报错截图,其中最高频的错误信息大概是这样的:
Failed loading mesh from file: assets/leaphand/meshes/lp_link_0.stl这时大家的第一反应往往是去检查代码里传给 Genesis 的 URDF 路径有没有写错,但实际上代码里只写了file="assets/leaphand/leaphand.urdf",代码层面的路径是能找对的,真正出错在 URDF 内部继续解析filename="meshes/lp_link_0.stl"的那一层。仿真器通常会以 URDF 文件所在目录作为后续相对路径的基准,如果 URDF 的实际位置和你在代码里传入的位置不一致,那内部相对路径就惨了。
如果传的是绝对路径,那问题更多出现在跨平台迁移时。比如在 Windows 上生成的 URDF 内部使用\分隔目录,拿到 Linux 机器上跑,反斜杠容易被当成普通字符,导致路径拼接异常。这也能解释为什么同一个模型包在同一台电脑的某些软件里能打开,在另一个软件里就打不开。
3. 实操:把 leaphand 模型跑进 Genesis Viewer
3.1 环境准备与安装
在开始前先把 Python 环境准备好。我建议用 Python 3.10 或更高版本,创建独立虚拟环境是必须的。
python -m venv genesis_env source genesis_env/bin/activate pip install genesis-world这里需要注意,genesis-world是官方发布到 PyPI 的包名,安装时不要拼错。第一次运行 Genesis 时,引擎内部可能还会拉取一些依赖和运行时文件,如果网络状况不稳定,安装过程可能比较慢,这一步要耐心等待。
安装完成后可以先做一个极简测试,看看引擎是否能正常初始化:
import genesis as gs gs.init(backend=gs.gpu) print("Genesis 初始化成功")如果你没有 NVIDIA GPU,或者只是想快速看可视化效果,也可以使用 CPU 模式:
import genesis as gs gs.init()Genesis 本身初始化很轻量,真正的耗时主要发生在后续scene.build()阶段。我第一次跑的时候就是因为没分清gs.init()和scene.build(),误以为程序卡死了,其实前者只是设置运行环境,后者才真正开始构建物理场景。
3.2 先整理目录,再写代码
model 文件的目录结构直接影响加载成功率。下面是我处理 leaphand 时推荐的统一目录结构:
your_project/ ├── main.py └── assets/ └── leaphand/ ├── leaphand.urdf └── meshes/ ├── lp_link_0.stl ├── lp_link_1.stl └── ...注意,所有 mesh 文件都放在 leaphand 目录下的meshes/子文件夹中,URDF 内部所有filename引用都写成meshes/xxx.stl这种相对路径。这样无论在哪个平台、哪个工作目录下运行,只要代码传入的是从项目根目录出发的正确 URDF 路径,Genesis 就能以该 URDF 所在目录为基准,继续正确索引meshes/下的资源。
如果有现成模型包里的 mesh 文件不齐全,先别急着写代码,直接 grep 一下 URDF 里的文件名,把它们和磁盘里的文件清单对齐。这一步我会在后面的排障脚本里详细说。
3.3 编写最小可加载代码并打开 Viewer
在项目根目录下新建main.py,录入以下代码:
import genesis as gs # 初始化引擎 gs.init() # 创建一个带视觉显示的场景 scene = gs.Scene.create(show_viewer=True) # 添加一个地面,方便观察坐标和碰撞 scene.add_entity(gs.morphs.Plane()) # 加载我们的 leaphand URDF leaphand = scene.add_entity( gs.morphs.URDF( file="assets/leaphand/leaphand.urdf", pos=(0.0, 0.0, 0.1), ) ) # 构建物理场景,完成后会弹出视觉窗口 scene.build()讲解几个核心点。
gs.Scene.create(show_viewer=True)是控制是否弹出可视化窗口的关键参数。如果你只想做后台仿真,不需要可视化,可以把它改成show_viewer=False,但本文既然目标是 visualize,那一定保持为True。
scene.add_entity(gs.morphs.Plane())是添加一个地面平面。加载 leaphand 这种灵巧手模型时,如果底座悬空,你很难直观判断它的形状和关节位置,加一个地面能让视角稳定许多。
pos=(0.0, 0.0, 0.1)控制模型初始位置。z=0.1的意思是让模型稍微离地抬高一点,避免刚 build 完就因为重力穿透地面,给视觉观察带来干扰。
运行脚本:
python main.py如果一切正常,屏幕上会弹出 Genesis Viewer 窗口,你能看到 leaphand 在窗口中展示出来。出现窗口后,按住鼠标左键拖动可以旋转视角,滚动滚轮可以缩放,按住右键拖动则可以平移视角。
这是整个工程中最舒服的一步,因为前面的血泪全部集中在路径和解析阶段。如果你在这一步已经看到完整模型,说明 asset 路径已经没有问题,可以直接跳到关节检查小节。
3.4 从“模型能显示”再到“结构能观察”
模型刚加载出来时可能只是以一个固定的手势摆放着,很多人会以为这就完事了,其实 Genesis Viewer 里还有很有价值的信息没有挖掘。“visualize 自己的模型”不只是看到外观,还包括验证以下三个层面:
- 模型有没有出现穿透或重叠:如果某个 link 的视觉网格和碰撞网格不匹配,或者在加载时由于 mesh 路径丢失导致系统用了替代几何,画面里可能出现部件互相穿模。
- 关节坐标系是否正确:一个灵巧手模型如果掌心方向不对,手指蜷缩方向会乱,转动角度看起来会像“鬼畜”。这个问题常见于坐标系约定不一致,需要回头检查 URDF 中每个 joint 的 origin。
- 模型树的层级是否符合预期:Genesis Viewer 通常支持场景树上选中节点,你可以在窗口中点选模型的一部分,高亮显示对应的 link。如果某根手指怎么都点不亮,那可能是对应的 mesh 文件没有加载成功。
对 leaphand 这种多自由度模型来说,我通常建议加载后轻轻拖一下视角,从多个方向看一遍。不要只从一个固定视角判断正确性,因为灵巧手每个手指都在不同平面上运动,侧看往往才能暴露方向错误。
如果你想更进一步,让关节动起来,那就要在scene.build()之后添加关节控制逻辑。比如在初始状态基础上,给手指所有关节一个目标位置,然后循环执行物理步进。更加详细的控制方式属于另一个话题,这里只提醒一点:Genesis 里让关节动起来需要一个控制策略,单纯调用scene.step()并不会自动产生运动,需要为关节配置位置控制或力矩控制。想快速验证结构的话,可以在 Viewer 中选择某个交互模式去手动拖动关节(具体取决于当前版本提供的交互工具),让模型动起来观察运动轨迹。
4. 解决 asset 路径错误:排查方法和修复心法
4.1 报错现象对比,先对号入座
很多人来问“asset 路径怎么改”,其实他们遇到的报错各不相同。我整理了一张对照表,你可以先对号入座,看看自己卡在哪一类。
| 常见现象 | 根因方向 | 解决思路 |
|---|---|---|
加载时报Failed loading mesh from file: ... | URDF 里 mesh 相对路径基准与仿真器解析基准不一致 | 把 URDF 改成标准相对路径,mesh 目录和 URDF 同级安装 |
| 模型不报错,但画面里只有地面和坐标系原点 | mesh 路径错误但引擎做了降级处理,或者 visual mesh 没有加载 | 用脚本检查所有 mesh 是否存在,再查看是否有空几何 |
| 代码里写的是绝对路径,换目录运行后失效 | 绝对路径硬编码 | 改成相对路径或使用Path(__file__).resolve().parent动态拼接 |
URDF 里出现package://leaphand_description/... | ROS 专用协议,Genesis 不识别 | 手动替换成meshes/...并调整目录 |
| 在 Windows 上能用、Linux 上报错 | 路径分隔符反斜杠或文件名大小写不一致 | 统一使用/,文件名尽量小写 |
| URDF 加载成功但只是缺失了碰撞 mesh | collision 几何缺文件,仿真穿透 | 单独检查 collision 里的文件名 |
这张表给到你的并不是代码技巧,而是定位问题的切入点。定位到问题类型以后,再用下面的脚本去精确扫描。
4.2 写一个 30 秒路径体检脚本
我建议你直接在项目里放一个小的 Python 脚本,用于扫描 URDF 中所有被引用的 mesh 路径,并标记出哪些文件存在、哪些文件缺失。下面这个脚本就是我从日常项目里抽出来的,你可以直接复制使用。
import os import sys import xml.etree.ElementTree as ET def iter_all_mesh_filenames(urdf_path): """遍历 URDF 中所有 mesh 标签的 filename 属性""" tree = ET.parse(urdf_path) for elem in tree.iter(): if elem.tag == "mesh": filename = elem.attrib.get("filename", "") if filename: yield filename def check_urdf_assets(urdf_path): base_dir = os.path.dirname(os.path.abspath(urdf_path)) problems = [] ok_count = 0 for raw_filename in iter_all_mesh_filenames(urdf_path): # 1. 如果包含 package:// 前缀,标记为协议不支持 if raw_filename.startswith("package://"): problems.append(("package协议", raw_filename)) continue # 2. 去掉 file:// 前缀,统一正斜杠 cleaned = raw_filename.replace("file://", "").replace("\\", "/") # 3. 如果是绝对路径,标记为跨平台风险 if os.path.isabs(cleaned): problems.append(("绝对路径", raw_filename)) continue # 4. 以 URDF 所在目录为基准拼接相对路径 full_path = os.path.normpath(os.path.join(base_dir, cleaned)) if not os.path.exists(full_path): problems.append(("文件缺失", raw_filename)) else: ok_count += 1 return ok_count, problems if __name__ == "__main__": urdf_file = sys.argv[1] if len(sys.argv) > 1 else "assets/leaphand/leaphand.urdf" ok, issues = check_urdf_assets(urdf_file) print(f"[OK] 成功定位 {ok} 个mesh文件") for kind, msg in issues: print(f"[{kind}] {msg}")你的项目里有几个 URDF,你就可以对这几个文件分别运行这个脚本:
python check_urdf.py assets/leaphand/leaphand.urdf脚本输出里如果只有[OK],说明路径层面没有问题,接下来的报错可以往 joint 属性、惯性参数等方向排查。如果出现[package协议],那基本可以确认问题所在——Genesis 无法直接处理package://这种 ROS 包定位方式。如果出现[文件缺失],说明文件名或目录层级不匹配,需要你把模型文件补全,或者修改 URDF 中的路径字符串。
这是我个人最推荐的第一道检查步骤,因为它在不进仿真器的情况下,直接把文件系统层面的问题暴露出来。对于新手来说,也避免了一边改代码一边反复打开 viewer 的无效循环。
4.3 两步修复法:把 ROS 风格路径改成标准相对路径
如果你发现 URDF 里全是package://xxx_description/meshes/xxx.stl,那恭喜你,问题基本锁定。修复思路是两步走。
第一步,在你的模型包里创建一个meshes文件夹,把所有网格文件放进去。第二步,把 URDF 中的所有package://xxx_description/meshes/前缀,统一替换成meshes/。
这里可以用 Python 脚本批量处理,也可以直接用文本编辑器的全局替换功能。我更推荐直接用脚本,一是避免手滑,二是可重复执行。提供一个小脚本:
from pathlib import Path # 替换前缀映射,把 ROS package 名替换成相对路径 urdf_path = Path("assets/leaphand/leaphand.urdf") replace_map = { "package://leaphand_description/meshes/": "meshes/", "package://leaphand_description/": "", } text = urdf_path.read_text(encoding="utf-8") for old, new in replace_map.items(): text = text.replace(old, new) urdf_path.write_text(text, encoding="utf-8") print("URDF 路径替换完成")执行完脚本后,再跑一遍 4.2 的体检脚本,确认所有 mesh 都能成功定位。很多时候问题就消失在这一步,不需要再动任何代码。
这里有个细节必须提:修改 URDF 后,要留意文件编码。URDF 中可能会包含非 ASCII 字符或者特殊注释,Python 在读写时统一使用utf-8比较稳妥。别问我为什么强调这一点,问就是曾经用默认编码把中文字符注释放进 URDF,结果整个 XML 解析直接崩了。
4.4 路径规范之外,容易被忽略的三个隐藏雷区
asset 路径错误解决后,模型有时还是加载不出来,或者显示异常。这时需要排查另外三个隐藏雷区。
第一个是 link 缺少inertial标签。如果某个 link 没有配置质量、惯性张量,Genesis 在构建物理体时可能会拒绝给这个 link 分配动态属性,轻则警告,重则报错。这种情况在纯视觉查看器中不会暴露,但在 Genesis 这种物理引擎里非常关键。可以用脚本遍历 URDF,检查每个 link 节点是否包含inertial子节点,不全的就要补上。
第二个是 mesh 文件格式兼容性。Genesis 能够支持常见的三角网格格式,但有些模型包里的文件是从 SolidWorks 或异性软件导出,网格单元可能出现退化三角形或者尺寸异常大。这类问题通常不直接指向路径错误,而是会在 build 阶段报出奇怪的几何错误。如果你发现路径检查全绿但加载仍然报错,用一些网格工具打开模型文件确认其有效性,是一个很好的中间步骤。
第三个是 URDF 中的 joint 原点设置错误。常见表现是模型能加载,但手指朝向完全不对,或者手指蜷曲方向逆向。这个问题和 asset 路径没有直接关系,但它会在 visualize 时让你产生“模型坏了”的误判,从而怀疑是路径问题。所以我在排查路径错误时总会提醒一下:如果模型外观完整但姿态别扭,优先检查 joint 的origin和axis,而不要继续在路径上浪费时间。
5. 实操经验总结,帮你少走一段弯路
5.1 Genesis 加载自建模型时的三个好习惯
使用多了以后,我总结出几个适用于 Genesis 加载自定义模型的好习惯,基本能覆盖九成的问题场景。
第一个好习惯是显式区分“脚本工作目录”和“URDF 所在目录”。很多朋友犯的错误是,在项目根目录创建了assets/leaphand/leaphand.urdf,代码里也写着file="assets/leaphand/leaphand.urdf",但某个内部模块调用时先把当前目录切走了,于是路径解析失败。最好的解决方法是:在代码中利用Path(__file__).resolve().parent来构造绝对路径,再把它传给 Genesis。例如:
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent urdf_file = BASE_DIR / "assets" / "leaphand" / "leaphand.urdf" leaphand = scene.add_entity( gs.morphs.URDF( file=str(urdf_file), ) )第二个好习惯是永远不要使用多余的空格、中文或特殊字符作为模型路径。这倒不是 Genesis 本身限制严格,而是这些字符在跨平台传输或 shell 路径处理时容易引入不可见干扰。老老实实把文件名都写成小写英文,至少能省掉很多不必要的检查时间。
第三个好习惯是每换一台机器就跑一遍体检脚本。模型包在 A 机器上能跑,不代表复制到 B 机器上也能跑,尤其是当 URDF 中残留绝对路径时,换个用户名、换台电脑就失效。我把 4.2 的脚本存为check_urdf.py放进每一个项目,任何环境变动后先跑一次,确实省下大量盲目排障时间。
5.2 使用 Viewer 后,我推荐你马上做的三件事
模型成功显示后,不要关掉窗口,建议接着做下面三件事。
第一件事是观察模型是否落在地面上,看看 leaphand 有没有在下落过程中产生明显的抖动或穿模。如果能看到物理引擎自动让手指弯曲了一下以适应碰撞,说明碰撞网格加载正常;如果手直接插进地面,说明碰撞属性没生效,此时要看collision标签是否引用了有效 mesh。
第二件事是逐一取消勾选或隐藏部分 link,检查内部结构。Genesis Viewer 支持场景中物体树的操作,把手指某一个 link 隐藏后再观察邻接部件,能够确认各 link 的父子关系是否符合模型设计。这点在排查模型关节装配问题时很有用,也能快速发现有没有 link 被意外合并。
第三件事是尝试给模型一个初始速度或初位置,验证关节的硬限位是否生效。leaphand 这种五指手,每个关节都有角度范围,Genesis 在 build 时会读取 URDF 里的limit字段。如果 limit 没写好,用鼠标拖拽模型手部时关节可能无限旋转或反转,这个现象指向的不是路径问题,而是 URDF 的关节约束参数缺失。
5.3 工具脚本和模型目录就是你的长期资产
现在很多工作流里,大家在同一个项目里会反复加载多套 URDF 模型。如果每次都现改路径、现调目录,非常浪费时间。我的建议是,把每个模型的目录固定成同一套模板:
assets/ ├── leaphand/ │ ├── leaphand.urdf │ ├── meshes/ │ ├── config/ │ └── README.md ├── franka/ └── other_robot/同时,把常用的几个脚本沉淀成项目里的tools/模块:check_urdf.py负责体检,fix_urdf.py负责批量修复 package 协议和绝对路径,load_robot.py负责统一加载。这样等下次需要加载一个新模型,你只需要把 URDF 和 mesh 文件丢进模板目录,跑一遍 fix 脚本,再跑一遍主程序,模型通常就能顺利出现在 Genesis 窗口里。
有一个很现实的好处是,如果之后你切换到其他仿真器或做批量仿真实验,这套标准目录同样适用。因为我们在修改 URDF 时其实是在把模型包整理成“通用格式”,这个动作不绑定 Genesis 也不绑定某个操作系统,是纯粹的资产规范工作。
5.4 给刚接触 Genesis 的读者的最后建议
模型可视化只是 Genesis 无数能力里的最表层。当你成功把 leaphand 显示出来以后,下一步可以尝试设置它的关节角度,让它做一个抓取动作;再往后可以接入相机、渲染深度图,甚至直接跑强化学习环境。一路上你会遇到很多新问题,比如控制器参数调不对外观正常但运动不稳定、传感器数据维度对不上等等,这些并不比你刚解决的路径问题更复杂,只是信息断层不同。
这次以 leaphand 为入口,把 asset 路径错误这个“第一道坎”迈过去,后续的路会顺畅很多。如果你正好在用 Genesis,建议立刻打开自己的模型文件,先跑一遍那个 30 秒体检脚本,看看能否输出全绿的 OK。如果出现了 package 协议或文件缺失的标记,就用 4.3 的方法处理,处理完再回来加载一次,大概率能看到模型稳稳地出现在 Viewer 里。