资讯详情

资讯详情

text-to-cad实战:从自然语言到三维CAD模型的生成系统设计与实现

1. 从一句话到三维模型text-to-cad 到底在解决什么问题第一次听到“text-to-cad”这个说法是在一个做机械设计的朋友那里。他当时正对着屏幕上一堆草图发愁嘴里念叨着“要是能直接打字生成模型就好了。”我当时觉得这想法挺天真的毕竟CAD建模在大多数人印象里就是鼠标点来点去、参数调来调去的手工活。但后来接触了一些生成式模型的项目之后我发现这个方向并不是空想它背后有一套相当清晰的逻辑。text-to-cad直译过来就是“文本到计算机辅助设计”。它的核心目标很直接你输入一段自然语言描述比如“一个边长50毫米、壁厚3毫米、带四个安装孔的法兰盘”系统输出一个可编辑的三维CAD模型文件。这件事如果做成影响的是整个产品设计的前端环节——从“脑子里有个想法”到“屏幕上有个能看的模型”之间的时间可能从几小时压缩到几十秒。它解决的核心痛点有三个。第一是建模门槛。传统CAD软件如SolidWorks、Fusion 360、FreeCAD学习曲线陡峭一个没接触过参数化建模的人光搞清楚草图约束和特征树的关系就要花好几天。第二是重复劳动。很多标准件、常见结构设计师每次都要重新画一遍虽然可以用模板但模板的灵活性有限。第三是早期沟通效率。产品经理或客户说“我想要一个圆角一点的、看起来稳一点的支架”设计师得先理解再建模来回改几轮才能对齐预期。text-to-cad试图把“语言”直接变成“几何”跳过中间的手工翻译环节。适合关注这个方向的人我大致分了三类。一类是CAD二次开发者想在自己的工具链里嵌入文本生成模型的能力一类是AI应用工程师在探索大语言模型与三维几何的结合点还有一类是产品设计从业者想了解这个技术到底能不能用在日常工作中现在到了什么程度。不管你是哪一类下面我会从整体设计思路开始一步步拆到实操层面把我知道的、试过的、踩过的坑都摊开来讲。2. 整体设计思路拆解为什么不能直接让语言模型画图2.1 核心矛盾语言是离散的几何是连续的text-to-cad最根本的难点在于语言和几何之间的表示鸿沟。自然语言是离散的符号序列而三维模型是连续的坐标、曲线、曲面。你让一个语言模型直接输出“一个边长50毫米的立方体”它可能会输出一堆数字但这些数字怎么变成CAD软件能识别的边界表示B-rep或网格中间缺了一座桥。我见过一些早期尝试思路是让模型直接生成顶点坐标和面索引也就是网格数据。这种做法在简单形状上勉强能用但一旦涉及圆角、倒角、孔位阵列这些CAD里最常见的特征网格表示就力不从心了。因为CAD的核心是参数化特征历史不是一堆三角面片。一个孔不是一个圆柱面的网格而是一个“拉伸切除”特征加上直径、深度、位置这些参数。所以合理的思路不是让语言模型直接“画”而是让它生成建模指令序列。这就像你告诉一个熟练的CAD操作员“先画一个矩形拉伸50毫米然后在四个角打直径8毫米的孔”他听到的是操作步骤不是最终的坐标点。text-to-cad系统要做的就是把自然语言翻译成这种操作步骤序列再由一个确定性的几何内核去执行。2.2 方案选型三种主流路线的取舍目前能看到的text-to-cad实现大致分三条路线。我在不同项目里都试过或评估过各有各的适用场景。第一条是基于脚本生成。系统让语言模型输出一段CAD脚本比如OpenSCAD的代码或者CadQuery的Python代码然后调用对应的解释器生成模型。这条路线的好处是几何内核成熟OpenSCAD和CadQuery背后是CGAL和OpenCASCADE布尔运算、圆角、倒角这些都有现成实现。缺点是模型必须能表达成脚本对于自由曲面、复杂有机形状脚本写起来非常吃力。而且语言模型生成的脚本经常有语法错误或逻辑错误需要额外的校验和修复环节。第二条是基于特征序列生成。系统定义一套领域特定的建模指令集比如“创建草图”“拉伸”“旋转”“打孔”“阵列”然后让语言模型输出这些指令的序列和参数。这条路线更贴近CAD的原生逻辑生成的模型天然具有参数化特征树后续修改方便。缺点是需要自己实现一个几何执行引擎或者对接现有CAD软件的API。开发成本比第一条路线高不少。第三条是基于隐式表示生成。系统让模型输出一个隐式函数或符号距离场SDF然后用 marching cubes 之类的算法提取网格。这条路线在学术研究里比较常见能生成复杂形状但输出的模型没有特征历史不可参数化编辑而且网格质量往往一般。对于工程应用来说实用性打折扣。我个人的判断是如果你的目标是做工程可用的CAD模型第二条路线最靠谱。第一条路线适合快速原型验证第三条路线适合形状探索和概念设计。下面我主要以第二条路线为基准来展开因为它的技术挑战最全面也最能体现text-to-cad的核心价值。2.3 系统架构四个模块的协作方式一个完整的text-to-cad系统我把它拆成四个模块。第一个是语言理解模块负责把自然语言解析成结构化的建模意图。第二个是指令生成模块把建模意图翻译成具体的建模指令序列。第三个是几何执行模块调用几何内核执行指令生成三维模型。第四个是校验与反馈模块检查生成的模型是否满足约束如果不满足回传给语言模型重新生成。这四个模块里语言理解模块和指令生成模块通常由一个大语言模型承担但需要针对CAD领域做微调或提示工程。几何执行模块是确定性的不涉及AI它的可靠性直接决定了最终输出的质量。校验与反馈模块是最容易被忽视但最关键的一环因为语言模型生成的指令经常有各种问题没有校验就等于把错误直接暴露给用户。我试过一个简化版的架构用提示工程让通用语言模型输出CadQuery代码然后本地执行。结果大概有30%的生成结果因为语法错误或几何错误无法执行。加上一个基于规则的校验层之后成功率提升到70%左右。这个数字说明text-to-cad不是“模型一跑就完事”的事情工程化的工作量比想象中大。3. 核心细节解析与实操要点3.1 语言理解模块怎么让模型听懂“人话”语言理解模块的任务是把用户那句可能很模糊的话变成明确的建模意图。这里最大的挑战是歧义消解和缺省补全。用户说“一个圆角盒子”圆角半径是多少盒子尺寸是多少壁厚有没有这些信息语言模型不知道但它不能随便猜也不能一直追问。我的做法是设计一套默认值策略加槽位填充的机制。系统预定义一套建模意图模板每个模板有若干槽位比如“创建盒子”这个意图有长、宽、高、圆角半径、壁厚五个槽位。语言模型的任务是从用户输入里抽取槽位值抽不到的用默认值填充。默认值不是随便定的而是根据常见工程实践来设。比如圆角半径默认2毫米壁厚默认3毫米这些数值在大多数场景下不会出大错。提示工程在这里很关键。我用的提示词大致是这样的结构先给模型几个示例展示从自然语言到槽位填充的映射然后给出当前用户输入最后要求模型以JSON格式输出意图和槽位。示例的选择要覆盖常见表达方式比如“带四个孔”和“四角打孔”要映射到同一个意图。实测下来给5到8个高质量示例通用语言模型在简单场景下的槽位抽取准确率能到85%以上。注意不要指望语言模型一次就能抽对所有槽位。我的经验是对于尺寸、数量这类数值槽位模型容易搞混单位或漏掉。最好在提示词里明确要求输出单位并且在后续模块里做单位统一。还有一个容易被忽略的点是否定表达的处理。用户说“不要圆角”和“圆角半径为零”是同一个意思但模型可能理解成“没有提到圆角”。我在提示词里专门加了一类示例覆盖“不要”“去掉”“取消”这些否定词让模型学会把它们映射到槽位的零值或空值。3.2 指令生成模块从意图到可执行代码拿到结构化的建模意图之后指令生成模块要把它变成几何内核能执行的代码。我选的是CadQuery因为它基于OpenCASCADE几何运算可靠而且Python语法对语言模型比较友好。生成的代码大致长这样import cadquery as cq result ( cq.Workplane(XY) .box(50, 50, 20) .edges(|Z) .fillet(2) .faces(Z) .workplane() .rect(30, 30, forConstructionTrue) .vertices() .hole(8) )这段代码创建一个50x50x20的盒子Z方向边倒2毫米圆角顶面打四个直径8毫米的孔。语言模型要生成的就是这种风格的代码。但直接让模型生成完整代码出错率很高。我的做法是分步生成先生成特征序列的伪代码再翻译成CadQuery代码。伪代码的格式很简单每行一个操作比如create_box length50 width50 height20 fillet_edges directionZ radius2 create_holes facetop diameter8 count4 patternrectangular这样做的好处是伪代码的语法空间比完整Python代码小得多模型更容易生成正确。而且伪代码和CadQuery之间的映射是确定性的可以用规则引擎完成翻译不依赖模型。实测下来分步生成的成功率比直接生成代码高出不少。提示在提示词里给模型提供CadQuery的API文档摘要比让它自己回忆API要可靠。我通常会把常用的十几个API函数的签名和说明放在提示词里模型生成代码时参考这些信息错误率明显下降。另一个实操要点是参数传递的格式。语言模型生成的数值经常是字符串而CadQuery需要浮点数。我在翻译层里加了一个类型转换和单位换算的步骤把所有长度单位统一到毫米角度统一到度。这个步骤看起来简单但少了它后面几何执行就会报一堆类型错误。3.3 几何执行模块确定性内核的可靠性保障几何执行模块是整个系统里唯一不涉及AI的部分但它的重要性怎么强调都不过分。语言模型可以犯错但几何内核不能。一个布尔运算失败或者圆角半径过大导致几何无效整个生成流程就断了。我用的CadQuery底层是OpenCASCADE这是一个经过几十年验证的几何内核可靠性很高。但即便如此还是有一些常见的坑。比如圆角半径超过相邻边长度的一半会导致圆角失败。打孔位置太靠近边缘可能导致孔穿透到外面。布尔运算的两个实体没有交集会返回空结果。这些情况在手工建模时设计师会自然避免但语言模型不知道这些约束生成的参数可能越界。我的做法是在几何执行之前加一个参数合法性检查层。这个检查层根据建模意图的类型验证参数是否在合理范围内。比如对于圆角操作检查半径是否小于相邻边最短长度的一半对于打孔操作检查孔中心到边缘的距离是否大于孔径。如果检查不通过不是直接报错而是把问题回传给语言模型让它调整参数重新生成。这个反馈循环我设了最多三轮三轮还不行就返回失败让用户手动调整。def validate_fillet(shape, edge_selector, radius): edges shape.edges(edge_selector).vals() for edge in edges: length edge.Length() if radius length / 2: return False, f圆角半径{radius}超过边长度{length}的一半 return True, OK这段校验代码很简单但能拦住大部分几何错误。我统计过加上校验层之后几何执行失败率从25%降到了5%以下。剩下的5%主要是OpenCASCADE内部的数值精度问题比较难完全避免。3.4 校验与反馈模块让系统自己发现错误校验与反馈模块是text-to-cad系统里最像“工程”而不是“研究”的部分。它的任务是在模型生成之后检查模型是否满足用户的原始意图。这里分两个层次几何有效性校验和语义一致性校验。几何有效性校验比较简单调用几何内核的检查函数就行。比如检查模型是否是有效的实体solid有没有自相交体积是否大于零。这些检查CadQuery和OpenCASCADE都有现成的API。语义一致性校验就复杂一些。用户说“带四个孔”生成的模型是不是真的有四个孔用户说“边长50毫米”模型的包围盒尺寸是不是50毫米这些检查需要从生成的模型中提取几何特征然后和原始意图做比对。我的做法是写一组特征提取器比如统计孔的数量、测量包围盒尺寸、检测圆角是否存在。然后把这些提取结果和意图槽位做比对不一致就触发重新生成。注意语义一致性校验不要做得太严格。比如用户说“大约50毫米”模型生成49.8毫米这不应该算错误。我在比对时设了一个容差尺寸类参数容差5%数量类参数要求精确匹配。这个容差范围可以根据实际需求调整。反馈模块的提示词设计也有讲究。不能只说“错了重新生成”而要告诉模型具体哪里错了、期望值是多少。比如“生成的模型有3个孔但用户要求4个孔请调整阵列参数”。这种具体的反馈能让模型更快收敛。我试过带具体反馈的重新生成成功率比不带反馈高出40%左右。4. 实操过程与核心环节实现4.1 环境搭建从零开始的最小可行系统如果你想自己搭一个text-to-cad的验证系统我建议从最小可行版本开始。不需要一上来就搞微调用提示工程加通用语言模型就能跑通基本流程。下面是我实际用的一套环境配置。首先是Python环境建议3.9以上。核心依赖是CadQuery和OpenAI的API客户端或者任何兼容的本地模型接口。CadQuery的安装用conda比较省事pip安装有时候会遇到OpenCASCADE的二进制依赖问题。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install openai然后是项目结构。我习惯分成四个文件intent_parser.py负责语言理解code_generator.py负责指令生成geometry_executor.py负责几何执行main.py是入口。每个模块之间用JSON格式的数据结构传递信息这样方便调试和替换。# intent_parser.py 的核心逻辑 def parse_intent(user_input): prompt build_prompt(user_input) response call_llm(prompt) intent json.loads(response) intent fill_defaults(intent) return intentbuild_prompt函数里放示例和格式要求fill_defaults函数里放默认值策略。这两个函数是语言理解模块的核心值得花时间打磨。4.2 提示词工程让通用模型输出结构化结果提示词的质量直接决定了语言理解模块的准确率。我经过多次迭代总结出一个比较稳定的提示词模板。它的结构分四部分角色设定、任务说明、示例、输出格式要求。角色设定部分告诉模型它是一个CAD建模助手负责把自然语言转成建模意图。任务说明部分解释槽位填充的规则包括哪些槽位是必填的、哪些有默认值、数值的单位是什么。示例部分给5到8个覆盖常见场景的输入输出对。输出格式要求部分明确指定JSON的schema。你是一个CAD建模意图解析助手。用户会用自然语言描述一个三维模型你需要把它解析成结构化的建模意图。 可用的意图类型 - create_box: 创建长方体槽位有length, width, height, fillet_radius - create_cylinder: 创建圆柱体槽位有radius, height - create_hole: 打孔槽位有face, diameter, count, pattern 默认值 - fillet_radius: 2 - 单位: 毫米 示例1 输入一个50毫米长、30毫米宽、20毫米高的盒子带2毫米圆角 输出{intent: create_box, slots: {length: 50, width: 30, height: 20, fillet_radius: 2}} 示例2 输入顶面打四个直径8毫米的孔 输出{intent: create_hole, slots: {face: top, diameter: 8, count: 4, pattern: rectangular}} 现在请解析以下输入 {user_input} 请只输出JSON不要有其他内容。这个模板的关键在于示例的质量。示例要覆盖不同的表达方式比如“盒子”和“长方体”要映射到同一个意图“打孔”和“钻孔”也要映射到同一个意图。我通常会准备20个左右的示例根据实际测试中出现的错误不断补充。提示如果你的模型支持系统提示词和用户提示词分离把角色设定和任务说明放在系统提示词里示例和用户输入放在用户提示词里。这样模型对角色和规则的记忆更稳定。4.3 代码生成与执行从伪代码到三维模型指令生成模块的输入是结构化的建模意图输出是CadQuery代码。我采用两阶段生成先生成伪代码再翻译成CadQuery。伪代码的生成也用提示词工程但比意图解析简单因为伪代码的语法空间很小。伪代码的格式我定义得很简单每行一个操作操作名和参数用空格分隔。比如create_box length50 width30 height20 fillet_edges directionZ radius2 create_holes facetop diameter8 count4 patternrectangular生成伪代码的提示词里我会给出所有可用操作的列表和参数说明。模型的任务就是根据建模意图选择合适的操作并填参数。因为操作数量有限模型的选择空间不大准确率比较高。翻译层是一个纯规则的Python函数把每行伪代码映射到对应的CadQuery调用。这个函数不需要AI写起来很直接。def execute_pseudo_code(pseudo_code): result None for line in pseudo_code.strip().split(\n): parts line.split() op parts[0] params dict(p.split() for p in parts[1:]) if op create_box: result cq.Workplane(XY).box( float(params[length]), float(params[width]), float(params[height]) ) elif op fillet_edges: result result.edges(params[direction]).fillet(float(params[radius])) elif op create_holes: result result.faces(params[face]).workplane().rect( 30, 30, forConstructionTrue ).vertices().hole(float(params[diameter])) return result这段代码是简化版实际项目中需要处理更多操作类型和参数组合。但核心逻辑就是这样顺序执行伪代码指令每一步在上一步的结果上操作。4.4 完整流程走一遍一个法兰盘的生成实例我用一个具体例子把整个流程串起来。用户输入是“一个外径100毫米、内径60毫米、厚度10毫米的法兰盘带六个直径8毫米的安装孔孔均匀分布在一个直径80毫米的圆上。”第一步意图解析。语言模型输出{ intent: create_flange, slots: { outer_diameter: 100, inner_diameter: 60, thickness: 10, hole_diameter: 8, hole_count: 6, hole_circle_diameter: 80 } }第二步伪代码生成。模型输出create_cylinder radius50 height10 create_hole diameter60 create_holes diameter8 count6 patterncircular circle_diameter80第三步代码翻译和执行。翻译层把伪代码转成CadQuery代码result ( cq.Workplane(XY) .circle(50) .extrude(10) .faces(Z) .workplane() .hole(60) .faces(Z) .workplane() .polarArray(40, 0, 360, 6) .hole(8) )第四步校验。几何有效性检查通过模型是一个有效的实体。语义一致性检查外径100毫米内径60毫米厚度10毫米孔数6个孔分布圆直径80毫米全部匹配。生成成功。整个流程从输入到输出在我本地环境上大概需要5到10秒主要时间花在语言模型的API调用上。如果换成更小的本地模型时间可以压缩到2秒以内但准确率会下降一些。注意polarArray的参数是半径、起始角度、终止角度、数量。这里半径是40毫米因为孔分布圆直径是80毫米。这个换算关系容易搞错我在翻译层里加了一个检查如果伪代码里给的是直径自动除以2再传给polarArray。5. 常见问题与排查技巧实录5.1 语言模型输出格式错误怎么办这是最常见的问题。你要求模型输出JSON它可能输出一段解释文字加JSON或者JSON的键名拼错或者数值带了单位字符串。我的处理策略是多层容错。第一层在提示词里反复强调“只输出JSON”。第二层用正则表达式从模型输出里提取JSON部分忽略前后的解释文字。第三层用JSON schema校验如果键名不对或类型不对尝试自动修复。比如把length: 50mm修复成length: 50.0。第四层如果修复失败把错误信息回传给模型让它重新生成。import re import json def extract_json(text): match re.search(r\{.*\}, text, re.DOTALL) if not match: return None try: return json.loads(match.group()) except json.JSONDecodeError: return None这个函数能处理大部分格式问题。实测下来加上这层提取之后格式错误率从15%降到了3%以下。5.2 几何执行失败怎么排查几何执行失败的原因很多我整理了一个排查顺序。先看错误信息OpenCASCADE的报错通常比较具体比如“BRep_API: command not done”通常意味着布尔运算失败“Standard_ConstructionError”通常是参数越界。然后检查参数合法性用前面说的校验层过一遍。最后检查操作顺序有些操作必须在特定操作之前或之后执行比如打孔必须在拉伸之后圆角必须在打孔之前否则圆角可能被孔打断。我遇到最多的几何失败是圆角半径过大。语言模型经常生成一个看起来合理但实际超过边长的圆角半径。比如一个10毫米高的盒子模型生成5毫米的圆角这刚好等于高度的一半OpenCASCADE会报错。我的校验层里设的阈值是边长的0.4倍超过就触发重新生成。另一个常见问题是孔的位置越界。语言模型生成孔阵列时可能把孔放在实体外面。比如一个50x50的盒子模型生成孔分布圆直径60毫米孔就跑到外面去了。校验层里检查孔中心到实体边缘的距离小于孔径就报错。5.3 生成结果和预期不符怎么调整有时候几何执行成功了但模型不是用户想要的。比如用户说“一个圆角盒子”模型生成了一个带圆角的盒子但圆角在错误的边上。或者用户说“四个孔”模型打了四个孔但位置不对。这类问题的根源通常在意图解析阶段。语言模型对“圆角”的理解可能是在所有边上倒圆角而用户想要的是只在垂直边上倒圆角。解决办法是在意图解析的槽位里增加更细粒度的控制比如fillet_edges槽位指定哪些边需要倒圆角。然后在提示词里增加示例展示不同边选择对应的输出。还有一种情况是默认值不符合用户预期。用户说“一个盒子”没有指定尺寸系统用了默认的50x50x20。但用户心里想的是100x100x50。这种情况没法完全避免但可以在生成之后给用户一个反馈界面让用户看到实际使用的参数方便手动调整。5.4 常见问题速查表问题现象可能原因排查方法解决策略模型输出不是JSON提示词不够明确检查提示词是否强调只输出JSON加正则提取层回传错误重新生成JSON键名拼写错误模型幻觉对比schema检查键名加键名映射表自动纠正几何执行报错参数越界或操作顺序错误查看OpenCASCADE错误信息加参数校验层调整操作顺序圆角失败半径超过边长一半检查圆角半径和相邻边长度设阈值0.4倍边长超限重新生成孔位置越界阵列参数错误检查孔中心到边缘距离校验距离大于孔径否则重新生成生成结果不符预期意图解析偏差对比用户输入和解析结果增加示例细化槽位粒度生成速度慢语言模型API延迟测量各模块耗时换本地小模型或缓存常用意图提示这张表是我在实际调试中慢慢积累的建议你在自己的项目里也维护一份。每次遇到新问题就加一行时间长了就是一份很有价值的排查手册。5.5 几个容易被忽视的实操心得第一个心得是缓存常用意图的解析结果。很多用户输入是重复的比如“一个50x50x20的盒子”可能被不同用户反复输入。把意图解析的结果缓存起来相同的输入直接返回缓存能省不少API调用。我用一个简单的字典做缓存命中率大概有20%左右。第二个心得是给语言模型提供几何约束的反馈。在重新生成的提示词里不仅告诉模型“错了”还要告诉它“为什么错了”。比如“圆角半径5毫米超过了边长10毫米的一半请把半径改到4毫米以下”。这种具体的约束信息能让模型更快找到可行解。第三个心得是不要追求一次生成完美。text-to-cad系统的最佳交互模式是“生成-预览-调整”而不是“一次生成最终模型”。用户看到初步结果后可以用自然语言继续调整比如“把孔的数量改成8个”“圆角再大一点”。这种迭代式的交互比一次性生成更符合实际设计流程。6. 这个方向后续可以怎么扩展我在实际项目里跑通基本流程之后发现text-to-cad的扩展空间比预想的大。一个方向是多模态输入用户不仅可以打字还可以画一个粗略的草图或者拍一张照片系统结合文本和图像一起理解意图。另一个方向是装配体生成不只是单个零件而是多个零件的装配关系比如“一个齿轮箱包含输入轴、输出轴和壳体”。这个难度比单零件高一个量级因为涉及零件之间的配合约束。还有一个我觉得很有价值的方向是与现有CAD软件深度集成。现在的实现大多是生成独立的模型文件用户还得导入到CAD软件里继续编辑。如果能做成CAD软件的一个插件生成的模型直接出现在当前的设计树里并且保留完整的特征历史那实用性会大大提升。我试过用FreeCAD的Python API做类似的事情技术上可行但稳定性和性能还需要打磨。最后分享一个小技巧。如果你也在做类似的项目建议从标准件库开始验证。标准件形状固定、参数明确、生成逻辑简单是验证text-to-cad流程的理想场景。等标准件跑通了再逐步扩展到自由形状和复杂装配。我在项目初期花了两周时间只做螺栓和法兰盘把整个流程的坑都踩了一遍后面扩展到其他零件时顺利很多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →