尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Text-to-CAD实战:用LLM生成可编辑的CadQuery参数化模型

Text-to-CAD实战:用LLM生成可编辑的CadQuery参数化模型 这两年“文本生成3D”的概念被炒得火热但大多数 Demo 只能生成看起来好看的低模网格真要拿到机械加工、装配验证里用立刻见光死。我之所以开始认真研究 text-to-cad是因为某天下午工厂那边扔过来一个需求能不能让产线工人直接口述“一个 120×80 的底板四角倒角中间再加一个方形凸台”系统自动把可编辑的三维模型建出来这个需求听起来像科幻片落地时却牵扯出一整套工程问题。今天这篇文章我就把这几个月折腾 text-to-cad 的完整经验拆开来讲包括技术选型、原理、可复现的实操流程以及各种坑。1. 为什么 text-to-cad 和普通的文本生成 3D 不是一回事1.1 你要的不是“像”而是“准”市面上的文生3D模型比如基于扩散模型的 DreamFusion、SDF 类生成网络输出的是网格或者神经隐式场。这类结果拿来做展示、游戏资产、概念预览完全没问题但放在工业场景里几乎不可用。原因很简单CAD 模型的核心是“边界表示”是带特征历史、约束、参数关系的实体模型不是一堆三角形拼出来的表面。举个例子工人说“一块板子长 100 毫米、宽 40 毫米、高 10 毫米四角倒 R5 的圆角”。普通的文生3D能生成一个大致像板子的 mesh但你用测量工具一量长度可能是 98.7 毫米圆角也不是精确的 R5。机械装配里这种误差就是废品。CAD 模型要求的是“准”每个尺寸必须有明确数值每个特征必须可编辑、可抑制、可修改参数。也就是说text-to-cad 的最终产物不应该是一张图或一个 mesh而应该是一个参数化三维实体能导出 STEP能进 CAM 编程能回到 CAD 软件里继续改。另一个“准”体现在约束关系上。真正的 CAD 工程师不会只关心“这里有个孔”还会关心孔和某个边是否同心、是否对称、是否共面。普通生成模型根本不理解这些拓扑约束。所以做 text-to-cad 时如果只把文本丢给一个图片生成模型结果一定翻车。这也是为什么当前工业界更认可“LLM 程序化 CAD 脚本”这条路线因为代码天然能够表达精确数值和几何约束。1.2 两条主流技术路线我为什么选代码生成现在 text-to-cad 大致可以分成两类路线。第一类是“预测式”让文本编码器与三维形状编码器对齐直接生成 SDF 场或者点云再通过 Marching Cubes 之类的算法提取网格。第二类是“程序式”用大语言模型直接生成参数化建模脚本交给 CAD 内核解释执行。我实际调研发现在工程落地上第一类路线很难直接用于制造业。它生成的是离散表示虽然也可以通过算法转成 STEP但转换后没有任何特征历史后期修改几乎等于重新建模而且尺寸精度受限于体素分辨率。第二类路线则完全不同模型输出的是 OpenSCAD、CadQuery 或者 FreeCAD 的 Python 脚本脚本里写明了box(80, 40, 12)这样的操作执行后由 OCCT 这类 CAD 内核计算精确的边界表示。尺寸是多少就是多少倒角多大就是多大而且脚本本身就是可参数化的改一个变量模型自动更新。这两条路线的核心差异可以用“图像生成”和“参数化绘图”来类比。前者画出来一张像素图改个颜色都要重新画后者留下一份图纸改一个标注全图联动。对制造业来说后者才是真正能进产线的。所以我后面所有实操方案都围绕“LLM 生成 CadQuery 代码”展开。如果你想快速做概念验证可以用 OpenSCAD但要做接近工程级的模型我更推荐 CadQuery。对比维度程序式LLM CadQuery/OpenSCAD预测式SDF/扩散 网格提取输出格式脚本 STEP/STLMeshSTL/OBJ可转STEP但无特征可编辑性完全参数化可修改特征几乎不可编辑需重建尺寸精度精确由内核计算受网格分辨率限制约束支持可通过代码表达同心、对称很难表达适合场景机械零件、标准件、工装夹具概念模型、3D打印摆件落地难度需要处理LLM代码质量需要大量配对数据训练2. 核心原理拆解从一句话到可编辑的 CAD 文件2.1 语言模型是怎么“懂”几何的很多人好奇LLM 连“看”都看不到图像凭什么能生成几何代码原理其实不算玄学。大语言模型是在海量代码和文本上训练的它见过无数种 API 调用方式和注释说明。当你说“创建一个 80 毫米长、40 毫米宽、12 毫米高的长方体”模型在内部会把它拆成语义单元然后根据概率生成一段 CadQuery 代码box(80, 40, 12)。模型并没有进行可视化推理它只是学习到了“某种描述对应某种 API 调用序列”的映射关系。这里有个关键点LLM 对尺寸单位的敏感度取决于提示词。如果描述里没有明确“毫米”模型可能会用英寸、厘米甚至抽象数值。所以我在提示词工程环节永远会加一句“所有尺寸单位均为毫米”。从Transformer机制的角度看文本描述经过 tokenizer 变成 token id送入多层自注意力网络最后解码出目标代码。这个过程中没有显式的三维几何计算几何计算全部交给 CAD 内核。换句话说LLM 负责“翻译”OCCT 负责“算”。这也解释了一个常见现象简单零件板、轴、孔模型生成很准因为这类代码在训练语料里大量出现复杂曲面或自由造型LLM 生成的代码经常逻辑混乱因为它在训练数据里没见过足够多的此类表达。2.2 文本编码与跨模态对齐除了代码生成路线学术界还有一类 work 是构建文本描述与三维形状的跨模态空间。比如用 CLIP 的文本编码器提取描述向量再用这个向量控制一个 SDF 生成网络让模型生成与文本语义一致的形状。这类方法在生成“像一只猫的杯子”这种模糊概念时很有优势但落到 CAD 上就尴尬了CAD 模型要求的是精确的拓扑和尺寸而 CLIP 向量根本无法表达“20 毫米深的盲孔”这种量化信息。所以我个人认为对于 text-to-cad 这个方向跨模态对齐更适合做“形状检索”或“语义分类”不适合做精确建模。如果你想搭建一条能出工程图的流水线应该把重点放在“文本 → 代码 → B-rep”这条链路上。B-repBoundary Representation是 CAD 内核的标准数据模型它用面、边、顶点和拓扑关系描述实体。CadQuery 执行脚本时OCCT 内核会生成一个精确的 B-rep 实体然后再由导出器生成 STEP、STL 或者 DXF。整个过程没有“猜测”只有计算。2.3 为什么用 CadQuery 而不是 OpenSCAD在程序式路线里目标 DSL 的选择直接影响生成成功率和下游可用性。OpenSCAD 语法简单善于布尔运算适合快速生成齿轮、外壳这类纯 CSG 模型但它的 2D 草图能力很弱倒圆角、倒斜角也远不如参数化建模工具方便。CadQuery 则构建在 OpenCascade 之上支持真正的草绘、拉伸、旋转、放样、扫掠还有面选择器、边选择器这类高度面向 CAD 的操作。举一个例子要在长方体顶面打一个居中通孔OpenSCAD 需要手动用translate把圆柱移到中心再difference。CadQuery 则可以直接通过面选择器进入顶面在局部坐标系下定位圆心写起来更像工程师的思维方式。LLM 也更擅长生成 CadQuery 代码因为 CadQuery 的 API 语义化更强函数名本身就是清晰的指令。实际做项目时我还会结合 FreeCAD 的 Python API但它的对象模型比较重LLM 生成的代码经常因为缺少FreeCAD.Console.PrintLog等初始化语句而报错。CadQuery 就轻量得多一条pip install cadquery就能跑。下面的对比表可以帮助你快速决策。目标 DSL底层内核草图能力特征操作导出格式LLM生成友好度OpenSCAD自定义 CSG弱弱需布尔STL, DXF, OFF中适合简单几何CadQueryOpenCascade强强倒角/放样/扫掠STEP, STL, DXF, SVG高API语义清晰FreeCAD PythonOpenCascade强强多格式低样板代码多Blender Python多边形网格弱弱多格式低不适合机械3. 实操搭建一个最小可用的 text-to-cad 流程3.1 环境准备与模型选型我本地跑 text-to-cad 用的是一台 32GB 内存的 Linux 工作站没有独显时也能扛住 7B 模型的 CPU 推理就是速度慢一些。如果想在 GPU 上跑建议最少 8GB 显存可以塞下 Qwen2.5-Coder 7B 的 4-bit 量化版。这里我给出一个不依赖复杂框架的快速方案。首先是安装 CadQuery。官方推荐用 conda 装因为 OCCT 库体积大conda 能自动解决依赖conda create -n cad python3.10 -y conda activate cad conda install -c conda-forge cadquery2.4如果你想用 pip 直接装也可以但在某些环境下容易遇到 OCCT 链接库缺失的问题。我第一次就是直接pip install cadquery结果运行时报libTKernel.so: cannot open shared object file最后老老实实换成 conda。然后是部署语言模型。我推荐先用 Ollama 跑本地模型启动方便还能提供 REST API。安装完成后拉取一个代码模型ollama pull qwen2.5-coder:7b拉取完之后Ollama 默认在11434端口提供 API。你可以用curl先测一下能不能跑通curl http://localhost:11434/api/generate -d {model:qwen2.5-coder:7b,prompt:用Python写一个hello world,stream:false}如果本地显存不够也可以用 OpenAI 兼容的 API 服务或者直接调用云端大模型。但注意如果你正在处理内部图纸最好还是本地化部署避免数据外传。本地 7B 模型在简单零件上已经能用复杂零件建议用 14B 甚至 32B 模型。3.2 提示词工程用结构化描述约束生成text-to-cad 的成败一半在提示词。我踩过最大的坑就是让模型“自由发挥”。工程师描述零件时脑子里是有明确规范的但 LLM 不知道所以必须把需求结构化。我的提示词模板包含五要素零件名称、全局尺寸、草图形状、特征操作、约束关系。下面是一个典型的输入你是一名机械CAD工程师。请用 CadQuery 生成一个零件。 要求使用毫米作为单位基准面为XY平面Z轴为轴向。 零件名称带法兰的轴套 参数 - 内径20mm - 外径40mm - 法兰直径60mm - 法兰厚度5mm - 轴套总长30mm - 内孔沿Z轴贯穿 - 法兰外圆与轴套外圆同心 只输出从 import cadquery 开始的Python代码不要包含任何解释。为什么非要强调“只输出代码”因为模型在生成过程中经常会夹带# 下面是代码、这个模型可以通过以下代码实现之类的废话。这些文字一旦被exec执行就会直接报错。所以提示词里必须把输出格式钉死。另外如果用 7B 小模型一次提示很难生成复杂零件。我通常会在提示词里塞一个 few-shot 示例让模型模仿格式。比如先给一个“矩形板打孔”的输入输出对再让它生成目标零件。这个技巧能显著降低语法错误率。3.3 完整示例从文本到 STEP/STL下面是我实际跑通的一段 Python 脚本。它调用 Ollama 的 API让模型生成 CadQuery 代码提取代码后执行并导出。我特意把“提取代码块”这一步放进去因为模型输出经常包在 Markdown 代码块里。import requests import re import cadquery as cq prompt 你是机械CAD工程师。请用 CadQuery 生成零件 一块 80mm 长、40mm 宽、12mm 高的平板 四角倒 R5 圆角 平板顶部中心有一个直径 8mm 的通孔。 只输出从 import cadquery 开始的 Python 代码不要解释。 # 调用 Ollama resp requests.post( http://localhost:11434/api/generate, json{ model: qwen2.5-coder:7b, prompt: prompt, stream: False } ) raw resp.json()[response] # 提取 Markdown 代码块 match re.search(r(?:python)?\n(.*?), raw, re.S) if match: code match.group(1) else: code raw.strip() # 执行代码 exec(code, globals()) # 导出模型假设模型变量叫 result实际可根据生成代码调整 cq.exporters.export(result, plate.step) cq.exporters.export(result, plate.stl)对于上述提示词模型大概率会生成类似下面这一段import cadquery as cq result ( cq.Workplane(XY) .box(80, 40, 12) .edges(|Z) .fillet(5) .faces(Z) .workplane() .hole(8) )这段代码值得仔细讲一下box(80, 40, 12)创建以原点为中心的长方体.edges(|Z)选中所有平行于 Z 轴的边也就是四个竖向直角边.fillet(5)给这四条边倒 R5 圆角.faces(Z)选中当前实体中 Z 轴正方向的面也就是顶面.workplane()以该面为基准创建一个新的绘图平面.hole(8)在平面原点也是顶面中心打一个直径 8 的孔。由于hole默认贯穿整个实体所以生成的是一个通孔。我第一次跑这条流程的时候模型生成的是.edges(Z)而不是.edges(|Z)结果把顶面四条边也倒成圆角了。后来我在提示词里补充了一句“圆角只加在高度方向的竖边上”正确率提高了很多。这说明模型对 CadQuery 的语义理解还需要用户帮它细化。3.4 生成后的自动校验生成完不能直接拿出去用我习惯加一段自动校验逻辑。最简单的校验就是看包围盒尺寸和体积# 获取边界框 bb result.val().BoundingBox() print(X:, bb.xlen, Y:, bb.ylen, Z:, bb.zlen) print(体积:, result.val().Volume())对于上面那个 80×40×12 的平板体积应该等于80*40*12 - π*4*4*12也就是38400 - 603.19 37796.81 mm³。如果程序输出的体积和这个偏差超过 1%说明某个特征没有正确执行。我建议把这套校验逻辑和 CAD 内核绑定每次生成后自动跑一遍有问题直接让模型重新生成。除了体积还可以检查实体数量。CadQuery 中result.val().Solids()返回所有独立实体列表正常情况应该只有一个实体。有时候模型生成代码里用了union但没合并就会出现两个实体导出 STEP 后进装配体就会出问题。4. 常见问题与排查技巧实录4.1 尺寸不对单位、幻觉和覆盖尺寸错误是最常见的问题。我遇到过三种情况第一种是单位混淆。模型在训练数据里见过英制单位提示词里如果说“一块 10 宽的板”它可能当成英寸处理。第二种是模型幻觉把 80 写成了 800。第三种是后置参数覆盖比如代码里先box(80,40,12)后面又translate了某个量但实际尺寸没变。排查思路很简单先看生成的代码再对照 BoundingBox。如果代码里写的是box(80, 40, 12)但导出的包围盒 X 长度是 800那一定是单位或者中间处理问题。如果是 79.98 这种值可能是 OCCT 内部拟合误差在公差范围内可以接受。我的经验是在提示词里加上“所有尺寸数值必须严格来自用户输入不允许四舍五入或估算”能减少一部分幻觉。但最稳妥的还是后置校验脚本把长宽高和体积全部打印出来人工扫一眼。4.2 特征丢失同心、对称这类约束不好搞LLM 对隐式约束的理解很弱。你说“法兰外圆与轴套外圆同心”它可能会生成两个圆但圆心不在同一点。你说“四个孔等距分布在分度圆上”它可能会只生成一个孔。这并不是模型笨而是自然语言里的约束关系在代码里需要转换成具体的坐标系计算模型经常图省事省略了。解决办法有两个。一个是提示词里把所有约束“翻译”成可检查的几何条件比如“法兰圆心到轴套圆心距离必须为0”。另一个是写一个自动检查脚本。比如对于同心约束可以分别获取法兰圆柱和轴套圆柱的轴线计算它们之间的距离# 假设两个面已经找到 face1 result.faces(Z).first() # 示例实际需根据模型调整 # 用 CADQuery 内置方法获取平面中心或圆心实际操作中CadQuery 对曲线和面的分析方法不如专门的几何库方便。我一般把 STEP 导出后用gmsh或pythonocc-core做二次检测。但这样做又增加了依赖。如果你只是做原型最简单的还是人工看一下代码里的坐标计算模型是否用了center或者moveTo来对齐圆心。4.3 代码输出不干净Markdown、解释文字、语法错误这个问题出现频率极高。小模型尤其喜欢在代码前后加解释比如“以下是生成的 CadQuery 代码”。所以提取代码块是刚需。但光提取还不够有些代码块内第一行是python标签有些是空行有些代码里有...省略号。我用的提取规则是优先找python找不到就找再找不到就把整个文本当作代码。在exec之前我会先调用ast.parse做语法检查import ast try: ast.parse(code) except SyntaxError as e: print(语法错误:, e) # 让模型重新生成如果语法检查通过再执行。这样能避免exec执行到一半时出错导致进程状态被污染。还有个细节不要直接exec任意来源的代码尤其是企业环境。可以把生成代码写入临时文件在子进程里执行并把产物输出到指定目录。这样即使代码有恶意操作也不会影响主进程。我在本地开发时图省事用exec但生产环境的版本是沙箱模式。4.4 算力不足本地小模型能力跟不上怎么办如果你和我一样起初用 7B 模型跑很快会发现复杂零件经常错。比如生成一个带法兰的轴套代码能画出来但倒角和打孔的位置乱掉。这时候有几个优化手段。第一个是量化级别。Ollama 默认可能用 Q4 量化速度尚可但代码能力有些损失。显存够的话可以拉取更高精度的量化版本或者直接用qwen2.5-coder:14b。第二个是 few-shot 提示。把典型零件带孔板、轴套、支架的输入输出对放进提示词里模型会模仿得更准确。第三个是微调。如果你有几十个标准件的 CAD 脚本可以用 LoRA 微调一个专用模型。我在公司内部尝试过用 500 条标准法兰数据做微调准确率提升了 40% 以上。这里也说说速度。在 Apple Silicon 上跑 7B Q4 模型推理速度大约每秒 25 token生成一段 80 行的 CadQuery 代码需要 3 秒左右完全够用。如果是老款 Intel CPU速度可能掉到每秒 5 token体验就很差了。建议至少保证 16GB 内存。4.5 从“能用”到“好用”人机协同闭环最后想聊一个理念问题。text-to-cad 这个方向短期内很难做到全自动“一句话出最终图纸”。我自己的项目里比较有效的工作流是“AI 生成初版 人工审查特征树 参数化模板沉淀”。具体来讲用户用自然语言描述需求系统生成 CadQuery 代码并自动导出 STEP 和预览图。工程师打开预览图如果尺寸不对直接说“孔往右移 5 毫米”系统通过对话记录重新生成修改后的代码。这时候上下文里的原始代码和修改指令同样作为提示词的一部分模型只需要调整center的坐标即可。这套闭环跑起来后单个标准件的建模时间从原来的 20 分钟缩短到 3 分钟而且模型生成的特征树可编辑后续改参数也能直接联动。5. 那些只可意会的实操心得做了这么多实验我发现 text-to-cad 真正能落地的关键不是模型有多强而是你如何把工程需求翻译成机器能严格遵守的规则。自然语言天生有歧义而 CAD 要求的是无歧义。所以提示词里所有关键尺寸不仅要写数值还要写单位和基准。最好再让模型把关键参数提取出来放到代码开头的变量里而不是埋在操作链中间。比如我要求模型生成的代码开头永远是L 80 # 长度 mm W 40 # 宽度 mm H 12 # 高度 mm R 5 # 圆角半径 mm D 8 # 孔径 mm这样后续人工修改只需改上面几个数字整个模型自动更新。这也是把 text-to-cad 和普通代码生成区别开的核心模型不仅要生成“能看的形状”还要生成“能维护的模型”。目前大部分公开模型默认不会这么做需要在提示词里显式要求。另外一个小技巧在提示词里加入“生成后请用cq.exporters.export(result, output.step)导出文件”很多模型会自动补上导出语句省去手工拼接扩展名。我还有段时间尝试过让模型输出模型截图但发现还不如直接让 Qt 窗口显示 CadQuery 生成的形状。这套流程跑到现在我正在考虑把标准件库里的 30 多个典型零件全部做成 few-shot 模板让车间工人通过一个简单的表单界面输入参数系统调用本地模型生成代码并完成自检。虽然离“口述建模”还有距离但已经足够让重复性建模变得轻松。如果你也在折腾 text-to-cad建议先别贪大找一种自己做过的、规则明确的零件类型作为起点把提示词、校验、导出链路跑通后再拓展。这条路值得走下去。
返回列表