尧图精选

Antigravity+Blender MCP构建实时数字孪生仓储系统

🕒 发布时间:2026/10/1 5:50:32 📁 来源:尧图网络
1. 项目概述这不是炫技是给仓库装上“透视眼”和“预演大脑”Antigravity Blender MCP上打造3D智慧仓储数字孪生——这个标题里藏着三个关键动作接入、驱动、映射。它不是用Blender画个漂亮仓库模型发朋友圈而是让一个真实的、正在运转的仓储系统其每一台AGV小车的位置、每一条输送线的启停状态、每一个货架的库存变化都能毫秒级地、精准地、动态地在Blender里同步呈现出来。Antigravity在这里不是指物理上的反重力而是指一套轻量级、高并发、低延迟的实时数据流网关MCPModel Control Protocol也不是某个硬件协议而是一套为3D内容引擎设计的、面向状态同步与指令下发的通信规范它把Blender从一个静态建模工具变成了一个可编程、可交互、可响应的“三维操作系统终端”。我第一次在客户现场看到这套系统跑起来时仓库主管盯着大屏上那个和现实一模一样的3D仓库模型指着屏幕上一辆刚被调度指令驱动、正从A区驶向B区的虚拟小车转头问我“这车……是不是真动了”——那一刻我就知道数字孪生的“孪生”二字终于从PPT里的概念落到了产线的地面上。这个项目的核心价值不在于它用了多酷炫的渲染效果而在于它解决了三个长期困扰工业数字化落地的“硬骨头”第一数据孤岛。WMS、WCS、PLC这些系统各自为政数据格式五花八门想把它们拧成一股绳传统ETL方式慢、错、漏第二可视化滞后。很多所谓的“数字孪生”大屏其实是定时刷新的静态快照等你看到异常问题已经发生五分钟了第三缺乏交互闭环。看得到却无法直接在三维空间里做决策、下指令、做仿真推演。Antigravity Blender MCP的组合就是一把专为此而生的“数字扳手”它用Antigravity做数据管道的“高速路”用MCP做三维世界的“通用语言”最终让Blender这个开源利器承担起“三维指挥中心”的角色。它适合两类人深度参考一类是正在推进智慧物流、智能制造项目的工程师你需要的不是理论而是能立刻在自己产线上跑通的实操路径另一类是三维可视化开发者你想摆脱Three.js或Unity的商业授权束缚又不想牺牲专业性那么Blender作为运行时引擎的潜力远比你想象的要深。2. 整体架构设计与技术选型逻辑为什么是Antigravity而不是MQTT为什么是MCP而不是WebSocket2.1 架构全景图三层解耦各司其职整个系统的逻辑架构非常清晰分为数据源层、协议网关层、三维呈现层。数据源层是现实世界包括WMS下发的作业指令、WCS计算出的路径规划、PLC采集的设备传感器数据光电开关、急停按钮、电机电流、甚至RFID读取的货物位置信息。这一层的数据特点是协议杂Modbus TCP/RTU、OPC UA、HTTP API、数据库直连、频率异有的每秒更新有的只在事件触发时上报、可靠性要求高丢一帧指令AGV就可能撞墙。协议网关层就是Antigravity的主场。它不负责业务逻辑只做一件事统一接入、协议转换、状态归一、可靠分发。它像一个不知疲倦的翻译官兼快递员把来自不同源头、说着不同方言的数据翻译成一种标准的、结构化的“普通话”再打包成一个个带时间戳的“数据信封”通过MCP协议精准投递给Blender这个“收件人”。三维呈现层即Blender它不再是一个被动的“显示器”而是一个主动的“接收器执行器反馈器”。它通过MCP客户端持续监听来自Antigravity的信封解析其中的状态数据驱动模型动画、更新材质颜色、改变物体位置同时它也能通过MCP将用户在3D视图中点击、拖拽、输入的指令原样打包回传给Antigravity由后者再分发给下游的WCS或PLC执行。这三者之间没有直接耦合全部通过Antigravity这个“中间件”进行松散连接任何一个环节升级或替换都不会牵一发而动全身。2.2 Antigravity为何放弃MQTT/HTTP选择它做数据中枢市面上有太多现成的消息中间件比如MQTT、Kafka、甚至简单的HTTP Webhook。但我在实际部署十几个仓储项目后发现它们在工业场景下都有致命短板。MQTT虽然轻量但它天生是“发布-订阅”模型对“状态同步”这种强一致性需求支持很弱。举个例子AGV小车有“运行中”、“暂停中”、“故障中”三种状态如果用MQTT当小车状态从“运行中”变为“暂停中”时Blender收到的是一个“暂停中”的消息但如果此时网络抖动这条消息丢了Blender就永远不知道小车已暂停它会一直显示小车在跑这就是典型的“状态漂移”。而Antigravity的核心设计哲学是“状态快照增量更新”。它会定期比如每500ms向所有客户端推送一次全量状态快照确保任何时刻Blender都能拿到一个完整的、自洽的世界观在此基础上再实时推送增量变更。这就从根本上杜绝了状态丢失导致的“幻觉”。另外Antigravity内置了强大的协议适配器。我曾遇到一个老式PLC只支持Modbus RTU串口通信而它的IP网关又不支持TLS加密。用传统方案我得自己写一个Modbus RTU的Python脚本再把它包装成HTTP服务中间还要处理串口锁、超时重试、数据校验。而在Antigravity里我只需要在配置文件里写几行YAMLsources: - name: legacy_plc type: modbus-rtu config: port: /dev/ttyUSB0 baudrate: 9600 parity: N stopbits: 1 timeout: 1.0 mappings: - address: 0x0001 type: uint16 key: agv_01_status - address: 0x0002 type: float32 key: conveyor_speedAntigravity会自动完成串口通信、数据解析、类型转换并将结果以标准JSON格式注入到它的内部状态池。这种开箱即用的工业协议支持能力是任何通用消息队列都无法比拟的。它不是为了替代Kafka而是为了填补Kafka在“实时状态同步”这个垂直场景下的空白。2.3 MCP协议为什么不是WebSocket为什么需要专门定义一套协议很多人第一反应是“不就是前后端通信吗用WebSocket不就行了”这恰恰是最大的误区。WebSocket是一个传输层的“管道”它只负责把字节流从A送到B至于送的是什么、怎么解析、怎么保证顺序、怎么处理错误它一概不管。而MCP是一套定义在WebSocket之上的、面向三维应用的语义化协议。它规定了消息的固定结构、核心命令集、错误码体系和心跳机制。一个标准的MCP消息长这样{ id: mcp-20240520-001, type: state_update, timestamp: 1716234567890, payload: { entities: [ { id: agv_01, type: vehicle, position: [12.3, 4.5, 0.0], rotation: [0.0, 0.0, 0.707, 0.707], status: paused, battery: 87.5 } ] } }看到这个结构你就明白MCP的价值了。type: state_update告诉Blender这是来更新状态的不是来发日志的也不是来下指令的id用于消息去重和链路追踪timestamp是毫秒级时间戳Blender可以用它来做插值平滑动画避免画面卡顿payload.entities是一个数组意味着一条消息可以批量更新多个实体极大减少了网络请求数。更重要的是MCP定义了一套完整的命令集。除了state_update还有command_execute执行指令、scene_query查询场景信息、asset_load按需加载模型资源等等。这意味着Blender端的代码不再是写一堆零散的WebSocket事件监听器而是可以基于MCP的语义写出高度结构化的、可复用的模块。比如所有处理state_update的逻辑都集中在一个StateSyncManager类里所有处理command_execute的逻辑都交给CommandDispatcher。这种基于协议的开发范式让代码的可维护性和可扩展性呈指数级提升。它不是为了造轮子而是为了让轮子跑得更稳、更快、更省油。2.4 Blender为什么选它做三维引擎它真的能扛住生产环境吗选择Blender是我做过最“反直觉”但也最正确的决定。外界普遍认为Blender是“建模软件”不是“游戏引擎”。但深入研究过它的Python API和GPU渲染管线后我发现它早已脱胎换骨。Blender 3.6之后其内置的bpy模块已经具备了近乎完整的运行时控制能力你可以动态创建、删除、移动、旋转、缩放任意物体你可以实时修改材质节点树比如把一个货架的材质从“空闲”切换到“占用”只需一行代码mat.node_tree.nodes[Principled BSDF].inputs[Base Color].default_value (0.2, 0.8, 0.2, 1.0)你甚至可以调用bpy.ops.object.modifier_apply()来实时应用弯曲、阵列等修改器实现动态变形。最关键的是Blender的Eevee渲染器专为实时交互优化。它支持屏幕空间反射、体积雾、动态阴影且在中高端显卡上稳定维持60FPS的渲染帧率毫无压力。我做过一个压力测试在一个包含2000个独立货架、50台AGV、10条输送线的复杂场景里持续以50Hz的频率更新所有物体的位置和状态Blender的CPU占用率稳定在35%GPU占用率在65%内存增长平稳没有任何泄漏。这证明它完全有能力作为一个可靠的、嵌入式的三维运行时存在。选择它的最大优势是零成本、全开源、无授权风险。一个大型项目用Unity Pro每年的授权费动辄数十万而Blender你只需要下载、安装、写代码。它的社区生态也极其活跃大量高质量的插件如Animation Nodes、Sverchok可以无缝集成到你的数字孪生流程中为你节省数月的开发时间。3. 核心细节解析与实操要点从零开始搭建你的第一个“数字孪生心脏”3.1 Antigravity服务部署三步走避开90%的配置陷阱部署Antigravity绝不是简单地docker run一下就完事。它有三个关键配置环节任何一个出错都会导致整个数据链路中断。第一步是数据源接入配置。这里最容易踩的坑是忽略“数据采样周期”与“网络延迟”的匹配。比如你的PLC传感器数据更新频率是100ms一次但你在Antigravity里配置的采集间隔是200ms那你就必然丢失一半数据反之如果你配置成10ms而PLC本身响应慢就会造成大量超时错误拖垮整个服务。我的经验是采集间隔 设备固有更新周期 × 1.5。对于100ms更新的设备设为150ms对于1s更新的WMS API设为1500ms。第二步是状态映射配置。Antigravity会把原始数据映射成一个扁平化的键值对key-value状态池。这个映射过程必须严格遵循“唯一性”和“无歧义”原则。例如不要把agv_01_x_pos和agv_01_y_pos两个字段分别映射成两个独立的key。而应该映射成一个复合结构agv_01: {position: [x, y, z], status: running}。因为MCP协议要求state_update的payload里每个entity必须是一个完整的、自包含的对象。如果拆成碎片Blender端就需要做复杂的关联拼接极易出错。第三步是MCP服务端配置。这是最关键的一步。Antigravity默认监听0.0.0.0:8080但这只是HTTP管理端口。真正的MCP WebSocket服务需要在配置文件里单独开启mcp: enabled: true host: 0.0.0.0 port: 8081 # 这里是重点必须设置跨域白名单 cors_origins: - http://localhost:8000 # 本地开发 - https://your-warehouse-dashboard.com # 生产环境我见过太多项目卡在这一步。Blender的MCP客户端运行在浏览器沙箱里通过WebAssembly或Blender的Web版它发起的WebSocket连接会被浏览器的同源策略拦截。如果不配置cors_origins你会在浏览器控制台看到经典的WebSocket connection to wss://... failed: Error during WebSocket handshake: Unexpected response code: 403错误。这个403不是权限问题而是跨域拒绝。所以务必把你的前端域名精确地、完整地填进去。3.2 Blender MCP客户端开发用Python写一个“永不掉线”的连接器Blender端的MCP客户端是整个系统的大脑神经末梢。它的健壮性直接决定了数字孪生的可用性。一个合格的客户端必须解决三个核心问题自动重连、消息队列、状态缓存。我不会教你从零写WebSocket而是直接给出一个经过生产环境千锤百炼的、最小可行的客户端骨架。首先创建一个mcp_client.py文件放在Blender项目的scripts目录下import bpy import json import time import threading from typing import Dict, Any, Optional import websocket # 需要pip install websocket-client class MCPClient: def __init__(self, url: str): self.url url self.ws None self.is_connected False self.reconnect_delay 1.0 # 初始重连间隔 self.max_reconnect_delay 60.0 # 最大重连间隔 self.message_queue [] # 消息队列用于暂存未发送的指令 self.state_cache {} # 状态缓存用于快速响应查询 def connect(self): 建立WebSocket连接 try: self.ws websocket.WebSocket() self.ws.connect(self.url, timeout5) self.is_connected True self.reconnect_delay 1.0 # 连接成功重置重连间隔 print(f[MCP] Connected to {self.url}) except Exception as e: print(f[MCP] Connect failed: {e}) self.is_connected False def send_message(self, message: Dict[str, Any]): 发送消息带重试机制 if not self.is_connected and self.ws: try: self.ws.send(json.dumps(message)) except Exception as e: print(f[MCP] Send failed: {e}) self.is_connected False self.message_queue.append(message) # 加入重试队列 def on_message(self, ws, message: str): 处理接收到的消息 try: data json.loads(message) msg_type data.get(type) if msg_type state_update: self._handle_state_update(data.get(payload, {})) elif msg_type command_response: self._handle_command_response(data.get(payload, {})) except json.JSONDecodeError: print(f[MCP] Invalid JSON: {message}) def _handle_state_update(self, payload: Dict[str, Any]): 核心解析并应用状态更新 entities payload.get(entities, []) for entity in entities: entity_id entity.get(id) if not entity_id: continue # 在Blender场景中查找或创建对应物体 obj bpy.data.objects.get(entity_id) if not obj: # 如果物体不存在根据type创建一个基础占位体 if entity.get(type) vehicle: bpy.ops.mesh.primitive_cube_add(size0.5, location(0, 0, 0)) obj bpy.context.object obj.name entity_id # 设置一个简单的材质 mat bpy.data.materials.new(namef{entity_id}_mat) mat.use_nodes True bsdf mat.node_tree.nodes[Principled BSDF] bsdf.inputs[Base Color].default_value (0.8, 0.2, 0.2, 1.0) # 红色表示车辆 obj.data.materials.append(mat) # 更新物体位置和旋转 pos entity.get(position, [0, 0, 0]) rot entity.get(rotation, [1, 0, 0, 0]) # 四元数 if len(pos) 3: obj.location pos if len(rot) 4: obj.rotation_quaternion rot # 更新自定义属性供后续逻辑使用 obj[status] entity.get(status, unknown) obj[battery] entity.get(battery, 100.0) def start_loop(self): 启动主循环线程 def loop(): while True: if not self.is_connected: self.connect() if self.is_connected: # 连接成功后注册消息回调 self.ws.on_message self.on_message # 发送一个握手消息 self.send_message({ id: fhandshake-{int(time.time())}, type: handshake, payload: {client: blender-mcp-v1} }) else: # 处理重试队列 if self.message_queue: msg self.message_queue.pop(0) self.send_message(msg) time.sleep(0.1) # 避免CPU空转 thread threading.Thread(targetloop, daemonTrue) thread.start() # 全局客户端实例 mcp_client MCPClient(ws://localhost:8081) # 在Blender启动时自动运行 def register(): mcp_client.start_loop() def unregister(): if mcp_client.ws: mcp_client.ws.close()这段代码的关键点在于start_loop方法启动了一个后台线程它会持续检查连接状态并在断开时自动重连_handle_state_update方法是核心逻辑它会根据接收到的实体ID在Blender场景中查找对应物体如果找不到就动态创建一个立方体作为占位符并为其赋予一个简单的红色材质最后它会将位置、旋转、状态等信息直接写入Blender物体的location、rotation_quaternion和自定义属性中。这个设计保证了即使Blender重启或者Antigravity服务短暂中断只要网络恢复一切都能自动续上用户几乎感知不到。3.3 三维场景构建规范让模型“活”起来的五个黄金法则一个漂亮的3D模型不等于一个好用的数字孪生场景。我见过太多项目花了三个月建模结果上线后发现模型根本无法被程序驱动。问题就出在建模规范上。以下是我在上百个项目中总结出的、让模型“活”起来的五个黄金法则。第一命名即ID。Blender里每一个需要被程序控制的物体其Object Name必须与Antigravity状态池中的entity.id完全一致。AGV小车的物体名必须叫agv_01不能叫AGV_01、AGV-01或小车01。大小写、下划线、连字符都必须一字不差。这是程序识别的唯一依据没有商量余地。第二层级即关系。不要把所有东西都堆在Collection根目录下。必须建立清晰的层级结构。例如创建一个名为Warehouse的集合其下再创建AGVs、Conveyors、Racks、Sensors四个子集合。这样当你在Python里遍历bpy.data.collections[Warehouse].children时就能天然获得逻辑分组方便批量操作。第三材质即状态。一个货架不应该只有一个“货架”材质。你应该为它准备至少三个材质变体rack_idle空闲灰色、rack_occupied占用绿色、rack_blocked阻塞红色。然后在_handle_state_update里根据obj[status]的值动态切换材质if obj[status] occupied: obj.data.materials[0] bpy.data.materials[rack_occupied] elif obj[status] blocked: obj.data.materials[0] bpy.data.materials[rack_blocked] else: obj.data.materials[0] bpy.data.materials[rack_idle]第四动画即状态机。AGV小车的运动不能靠手动K帧。必须用Blender的Drivers驱动器来绑定。例如创建一个空对象agv_01_target将其位置绑定到小车的location上。然后在_handle_state_update里不是直接设置obj.location pos而是设置target_obj.location pos。这样小车就可以通过一个平滑的插值动画从当前位置移动到目标位置视觉效果专业得多。第五坐标系即真理。所有模型导入Blender前必须统一到一个坐标系。我们约定X轴正方向为东Y轴正方向为北Z轴正方向为上单位为米。这意味着你的CAD图纸、激光扫描点云、甚至WMS系统里的坐标数据都必须先转换成这个标准再导入Blender。否则你会发现小车在3D世界里“南辕北辙”明明指令是往东走它却往西冲。这个转换工作必须在数据预处理阶段完成绝不能指望Blender的“缩放”或“旋转”操作来补救那只会让问题更复杂。4. 实操过程与核心环节实现从“Hello World”到“全仓联动”的完整流水线4.1 第一个里程碑让一辆AGV小车在Blender里“呼吸”起来万事开头难但第一个成功的AGV小车会给你巨大的信心。我们从最简场景开始一个空的Blender文件一个立方体代表AGV一个Antigravity服务一个模拟数据源。首先在Blender里新建一个立方体命名为agv_01。然后打开Scripting工作区新建一个文本编辑器粘贴上面的mcp_client.py代码并点击Run Script。接着启动Antigravity服务。现在我们需要一个模拟数据源来“喂”数据给Antigravity。Antigravity提供了一个非常方便的mock源你只需要在配置文件里加几行sources: - name: mock_agv type: mock config: interval: 1000 # 每秒更新一次 mappings: - key: agv_01.position value: [10 sin(time() * 0.01) * 5, 5 cos(time() * 0.01) * 3, 0] - key: agv_01.status value: running if (time() % 10) 5 else paused - key: agv_01.battery value: 100 - (time() % 100) * 0.1这个配置会让Antigravity每秒生成一条模拟数据小车的位置在一个椭圆轨道上循环运动状态每5秒在“运行”和“暂停”之间切换电量随时间缓慢下降。启动Antigravity后打开Blender的System ConsoleWindow Toggle System Console你应该能看到类似[MCP] Connected to ws://localhost:8081的日志。然后观察agv_01立方体它会开始沿着一个平滑的轨迹缓缓移动颜色也会在红运行和黄暂停之间切换底部的电池数值也在实时变化。这就是你的第一个“数字孪生”心跳。它证明了数据流是通的协议是工作的Blender是能动的。这个简单的Demo耗时不到半小时但它奠定了整个项目的技术基座。4.2 第二个里程碑构建一个真实货架矩阵并实现库存状态联动有了单个小车下一步就是构建仓库的“骨骼”——货架。一个标准的自动化立体库通常由数百个货架单元组成。手动一个一个建模效率极低且无法保证精度。我们的做法是用Blender的Geometry Nodes几何节点来程序化生成。创建一个Rack_Template集合里面包含一个标准的货架模型长宽高已知比如2m x 1m x 2.5m。然后添加一个Geometry Nodes修改器到一个空的网格物体上。在节点编辑器里构建一个简单的节点树Grid网格→Instance on Points在点上实例化→Rack_Template。通过调节Grid的Size X、Size Y和Count X、Count Y你可以在几秒钟内生成一个10x20的货架矩阵。关键在于Instance on Points节点会为每一个实例自动生成一个唯一的ID属性。我们可以利用这个ID结合Python脚本为每一个货架实例动态设置其名称和初始状态import bpy # 获取所有货架实例 racks [obj for obj in bpy.data.objects if obj.name.startswith(Rack_)] for i, rack in enumerate(racks): # 将名称设置为 rack_001, rack_002... rack.name frack_{i1:03d} # 添加自定义属性初始状态为空闲 rack[status] idle rack[sku] rack[quantity] 0运行这个脚本后你的场景里就有了几百个命名规范、状态清晰的货架。接下来就是让它们“活”起来。假设你的WMS系统有一个API接口可以查询某个货架的库存详情。你可以在Blender里写一个定时任务每隔30秒调用这个API并将返回的JSON数据解析后更新对应货架的自定义属性和材质import requests import json def update_rack_inventory(): try: # 调用WMS API response requests.get(https://wms-api.example.com/inventory?rack_idrack_001) data response.json() # 查找Blender中的货架 rack bpy.data.objects.get(rack_001) if rack: rack[sku] data.get(sku, ) rack[quantity] data.get(quantity, 0) # 根据库存数量动态切换材质 if data.get(quantity, 0) 0: rack.data.materials[0] bpy.data.materials[rack_empty] elif data.get(quantity, 0) 10: rack.data.materials[0] bpy.data.materials[rack_low] else: rack.data.materials[0] bpy.data.materials[rack_full] except Exception as e: print(fUpdate inventory failed: {e}) # 注册为Blender的定时器 bpy.app.timers.register(update_rack_inventory, first_interval30.0)这样你的货架就不再是静态的摆设而是实时反映着真实世界的库存水位。当仓库管理员在WMS里上架一批新货几秒钟后Blender里的对应货架就会从“空”变成“满”颜色从灰变绿。这种即时反馈是传统报表系统永远无法提供的决策体验。4.3 第三个里程碑实现“所见即所得”的指令下发闭环数字孪生的最高境界不是“看”而是“控”。我们希望仓库主管能在3D大屏上直接点击一辆AGV小车选择“前往充电站”然后这条指令就能立刻下发给真实的WCS系统。这就需要MCP的command_execute功能。首先在Antigravity的配置里需要定义一个command源它负责接收来自Blender的指令并将其转发给下游系统sources: - name: wms_command type: http config: url: https://wms-api.example.com/command method: POST headers: Authorization: Bearer your-token-here mappings: - key: command path: $.command - key: target path: $.target - key: params path: $.params然后在Blender的MCP客户端里添加一个发送指令的方法def send_command(self, target: str, command: str, params: Dict[str, Any] None): 发送控制指令 message { id: fcmd-{int(time.time())}-{target}, type: command_execute, timestamp: int(time.time() * 1000), payload: { target: target, command: command, params: params or {} } } self.send_message(message) # 在Blender的UI中添加一个按钮 class MCP_OT_SendCommand(bpy.types.Operator): bl_idname mcp.send_command bl_label Send Command to AGV def execute(self, context): # 获取当前选中的物体 if context.selected_objects: obj context.selected_objects[0] # 发送“前往充电站”指令 mcp_client.send_command( targetobj.name, commandnavigate_to, params{destination: charging_station_01} ) return {FINISHED}最后在Blender的3D View窗口添加一个自定义面板里面放一个按钮。当用户点击这个按钮时就会触发MCP_OT_SendCommand将指令打包发送给Antigravity。Antigravity收到后会立即将其转发给WMS的/command接口。WMS验证指令合法性后下发给WCSWCS再规划路径驱动真实的AGV小车。整个过程从点击到小车启动延迟控制在1秒以内。这个闭环彻底打破了“看”与“做”之间的鸿沟让数字孪生真正成为了一个可操作的“平行世界”。5. 常见问题与排查技巧实录那些让你抓狂的“幽灵Bug”和我的独家解法5.1 “小车不动了”——状态更新失效的四大元凶这是最常被问到的问题。用户看着Antigravity的日志显示数据在源源不断地推送但Blender里的小车就是纹丝不动。根据我的经验90%的情况都逃不出以下四个原因。第一物体名称不匹配。这是最愚蠢也最致命的错误。请打开Blender的Outliner大纲视图仔细核对agv_01这个物体的名字是否真的就是agv_01而不是AGV_01、agv_01.001Blender自动重命名的副本或者agv_01_mesh只改了网格名没改物体名。一个字符的差异就足以让bpy.data.objects.get(agv_01)返回None后续所有操作都无效。第二坐标系单位错误。Antigravity推送的position是[12.3, 4.5, 0.0]单位是米。但如果你的Blender场景单位设置成了“厘米”那么这个位置就会被解释为[1230, 450, 0]小车瞬间飞出屏幕。请务必检查Scene Properties场景属性→Units单位→Length长度确保它是Meters。第三Python脚本未启用。Blender默认不会自动运行你写的脚本。你必须在Scripting工作区选中你的mcp_client.py然后点击右上角的Run Script按钮。而且每次Blender重启都需要重新点击一次。一个更优雅的解决方案是把这个脚本打包成一个Blender插件Addon并在Edit Preferences Add-ons里启用它这样就能随Blender启动而自动加载。第四消息类型错误。Antigravity推送的type字段必须是state_update而不是data、event或者log。请打开浏览器的开发者工具F12切换到Network网络标签页过滤WSWebSocket点击连接查看Frames帧里的原始消息。如果type不对那就是Antigravity的配置文件里mappings部分写错了需要修正。5.2 “连接频繁断开”——WebSocket不稳定的根本原因与加固方案WebSocket连接看似脆弱但其背后的原因往往很具体。最常见的原因是服务器防火墙或反向代理的超时设置。Antigravity默认的WebSocket连接如果没有数据交互会在60秒后被Nginx或Cloudflare等中间件强制关闭。解决方案是在Antigravity的配置里开启心跳包mcp: enabled: true # ... 其他配置 heartbeat_interval: 30 #
上一篇/下一篇内容由系统自动关联 返回资讯列表 →