最近在给一个仓储仿真Demo做数字孪生收尾的时候,有同行问我:你用 Antigravity 让 AI 在 Blender 里搭的这座仓库,为什么看起来不像模型堆砌,反而像真能运营起来?我说,因为第二轮实战我压根没让 AI“逐个建模”,而是让它去拆规则、定参数、摆逻辑。
这篇是 Antigravity + Blender MCP 系列的下篇,主题是 3D 智慧仓储数字孪生的进阶落地。假设你已经像上篇那样,装好了 Blender 4.x,把社区主流的 Blender MCP 服务端插件跑起来,在 Antigravity 里配置好 MCP 扩展,也能用一句话让 AI 在场景里“捏个立方体出来”。接下来这轮,我们要把它从玩具变成工具:定标一片真实的仓储场景、批量生成几百个货架单元、让库存数据驱动货架上的箱子实时变化、让 AGV 小车沿着动线跑起来,最后还要保证场景不卡、渲染能出片。
内容偏实操,适合已经过了“环境搭建”阶段、想正经做一版可交付数字孪生演示的开发者。文章里的参数表、对话样例、踩坑记录都是我这轮真实跑过的,你直接抄作业即可。
1. 上篇的底子与这次进阶的边界
1.1 从“能捏立方体”到“能还原一座仓库”
上篇结束时,我们把 MCP 基本链路跑通了:Antigravity 里输入“在原点创建一个立方体,边长 2 米,涂成红色”,过几秒 Blender 里就真的多了一个红色立方体。
这条链路里有几个关键环节,先帮大家把概念串起来:
- Antigravity 是带 AI 对话能力的云端 IDE,可以在扩展市场里装 MCP 相关扩展;
- Blender MCP 服务端插件在 Blender 内部启动一个本地 MCP 服务,暴露出一系列操作 Blender 的“工具”;
- IDE 通过 MCP 协议调用这些工具,工具内部最终执行的是 Blender Python API;
- 所以你在 IDE 里说的话,本质上是“让大模型帮你写 Python 脚本并执行”。
很多教程到这一步就停了,但离“数字孪生”还差得远。数字孪生不是“用 3D 软件画一个仓库”,而是“让虚拟仓库跟真实运营数据同步”。盲目用 AI 建模做出来的只是空壳,拿到需求现场根本扛不住追问:库容量多少?通道塞得下叉车吗?空库和满库看起来有什么区别?数据一变,画面会不会跟着变?
这些问题,恰恰是进阶实战的核心。
1.2 数字孪生分三层:结构、状态、行为
我是做数字孪生项目时习惯把目标拆成三层,方便控制迭代节奏,也方便跟需求方对齐预期:
- 结构孪生:仓库的几何和布局是对的,货架间距、通道宽度、设备尺寸能对上真实平面图;
- 状态孪生:库存多少、库位是否占用、设备是否在运行,能在画面上一眼分辨;
- 行为孪生:AGV、叉车、人员的动线能按调度逻辑运动起来,而不是静止摆拍。
这次进阶实战,就是按这三层逐层做。第一层解决“像不像”,第二层解决“准不准”,第三层解决“活不活”。
如果只停留在第一层,你得到的只是一张 3D 效果图;做到第三层,才能叫“孪生”。有人说数字孪生门槛高,确实高,但主要高在“把数据接进来”和“把动线组织好”,而不是高在建模本身。建模这件事,现在有了 AI 工具链,已经把时间压缩了非常多。
2. 场景定标:让AI生成的仓库尺寸符合行业规范
2.1 为什么AI生成的“合理尺寸”经常不专业
我打开过不少人用 AI 生成的仓储场景,第一眼很好看,第二眼就看出问题:货架矮得离谱、通道宽得浪费、托盘比例不对。
原因不难理解——大模型在预训练时见过大量仓储示意图,但示意图为了美观会压缩比例,不代表工程标准。你不给它约束,它就会按“视觉合理”去生成,而不是按“行业可用”去生成。货架高度和通道宽度这些数据,恰恰是仓储方案里最敏感的东西。
所以动手建之前,我强烈建议先花十分钟定标。这一条,比我后面要讲的所有操作技巧都重要。
2.2 用一套参数把场景“钉”在真实坐标上
以国内常见的托盘式货架仓库为例,我整理了一套可以直接用的定标参数:
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| 托盘尺寸 | 1.2m × 1.0m × 0.15m | 标准平托,Blender 里 1 单位 = 1 米 |
| 货架单元宽度 | 2.6m - 2.7m | 双托盘位,两立柱间距 |
| 货架深度 | 0.9m - 1.1m | 单深位托盘存放 |
| 货架总高 | 4.5m 左右 | 3 层横梁 + 顶部结构 |
| 横梁层间隔 | 1.0m - 1.2m | 需考虑货物高度 + 100mm 安全余量 |
| 主通道宽度 | 3.5m 以上 | 叉车双向会车 |
| 货架间巷道 | 1.5m - 2.0m | AGV 通行预留 |
这些数字不是随便写的,是从仓储方案设计里常用的规范值收敛出来的。数字孪生如果只是内部演示,可以略宽松;但一旦要拿去跟客户对平面图,差 1 米都很尴尬。
我建议把这张表放在 Antigravity 项目的说明文档里,每次对话前让它“读一遍参数”,后面生成的物体尺寸就不会跑偏。这一步也可以叫“提示词里的业务上下文”。
2.3 把参数直接喂给MCP,而不是让AI自由发挥
定标完之后,第一轮 MCP 对话就不要给模糊指令了。不要只说“帮我建一个货架”,而是直接给规格:
请按以下规格创建托盘货架单元: - 货架总高 4.5 米,深度 1.0 米 - 两根立柱分别位于 x=0 和 x=2.6,y=0,立柱截面 0.12m × 0.12m - 三根横梁,高度分别为 1.2m、2.4m、3.6m,横梁截面 0.1m × 0.08m - 每根横梁上放一个托盘,尺寸 1.2m × 1.0m × 0.15m - 生成后把这些对象放入集合 COL_Rack_Demo在我实际的项目里,这类定标对话我通常会在开头再补一句前提:“所有尺寸单位均为米,使用 Blender 真实世界比例。”
这么做的好处是:AI 是执行者,不是设计者。你给它的定标越完整,后面返工越少。
2.4 为什么“单位”是数字孪生最容易翻车的细节
仓储场景里一个典型的翻车现场:有人在 MCP 里说“创建 120 × 100 的托盘”,AI 会直接生成 120 米宽的方块。你需要在项目一开始就把单位跟 MCP 对话约定好。
Blender 默认单位就是米,所以只要一直用“米”作为尺寸表述,就不会出大问题。真正容易翻车的是“通道宽度”这种模棱两可的词——AI 可能理解成“货架之间的横向间隔”,也可能理解成“巷道宽度”。我建议在项目说明文档里写清楚:
- 术语统一定义:主通道、巷道、货架间距、安全距离;
- 尺寸统一带单位:1.2m 而不是 120;
- 坐标系约定:X 为货架排布方向,Y 为巷道方向,Z 为高度。
这个“术语约束”的过程,也是做个合格数字孪生项目的底层功夫。
3. 批量建库实路:一个货架单元的MCP生成到整库复制
3.1 用MCP对话拆解“生成货架单元”这一句话
新的同学容易犯一个错误:一句话让 AI 生成“一整个仓库”。结果要么超时,要么生成的物体命名全是 Cube.001、Cube.002,后面根本没法管理。
我的习惯是拆解任务,一次只让 MCP 做一个层次的动作。生成一个货架单元,我会分四步走:
第一步,建材质:
创建两个材质:MAT_RackSteel,深灰哑光金属,粗糙度 0.6;MAT_Pallet,浅木纹色,粗糙度 0.8。第二步,建货架骨架(立柱和横梁),用前面的定标参数。
第三步,建托盘和货物箱:
在每根横梁上方放置一个托盘,规格 1.2m × 1.0m × 0.15m; 每个托盘上再放三个货物箱,每个箱子尺寸 0.8m × 0.6m × 0.5m,颜色橙色。第四步,把整个单元打包进一个集合:
选择当前场景中所有与货架、托盘、箱子相关的对象,放入新集合 COL_Rack_Cell_01。每一步之间,我都会切到 Blender 视口确认一下结果,再继续下一步。MCP 执行本身是在 Blender 侧执行的,AI 返回“执行成功”不代表模型没有把 XY 轴写反。你花十几秒拖拽检查一下,比最后整体返工划算得多。
3.2 阵列复制与集合实例:复制一百个不等于建一百个
一个货架单元生成之后,如果继续用 MCP 对话“再复制一个,位置偏移一点点”,你会发现五千平米的仓库要对话到天亮。正确的做法是:让 AI 用阵列复制的方式批量铺开。
我通常会在对话里这样下指令:
选择集合 COL_Rack_Cell_01 中的所有对象, 添加 Array Modifier,相对偏移 X = 2.9m,数量 8; 再添加一个 Array Modifier,相对偏移 Y = 2.0m,数量 4。执行完之后,一个 8 排 × 4 列的货架区就出现了。渲染时它是 32 个货架单元,但底层数据只有一份源几何。
这里要强调一下为什么用 Array Modifier 而不是逐条复制:Array 是实例化的,不会真的复制一堆几何数据,场景文件不会爆炸。每个货架单元在内存里只有一份源数据,其余的都是“引用”。这在后面处理上千物体时是保命级别的操作。
3.3 Outliner命名规范:数字孪生场景的“门牌号”
数字孪生要跟数据对应,物体的名字就是“门牌号”,名字不规范,数据驱动阶段就是灾难。
我这轮实战用的前缀规范很简单:
| 前缀 | 含义 | 示例 |
|---|---|---|
| MAT_ | 材质 | MAT_RackSteel |
| OBJ_ | 普通物体 | OBJ_Rack_01_L1_Pallet |
| COL_ | 集合 | COL_Zone_A_Receiving |
| EPT_ | 空物体 | EPT_AGV_Path_01 |
| CRV_ | 曲线 | CRV_AGV_Route_02 |
这个规范我会写进项目说明文档,然后要求 MCP 对话里所有生成的对象都按这个规则命名。AI 执行时一般都会遵守,偶尔漏了,你手动改一下就行。
命名的重要性,在后面“数据上屏”那一章会更加明显——脚本要按名字找物体,名字全乱的话,你会在调试数据映射上浪费掉半天时间。
4. 让库存“开口说话”:数据驱动的货架可视化方案
4.1 数据链路:WMS快照 → 自定义属性 → 场景呈现
搞定了结构孪生,接下来是状态孪生:让货架上的东西随库存数据变化。
真实项目里,数据一般来自 WMS(仓储管理系统)或 OMS 订单系统,返回的是 JSON 快照。演示项目里没有真实接口,我就写一个模拟器,但数据链路的形状必须跟真实项目一致:取数 → 解析 → 映射 → 刷新场景。
一个典型的库存快照长这样:
{ "snapshot_time": "2025-06-15 10:30:00", "bins": [ {"bin_id": "RACK_01_L1", "sku": "A1001", "qty": 8, "max_qty": 12}, {"bin_id": "RACK_01_L2", "sku": "A1002", "qty": 3, "max_qty": 12}, {"bin_id": "RACK_01_L3", "sku": "", "qty": 0, "max_qty": 12} ] }在 Blender 这边,策略是:每个库位对应一个物体,给物体加自定义属性stock_qty。Blender 物体的自定义属性可以被 Python 读取,也可以被 Driver 引用,所以它就相当于是“数据层”和“表现层”之间的接口。
4.2 三种映射方式:可见性、缩放、换色
拿到库存数量之后,怎么在画面上表现?我试过三种方式,各有适用场景:
- 可见性映射:在库位上预先叠好 4 层箱子,
stock_qty决定显示前几层。最直观,也最不容易穿帮。 - 缩放映射:把整组箱体的高度按填充率缩放。适合表达“批量体积”,但货物是硬箱子,缩放会变形,用得少。
- 颜色映射:空库位灰色、满库位绿色、拣货中橙色、盘点中蓝色。适合做库位状态总览,数据密度极高。
实战里我推荐“可见性 + 颜色”组合。远看配色能看出库区饱和度,近看箱子层数能看出具体数量,信息量足够大。
实现方式可以用一段 Python 脚本,直接在 Blender 文本编辑器里运行:
import bpy def update_bin_visual(bin_obj): qty = bin_obj.get("stock_qty", 0) max_qty = bin_obj.get("stock_capacity", 12) # 按填充率控制箱体可见层数 for i in range(1, 5): layer_obj = bpy.data.objects.get(f"OBJ_{bin_obj.name}_Layer_{i}") if layer_obj: layer_obj.hide_set(i > qty) layer_obj.hide_render = i > qty # 按状态切换颜色 if qty == 0: set_obj_color(bin_obj, (0.4, 0.4, 0.4, 1.0)) # 灰色空库位 elif qty / max_qty > 0.7: set_obj_color(bin_obj, (0.2, 0.7, 0.3, 1.0)) # 绿色满库 else: set_obj_color(bin_obj, (0.9, 0.6, 0.1, 1.0)) # 橙色拣货这个函数的功能很朴素:读属性、改可见性、改颜色。但把它挂在数据链路后面,整个场景就有了“实时感”。
4.3 用定时器脚本让场景主动跟随数据刷新
真实数字孪生项目里,数据不会只刷一次。演示场景里我做了一个每秒刷新的模拟器,用 Blender 的 Application Timer 来驱动:
import bpy import random def mock_wms_snapshot(): return [ {"bin_id": "RACK_01", "qty": random.randint(0, 12)}, {"bin_id": "RACK_02", "qty": random.randint(0, 12)}, ] def refresh_from_wms(): data = mock_wms_snapshot() for item in data: obj = bpy.data.objects.get(item["bin_id"]) if obj: obj["stock_qty"] = item["qty"] update_bin_visual(obj) # 返回间隔秒数,Blender 会周期性调用 return 1.5 bpy.app.timers.register(refresh_from_wms)运行之后,每隔 1.5 秒场景里的库存就会增加或减少、箱子层数会变化、库位颜色会切换。把模拟数据换成真实的 WMS 接口,逻辑完全不用改。
这一层做完成,你拿着场景给客户演示“库存实时可视化”就没有任何问题了。
5. AGV与叉车的动线仿真:曲线、约束与层级组装
5.1 用贝塞尔曲线给AGV修一条“路”
状态孪生做完,进入行为孪生:让设备动起来。
AGV 运动第一步,是给小车修一条路。Blender 里最合适的对象是贝塞尔曲线,因为它是 3D 的、可编辑、能控制转弯半径。我一般用 MCP 对话生成路径:
创建一条 3D 贝塞尔曲线,命名为 CRV_AGV_Route_01, 依次经过点:(0, 0, 0) → (10, 0, 0) → (12, 4, 0) → (20, 4, 0), 所有控制点手柄类型设为自动。如果你在脚本里做,也很简单:
import bpy def create_agv_path(name, points): curve_data = bpy.data.curves.new(name, type='CURVE') curve_data.dimensions = '3D' spline = curve_data.splines.new('BEZIER') spline.bezier_points.add(len(points) - 1) for i, (x, y, z) in enumerate(points): bp = spline.bezier_points[i] bp.co = (x, y, z) bp.handle_left_type = 'AUTO' bp.handle_right_type = 'AUTO' obj = bpy.data.objects.new(name, curve_data) bpy.context.collection.objects.link(obj) return obj路径的转弯半径不要太锐。真 AGV 的转弯半径一般是 2m 到 4m,曲线如果折成直角,动画看起来会像人在跳华尔兹;而且约束算法在急弯处会出现车身“漂移”的视觉问题。
5.2 Follow Path + Empty:让小车不“点头”的封装方式
给小车直接加 Follow Path 约束,最常见的坑就是小车上下“点头”——因为约束的轴向对齐没设置好,经过曲率变化时车体会顺着曲线法线翻转。
我的标准做法是引入一个 Empty 作为“代理”:
- 创建 Empty_AGV_01,给它加 Follow Path 约束,目标指向 CRV_AGV_Route_01;
- 把“车体模型”作为 Empty 的子级挂进去;
- 车体模型的朝向手动对齐一次,让车头方向跟路径前进方向一致;
- 通过修改 Empty 的
offset值,让车沿路径移动。
为什么套一层 Empty?因为 Follow Path 约束会把物体的原点拽到曲线上,还会修改物体的旋转,直接在车体上加会很不可控。隔一层 Empty 之后,你随时可以调整车体相对路径的偏移和姿态,不影响约束本身。
在 MCP 对话里,我会说:
创建 Empty 对象 EPT_AGV_01,添加 Follow Path 约束,目标为 CRV_AGV_Route_01, 开启 Use Curve Follow,forward 轴设为 -Y,up 轴设为 Z; 将 AGV 车体模型设为 EPT_AGV_01 的子级。5.3 用路径进度驱动轮子旋转和叉子升降
设备走路只是基础,细节在轮子转不转、叉子抬不抬。
Follow Path 约束里有个 Offset 值,范围默认是 0 到 1,代表走完整条路径的进度。我们可以把四个轮子的 Z 轴旋转跟这个 Offset 挂钩,让轮子转起来。
在脚本里我用 Driver 实现:
def add_wheel_driver(wheel_obj, empty_obj): driver = wheel_obj.animation_data.driver_add("rotation_euler", 1) # Y轴 driver.driver.type = 'SCRIPTED' driver.driver.expression = "offset * 18.0 * 6.28" var = driver.driver.variables.new() var.name = "offset" var.targets[0].id = empty_obj var.targets[0].data_path = '["offset"]'这里的 18.0 表示小车走完整条路径时轮子旋转 18 圈,数值可以按轮径和路径长度反推。关键是“轮子转速跟 Offset 变化率保持一致”,只要表达式里是线性关系,轮子看着就会跟地面摩擦吻合。
叉子升降多数场景不需要 Driver,直接 K 帧就行。比如 Offset 走到 0.35 时叉子抬到 0.8m,Offset 到 0.5 时放下来。这种关键帧动画用 MCP 对话来打也完全可行。
6. 从单台设备到协同场景:调度逻辑的呈现方式
6.1 用数据任务队列驱动多台车辆
单台 AGV 会走之后,就该考虑“一个车队同时干活”的画面了。真实 WMS 里任务本质是“从哪取货、送到哪”,所以我的演示模型也是按这个思路来:
tasks = [ {"vehicle": "AGV_01", "from_bin": "RACK_01_L1", "to_station": "DOCK_01"}, {"vehicle": "AGV_02", "from_bin": "RACK_03_L2", "to_station": "DOCK_02"}, ]每个任务生成一条对应路径曲线,AGV 各自沿路径运动。路径既可以预先在路网上计算,也可以在最简单的情况下用“直线 + 固定拐角”拼出来。
我实际项目的做法是:在场地里预埋路网节点,任务下发后,数据层算好关键路径点,再让脚本依据路径点生成贝塞尔曲线,最终赋值给每台 AGV 的 Follow Path 约束。Blender 只负责表现,业务层的调度、避碰、任务分配全放在数据层。这个边界很重要,否则你会花大量时间在 Blender 里写调度算法,那不是动画软件该干的事。
6.2 多设备的时间轴组织
两台 AGV 同时跑,时间轴就乱起来了。我的组织方式有三个原则:
- 每台设备一个集合,集合里含曲线、Empty、车体三件套;
- 每台设备的关键帧打在 Empty 的 Offset 属性上,不要打在车体子物体上;
- 用 Blender 的 Action 把每台设备的动画拆开,方便单独循环或重新编排。
如果想让画面看起来更像“调度”,可以把不同设备的 Offset 动画在时间轴上错开一点。比如 AGV_01 在 0 到 120 帧跑完,AGV_02 在 60 到 180 帧跑,中间自然形成交叉作业的观感。
值得强调的是,不要为了让所有设备“同时出发”而把时间轴排得太整齐。真实仓库里车辆是陆续接任务的,错峰出发反而更像真实运营。
6.3 把“撞车”问题留给数据层
初学做多车动画时,很容易试图在 Blender 里做防碰撞——让两辆车快接近时互相刹车。这个方向性价比极低。
我建议把调度冲突放到数据层处理:任务队列生成时,就保证同一时间段两台车不占用同一路径段。Blender 只负责“把数据层给的结果演出来”。这样做,画面干净,代码也好控制。
如果你一定要在演示里体现“避让”场景,也可以在数据层人为制造一个“AGV_02 在路口等待,AGV_01 先通过”的任务序列,然后用 Offset 动画的停顿区间表现出来。这比实时碰撞检测稳定得多。
7. 性能底线与渲染收敛:上千物体不卡的整理术
7.1 几何实例和材质复用的实际收益
场景铺满货架、托盘、AGV 之后,物体数量很容易冲到两三千。如果不做任何优化,Blender 会明显开始卡顿。
| 方案 | 1个货架单元的对象数 | 50个单元的几何数据 | 视口流畅度 |
|---|---|---|---|
| 普通复制 | 12 | 600 份独立几何 | 明显卡顿 |
| Array Modifier | 12 | 1 份源几何 + 实例 | 流畅 |
| Geometry Nodes 实例化 | 12 | 1 份源几何 + 节点实例 | 流畅 |
所以核心原则就是:重复资产永远不要“真复制”,必须用 Array Modifier 或 Geometry Nodes 的实例化能力。
这块我在前面货架铺排时已经用上了,但要注意:Array Modifier 产生的实例不能单独变换。如果某个位置要特殊处理,比如某个库位缺货、某个货架损坏,你需要先对那个实例单独“Apply”成真几何,再修改它。
7.2 材质复用的原则
材质是另一个容易失控的地方。我见过有人给每个货架都分配一个新材质球,结果材质列表上百个,后期调色痛苦不堪。
我的原则很简单:
- 一个金属材质贯穿所有货架,粗糙度统一;
- 托盘一个木纹材质;
- 货物箱用 2 到 3 个固定颜色变体;
- 地面、墙面、标线各自一个基础材质。
总材质数量控制在 15 个以内。颜色变体优先用同一套 Principled BSDF 的色值微调,避免复制出一堆节点树。这样渲染器加载时间短、显存占用低,而且画面风格更统一。
7.3 EEVEE Next的渲染参数与灯光思路
数字孪生演示通常要求“能出工程视频”,我用的是 Blender 4.2 以上的 EEVEE Next 实时渲染器作为主引擎,速度比 Cycles 快得多,画面质感也足够。参数参考:
| 参数项 | 推荐值 |
|---|---|
| 分辨率 | 1920 × 1080 |
| 采样 | 64 |
| 阴影分辨率 | 2048 |
| 环境光遮蔽 | 开启 |
| 辉光(Bloom) | 开启,强度 0.3 左右 |
灯光方面,仓储场景不用追求电影感,重要的是“看得清”和“层次分明”:
- 大面积柔光用 Area Light 打底,模拟顶棚照明;
- 巷道方向加一两个聚光灯做引导视线;
- 货架深色金属容易“死黑”,适当提高环境光的亮度。
这种布光方式对 EEVEE 很友好,实时预览的所见,基本就是渲染输出的所得。
8. 实战踩坑记录:命名塌方、实例串位与刷新错位
8.1 MCP操作“静默失败”:Console与命名纪律
这轮实战我最开始翻的第一个车,是 MCP 返回“成功”,但 Blender 里什么也没发生。
原因有几类:要么是对象名写错了,脚本执行到一半抛异常;要么是 MCP 调用的工具跟插件版本不匹配;要么是对话上下文太长导致 AI 搞混了操作对象。
排查方法很简单:在 Blender 里打开 Window > Toggle System Console,所有 Python 报错都会打在这个控制台里。MCP 在 IDE 里返回的“成功”信息往往比较笼统,不会告诉你背面具体的堆栈。
另外,命名纪律真是救命的。我试过生成一批货架后,AI 在下一轮对话里把“COL_Rack_Cell_01”理解成了“COL_Rack_Cell_1”,然后对新集合做操作,导致场景里出现了两个看起来一样的集合,改起来非常痛苦。从那以后,我对所有 MCP 对话里的对象名,都坚持用统一前缀的完整命名。
8.2 实例化后局部变更引发的“牵一发动全身”
第二个坑来自 Array Modifier 的实例特性。
货架区铺好之后,我想把第 5 排第 2 列的货架“清空”来显示空库位效果。直接选中那个实例、删除上面的箱子,结果所有阵列实例一起变了——因为它们共享同一份源几何。
解决方案有两种:
- 对指定位置的实例执行 Convert(应用修饰器),把它变成独立物体再单独修改;
- 或者在数据驱动阶段,用“隐藏”而不是“删除”来处理空库位。
我实际采用后者更多,因为隐藏是数据层可直接控制的,不需要手动建模干预,也更符合数字孪生的“状态可变”理念。
8.3 动画与数据刷新时机错位
第三个坑出现在库存刷新和 AGV 动画同时运行时。
我的 Timer 是 1.5 秒刷新一次,AGV 路径动画是 0 到 240 帧。在动画播放中,刷新库存会触发大量隐藏/显示操作,这些操作会打断当前帧的设备运动状态,导致小车在路径上“瞬移”或者停顿。
后来我的解决办法是:把“数据刷新”和“设备动画”放在两个不同的场景层,或者用 Blender 的 场景时间 做分区——在设备跑动的时间窗口内,暂停库存刷新;库存刷新集中在展示“库存总览”的镜头时段。
数字孪生项目本质是多个系统在抢同一个画面资源,谁先谁后,必须提前设计好。这是我这一轮实战最大的体感。
如果后续你还想继续扩展,可以把这一版场景接上真实的 WMS 接口,把 JSON 快照换成实时推送;也可以在输出端加一个自动渲染队列,让 Antigravity 根据脚本批量渲染不同镜头。数字孪生的路,建模只是起点,数据才是灵魂。