从自然语言到可编辑CAD模型:text-to-cad技术原理与工程落地 前阵子在技术社区刷到一个演示有人用一句话“帮我做一个直径120的圆盘中心开一个直径20的圆孔”直接在CAD工具里生成了模型。底下评论最多的不是“好酷”而是“这玩意儿真的能改参数吗”。这个问题问到了点子上。text-to-cad文本到CAD这个方向这几年确实从小众玩具变成了被认真对待的工程课题。它要解决的核心事是把自然语言描述直接转成可编辑、可加工的CAD模型而不是一张看着像的渲染图。这篇文章我想从自己的视角把这几年关注和实操text-to-cad的经验完整梳理一遍。会讲到它背后的几何本质、几种主流实现路径、一个能跑通的最小代码Demo以及大量落地时才会遇到的坑。适合三类人看做AI工程落地、想给大语言模型接“手”的开发者机械和产品设计背景、想搞清楚AI建模边界在哪的设计师以及单纯对“用一句话生成3D模型”好奇、想自己动手跑一遍的爱好者。1. 一句话到图纸的差距比想象中大得多1.1 CAD不是“画图”是一套严格的几何描述体系先说一个最常见的误解很多人觉得text-to-cad和文生图差不多把描述“翻译”成画面就行。但CAD模型本质上不是一张图而是一套精确到微米、可被程序逐层解析的几何描述。一个工业级的CAD文件里不仅有点、线、面还包含拓扑关系、尺寸约束、特征历史树甚至公差信息。一根轴和孔之间到底是有0.02毫米间隙还是过盈配合都直接决定零件能不能装上、能不能转。所以text-to-cad真正要做的不是“让模型看起来像”而是构造一个逻辑自洽、能被下游CAM软件或3D打印机直接消费的几何体。文生图场景下模型把猫画成三条腿人眼大概率察觉不到或者说即使察觉了也只是一张图的事。但在CAD场景里一个面片的法线方向反了、一个边缘没闭合后续就没法做切片、没法做布尔运算。这种“零容错”的特性决定了这个方向天然比文生图更硬核。1.2 语义的模糊性怎么量化才是真正的分水岭你可能会问“一段自然语言本身就不精确凭什么要求生成绝对精确的几何”这就是另一个核心矛盾。说“一个大一点的杯子”大多少说“能放下一部手机”手机是长165毫米、宽75毫米、厚8毫米还是带壳后加10毫米文生图可以让模型自己发挥画出来的手机越大越好。但CAD不行因为下游要建模、要装配、要开模“大一点”必须变成一个具体的数值。这也是为什么text-to-cad不能简单套用“文本到图像”的扩散模型路线。你要做的是在语义和参数之间搭一座桥先把模糊描述转成明确的约束方程组再通过求解得到精确的几何参数。这个概念我会在后面的管线里重点拆解。1.3 为什么最近两年才真正进入大众视野严格来说文本驱动建模的尝试在CAD圈子已经存在很多年早期主要靠关键词匹配和脚本模板。真正让这个方向“破圈”的是两股力量汇合一方面大语言模型把“意图解析”的能力拉高了一大截以前需要写一堆规则才能从一句话里抽出“直径”“高度”这类参数现在模型可以直接结构化输出另一方面参数化建模内核和开源几何库已经足够成熟生成一个带完整拓扑的实体模型不再是难事。两股力量一接text-to-cad就从一个学术Demo变成了可以动手验证的工程方向。2. 从文本到模型的完整链路五个环节拆开看我习惯把text-to-cad的完整流程拆成五个环节意图识别、模板匹配、参数解算、几何构建与校验、导出后处理。每一环都有自己独立的难点任何一个环掉链子最终模型都会翻车。2.1 第一步意图识别与实体抽取这一步要回答两个问题用户想建什么用户给了哪些参数“想建什么”是意图分类。“给了一个直径120、高度30的圆柱底座”这句话里“圆柱”是对象“直径120”和“高度30”是参数。常规做法是让大语言模型做结构化抽取输出类似“{type: cylinder, diameter: 120, height: 30}”的JSON同时用一套关键词正则做兜底因为LLM也有抽错的时候兜底逻辑能保证在关键短语缺失时依然有输出。我实际试下来的经验是把“提示词模板”写清楚比换更大的模型更有效。比如强制模型只输出JSON、字段名固定、单位默认毫米这些约束能让抽取准确率明显提升。反直觉的是如果你让模型自由发挥“友好地解释一下”反而更容易出错。2.2 第二步匹配建模模板或生成路径拿到意图和参数之后下一步是决定“怎么把这东西造出来”。这里分两条路检索已有模型库里的相似组件和调用参数化函数现场生成。检索适合标准件比如内六角螺丝、深沟球轴承、铝型材。这些东西本身就有国家标准和成熟三维模型没必要重新建模直接从库里捞出来改改参数就好。参数化生成适合新设计比如一个特定尺寸的杯子、一个按需求定制的外壳。这时候要有一组“模板函数”——每个函数知道某种类型对象的建模步骤、特征顺序、约束逻辑。打个比方模板库像菜谱鱼香肉丝有固定做法只是咸淡甜辣可以调但如果你想做一道厨师没见过的菜菜谱就失灵了。这也是目前text-to-cad和真正“通用设计智能”之间最大的鸿沟。2.3 第三步参数解算与约束求解这一步是整个链路里最容易被忽略、又最影响成品质量的环节。很多输入描述根本不会直接给出全部参数而是给出约束条件。比如“做一个能放进一个鸡蛋的杯子”你没法直接抽到“直径”“高度”得先知道鸡蛋的大概尺寸再反推杯子的内径和深度。再比如“M8的螺丝配一个通孔”孔的直径不能直接等M8标准里要留间隙通常通孔直径是8.4毫米或9毫米。这类知识不在文本里也不在模板库里而在约束求解层。参数解算环节就是把“模糊约束”换算成“具体数值”必要时还要处理互相矛盾的条件比如“总高度不超过50但放得下一个高70的瓶子”这就要报错或者反问用户。2.4 第四步几何构建与合法性校验参数算完终于到了真正“生成模型”的环节。不管底层用的是参数化内核还是网格布尔运算构建完后必须做合法性校验检查壳体是否闭合、面片法线是否一致、有没有自相交、布尔运算是否产生退化面。这一步在学术Demo里经常被跳过但在工程里是生死线。一个看起来没问题的模型可能内部有非流形边、微小的破面加工设备根本不会认。我自己的做法是生成之后强制跑三道检查统计几何体体积是否为正值检查边界边是否成对出现再随机抽样若干点测试是否落在体内。体积为负基本就是面片法线反了边界不成对说明有裂缝抽样测试能发现内部面交错之类的隐性问题。2.5 第五步导出与后处理最后一步是把几何体导出成目标格式。如果是3D打印用STL或者3MF如果是机械加工用STEP如果是激光切割可能是DXF或SVG。每种格式的侧重点不一样STL只关心三角形网格STEP要保留精确曲面和拓扑DXF则要求扁平面的导出形态。格式选错了下游工具很有可能直接拒绝打开或者加工变形。后处理还有一个容易忽略的点单位。文本里可能写“50厘米”“50mm”“50cm”不统一换算的后果是模型尺寸直接差10倍。我在Demo里会把所有输入默认当成毫米遇到“厘米”就乘10即便如此我还是建议在系统里把“默认单位”显式告诉用户。3. 技术路线对比端到端生成、参数化生成、组件检索到底哪条靠谱这个方向目前没有一条路线是万能的。我把常见的几条路线放在一起对比再说说各自的适用场景和翻车点。技术路线基本原理优点缺点适合场景端到端生成用大模型直接输出体素/点云/网格不需要模板库理论上能生成任意形状精度差难以转成B-Rep无法参数化编辑快速概念草模、文创造型参数化程序生成从语义中抽取参数调用建模内核API生成实体精度可控结果可编辑、可复用依赖模板库覆盖率没见过的东西生成不了标准件异形化、外壳设计、结构件组件检索拼接从标准件库/历史模型库中检索、组合排列可靠性高符合实际制造需求只能做“拼积木”无法真正创新装配体布局、标准件调用多模态协同文本加草图/图像约束生成消除文本歧义条件更明确需要额外输入不好采集草图改图、图片驱动逆向建模3.1 端到端生成听着酷工程上却最头疼端到端这条路基本思路是把“文本编码成条件再让生成网络直接输出三维表示”。问题出在中间表示上体素效率低、分辨率上不去点云松散、不是实体网格生成看起来最接近可用但生成出来的网格大多只适合渲染没法缝合、没法倒角、没法做成可编辑特征。你拿一个端到端生成的“杯子”去打开特征树里面什么都没有——它只是一个三角形壳而不是一个“旋转体加抽壳加倒角”的历史结构。我见过很多做AIGC的朋友在这条路上死磕最后结论都类似端到端生成适合做“手办造型”和“概念可视化”但要进入工程链还得自己做一层“网格转B-Rep”的逆向复杂度不亚于重新造一个CAD内核。短期来看它不是最优解。3.2 参数化程序生成目前最踏实的一条路参数化这条路的思路反而更“老派”先定义一组可参数化的模板每个模板对应一类对象的建模步骤然后用自然语言抽取参数去调用这些模板。好处非常直接生成的模型天生就是可编辑的实体特征树完整尺寸改起来只要改参数下游加工链路完全畅通。代价是模板覆盖率。你准备了多少模板就只能处理多少类对象。我自己的经验是与其追求覆盖大量类型不如把每一类模板的参数做深。比如“外壳”模板不仅要有长宽高还要有拔模角、壁厚、加强筋、螺丝柱高度等几十个参数。这样用户说“一个带USB开孔的方形外壳”时系统能在核心模板上做特征扩展而不是从头生成一个不可控的形状。3.3 组件检索拼接适合“搭积木”的场景组件检索的核心是把“从零生成”变成“从库里选”。如果用户要的是一个带法兰的轴、一个减速箱外壳、一台标准电机座最快最稳的方法就是检索已有模型再根据尺寸参数缩放组合。这条路径在装配体级别的text-to-cad里特别实用因为它天然符合工程师的思考方式优先用成熟零件实在没有才单独设计。它的天花板也明显只能做已有零部件的排列组合。用户如果说“我要一个有机曲线造型的底座”库里没有检索就会失败。所以这条路线的最佳形态是和其他生成方式配合而不是单打独斗。3.4 多模态是一条加分项纯文本最大的问题是歧义——“一个凳子”到底几个腿方的还是圆的高多少但如果用户同时画了一张草图哪怕画得很潦草很多歧义就能消除。多模态合并方案在实践中的表现远好于纯文本因为草图本身就是一种粗糙的几何约束。我建议所有做text-to-cad的人在设计输入接口时一开始就把“画一笔/传图片”作为可选项它能把成功率拉高一截。4. 最小可运行的文本转CAD Demo写代码实测一遍光讲原理容易虚我做一个不需要任何第三方依赖、纯Python就能跑通的最小Demo。思路就用最老实的参数化生成从文本里抽关键词和数字调用模板函数生成三角网格再导出一个STL文件。你跑完这个Demo就能理解text-to-cad的核心链路后面换成真正的CAD内核只是接口问题。4.1 设计一个最小的模板系统先定义两个模板圆柱和圆台。它们的基本参数是半径、高度分段数决定表面光滑度。下面这个函数生成一个圆柱体表面的三角形网格注意侧面、顶盖、底盖三个部分都要覆盖否则最后导出的STL不是闭合壳体。import math def make_cylinder(radius, height, segments32): top [ (radius * math.cos(2 * math.pi * i / segments), radius * math.sin(2 * math.pi * i / segments), height) for i in range(segments) ] bottom [ (radius * math.cos(2 * math.pi * i / segments), radius * math.sin(2 * math.pi * i / segments), 0.0) for i in range(segments) ] triangles [] for i in range(segments): nxt (i 1) % segments # 侧面两个三角形 triangles.append((bottom[i], bottom[nxt], top[nxt])) triangles.append((bottom[i], top[nxt], top[i])) center_top (0.0, 0.0, height) for i in range(segments): nxt (i 1) % segments triangles.append((top[i], top[nxt], center_top)) center_bottom (0.0, 0.0, 0.0) for i in range(segments): nxt (i 1) % segments triangles.append((bottom[nxt], bottom[i], center_bottom)) return triangles这里三角形顶点顺序不是随便排的它决定了面片的法线方向。侧面两个三角形的绕序都是从底部外侧指向顶部外侧顶盖的绕序朝上底盖朝下。法线一乱STL文件在很多切片软件里会出现表面发黑、切片失败的问题。4.2 自己写一个STL导出器STL格式很简单就是把每个三角形面片连同法向量写进文件。不需要任何第三方库用纯Python写一个ASCII版def sub(a, b): return (a[0] - b[0], a[1] - b[1], a[2] - b[2]) def cross(a, b): return (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]) def normalize(v): length math.sqrt(v[0] ** 2 v[1] ** 2 v[2] ** 2) if length 0: return (0.0, 0.0, 0.0) return (v[0] / length, v[1] / length, v[2] / length) def write_stl_ascii(path, triangles): with open(path, w) as f: f.write(solid text_to_cad\n) for tri in triangles: normal normalize(cross(sub(tri[1], tri[0]), sub(tri[2], tri[0]))) f.write( facet normal {} {} {}\n.format(*normal)) f.write( outer loop\n) for v in tri: f.write( vertex {} {} {}\n.format(*v)) f.write( endloop\n) f.write( endfacet\n) f.write(endsolid text_to_cad\n)这个导出器只有三十来行但已经能生成可被大多数三维查看器和切片软件正常识别的STL。这里法向量是用三角形的两条边叉积算出来的所以前面三角形顶点的绕序必须保证一致。4.3 把文本解析和几何生成串起来接下来写一个非常简陋的意图解析器。真实项目里这里会接大语言模型但为了这个Demo可以离线跑通我直接用关键词和正则匹配import re def parse_intent(text): if 杯 in text: return cup if 圆柱 in text or cylinder in text.lower(): return cylinder if 底座 in text or 托盘 in text: return base return unknown def extract_param(text, key, default, aliasNone): patterns [key] (alias or []) for pattern in patterns: m re.search(pattern r[\s:]*([0-9](?:\\.[0-9])?), text, re.IGNORECASE) if m: return float(m.group(1)) return default def build_from_text(text): intent parse_intent(text) if intent cup: diameter extract_param(text, 直径, 60.0, alias[口径]) height extract_param(text, 高度, 40.0, alias[高]) return make_cylinder(diameter / 2.0, height, segments48) if intent cylinder: diameter extract_param(text, 直径, 30.0) height extract_param(text, 高度, 50.0) return make_cylinder(diameter / 2.0, height, segments32) if intent base: diameter extract_param(text, 直径, 100.0) height extract_param(text, 厚度, 10.0, alias[高]) return make_cylinder(diameter / 2.0, height, segments32) return []在命令行里跑一下试试python text_to_cad_demo.py 生成一个直径60、高度40的杯子它会输出一个cup.stl文件。用任意免费的STL查看器打开就是一个带底、带盖、侧面光滑的圆柱体。虽然它还没杯壁厚度但整个链路——从自然语言到参数从参数到三角网格从网格到文件——已经完整跑通了。4.4 实测下来发现的几个问题这个Demo跑通之后我再往里面加功能时踩了一堆坑值得写下来。第一关键词正则非常脆弱。用户说“杯子高度40直径60”顺序一换正则就抽不到“直径”了。真实场景里用LLM做抽取是必须的但LLM也会偶尔输出非法JSON所以解析层永远是“大模型规则兜底”双保险。第二单位混乱。“60”到底是多少我默认当成毫米但用户习惯说厘米、英寸甚至直接说“20的孔”这种省略单位的话。Demo里不做单位换算真实工具里一定要有显式的单位声明和换算逻辑。第三缺省参数全靠猜。真实输入很少把所有参数都写全比如用户就说“一个杯子”那我默认直径60、高度40。这个“默认值策略”看起来简单实际上直接影响生成结果和用户预期。后来我改成缺参时反问用户效果反而好了很多。第四结构简单不等于能加工。Demo生成的杯子就是一个实心圆柱没有壁厚、没有圆角、没有拔模角。如果直接拿去加工要么材料浪费严重要么脱模都困难。这也印证了我前面说的参数化模板必须把工艺参数做进模板里而不是只搭一个大致外形。5. 真正落地时躲不开的坑与心得5.1 数据是最卡脖子的地方text-to-cad方向想训练一个端到端模型或者想打磨一个检索系统最大的问题不是算法而是数据。网上公开的CAD模型本来就少很多还因为版权不能直接用带自然语言标注的CAD模型更是稀缺。自己造合成数据吧模型很容易学会“看起来像CAD”但实际几何完全不合格人工标注吧一个复杂零件的标注可能要一个熟练工程师花一整天。我见过有些团队用程序化生成大量“带参数描述”的模型来做预训练方向是对的但要特别小心合成数据的分布偏差。比如只生成圆柱、方块、简单孔板模型就学不会曲面造型只生成100毫米以下的小零件大尺寸建模表现就会崩。数据多样化不是数量问题是覆盖维度的问题。5.2 几何质量校验是生成工具的生命线“生成出来了”和“能用了”之间差着一条河。我自己用端到端模型跑过一个手机支架打开一看整个模型是二十多块互相悬浮的碎片面片之间有缝隙法线方向乱成一团连切片都没法切。从那之后我学乖了无论算法多高级输出之后必须挂一道硬性的几何校验关卡。通用的校验项至少包含这些壳体是否闭合网格是否有悬挂边、重复面法线方向是否一致体积是否为正布尔运算后有没有退化元素最小特征尺寸是否满足下游工艺要求。这些校验不需要多聪明但它能把一大批“看起来对、实际废”的结果拦在门外。做这个方向校验模块应该和生成模块同等重要甚至更重要。5.3 和用户“讨价还价”比直接猜更有效我早期设计过一个转换系统文本缺什么参数就补默认值结果经常生成一些让人哭笑不得的模型。后来改成对话式交互脚本识别出用户没说“高度”就反问一句“高度需要多少还是用默认40毫米”。用户给出明确数值之后再进入生成环节。这个改动看起来简单却把首次生成的满意率提升了不止一倍。这背后其实是一个很朴素的道理自然语言的信息密度有限与其依赖模型去猜用户的隐藏意图不如主动把缺失信息补回来。text-to-cad作为一个交互系统天然适合做成“生成-反馈-修改”的循环而不是一次性的文本到模型映射。5.4 从“生成出来”到“能加工出来”还有很长的路现在很多text-to-cad的Demo本质上做的还是“把自然语言变成三维模型的外形”。但一个真正能进工厂的CAD模型还需要考虑公差、装配约束、材料属性、加工工艺比如注塑件要有拔模角钣金件要考虑折弯半径机加工件要留余量。这些信息用户不会写进一句话里甚至很多用户自己都没意识到。所以我认为下一步的进化方向不是“让模型生成更复杂的曲面”而是“让模型理解制造约束”。一个牛掰的text-to-cad系统应该是你说“一个铝合金的方形外壳”它不仅生成形状还自动把壁厚、拔模角、加强筋、螺丝柱位置全部按工艺规范排好。那才是真正能替代工程师日常重复建模工作的状态。我把这个Demo跑通之后最大的感受是text-to-cad的难点不在“文本”一头也不在“CAD”一头而在两端之间的约束传递。把一个模糊想法变成一组精确参数再把参数变成一段无懈可击的几何这个过程里每一步都藏着可以深挖的工程细节。如果你也想试试这个方向我的建议很简单先跑一个最笨的最小系统把从文字到文件的完整闭环打通再去考虑模型选型和效果优化。毕竟能跑起来的系统才有资格谈迭代。