尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

AI驱动实时参数化3D建模:基于LLM与几何内核的CAD交互新范式

AI驱动实时参数化3D建模:基于LLM与几何内核的CAD交互新范式 1. 项目概述从痛点出发一个AI驱动的3D建模新思路最近两年AI在内容生成领域的爆发大家有目共睹从文生图到文生视频创造力似乎被无限放大。但作为一名长期与三维设计打交道的工程师我观察到一个有趣的“断层”在工业设计、产品原型、建筑规划等硬核领域主流的3D建模软件比如SolidWorks、Fusion 360、Rhino依然遵循着严谨但繁琐的“草图-约束-特征”工作流。想改一个尺寸你得找到对应的草图修改驱动尺寸然后等待模型重建。这个过程对于创意迸发和快速迭代来说无疑是一道高墙。于是一个想法在我脑子里盘旋能不能让AI理解我们的设计意图像对话一样去修改一个复杂的参数化3D模型比如我说“把那个圆柱体的高度增加20毫米并且把顶部的倒角半径改成5毫米”模型就能实时、准确地响应。这听起来像是CAD软件的终极幻想但结合当下的大语言模型LLM和几何内核技术我认为已经具备了初步实现的可能性。经过两天的集中“倒腾”我把这个想法变成了一个可运行的原型并决定将其开源。我把它称为“AI驱动的实时3D CAD / 参数化建模工作台”。它的核心目标不是替代传统的CAD软件而是探索一种全新的、更直观的人机交互范式让设计师和工程师能更专注于“设计什么”而不是“怎么画”。这个工作台本质上是一个桥梁一端连接着自然语言或高级指令另一端连接着底层的几何建模内核通过AI来解析意图并驱动参数变化实现模型的实时更新。2. 核心设计思路与技术选型拆解2.1 整体架构三层解耦各司其职要实现“AI驱动实时参数化建模”整个系统需要清晰的分层我将其设计为三层结构交互层、AI解析层和几何引擎层。这种解耦设计保证了系统的灵活性和可扩展性。交互层是用户入口可以是Web界面、桌面应用插件甚至是一个聊天机器人接口。在这个原型中我选择用Web前端React Three.js来快速搭建一个可视化环境用户可以在3D视口中查看模型并在一个输入框里用自然语言描述修改意图。AI解析层是整个系统的“大脑”也是最关键的部分。它的任务是将用户模糊的自然语言指令精准地翻译成几何引擎可以理解的、结构化的操作命令。这里面临几个核心挑战1) 理解设计语境哪个零件、哪个特征2) 识别几何实体面、边、体3) 解析数值和操作增加、减少、改为。我选择使用大语言模型LLM作为解析核心但不是直接调用公开的通用模型而是通过精心设计的提示工程Prompt Engineering和函数调用Function Calling技术将其“调教”成一个专业的CAD指令翻译官。几何引擎层是系统的“双手”负责执行具体的建模操作。市面上有开源的几何内核可供选择如Open CASCADE Technology (OCCT) 和 CGAL。考虑到OCCT在工业级B-Rep边界表示建模方面的成熟度和社区支持我选择了它作为后端的几何建模内核。AI解析层输出的结构化命令将被转换成对OCCT API的调用从而真正地修改模型的参数、重建几何体。2.2 为什么是“参数化”与“实时”参数化建模是传统CAD的基石它意味着模型由一系列特征拉伸、旋转、打孔、倒角等和驱动这些特征的参数尺寸、角度、位置等构成。修改参数模型自动更新。我们的工作台并非创造新的参数化系统而是“接入”并“操控”现有的参数化定义。“实时”则是本次尝试追求的体验突破。在传统CAD中修改参数后需要手动点击“重建”或“更新”按钮计算复杂的模型时可能会有卡顿。我们的目标是实现“指令即更新”用户输入指令AI解析并生成操作几何引擎计算结果近乎同步地反馈到3D视图中。这要求AI解析速度要快几何引擎的局部更新能力要强。为此我在架构上做了优化比如让AI只解析增量修改指令并尝试利用OCCT的局部重生成特性避免每次都对整个模型进行完全重建。2.3 技术栈的深度考量前端 (React Three.js React Three Fiber)React用于构建高效的UI组件Three.js是强大的WebGL 3D库而React Three Fiber简称R3F是用React的方式来编写Three.js代码的渲染器它能极大简化Three.js的复杂度让我们像写React组件一样去管理3D场景、模型和交互开发效率非常高。后端 (Python FastAPI)Python在AI和科学计算领域的生态无可替代。FastAPI是一个现代、快速高性能的Web框架用于构建API。它自动生成交互式API文档非常适合作为连接前端、AI服务和几何引擎的中介。AI服务 (OpenAI API / 本地LLM)初期为了验证核心逻辑的可行性我直接使用了OpenAI的GPT-4 API因为它具有强大的指令跟随和结构化输出能力。在系统提示词System Prompt中我详细定义了“角色”你是一个CAD专家、可用的“函数”修改尺寸、添加倒角、移动位置等以及输入输出的JSON格式。长远来看为了降低成本和控制数据隐私可以微调开源的较小模型如Code Llama、DeepSeek-Coder专门用于CAD指令解析。几何内核 (Open CASCADE): OCCT是一个功能完整的开源几何建模内核。它通过C暴露API。为了在Python环境中调用我使用了pythonocc-core这个Python封装库。它允许我们在Python中直接创建和修改B-Rep模型虽然性能上可能略逊于原生C但对于原型验证和许多应用场景来说已经完全足够。注意直接使用云端的LLM API意味着你的设计指令数据会发送到第三方服务器。对于涉密或敏感的商业设计这是不可接受的。因此生产环境必须考虑部署私有化模型或使用具备本地推理能力的开源模型。3. 核心模块实现与实操要点3.1 AI指令解析器的构建从“人话”到“机器指令”这是项目中最具挑战也最有趣的部分。你不能简单地把用户的话扔给GPT然后说“去改模型”。你需要引导它给它工具。首先我定义了一个清晰的“对话协议”。每次用户输入系统都会附带当前的模型状态摘要以文本形式描述如“模型中有一个名为‘BasePlate’的长方体长100mm宽50mm高10mm其上有一个名为‘Cylinder_1’的圆柱体位于长方体上表面中心直径20mm高30mm。”。这为AI提供了上下文。其次我利用LLM的函数调用能力预定义了一系列“CAD操作函数”。例如{ name: modify_dimension, description: 修改一个已存在特征的驱动尺寸。, parameters: { type: object, properties: { feature_name: {type: string, description: 需要修改的特征名称如 Extrude1}, parameter_name: {type: string, description: 参数名称如 height, diameter}, new_value: {type: number, description: 新的参数值单位毫米}, value_type: {type: string, enum: [absolute, delta], description: ‘absolute’表示设为该值‘delta’表示增加或减少该值} }, required: [feature_name, parameter_name, new_value, value_type] } }当用户输入“把圆柱的高度增加10毫米”时AI的思考过程应该是1) 上下文里有个“Cylinder_1”2) 它的高度参数是30mm3) 用户说要“增加10毫米”这是delta操作4) 因此调用modify_dimension函数参数为{“feature_name”: “Cylinder_1”, “parameter_name”: “height”, “new_value”: 10, “value_type”: “delta”}。后端收到这个结构化JSON后就可以精准地去OCCT模型中找到对应特征和参数进行修改。实操心得编写高质量的函数描述和系统提示词是关键。描述要极度精确避免歧义。例如“修改尺寸”要明确是“驱动尺寸”而不是模型上某个测量值。初期需要大量“人肉测试”来调整提示词收集AI理解出错的案例不断优化描述语言。3.2 几何引擎的对接与参数映射OCCT模型本身并不直接存储我们熟悉的“特征树”和“参数名”。在OCCT中你通过一系列API调用BRepPrimAPI_MakeBox,BRepFilletAPI_MakeFillet等创建形状修改则需要通过变换或重建。为了建立参数化关联我引入了一个“特征定义层”。每当通过AI或界面创建一个特征如拉伸一个草图我不仅用OCCT生成最终形状还会在内存中记录一个“特征对象”。这个对象包含唯一ID、类型拉伸、旋转等、所依赖的草图或边、以及一个参数字典。例如一个拉伸特征的特征对象可能像这样{ “id”: “extrude_001”, “type”: “extrude”, “profile_sketch_id”: “sketch_001”, “parameters”: {“distance”: 30, “direction”: “positive”}, “occt_shape”: OCCT TopoDS_Shape object # 关联的OCCT几何体 }当AI指令要求修改extrude_001的distance为50时后端会1) 根据ID找到特征对象2) 更新参数字典3) 根据特征类型和参数重新执行一遍该特征的构建逻辑用新的距离值再次调用拉伸API4) 用新生成的OCCT形状替换旧的。这就实现了参数化驱动。注意事项特征之间的依赖关系父子关系必须被妥善管理。例如一个打孔特征依赖于某个实体面。当父实体如一个方块的尺寸被修改后打孔特征所依赖的面可能已经不存在比如被拉伸掉了。一个健壮的系统需要能检测这种拓扑变化并尝试重新附着特征或给出明确的错误提示。在原型阶段我简化处理了这个问题主要关注独立特征的参数修改。3.3 实时同步与前端渲染为了实现“实时”效果我采用了WebSocket进行前后端全双工通信。流程如下用户在网页输入框输入“将长方体长度改为150mm”。前端通过WebSocket将指令和当前场景ID发送给后端。后端AI解析、OCCT计算生成新的模型数据。后端将模型变化部分通常是整个更新后的模型或差异数据通过WebSocket推回前端。前端Three.js收到数据更新3D场景中的对应模型网格。这里的一个性能关键是模型数据的序列化与传输。OCCT生成的B-Rep模型数据很庞大直接传输不可行。我的做法是在后端将OCCT形状转换为轻量级的三角网格数据使用OCCT的BRepMesh_IncrementalMesh类进行网格化然后只将顶点、法线、索引等网格信息序列化为JSON或二进制格式如GLB发送给前端。前端Three.js接收后直接更新BufferGeometry的属性实现快速重绘。踩坑记录最初我尝试在每次修改后传输整个模型的网格数据当模型稍微复杂时网络延迟和前端解析耗时就会让“实时”体验大打折扣。后来改为“差异更新”策略即只传输发生变化的特征对应的网格数据并在前端进行局部替换流畅度得到了质的提升。这要求前后端对模型树的组织结构有精确的同步映射。4. 从零搭建手把手实现一个基础原型如果你也想亲手尝试构建这样一个系统的核心部分可以跟着以下步骤操作。我们将聚焦于最关键的“AI解析参数修改”链条用一个简单的长方体修改为例。4.1 环境准备与依赖安装首先创建一个新的Python虚拟环境是个好习惯。# 创建并激活虚拟环境 python -m venv aicad_env source aicad_env/bin/activate # Linux/macOS # 或 aicad_env\Scripts\activate # Windows # 安装核心依赖 pip install fastapi uvicorn websockets openai pythonocc-core pip install “pydantic[email]” # 用于数据验证这里fastapi和uvicorn用于构建Web服务器和APIwebsockets用于WebSocket支持openai是调用GPT API的库pythonocc-core就是OCCT的Python绑定。4.2 后端核心服务搭建我们创建一个main.py文件构建一个简单的FastAPI应用包含WebSocket端点和模型管理逻辑。from fastapi import FastAPI, WebSocket, WebSocketDisconnect from pydantic import BaseModel import openai import json from OCC.Core.gp import gp_Pnt from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox from OCC.Core.BRepTools import BRepTools_ShapeSet from OCC.Core.TopoDS import TopoDS_Shape import asyncio app FastAPI() # 初始化OpenAI客户端请替换为你的API Key openai.api_key “your-openai-api-key” # 简单的内存模型存储 class CadModel: def __init__(self): self.features {} # 存储特征对象 self.next_id 1 def create_box(self, name, length, width, height): 创建一个长方体特征 box_builder BRepPrimAPI_MakeBox(length, width, height) shape box_builder.Shape() feature_id f”feature_{self.next_id}” self.next_id 1 feature { “id”: feature_id, “name”: name, “type”: “box”, “parameters”: {“length”: length, “width”: width, “height”: height}, “shape”: shape } self.features[feature_id] feature return feature_id, shape def modify_box_dimension(self, feature_id, param_name, new_value): 修改长方体尺寸并重建 if feature_id not in self.features: return None feature self.features[feature_id] if feature[“type”] ! “box”: return None # 更新参数 feature[“parameters”][param_name] new_value l feature[“parameters”][“length”] w feature[“parameters”][“width”] h feature[“parameters”][“height”] # 重建几何体 box_builder BRepPrimAPI_MakeBox(l, w, h) new_shape box_builder.Shape() feature[“shape”] new_shape return new_shape # 全局模型实例 global_model CadModel() box_id, initial_shape global_model.create_box(“MyBox”, 100.0, 50.0, 10.0) def shape_to_simple_dict(shape: TopoDS_Shape): 一个极其简化的函数将形状转换为可JSON序列化的描述。 实际应用中这里应该进行网格化并导出顶点数据。 # 此处仅为演示返回一个边界框 from OCC.Core.Bnd import Bnd_Box from OCC.Core.BRepBndLib import brepbndlib_Add bbox Bnd_Box() brepbndlib_Add(shape, bbox) xmin, ymin, zmin, xmax, ymax, zmax bbox.Get() return {“bounds”: [xmin, ymin, zmin, xmax, ymax, zmax]} app.websocket(“/ws”) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() try: # 首次连接发送初始模型状态 model_state { “features”: list(global_model.features.values()), “shape_info”: shape_to_simple_dict(initial_shape) } await websocket.send_json({“type”: “init”, “data”: model_state}) while True: # 接收用户指令 data await websocket.receive_text() user_message json.loads(data).get(“message”, “”) # 构建给AI的提示词 system_prompt “””你是一个CAD操作解析器。用户会描述对3D模型的修改。 当前模型有一个长方体特征ID是 ‘{box_id}’ 名称是 ‘MyBox’ 当前尺寸长100mm 宽50mm 高10mm。 你只能回复一个JSON对象格式如下 {“feature_id”: “feature_1”, “operation”: “modify_dimension”, “parameter”: “length”, “value”: 150, “action”: “set”} 其中operation目前只支持 ‘modify_dimension’。 parameter可以是 ‘length’, ‘width’, ‘height’。 action可以是 ‘set’ (设置为) 或 ‘delta’ (增加/减少)。 如果无法理解返回 {“error”: “无法解析指令”}。 “””.format(box_idbox_id) # 调用OpenAI API response openai.ChatCompletion.create( model”gpt-3.5-turbo”, # 为快速演示使用gpt-3.5 messages[ {“role”: “system”, “content”: system_prompt}, {“role”: “user”, “content”: user_message} ], temperature0.1 # 低温度保证输出稳定 ) ai_response response.choices[0].message.content print(“AI Raw Response:”, ai_response) try: command json.loads(ai_response) if “error” in command: await websocket.send_json({“type”: “error”, “message”: command[“error”]}) continue # 执行命令 if command[“operation”] “modify_dimension”: fid command[“feature_id”] param command[“parameter”] val command[“value”] # 这里简化处理假设action’set’ new_shape global_model.modify_box_dimension(fid, param, val) if new_shape: updated_info shape_to_simple_dict(new_shape) await websocket.send_json({ “type”: “model_updated”, “feature_id”: fid, “new_bounds”: updated_info[“bounds”] }) else: await websocket.send_json({“type”: “error”, “message”: “修改失败”}) else: await websocket.send_json({“type”: “error”, “message”: “不支持的操作”}) except json.JSONDecodeError: await websocket.send_json({“type”: “error”, “message”: “AI返回格式错误”}) except WebSocketDisconnect: print(“Client disconnected”) if __name__ “__main__”: import uvicorn uvicorn.run(app, host”0.0.0.0″, port8000)4.3 前端简易界面实现创建一个index.html文件使用原生JavaScript和WebSocket进行通信。!DOCTYPE html html head titleAI CAD 原型/title script src”https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js”/script style body { margin: 0; overflow: hidden; } #container { width: 100vw; height: 100vh; } #inputPanel { position: absolute; top: 10px; left: 10px; background: rgba(255,255,255,0.8); padding: 10px; border-radius: 5px; } /style /head body div id”container”/div div id”inputPanel” input type”text” id”commandInput” placeholder”输入修改指令如把长度改为150毫米” style”width: 300px;” / button onclick”sendCommand()”执行/button p id”status”状态等待连接.../p p id”modelInfo”/p /div script let socket; let scene, camera, renderer, boxMesh; const wsUrl ‘ws://localhost:8000/ws’; function init() { // 1. 初始化Three.js场景 scene new THREE.Scene(); camera new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById(‘container’).appendChild(renderer.domElement); camera.position.z 100; // 2. 添加基础光源 const light new THREE.DirectionalLight(0xffffff, 1); light.position.set(10, 10, 10); scene.add(light); scene.add(new THREE.AmbientLight(0x404040)); // 3. 连接WebSocket connectWebSocket(); // 4. 动画循环 animate(); } function connectWebSocket() { socket new WebSocket(wsUrl); socket.onopen function(e) { document.getElementById(‘status’).textContent ‘状态已连接’; }; socket.onmessage function(event) { const data JSON.parse(event.data); console.log(‘Received:’, data); handleServerMessage(data); }; socket.onerror function(error) { console.error(‘WebSocket Error:’, error); document.getElementById(‘status’).textContent ‘状态连接错误’; }; socket.onclose function(event) { console.log(‘WebSocket Closed:’, event); document.getElementById(‘status’).textContent ‘状态连接关闭’; }; } function handleServerMessage(data) { switch(data.type) { case ‘init’: updateModelDisplay(data.data); break; case ‘model_updated’: alert(模型已更新特征 ${data.feature_id} 的新边界: ${data.new_bounds}); // 在实际中这里应该请求完整的模型数据或差异数据来更新网格 // 此处仅作演示 break; case ‘error’: alert(错误${data.message}); break; } } function updateModelDisplay(modelData) { // 清空旧模型 if (boxMesh) scene.remove(boxMesh); // 根据 bounds 创建一个简单的线框盒子用于演示 const bounds modelData.shape_info.bounds; const size new THREE.Vector3( bounds[3] - bounds[0], bounds[4] - bounds[1], bounds[5] - bounds[2] ); const center new THREE.Vector3( (bounds[0] bounds[3]) / 2, (bounds[1] bounds[4]) / 2, (bounds[2] bounds[5]) / 2 ); const geometry new THREE.BoxGeometry(size.x, size.y, size.z); const material new THREE.MeshNormalMaterial({ wireframe: true }); boxMesh new THREE.Mesh(geometry, material); boxMesh.position.copy(center); scene.add(boxMesh); document.getElementById(‘modelInfo’).textContent 模型加载成功。特征数${modelData.features.length}; } function sendCommand() { const input document.getElementById(‘commandInput’); const command input.value.trim(); if (!command || !socket || socket.readyState ! WebSocket.OPEN) return; socket.send(JSON.stringify({ message: command })); input.value ‘’; } function animate() { requestAnimationFrame(animate); if (boxMesh) boxMesh.rotation.y 0.01; renderer.render(scene, camera); } window.onload init; window.onresize function() { camera.aspect window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }; /script /body /html4.4 运行与测试将上述main.py中的your-openai-api-key替换为你自己的OpenAI API Key。在终端运行后端服务python main.py。服务将在http://localhost:8000启动。用浏览器直接打开index.html文件或通过一个简单的HTTP服务器如python -m http.server。在网页输入框中尝试输入自然语言指令例如“把长方体的长度设置为150”、“将宽度增加到80毫米”。观察后端控制台的AI解析日志和前端的状态变化。这个原型极其简陋前端只显示了一个根据边界框生成的线框但它完整演示了“自然语言 - AI解析 - 结构化命令 - 几何引擎修改 - 前端更新”的核心闭环。你可以在此基础上用更强大的前端框架如React Three Fiber替换Three.js原生代码实现真正的网格更新和更丰富的交互。5. 面临的挑战与未来优化方向两天的密集开发让原型跑了起来但距离一个真正可用的“工作台”还有很长的路。在实际操作和思考中我遇到了以下几个核心挑战这也是未来需要重点优化的方向。5.1 AI理解的准确性与上下文管理问题当前提示词工程只能处理非常简单的、描述清晰的指令。面对复杂场景AI容易“幻觉”或误解。歧义“把那个孔变大一点”——“那个”指哪个孔“变大”是直径变大还是深度变大复杂逻辑“在所有厚度小于5mm的壁上添加加强筋”——这需要AI理解“选择过滤器”和“特征创建”的组合逻辑。长对话上下文在多轮修改中AI需要记住之前所有的操作和当前模型状态上下文窗口可能不够用。解决思路细化特征描述不仅给AI特征ID和名称还可以附带更丰富的属性如“位于顶面的通孔”、“与边线相距10mm的圆角”等。引入视觉辅助将当前3D视图的截图或简化的2D草图一并发送给AI多模态模型让其结合图像和文本来理解指令。分步确认对于复杂指令AI可以先输出一个执行计划“我将执行以下操作1. 选择孔A2. 将其直径从5mm改为8mm”经用户确认后再执行。微调专用模型收集大量“CAD指令-操作序列”配对数据微调一个专属的小模型专门用于此领域提高准确率和降低延迟。5.2 几何引擎的健壮性与性能问题参数化建模中的特征依赖和拓扑变化是最大的难题。父特征修改导致子特征参考丢失即“重建失败”在传统CAD中也很常见。参考丢失移动了一个面导致基于此面的倒角或孔无处附着。循环依赖两个特征的参数相互引用形成死循环。性能瓶颈复杂装配体下一个参数的修改可能触发数百个特征的重建实时响应变得困难。解决思路更智能的参考解析在特征定义中不仅记录参考的几何实体如面、边还记录其“创建逻辑”如“此边由草图1的线段L1拉伸生成”。当父特征修改后尝试根据逻辑重新计算子特征的参考位置而不是简单的几何匹配。引入约束求解器对于尺寸和几何约束可以集成一个轻量级的约束求解器。AI修改参数后由求解器计算出一组满足所有约束的、变化最小的新参数值再驱动模型更新这更符合设计师的直觉。增量更新与并行计算深入研究OCCT等内核的局部更新API。将模型按特征依赖关系分区对未受影响的分区跳过重建。同时对可并行重建的独立特征树利用多线程计算。5.3 工程化与用户体验问题原型只是一个技术验证要成为工具还需大量工程化工作。数据格式需要定义一套完整的、版本化的项目文件格式用于保存特征树、参数、AI对话历史等。撤销/重做基于参数和操作历史的撤销重做机制比基于几何状态的更复杂。错误处理与反馈当AI指令无法执行或产生错误时需要给用户清晰、友好的反馈而不是崩溃或静默失败。解决思路定义开放标准考虑采用或扩展现有的开源CAD数据格式如STEP、BREP并附加存储我们的特征参数和关系数据。命令模式Command Pattern将每一个AI解析出的操作都封装成一个“命令”对象该对象包含执行execute、撤销undo所需的所有信息。整个修改历史就是一系列命令对象的队列。建立错误码体系定义详细的错误类型如“参考丢失错误”、“参数越界错误”、“解析歧义错误”并设计对应的用户提示和修复建议如“请手动重新选择参考面”。开源这个项目是希望抛砖引玉。AI与专业工具的结合远不止于生成图片和文本深入到CAD、CAE、CAM等工业软件核心用自然交互解放工程师的创造力这片蓝海充满了机遇与挑战。我个人的体会是最大的难点不在于AI或几何内核本身而在于如何让两者“说同一种语言”并构建一个稳定、可预测的交互闭环。这需要既懂AI又懂CAD的跨界思维。期待有更多同行和爱好者加入一起“倒腾”让这个想法走得更远。
返回列表