ARTICLE DETAIL

资讯详情

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

text-to-cad实战:LLM解析与参数化建模生成STEP/STL/GLB

text-to-cad实战:LLM解析与参数化建模生成STEP/STL/GLB 1. 从一句话到三维模型text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词我脑子里蹦出来的画面是对着电脑敲一行字比如“一个 80×60×20mm 的法兰盘中心开直径 30mm 通孔四角各一个 M6 沉头孔”然后软件直接吐出一个可以编辑、可以导出、可以拿去加工的实体模型。这个画面放在五年前还属于科幻范畴但放到今天它已经是一条能跑通的技术链路了。text-to-cad 的核心价值说白了就是把自然语言描述转换成 CAD 可识别的几何数据。传统流程里你得打开 SolidWorks、Fusion 360 或者中望 CAD手动拉伸、打孔、倒角一个中等复杂度的零件少说半小时。而 text-to-cad 想做的事情是让这段描述直接变成 STEP、GLB 或 STL 文件——STEP 给加工和工程软件用GLB 给渲染和 Web 展示用STL 给 3D 打印和切片软件用。这三个格式基本覆盖了从设计到制造到展示的全链路。我之所以对这个方向感兴趣是因为它踩中了几个真实的痛点。第一非专业设计人员想快速验证结构想法比如做机器人底盘的创客、做夹具的产线工程师他们不需要精通参数化建模只需要一个能用的模型。第二批量化的标准件生成比如一批不同孔径的法兰、不同长度的支架用脚本生成比手动建模快十倍。第三AI 辅助设计的入口大语言模型能理解文字但理解不了几何text-to-cad 就是那座桥。这篇文章适合谁看如果你是会写 Python、懂一点三维几何概念、想自己搭一套文字转模型流水线的开发者那这篇就是给你写的。如果你只是好奇这个方向怎么落地、有哪些坑也能从里面拿到足够的信息。我会把整体架构、格式选型、核心代码、参数计算、踩坑经验全部摊开讲尽量做到你照着做就能跑出一个能用的版本。需要先说明一点text-to-cad 目前没有哪个开源方案能做到“任意文字描述直接生成任意复杂模型”它更适合结构化描述生成参数化零件这个场景。指望它一句话生成一个完整的汽车外壳现阶段不现实。但生成法兰、支架、齿轮、外壳盒子这类规则几何体完全可行而且效果相当稳定。2. 整体架构设计为什么我选择“LLM 解析 参数化建模”这条路线2.1 三种技术路线的取舍分析在动手之前我调研过三条主流路线这里把它们的优劣摆出来方便你判断自己该走哪条。路线核心思路优势劣势适用场景端到端生成用扩散模型或 Transformer 直接生成网格顶点理论上能生成任意形状训练成本极高可控性差尺寸不准学术研究、艺术造型代码生成LLM 生成 CadQuery/OpenSCAD 代码再执行灵活可表达复杂逻辑代码容易出错需要沙箱执行有一定编程能力的用户参数化解析LLM 抽取结构化参数喂给建模库稳定、可控、尺寸精确只能生成预定义类型的零件工程零件、标准件批量生成我最终选了第三条路理由很直接工程场景对尺寸精度的要求是刚性的。一个法兰盘的中心孔如果是 30mm那它就必须是 30mm不能是 29.7mm 也不能是 30.3mm。端到端生成和代码生成在这点上都不够可靠而参数化解析把“理解文字”和“生成几何”两件事解耦了——LLM 只负责把文字变成 JSON建模库只负责把 JSON 变成几何。每一段都可测试、可调试、可替换。这个思路还有一个隐藏好处LLM 的输出是可校验的。JSON 里的字段类型、数值范围、必填项都能用 schema 卡住一旦 LLM 抽错了参数你在解析阶段就能发现而不是等到模型生成出来才发现孔打歪了。2.2 完整流水线的四个阶段整条流水线我拆成四段每段职责清晰文本理解阶段用户输入自然语言LLM 抽取零件类型和参数字典。比如“直径 100mm、厚 10mm、中心孔 30mm 的法兰”抽成{type: flange, outer_dia: 100, thickness: 10, bore_dia: 30}。参数校验阶段检查数值是否合理、单位是否统一、必填项是否齐全。这一步是防止 LLM 幻觉的关键闸门。几何生成阶段用 CadQuery 或 build123d 这类参数化建模库根据参数字典构造实体。格式导出阶段把实体导出成 STEP、GLB、STL按需分发。为什么用 CadQuery 而不是直接操作 OpenCASCADE 的 Python 绑定因为 CadQuery 的 API 是链式的写起来接近自然语言比如cq.Workplane(XY).circle(50).extrude(10)就是“在 XY 平面画半径 50 的圆拉伸 10mm”。这种表达方式对后续维护和扩展极其友好而且它底层就是 OpenCASCADE导出的 STEP 是真正的 B-Rep 实体不是网格近似。2.3 为什么格式导出要分三路走很多人会问生成一个 STL 不就行了吗为什么要同时支持 STEP 和 GLB这里涉及三种格式的本质差异我用一个表格说清楚。格式数据本质精度典型用途导出要点STEPB-Rep 边界表示精确数学曲面工程软件、CNC 加工保留实体拓扑可再编辑STL三角网格近似有弦高误差3D 打印、切片需设置合适的线性偏差GLB三角网格 材质近似可带颜色Web 展示、渲染需三角化并打包材质关键点在于STEP 是精确的STL 和 GLB 是近似的。如果你把模型发给加工厂必须给 STEP因为 CAM 软件需要精确的曲面信息来生成刀路。如果你只是想在网页上转一转看看效果GLB 最合适体积小、加载快、还能带材质。STL 则是 3D 打印的通用语言切片软件只认它。所以我的导出模块设计成三个独立函数共享同一个实体对象各自处理各自的三角化参数。这样既保证精度又保证灵活性。3. 核心细节解析LLM 提示词设计与参数 Schema 定义3.1 提示词怎么写才能让 LLM 稳定输出 JSON这是整个项目里最容易被低估的环节。我一开始觉得“让 GPT 输出 JSON”很简单结果实测下来不加约束的话LLM 会给你返回带解释文字的 JSON、字段名大小写不一致、数值带单位字符串、甚至把毫米和厘米混着用。我的解决方案是三段式提示词角色设定 输出格式约束 少量示例。角色设定部分告诉 LLM 它是一个 CAD 参数抽取器只做抽取不做解释。输出格式约束部分用 JSON Schema 的简化版描述字段明确每个字段的类型和单位。示例部分给两到三个典型输入输出对覆盖法兰、支架、盒子三种零件。实测下来加上示例之后字段名错误率从 30% 降到 5% 以下。这个投入产出比非常高值得花时间打磨示例。3.2 参数 Schema 的设计原则Schema 设计我遵循三条原则单位统一为毫米所有长度字段一律用 mm角度用度。LLM 在抽取时如果遇到“5cm”必须转换成 50。这个转换在提示词里明确要求。字段名用蛇形命名outer_dia、bore_dia、thickness避免驼峰和空格方便后续代码直接映射。必填项和可选项分开零件类型、主要尺寸是必填倒角半径、圆角半径是可选缺省时用默认值。下面是我实际用的一个简化 Schema 示例用 Python 的 dataclass 表达from dataclasses import dataclass, field from typing import Optional dataclass class FlangeParams: outer_dia: float # 外径 mm thickness: float # 厚度 mm bore_dia: float # 中心孔直径 mm bolt_count: int 4 # 螺栓孔数量 bolt_circle_dia: Optional[float] None # 螺栓孔分布圆直径 bolt_hole_dia: Optional[float] None # 螺栓孔直径 fillet_radius: float 0.0 # 边缘圆角这个 dataclass 既是校验依据也是建模函数的入参。LLM 输出的 JSON 直接**kwargs展开就能构造非常顺滑。3.3 参数校验的边界条件校验阶段我设了几条硬规则任何一条不满足就拒绝生成并返回错误信息所有尺寸必须为正数且不超过 1000mm防止 LLM 把单位搞错比如把 100mm 写成 100000。中心孔直径必须小于外径否则几何上无法构造。螺栓孔分布圆直径必须在外径和中心孔之间否则孔会打到外面或里面。厚度不能小于 0.5mm否则实体太薄导出 STL 时容易破面。这些规则看起来简单但每一条都对应我实际踩过的坑。比如有一次 LLM 把“直径 100”理解成了“半径 100”生成出来的法兰外径 200mm直接超出打印平台。加上上限校验之后这类错误在解析阶段就被拦住了。4. 实操过程从零搭一套可运行的 text-to-cad 流水线4.1 环境准备与依赖安装我用的环境是 Python 3.10 CadQuery 2.4。CadQuery 的安装是第一个坑因为它依赖 OpenCASCADE在 Windows 上直接 pip install 经常失败。我的建议是用 conda 装命令如下conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery2.4 pip install openai trimesh为什么用 conda 而不是 pip因为 conda-forge 渠道的 CadQuery 包已经把 OpenCASCADE 的二进制依赖打包好了pip 版本需要你自己编译或者找预编译 wheel在 Windows 上极其折腾。我试过 pip 装卡在 OCP 编译上两个小时没动静换 conda 五分钟搞定。trimesh 是用来做 GLB 导出的CadQuery 原生只支持 STEP 和 STLGLB 需要先转成 trimesh 的 mesh 对象再导出。openai 库是用来调 LLM 的如果你用其他模型换成对应的 SDK 即可。4.2 文本解析模块的实现解析模块的核心是一个函数输入自然语言输出参数字典。我把它拆成两步调 LLM 拿原始 JSON再用 dataclass 校验。import json from openai import OpenAI client OpenAI(api_keyyour-key) SYSTEM_PROMPT 你是一个CAD参数抽取器。用户会用自然语言描述一个零件 你需要抽取零件类型和尺寸参数输出纯JSON不要任何解释文字。 所有长度单位统一为毫米角度单位为度。 如果用户给的单位是厘米乘以10如果是米乘以1000。 def parse_text(user_input: str) - dict: resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input} ], response_format{type: json_object} ) raw resp.choices[0].message.content return json.loads(raw)这里有个细节response_format{type: json_object}这个参数非常关键它强制 LLM 输出合法 JSON省去了自己写正则提取的麻烦。我用过不带这个参数的版本LLM 会在 JSON 前后加“好的以下是抽取结果”这类废话解析起来很烦。4.3 几何生成模块以法兰盘为例法兰盘是我用得最多的测试零件因为它的几何特征典型一个圆柱主体、一个中心通孔、一圈螺栓孔、可选倒角。用 CadQuery 实现大概二十行代码。import cadquery as cq def build_flange(p: FlangeParams) - cq.Workplane: # 主体圆柱 body cq.Workplane(XY).circle(p.outer_dia / 2).extrude(p.thickness) # 中心通孔 body body.faces(Z).workplane().hole(p.bore_dia) # 螺栓孔 if p.bolt_count 0 and p.bolt_circle_dia and p.bolt_hole_dia: body (body.faces(Z).workplane() .polarArray(p.bolt_circle_dia / 2, 0, 360, p.bolt_count) .hole(p.bolt_hole_dia)) # 可选倒角 if p.fillet_radius 0: body body.edges(|Z).fillet(p.fillet_radius) return body这段代码里有几个值得说的点。faces(Z)是选择 Z 方向最高的那个面作为工作平面这样打孔方向就是从上往下。polarArray是极坐标阵列参数依次是半径、起始角、总角度、数量用来均匀分布螺栓孔。edges(|Z)是选择所有平行于 Z 轴的边对这些边做圆角这样法兰的外圆柱面上下边缘会变圆润。参数计算方面螺栓孔分布圆直径如果用户没给我会按经验公式取(outer_dia bore_dia) / 2也就是外径和孔径的中间值。这个位置既不会太靠外导致孔壁太薄也不会太靠内导致和中心孔干涉。实测下来这个默认值在大多数场景下都合理。4.4 三格式导出与参数设置导出模块我写了三个函数分别对应 STEP、STL、GLB。def export_step(model, path): cq.exporters.export(model, path, exportTypeSTEP) def export_stl(model, path, tolerance0.01, angular_tolerance0.1): cq.exporters.export(model, path, exportTypeSTL, tolerancetolerance, angularToleranceangular_tolerance) def export_glb(model, path): import trimesh vertices, faces model.val().tessellate(0.01) mesh trimesh.Trimesh(verticesvertices, facesfaces) mesh.export(path, file_typeglb)STL 导出的tolerance参数是线性偏差单位是毫米0.01 意味着曲面被三角化时实际曲面和三角面之间的最大距离不超过 0.01mm。这个值越小网格越密文件越大。对于 3D 打印0.01 到 0.05 都够用对于精细展示可以调到 0.005。angular_tolerance是角度偏差控制圆弧被分成多少段0.1 弧度大约是 5.7 度一段视觉上已经足够圆滑。GLB 导出走的是 trimesh 的路径因为 CadQuery 没有原生 GLB 支持。tessellate方法把 B-Rep 实体转成三角网格然后交给 trimesh 打包成 GLB。这里要注意GLB 的坐标系和 CAD 的坐标系可能不一致trimesh 默认 Y 轴向上而 CAD 通常 Z 轴向上导出后可能需要在查看器里旋转一下。4.5 完整调用示例把上面几块拼起来一个完整的调用长这样user_input 生成一个外径120mm、厚15mm的法兰中心孔直径40mm6个直径8mm的螺栓孔均匀分布螺栓孔分布圆直径90mm params parse_text(user_input) flange FlangeParams(**params) model build_flange(flange) export_step(model, flange.step) export_stl(model, flange.stl) export_glb(model, flange.glb)跑通之后你会得到三个文件。STEP 拖进 FreeCAD 或者中望 CAD 能看到精确实体STL 拖进切片软件能直接打印GLB 拖进浏览器能实时旋转查看。整条链路从文字到模型熟练之后不到十秒。5. 常见问题与排查技巧实录5.1 LLM 抽取参数错误的典型模式这是最高频的问题我整理了四种典型错误和对应的解法。错误模式具体表现根因解法单位混淆把 5cm 当成 5mm提示词未强调单位转换在 system prompt 里明确要求转换半径直径混淆直径 100 抽成半径 100中文“直径”和“半径”语义接近字段名用 dia 而非 radius提示词强调数值幻觉用户没提螺栓孔LLM 自己编了 4 个LLM 倾向于补全常见模式Schema 里可选项默认 None不自动填充字段名漂移输出 outerDiameter 而非 outer_dia提示词未固定字段名在提示词里列出所有合法字段名其中数值幻觉最隐蔽因为生成的模型看起来“很正常”但多了用户没要求的特征。我的做法是在校验阶段对比用户原文和抽取结果如果某个可选字段被填充了但原文里找不到对应关键词就标记为可疑并询问用户确认。5.2 STL 导出破面与修复STL 破面是 3D 打印玩家的老朋友了。text-to-cad 生成的模型破面通常有三个原因壁厚太薄、三角化偏差太大、实体本身有自相交。壁厚问题最好解决在校验阶段加一条规则任何特征的最小尺寸不小于 0.8mm。三角化偏差问题通过调小 tolerance 解决但要注意文件体积会膨胀。自相交问题最麻烦通常是布尔运算的数值误差导致的解法是在 CadQuery 里对实体做一次clean()操作它会合并重合的面和边消除微小间隙。如果 STL 已经导出且发现破面可以用 trimesh 的修复功能补救import trimesh mesh trimesh.load(broken.stl) mesh.fill_holes() mesh.fix_normals() mesh.export(fixed.stl)fill_holes补洞fix_normals修正法线方向。这两个操作能解决 80% 的常见破面问题。剩下 20% 需要手动在 MeshLab 里修那就超出自动化范畴了。5.3 不同 CAD 软件的 STEP 兼容性STEP 虽然是国际标准但不同软件的实现有差异。我实测下来CadQuery 导出的 STEP 在 FreeCAD、中望 CAD、SolidWorks 里都能正常打开但在某些老版本软件里可能出现曲面丢失。如果你遇到 STEP 导入后曲面变平面大概率是导出时的精度设置问题。CadQuery 的 STEP 导出默认精度是 1e-6一般够用。如果目标软件对精度敏感可以在导出时显式指定cq.exporters.export(model, part.step, exportTypeSTEP, tolerance1e-7, angularTolerance1e-6)另外提醒一句STEP 文件里不包含材质和颜色信息如果你需要带颜色的模型得用 GLB 或者专门的渲染格式。这是格式本身的限制不是导出代码的问题。5.4 性能优化批量生成时的瓶颈单次生成一个零件耗时主要在 LLM 调用上大概 2 到 5 秒。但如果你要批量生成几百个零件瓶颈就转移到几何生成和导出上了。我的优化经验是LLM 调用可以并发几何生成必须串行。因为 CadQuery 底层的 OpenCASCADE 不是线程安全的多线程同时建模会崩溃。所以架构上我用一个线程池处理 LLM 调用把解析结果放进队列另一个单线程消费者从队列取参数、建模、导出。这样 LLM 的等待时间被重叠掉了整体吞吐量提升三到四倍。导出环节STL 的三角化最耗时。如果批量生成的是同一类零件只是尺寸不同可以考虑缓存三角化模板只对变化的部分重新计算。不过这个优化比较复杂除非你的批量规模上千否则不值得做。6. 扩展方向从单零件到装配体与参数化模板库6.1 多零件装配的描述方式单零件跑通之后自然会想生成装配体。比如“一个盒子底盖厚 3mm侧壁厚 2mm内部空腔 80×60×40mm盖子可分离”。这涉及多个实体的定位和配合。我的做法是在 Schema 里引入assembly类型包含一个parts数组每个零件有自己的参数和变换矩阵。LLM 负责把描述拆成多个零件几何生成模块逐个建模最后用 CadQuery 的Assembly类组装。assy cq.Assembly() assy.add(base, namebase, loccq.Location((0, 0, 0))) assy.add(lid, namelid, loccq.Location((0, 0, 43))) assy.save(box.step)Location控制零件的位置(0, 0, 43)表示盖子放在底盖上方 43mm 处正好是底盖高度加壁厚。装配体的 STEP 导出后在 CAD 软件里会显示为多个独立实体可以单独选中和移动。6.2 参数化模板库的积累思路text-to-cad 的长期价值不在于单次生成而在于模板库的积累。每跑通一种零件类型就把它固化成模板后续同类需求直接调用不再依赖 LLM 抽取。我的模板库目前覆盖了法兰、支架、齿轮、盒子、轴套五类。每类模板有独立的参数 dataclass 和建模函数注册到一个字典里根据 LLM 输出的type字段分发。新增模板只需要写一个 dataclass 和一个 build 函数注册进去即可扩展成本很低。模板库还有一个好处可以脱离 LLM 独立使用。如果你已经知道参数直接构造 dataclass 调用 build 函数就行不需要联网调模型。这在批量生成标准件时特别有用速度快且完全可控。6.3 与现有 CAD 工作流的衔接生成的 STEP 文件怎么融入现有工作流我的经验是把它当作“初稿”而不是“终稿”。text-to-cad 负责快速生成 80% 的几何剩下的 20% 细节——比如特殊倒角、螺纹特征、工程标注——在 CAD 软件里手动补。具体操作上我会把生成的 STEP 导入中望 CAD 或 SolidWorks然后在此基础上添加工程图、标注尺寸、设置公差。这样比从零建模快得多而且参数化模板保证了基础几何的准确性。对于 3D 打印场景STL 直接进切片软件调整打印参数即可。我通常会把 text-to-cad 生成的 STL 和手动修复的版本对比如果尺寸偏差在 0.1mm 以内就直接用生成的版本省去手动建模的时间。6.4 精度与效率的平衡点最后聊一个实操中反复权衡的问题精度和效率怎么平衡。我的经验值是对于大多数工程零件STL 的线性偏差设 0.02mm 是个甜点。再小文件体积翻倍但视觉和打印质量提升不明显再大圆弧面会出现肉眼可见的多边形棱角。STEP 导出没有这个权衡因为它本身就是精确的文件大小主要取决于几何复杂度和精度设置关系不大。所以我的策略是STEP 永远导出最高精度STL 按用途调偏差GLB 用中等偏差加材质。这样既保证工程可用性又控制文件体积。我在实际使用中发现text-to-cad 最大的价值不是替代 CAD 软件而是把“想法到初稿”的时间从半小时压缩到十秒。这个压缩带来的迭代速度提升才是它真正改变工作方式的地方。你可以在一分钟内试十个不同的尺寸组合快速找到最优解然后再用传统工具精修。这种“快速试错 精细打磨”的组合比单纯依赖任何一方都高效。
返回列表