ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Antigravity+Blender MCP:智慧仓储数字孪生场景建模实战

Antigravity+Blender MCP:智慧仓储数字孪生场景建模实战

上周接了个智慧仓储数字孪生原型需求:客户丢过来一张仓库CAD图纸,希望一周内在浏览器里看到货架、AGV路径、设备状态,后面还要能实时挂接调度数据。按老办法,我先在Blender里手动摆货架,一排排复制上百个库位,光这一步就把人耗干了,更别提后续还要写前端、调动画。这次我换了一套组合拳:用Antigravity当整个开发流程的调度大脑,用Blender MCP让AI能实时操控Blender建模。结果静态场景一天搭完,GLB资产导出和前端联调也一气呵成。

这篇文章是这套方案的上篇,记录从环境配置、MCP通道连接,到智慧仓储静态数字孪生场景拆解、批量建模、资产导出优化的完整过程。适合两类人:一类是想用AI工具加速三维场景构建的开发者,另一类是刚接触数字孪生、搞不清楚Blender、MCP、AI IDE之间关系的小团队。我会把每一处"为什么这样选"都讲透,毕竟知道原理比照抄步骤更重要。

1. MCP给Blender装上一双AI的手:分工与核心思路

1.1 数字孪生不是"画个模型",而是"搭一条数据回路"

很多人一听"数字孪生",第一反应是"把仓库用3D做出来",于是把精力全花在建模好不好看上了。实际上,行业里常说的数字孪生三层架构——物理对象、数字化模型、应用系统——最关键的是中间那层模型必须和真实世界里的物体一一对应,并且能被数据驱动。

放在智慧仓储场景下,这意味着每个货架、库位、AGV、充电桩,都不能只是一个"好看的长方体",它背后要能挂上货位编码、库存数量、设备状态、温度传感器ID这些业务信息。我这次建模时的第一原则就是:几何体造型简单不要紧,但物体的名称、层级、坐标组织必须严谨。后面所有动态仿真和业务联动,都建立在这层结构性基础上。

1.2 为什么选Blender,而不是Unity或直接在Three.js里建模

这个问题我几乎在每个项目里都会被问到。结论很直接:

  • Blender是DCC工具,天生适合"从零造几何体"。它有完整的Python API(bpy),可以脚本化批量建模、批量改材质、批量导资产。Unity、Unreal的强项是实时交互和物理仿真,建模和场景摆布能力反而弱,而且项目文件重、起步慢。
  • Three.js不是不能建模,而是不适合做"场景生产"。在代码里写死一个货架容易,但当你需要调整几百个货架的位置、分层、材质时,纯手写Web代码维护起来非常痛苦。Blender作为资产中间层,能把建模、摆布、校审分离出去,产出的GLB又正好是前端生态的标准格式。
  • 自动化条件是关键。让AI去操作一个软件,前提是软件必须有可编程接口。Blender的bpy模块就是那个接口,而且它做得特别干净——创建物体、改属性、组织集合、导入导出,全都能用脚本完成。这比让AI去点GUI按钮可靠得多。

如果落地端是Unity,Blender也没白做,无非是导出FBX时注意一下坐标轴,但管线思路完全一样。

1.3 Antigravity到底在这个组合里干什么

Antigravity在我的工作流里扮演的是Agent型IDE的角色——它不只是补全代码的编辑器,它能读项目文件、跑终端命令、执行脚本、观察报错、自己迭代修复。这意味着我可以把一个相对模糊的需求丢给它,比如"按JSON配置在Blender里批量建货架",它会自己拆步骤、写脚本、执行、检查结果。

但Antigravity不会自己打开Blender界面,它需要一个通道去触碰Blender内部的状态。这个通道就是MCP,全称Model Context Protocol。Blender MCP插件在Blender这头启动一个本地服务,把"读取场景""创建物体""修改对象""查询报错"这些能力暴露成标准接口;Antigravity那头作为MCP客户端连上去,就能像操作工具一样调用Blender。

打个比方:Antigravity是大脑,MCP是神经系统,Blender是双手。大脑负责决策,神经系统传递指令和反馈,双手负责执行。相比"AI写脚本→人复制粘贴→Blender手动执行→报错再回来问AI"的老模式,MCP的最大优势是形成了闭环:AI执行完脚本后能直接读回Blender里的场景状态,发现坐标错了、物体不见了,当场就能自查自纠。

2. Antigravity与Blender MCP的连接:环境准备和第一个测试

2.1 Antigravity侧要准备什么

Antigravity的安装本身不复杂,下载安装包、登录账号、选模型,基本就是标准IDE的流程。但有两个细节我建议一开始就做好:

  1. 一个项目一个工作目录。Antigravity这类Agent型工具会主动读写文件,如果你把它放在一个堆满各种旧项目的目录里,它很可能"好心办坏事",改错文件。我通常建一个干净的warehouse_dt/目录,里面再分assets/、scripts/、docs/、config/四个子目录。AI的权限范围,我明确约束在scripts/和config/里。
  2. 把需求文档写进项目里。不要只在对话里描述需求,而是把仓库参数、货架规格、坐标规则写成一个README.md或config.json。AI读完文档后干活,比一句一句追问"货架多高""通道多宽"要稳定得多。这其实也符合Agent开发的通用经验:上下文越结构化,AI越少自由发挥。

2.2 Blender侧开启MCP服务

Blender MCP插件有不少开源实现,核心机制大同小异。我以常见版本为例:

  1. 把blender-mcp插件包下载后放入Blender的addons目录。
  2. 打开Blender,进入"偏好设置→插件",搜索"Blender MCP",勾选启用。
  3. 在3D视口按N打开右侧面板,找到MCP标签,点击"Start MCP Server"。
  4. 面板上出现类似Server listening on 127.0.0.1:9876的字样,就说明服务起来了。

为什么要用127.0.0.1而不是0.0.0.0?因为这是本地回环地址,只有本机才能访问。MCP插件本质是一个能远程执行Blender脚本的服务,如果绑定到局域网,等于把操纵Blender的权限暴露给了同网段的其他人,安全隐患很大。日常开发用回环就完全够了。

2.3 Antigravity里配置MCP客户端

在Antigravity的MCP设置界面里,新增一个Server,类型选Streamable HTTP或SSE(取决于当前版本支持的协议,以及插件实现的是哪种),URL填插件面板上显示的地址,比如http://127.0.0.1:9876/sse。

这里有个小坑:MCP协议本身还在快速演进,有的插件是旧版SSE,有的新版支持Streamable HTTP。连不上时先看插件面板上写的URL后缀,再在Antigravity里选对应的类型。如果README里说同时支持,优先选新的Streamable HTTP。

保存后如果连接状态变绿,就说明通道通了。

2.4 第一个测试:让AI在Blender里创建立方体

连接成功后,先别急着建仓库,做一个最基础的通路测试。我在Antigravity对话里输入:

请连接Blender,清空当前场景,然后创建一个长宽高各2米的正方体,放在世界原点,材质设为红色。

然后在Blender视口里,你会看到场景被清空,出现一个红色立方体,同时Antigravity侧会返回脚本执行的结果。这一步的意义有两个:

  • 确认MCP链路通畅,指令能下去,状态能回来。
  • 确认AI确实有能力执行带环境交互的任务,而不只是生成一段没人验证过的代码。

我建议这个测试一定要做,而且不要跳过。我见过太多人配置了半天,最后发现Antigravity能连上但Blender插件版本太旧,工具调用全超时。用一个最简单的小操作提前暴露问题,比后面建到一半再排查划算得多。

3. 智慧仓储静态场景的建模拆解:从CAD图纸到bpy脚本

3.1 先决定建模数据从哪来

真实项目里,建仓储场景的数据来源通常有三种:

  • CAD图纸:客户给的仓库平面图,可能是DWG、DXF或PDF。最稳的做法是把CAD里的墙体、货架、通道坐标提取成结构化数据,再让AI用脚本批量建模。不要直接拿CAD导入Blender硬啃,CAD线稿往往有大量图层垃圾,导进来还得逐个清理,效率反而低。
  • 点云扫描:如果仓库已经建好,有激光雷达扫描得到的点云,甚至最近流行的3D Gaussian Splatting重建模型,可以先把点云或扫描网格导入Blender作为几何参考底图。但注意,数字孪生要求的是可交互、可语义化的模型,点云本身很难挂业务数据,所以正确的姿势是"点云当底片,规则几何体盖上去"。
  • 手工规划:很多项目其实没有完整图纸,只有一张手画的布局草图。那最简单的办法是直接定义坐标:哪个区域放货架,通道多宽,AGV路径经过哪些点。整理成一个JSON文件,一切好办。

这次项目客户给的是DXF,我先用QCAD把人库、货架、通道的关系读出来,人工整理成layout.json。整个过程半小时左右,但为后面AI建模省了无数口舌。

3.2 把仓储元素清单写成结构化数据

让AI直接从自然语言建仓库,它会把每个物体五花八门地命名,后期想挂业务数据就难了。所以我坚持先自己定义一份结构化配置,作为建模的唯一依据。

{ "warehouse": { "unit": "m", "origin": [0, 0, 0], "bounds": [80, 40, 12], "shelves": [ { "id": "S-001", "name": "A区货架1", "x": 2.0, "y": 3.0, "length": 2.6, "depth": 1.2, "height": 6.0, "levels": 5 } ], "agv_paths": [ { "path_id": "P1", "points": [[0, 2, 0], [5, 2, 0], [5, 10, 0]] } ], "devices": [ { "id": "CHARGER-01", "name": "充电桩1", "x": 1.0, "y": 1.0, "type": "charger" } ] } }

这份JSON同时成为后端数据和前端数据的雏形。让AI建模时,它只需要遍历shelves、devices、agv_paths,按id命名物体即可。以后模型要变化,改这个配置,重新生成一次,比在Blender里手动拖拽一百个货架舒服得多。

3.3 bpy脚本的编写套路:规则先行,批量为王

我让Antigravity写建模脚本前,先约定了几条硬规则:

  • 所有物体放在根节点ROOT_Warehouse下面,整体坐标不散落;
  • 物体命名一律用业务ID,比如货架就叫S-001,库位就叫SL-S001-01;
  • 单位用米,Z轴朝上;
  • 先写"创建一个货架"的函数,再写"遍历JSON批量创建"的循环,不允许直接堆鼠标操作。

一个基础的长方体货架脚本,核心就是bpy.ops.mesh.primitive_cube_add加尺寸缩放,或者用bpy.data.meshes.new()手动建面。简单场景用前者就够了:

import bpy import json def clear_scene(): """清空场景中的全部物体,避免旧数据干扰""" for obj in list(bpy.data.objects): bpy.data.objects.remove(obj, do_unlink=True) def create_shelf(item): """根据配置创建单个货架""" bpy.ops.mesh.primitive_cube_add(size=1, location=(item["x"], item["y"], item["height"] / 2)) obj = bpy.context.object obj.name = item["id"] obj.scale = (item["length"], item["depth"], item["height"]) bpy.ops.object.transform_apply(scale=True) return obj def create_scene(config): clear_scene() for shelf in config["warehouse"]["shelves"]: create_shelf(shelf) if __name__ == "__main__": with open("config/layout.json") as f: cfg = json.load(f) create_scene(cfg)

如果你觉得bpy.ops看着土,生产级项目可以用from_pydata直接构造Mesh对象,效率和可控性更高,但这里就不展开全量代码了,原理是一样的。

Antigravity写这种脚本非常顺手,因为它能看到JSON结构,又能执行脚本验证结果。我通常会追加一句话:"执行完后打印创建的物体数量,并读取场景树确认所有物体都在ROOT_Warehouse下"。这就是MCP闭环的价值:AI能自查,不用我盯着每一步。

3.4 实例化货架:别让一百个货架变成一百个独立模型

仓库里货架往往是同规格重复摆放,如果让AI循环创建一百个独立Mesh对象,Blender会卡,导出到前端更卡。Blender里解决这个问题最优雅的手段是Collection Instance(集合实例):

  • 先把一个标准货架做成集合ShelfMaster;
  • 再用"空物体挂集合实例"的方式复制出多个实例;
  • 改ShelfMaster里的原始模型,所有实例跟着变。

这样场景树里只有一份真实几何,其他都是引用。Antigravity生成实例的逻辑也很简单:创建空物体,设置instance_type为COLLECTION,再把ShelfMaster集合赋给它。这套结构在Blender里编辑极其流畅,导出前如果前端不需要分别控制单个货架,还能选择合并网格进一步减负。

3.5 AGV路径:静态场景也要为动态留好接口

智慧仓储数字孪生最后一定跑的是AGV调度、路径规划、任务状态这些动态逻辑。所以在静态建模阶段,我就把AGV路网用空物体和曲线搭出来了:

  • 路径点用空物体,坐标直接读自配置里的agv_paths;
  • 路径连线用Bezier曲线,方便以后做运动动画;
  • 可通行区域用一张半透明薄平面铺出来,导到前端后可以根据真实AGV位置动态变色。

之所以这么认真,是因为真实AGV导航常走Nav2方案,配合2D/3D雷达做感知和避障。数字孪生里的路径,至少要能和真实路网的点位对应上,才能在下篇做"调度指令→孪生场景运动"的映射。如果现在随便画几条线,后面对接数据时一定会返工。

4. 资产出口:Blender场景怎么变成前端能用的GLB

4.1 导出设置:单位、轴方向和选择范围

静态场景建完,下一步是把Blender场景导成GLB给Three.js之类的Web端用。导出时这几个坑必须先堵住:

  1. 只导出需要的东西。在Blender里选中ROOT_Warehouse相关集合,导出时勾选"Limit to selected objects"。不然你场景里残留的参考点云、辅助曲线、Blender自带的摄像机灯光,全会塞进GLB,前端加载一堆垃圾。
  2. 单位统一用米。CAD图纸经常是毫米,如果中途没换算,一个货架可能变成几百米高,前端加载后整个世界都是瓢的。
  3. 轴转换交给glTF导出器。Blender默认Z轴向上,Three.js默认Y轴向上,glTF导出器会自动完成Z-up到Y-up的转换。所以通常不需要手动旋转模型组,只要记住"导出后,Blender里的Z方向对应Web里的Y方向"即可。

4.2 模型优化清单:别让浏览器加载到崩溃

做数字孪生网站最怕模型面数爆炸。仓储场景相比园区级项目不算大,但上百个货架、上千个库位标识照样能拖垮性能。我习惯按这个清单优化:

  • 静态设备合并:货架、托盘的固定部分在导出前可以合并成一个Mesh,draw call立刻降下来。前提是你后面不需要单独改某一个货架的材质。
  • 材质数量压低:同外观的物体共享材质,贴图统一用1024分辨率。仓储场景大量使用灰白金属、蓝色货架、橙色警示线几种固定色,没必要一个物体一张贴图。
  • 用Draco压缩:glTF导出时勾选Draco网格压缩,顶点数据能省一半以上。
  • 考虑InstancedMesh:如果前端确实还需要上千个托盘单独挂数据,不要用一千个Three.js Mesh,用InstancedMesh配合属性数组管理,性能是天壤之别。

4.3 几何体和业务数据之间的ID映射

这是我在这个项目里觉得最有价值的一环。很多数字孪生前端做出来是"空壳"——模型有,但点一个货架根本不知道它是哪个货位。

我的做法是:Blender里物体名直接使用业务ID,并在导出时让Antigravity顺手生成一份map.json:

{ "S-001": { "bizId": "WS-A-01-001", "type": "shelf", "zone": "A区" }, "SL-S001-01": { "bizId": "WS-A-01-001-01", "type": "slot", "zone": "A区" } }

前端加载GLB后,遍历场景节点,用节点的name字段去查map.json,这样每个Mesh都能实时对应库存数据、设备状态、报警信息。代码模型大概是:

const loader = new GLTFLoader(); loader.load('/models/warehouse.glb', (gltf) => { gltf.scene.traverse((child) => { if (child.isMesh && mapData[child.name]) { child.userData = mapData[child.name]; } }); scene.add(gltf.scene); });

这一步做扎实了,后期挂数据就是"给userData赋值、改材质颜色"的活儿,而不是重新开发一整套映射逻辑。

5. 踩坑实录:连接失败、脚本报错、大规模卡顿

5.1 MCP Server一直连不上,卡在connecting

这个问题我在第一次配置时也遇到了。排查链路按顺序走:

  1. 回Blender看MCP面板,确认不是"start失败",而是真的显示了监听地址。如果插件面板没有地址,多半是Python依赖缺失,看Blender控制台报错,补装flask或fastapi之类的依赖。
  2. 确认Antigravity的URL和插件面板一致。最常见的坑是插件用的是/sse,Antigravity里却配成了/message;或者反过来。
  3. 重启Blender再试一次。MCP服务端口有时会被占,有时是插件代码改动后没重载,重启能解决一大半莫名其妙的问题。
  4. 确认Blender版本和插件要求的版本匹配。有的blender-mcp版本要求4.0以上,3.6 LTS也能跑,但个别API行为不同。

关键是不要一上来就怀疑网络问题。MCP走的是本地回环,绝大多数连不上是协议配对和版本问题。

5.2 AI生成的脚本老报"context incorrect"

这是Antigravity写Blender脚本时最常见的一个坑。比如它经常用bpy.ops.object.delete()清空场景,结果报错Operator bpy.ops.object.delete() ... context is incorrect。

原因很微妙:bpy.ops是一系列依赖"当前上下文"的操作符,在脚本非交互执行时,某些操作没有可用的活动对象、活动集合,它就会罢工。解决办法是让AI尽量避免bpy.ops里依赖上下文的那几个,改用bpy.data的底层API:

for obj in list(bpy.data.objects): bpy.data.objects.remove(obj, do_unlink=True)

这行代码不需要任何激活对象,直接删,干净利落。我在给Antigravity的指令里明确写了:清空场景别只用bpy.ops.delete,优先用bpy.data.objects.remove。后面脚本报错率下降很多。

5.3 几百个货架一摆就卡,Blender直接转圈

有一次我让AI建三百个货架,结果那个场景在Blender里转动视角都费劲。查了一下,AI老老实实生成了三百个独立Mesh,每个货架还上了两层架子,光网格对象就上千个。

后来改成"母货架集合+集合实例"的结构,场景瞬间轻快了。这里有个小对比可以参考:

建模方式Blender内编辑体验导出后前端Draw Call适合场景
独立Mesh逐个创建卡,文件体积大高,前端逐个渲染数量少、每个都要单独改
集合实例流畅,改母体即改全部中,glTF仍会展开多个实例大量重复摆件,建模阶段优选
导出前合并静态网格编辑期仍需实例支持低,合并成一个Mesh完全静态、无需单独交互的设备

核心思路是:能复用就用实例,能合并就合并,尽量别让浏览器和Blender同时维护上千个独立对象。

5.4 导出GLB后,前端模型发黑或发亮得刺眼

这里最常见的原因是材质参数。Blender的金属度和粗糙度导出到Three.js后,表现会和视口有差异。仓储设备一般不是高光金属,建议把货架的metallic压到0.1左右,roughness放到0.7-0.9,再配一个HDR环境贴图,视觉会舒服很多。

另一个低级但常见的错是:Blender单位是米,前端导入时又一个单位换算,导致整个模型要么大得离谱要么小得看不见。检查方法很简单,前端加载后看gltf.scene.scale,如果是1,再确认模型本身尺寸是不是十几米高。不要在两个环节各做一次缩放,很容易把坐标搞乱。

5.5 Antigravity在项目里误删文件

AI越权操作这种事,用过Agent类工具的人应该都遇到过。它读取到项目里有个"不相关但名字相近"的目录,顺手帮你清理了。我的应对措施是:

  • 把所有历史文件都放到项目目录之外的存档区;
  • 在Antigravity的指令文件里声明:scripts/和config/可以修改,docs/可以读取,assets/只读;
  • 大规模建模前,先让AI执行一次bpy.ops.wm.save_as_mainfile(filepath="backup.blend"),给自己留个后悔药。

这些动作看起来很基础,但在连续多轮交互里能救命。

5.6 AI建模时中途"彻底失联"或超时

Antigravity在处理大批量对象创建时,有时会长时间执行,看着像卡死了。其实不是卡死,是脚本在循环创建几百个物体,Blender的Python执行过程中又不刷新界面,看起来很吓人。我的习惯是:让AI每创建一批货架就打印一个进度标记,并且先跑一个10个货架的小数据,验证逻辑没问题再跑全量。宁可多花两分钟验证,也不要一次性铺几百个物体结果错了全删重来。


这次做下来的体会是:Antigravity加Blender MCP的组合,真正解决了数字孪生场景构建里"重复劳动太多、改版成本太高"的痛点。它不是让人彻底不画画、不建模,而是把大量机械的"复制、摆放、命名、导出"工作接管了,让我能把精力放到业务映射和交互设计上。

如果你准备上手,我个人的建议是先找一个小仓库,二十个货架、两条AGV路径,完整跑一遍"配置JSON→AI建模→导出GLB→前端加载"的链路,通透了再扩展到大园区甚至多楼层。下一篇文章我计划写动态部分:如何把AGV调度数据接进这个场景,包括路径规划可视化、设备状态着色、实时事件联动。到那一步,静态场景才算真正活起来。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进