ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Text-to-CAD实战:从自然语言到可制造STEP模型

Text-to-CAD实战:从自然语言到可制造STEP模型 1. text-to-cad 到底解决了什么问题为什么值得关注如果你画过 CAD一定懂那种折磨脑子里明明已经有了非常清晰的三维结构落到软件里却要一步一步拉伸、切除、倒角、打孔。一个简单零件大小操作加起来可能要点上百下鼠标。text-to-cad 这个方向就是想直接把描述变成模型——比如你输入“一块 80×60×12 的铝板四角 R8 圆角中心打一个直径 10 的通孔”它能直接返回一份可编辑、可制造、可进入 CAM 流程的 CAD 模型文件。我先把这个概念说得准确一点。text-to-cad 不是“从文字生成一张好看的渲染图”而是从自然语言出发生成带真实几何信息的 CAD 实体。输出可以是 .step、.iges、.scad 这类工业格式也可以是能一键生成这些文件的程序化建模脚本。它和 AI 画图最大的区别在于结果必须精确到毫米、必须能被修改参数、必须能交给下游做仿真和制造。简单说AI 画图骗过人眼就赢了text-to-cad 骗过 CAM 软件才算赢。这个方向适合三类人。第一类是机械、结构方向的工程师日常有大量重复的基础建模工作想用 AI 解放时间。第二类是 3D 打印和创客玩家经常把想法快速变成实物text-to-cad 能省掉从灵感到 STL 之间的那段苦差事。第三类是 AI 应用开发者正在探索大语言模型和工业软件的结合点这个方向目前算是工业软件智能交互里最接近落地的入口之一。我写这篇文章的姿态不是旁观者在讲概念而是亲手跑过完整流程之后来做复盘。我会从技术路径选型、中间表示设计、端到端代码实现、常见坑位排查四个层面把 text-to-cad 这个方向的真实玩法讲透。不堆论文公式只讲你能直接复现和调整的东西。2. 技术路径怎么选四种主流方案对比text-to-cad 不是某个单一模型的名字而是一整条技术路线的统称。很多朋友上来就搜“有没有现成的 text-to-cad 模型”结果发现要么没开源要么精度完全达不到工程要求。原因很简单这个方向还处于方法论竞速阶段不同技术路线之间的差别极大。我先把四种主流路径摊开讲再告诉大家我为什么最终选择了其中一条。2.1 端到端生成式模型听着性感落地最骨感最容易想到的方案是端到端生成把文本编码成向量丢给扩散模型或者生成式网络直接产出体素、点云或者三角网格。学术圈确实有这类工作把图像生成领域的技术迁移到 CAD 上理论上能生成人类很难手画的复杂自由曲面。但落到工程场景问题非常致命网格和点云不是 CAD 格式没有特征树不能回到参数表里改一个倒角半径没法进 CAE 做面接触分析更没法直接生成数控加工轨迹。而且生成式模型对毫米级尺寸的还原能力普遍拉胯稍微复杂一点的穿透孔、台阶面、对称阵列就会走样。我的结论是这条路线适合做概念设计、游戏资产、文创周边但不适合做工程制造。2.2 中间表示生成LLM 加程序化建模当前最稳第二条路是目前最务实的方案不让模型直接生成几何而是让大语言模型先理解自然语言把描述翻译成一段程序化建模指令再交给 CadQuery、OpenSCAD、SolidPython 这类库去真正执行建模。为什么这条路最稳因为它把问题拆成两层语言理解交给 LLM几何内核交给成熟的 CAD 库。这两个环节各自都是各自领域里的专家。我实测下来只要中间表示设计得好LLM 抽取参数和特征顺序的准确率能到很高水平。就算某个参数抽错了也非常容易通过规则校验发现并纠正。这个“可校验”的特性是端到端生成式模型完全不具备的也是我觉得它适合工程场景的根本原因。2.3 文法约束与检索编辑两条常被忽略的路线第三条路是文法约束生成。本质是给 LLM 一份严格的语法模板或者 schema让模型只能在合法操作里做选择每一处输出都会被解析器校验。它比单纯靠 prompt 约束要可靠得多能天然保证“特征顺序合法”“参数不缺失”。缺点是灵活性差一些覆盖不了千奇百怪的建模需求适合限定领域内的自动化。第四条路是检索加编辑。先把文本映射成一组检索条件从标准零件库里召回最相似的模型再让 LLM 生成增量修改指令。这个方案在标准件、重复件场景里效率极高比如法兰盘改几个孔的位置或者钣金件改一下外形尺寸。如果你的行业已经有厚实的零件库这条路很值得考虑它甚至不需要太强的模型能力。2.4 我的选型决策为什么主推中间表示路线我自己跑 text-to-cad demo 时最终选的是中间表示生成这条路线用 CadQuery 做几何内核自定义 JSON schema 做中间表示LLM 只负责抽取参数和特征链。选它有三个非常朴素的理由。第一它交付的就是真正的 .step 文件能进 CAM 也能进 CAE工程可交付性拉满。第二开发成本低不需要训练模型调提示词和校验逻辑就能起步。第三出错可解释我可以在建模前自动校验每个参数的值域这一步能提前拦截绝大多数明显错误。下面是四种路径的对比方便你按自己的场景对号入座方案精度/可制造性可编辑性数据成本落地难度典型输出端到端生成式中低差极高高mesh / 点云中间表示生成高好较低中step / 程序脚本文法约束生成高好中中高特征树检索编辑中上中上中低已有库加差异后面所有实操内容都围绕中间表示生成展开。你把这条路吃透了之后再看其他论文和开源项目会获得一种明显的“上帝视角”很多项目看起来高深本质上都是在中间表示设计上做文章。3. 中间表示是灵魂结构化约束的设计实操确定走中间表示生成之后你面临的最重要决定就不是选哪个大模型而是中间表示本身怎么设计。中间表示决定了整个系统的能力上限如果 schema 只支持倒角和打孔系统就永远做不了阵列如果不支持从面偏移就做不了常见筋板。许多 text-to-cad 项目翻车翻的不是模型的错而是中间表示一上来就设计歪了。3.1 先想清楚中间表示到底要表达什么中间表示的本质是一份人和机器都能读的几何意图描述。它至少要表达四类信息基本体素、几何参数、位置姿态、特征间的关系。基本体素是 box、cylinder、sphere 这类基础形状几何参数是长宽高、直径、半径、深度位置姿态是中心点、方向、旋转角特征间关系则包括布尔运算、阵列模式、倒角和孔的顺序。为什么不用自然语言当中间表示因为 LLM 的自由文本充满歧义机器没法稳定执行。为什么不让 LLM 直接输出 CadQuery 代码因为代码更容易出现语法错误和非法 API 调用而且你会被某个具体 CAD 库的语法细节绑架。JSON 的好处在于它近似于一张参数表格既容易解析校验也容易在界面上展示给用户确认。你可以在脑子里把一个零件想象成一张加工工单上面写清楚尺寸、位置、加工顺序让 LLM 只负责填这张工单剩下的交给车间里的老师傅——也就是几何内核。3.2 JSON schema 设计与常见特征表达一个我实际用下来的核心 schema 长这样{ unit: mm, features: [ { type: box, length: 80, width: 60, height: 12, center: [0, 0, 0] }, { type: fillet, target: previous, edges: vertical, radius: 8 }, { type: hole, target: previous, position: [0, 0], diameter: 10, depth: through } ] }这个设计里最关键的字段是 target 和 features 数组顺序。features 数组的顺序模拟了一个特征树后一个操作默认作用在前一个操作的结果上——这就像 CAD 软件里的建模历史记录。为什么要这么做因为几何建模的特征顺序不能乱先倒角再打孔和先打孔再倒角结果可能有本质区别。让 LLM 按顺序输出特征本质上是在约束它模仿软件里的真实建模步骤。对于复杂一点的特征我一般会补几个字段阵列用 linear_pattern 表达支持 copies、dx、dy 参数圆形阵列用 polar_pattern切除用布尔运算表达筋板用 profile 加 thickness 表达。但我强烈建议你不要一上来就设计一个覆盖一切的 schema而是从 box、cylinder、hole、fillet、chamfer 这五个特征起步跑通后再逐步加。我见过太多第一个版本就想包揽所有建模操作的项目最后校验逻辑比建模逻辑还庞大维护成本高到直接劝退。3.3 提示词设计让 LLM 稳定输出可解析的结构有了 schema下一件事是写一套让 LLM 稳定走流程的提示词。我把一个可直接套用的模板放在下面要点很明确说清楚角色、给出 schema、强制只输出 JSON、最后再附上用户描述。你是一个零件建模的参数抽取引擎。你的任务是把用户的自然语言描述转换成 JSON 建模指令。 输出规则 1. 只输出一个合法 JSON 对象不要输出任何解释、注释、Markdown 代码块。 2. 单位默认 mm所有数值必须是数字不能带单位字符串。 3. 用户提到“通孔”时depth 写 through没有提深度时默认深度为材料厚度。 4. schema 固定为 { unit: mm, features: [ {type: box, length: number, width: number, height: number, center: [number, number, number]}, {type: cylinder, radius: number, height: number, center: [number, number, number]}, {type: fillet, radius: number, edges: vertical, target: previous}, {type: hole, position: [number, number], diameter: number, depth: through | blind} ] } 用户描述{这里放用户输入}我拿这套提示词跑过几十种不同描述最大的体会是显式写出“只输出 JSON不要代码块”能大幅减少解析失败。把 schema 写进 prompt 而不是靠模型自己猜参数抽取准确率能稳定拉到很高。如果模型偶尔不听话在 JSON 前后加了废话解析端记得写个容错函数把首尾花括号之间的内容抽出来再解析基本就能兜住。4. 端到端跑通从自然语言到 .step 文件的完整实操这一部分我把整套流程完整放出来目标是输入一句自然语言拿到一个 .step 文件。整个流程只需要三个依赖一个能输出 JSON 的 LLM 接口、CadQuery、trimesh。我不会绑定任何具体厂商你只需要把自己手上的大模型接口替换到 call_llm 函数里就行。4.1 总体流程与依赖环境整体流程是自然语言输入LLM 抽取 JSON schema规则校验CadQuery 执行建模导出 STEP 和 STL最后用 trimesh 做轻量验证。这里有一个关键设计把校验放在建模之前。目的很直白在几何内核执行前拦截掉那些明显不合法的参数比如负数尺寸、超过材料厚度的孔径、不存在的特征类型。别小看这一步它能省掉你至少一半的调试时间。环境方面强烈建议用 conda 建独立环境再装 CadQuery不要硬怼到系统 Python 里。CadQuery 依赖 OCCT 几何内核在 Windows 上直接 pip 安装经常遇到二进制包冲突conda 环境会省掉很多眼泪。conda create -n text2cad python3.10 conda activate text2cad pip install cadquery trimesh numpy如果你机器上还有一堆深度学习库尽量别在这个项目里混着装。隔离环境能避免很多莫名其妙的 dll 冲突和版本错乱这类问题排查起来比建模逻辑本身恶心得多。4.2 代码实现LLM 结构化输出加 CadQuery 生成下面是我实际在跑的核心脚本分四个函数调用 LLM、解析 JSON、构建几何、导出验证。你可以直接复制修改。import json from typing import Optional from cadquery import Workplane, exporters # 1. 调用 LLM拿到原始字符串返回请替换成你自己可用的接口 def call_llm(prompt: str) - str: # 示例伪代码换成你手上可用的语言模型客户端即可 # response your_llm_client.chat(messages[{role: user, content: prompt}]) # return response[choices][0][message][content] raise NotImplementedError(替换为你的 LLM 调用) def build_prompt(user_text: str) - str: return f你是一个零件建模的参数抽取引擎。你的任务是把用户的自然语言描述转换成 JSON 建模指令。 输出规则 1. 只输出一个合法 JSON 对象不要输出任何解释、注释、Markdown 代码块。 2. 单位默认 mm所有数值必须是数字不能带单位字符串。 3. 用户提到“通孔”时depth 写 through没有提深度时默认深度为材料厚度。 4. schema 固定为 {{ unit: mm, features: [ {{type: box, length: number, width: number, height: number, center: [number, number, number]}}, {{type: cylinder, radius: number, height: number, center: [number, number, number]}}, {{type: fillet, radius: number, edges: vertical, target: previous}}, {{type: hole, position: [number, number], diameter: number, depth: through | blind}} ] }} 用户描述{user_text} # 2. 解析并校验 LLM 输出 def extract_json(raw: str) - dict: start raw.find({) end raw.rfind(}) if start 0 or end 0: raise ValueError(LLM 没有输出 JSON 对象) return json.loads(raw[start:end1]) def validate_schema(schema: dict) - dict: if schema.get(unit) ! mm: raise ValueError(暂时只支持 mm 单位) features schema.get(features, []) if not features: raise ValueError(features 不能为空) allowed {box, cylinder, fillet, hole, chamfer} for idx, feat in enumerate(features): if feat.get(type) not in allowed: raise ValueError(f不支持的特征类型: {feat.get(type)}) if feat.get(radius, 0) and feat[radius] 0: raise ValueError(f特征 {idx} 的半径必须为正数) if feat.get(diameter, 0) and feat[diameter] 0: raise ValueError(f特征 {idx} 的直径必须为正数) return schema # 3. CadQuery 建几何 def build_from_schema(schema: dict): wp None for feat in schema[features]: ftype feat[type] if ftype box: wp Workplane(XY).box(feat[length], feat[width], feat[height]) elif ftype cylinder: if wp is None: wp Workplane(XY).circle(feat[radius]).extrude(feat[height]) else: wp wp.union(Workplane(XY).circle(feat[radius]).extrude(feat[height])) elif ftype fillet: if wp is None: raise ValueError(fillet 需要前置特征) wp wp.edges(|Z).fillet(feat[radius]) elif ftype hole: if wp is None: raise ValueError(hole 需要前置特征) pos feat.get(position, [0, 0]) d feat[diameter] wp ( wp.faces(Z) .workplane() .pushPoints([(pos[0], pos[1])]) .hole(d, depthfeat.get(depth, through)) ) return wp # 4. 导出与轻量验证 def export_and_verify(workplane, step_pathoutput.step, stl_pathoutput.stl): exporters.export(workplane, step_path) exporters.export(workplane, stl_path) import trimesh mesh trimesh.load(stl_path) print(bounding box:, mesh.bounds.tolist()) print(volume:, mesh.volume, mm^3) print(watertight:, mesh.is_watertight) if __name__ __main__: user_text 一块80乘60乘12毫米的铝板四角R8圆角中心打10毫米通孔 raw_output call_llm(build_prompt(user_text)) schema validate_schema(extract_json(raw_output)) part build_from_schema(schema) export_and_verify(part)代码里我刻意按照特征顺序逐步叠加先有基础体再做倒角最后打孔。CadQuery 的 faces(Z).workplane() 取的是当前实体顶面的中心平面pushPoints 的坐标是相对这个工作平面中心的所以日常用来打中心孔、阵列孔都非常顺手。4.3 实测一个典型零件的全流程记录与结果验证用上面那句“80乘60乘12毫米铝板”跑完整条流水线LLM 返回的 JSON 经过 extract_json 解析后大概是这样的{ unit: mm, features: [ {type: box, length: 80, width: 60, height: 12, center: [0, 0, 0]}, {type: fillet, radius: 8, edges: vertical, target: previous}, {type: hole, position: [0, 0], diameter: 10, depth: through} ] }校验通过后进入 build_from_schema导出的 STEP 文件很小。用 trimesh 读回 STLbounding box 是 [-40, -30, 0] 到 [40, 30, 12]长宽高正好是 80、60、12。中心孔子直径是 10整体轮廓与用户描述完全对齐。在交付前我还会做一个更朴素的验证动作读取 STL 的水密性。CAD 模型里有自由边、有孔没打通、存在重叠面在 STL 的水密检测里都会暴露出来。is_watertight 为 True 基本能说明当前固体是封闭的。对于更高要求的工程件请务必再用专业 STEP 查看器打开特征树确认特征顺序是否符合工艺预期。这一步其实比几何验证更重要因为 STEP 文件里的特征顺序直接决定了别人能不能顺畅地二次编辑这个模型。5. 常见问题与排查技巧实录text-to-cad 项目跑通门槛并不高但真的让它稳定运行、能交付给别人用细节全在坑里。我把实操中踩过的典型问题整理成一张速查表再挑几个印象最深的展开说。5.1 高频问题速查表问题现象直接原因排查思路与方法LLM 输出一大堆解释文字不是 JSONprompt 缺少强约束prompt 里加“只输出 JSON不要代码块”解析端用 extract_json 截取花括号内容兜底尺寸数值严重偏离常识模型对自然语言中的数字理解偏差schema 加 min/max 值域约束解析端校验必要时让模型输出时做一次单位换算孔的位置跑到板的外面position 被当成全局坐标明确 position 是相对顶面工作平面中心的局部坐标提示词里写清楚坐标规则倒角/圆角执行报错特征顺序错误先打孔再倒角固定特征链顺序基础体优先倒角切角次之孔最后STL 网格不水密布尔运算或叠加特征产生了非流形几何用 trimesh 检查 is_watertight严重时重建某一特征CadQuery 安装失败系统 Python 环境混乱用 conda 创建独立环境python 3.10 下最顺手5.2 几个印象最深的踩坑细节第一个是单位。用户描述里经常混着“5cm”“8in”“大概10mm”这类表达。如果 prompt 不写死默认 mm模型抽出来的数字里经常夹带单位字符串直接传给 CadQuery 就会崩溃。我现在的处理方式是在校验层统一把所有数值转成 float把单位换算规则写进 promptcm 乘以 10in 乘以 25.4。模型干这个活很稳前提是你必须明确告诉它规则。第二个是通孔和盲孔的区分。很多初级描述只说“打一个直径 6 的孔”没有提深度。CadQuery 的 hole 默认按材料厚度贯穿这通常就是用户要的。但问题在于如果后面材料厚度改了孔不会自动跟着变深你必须重新生成。所以我建议 schema 里强制带 depth 字段取值是 through 或 blind盲孔再配一个 depth 数值。这样不仅建模对后续编辑也更清楚。第三个是特征顺序。我复现过很多次的崩溃场景是这样的用户说“一个圆盘中心打孔四周倒角”模型输出的顺序可能是 cylinder → hole → fillet。结果 fillet 要作用的边线已经被 hole 打断CadQuery 直接抛异常。这种时候不要指望模型自觉调整顺序应该在解析层做强制排序基础体排最前倒角/切角其次孔这类切除操作放最后。哪怕用户描述里讲的顺序是“先打孔再倒角”你也要按制造合理性调整顺序。这个排序规则应该写死在代码里而不是放在 prompt 里靠模型自觉。排查这类问题我有一个习惯先把 LLM 出的 JSON 和最终 STL 的 bounding box 对比看轮廓尺寸对不对再看体积是否合理最后才打开图形界面看特征。这个顺序能帮你第一时间把理解错误和建模执行错误分开避免在 CadQuery 代码里白折腾半天。最后分享一点我自己的体会text-to-cad 这个方向的边界不在模型而在你对中间表示和校验流水线的认真程度。建议从一个极小的特征集开始先让整套流程在几十个测试句子上做到零崩溃再逐步加阵列、布尔运算、拉伸切除这些高级操作。同时别把界面想得太复杂一句自然语言加一个 STEP 预览已经足以改变很多手工建图流程。文本生成 CAD 真正有价值的地方不只是自动出图而是它把人的设计意图变成了一条可复现、可审计、可修改的参数链这对制造业的意义比生成一个模型文件本身要大得多。
返回列表