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

资讯详情

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

text-to-cad 实战:从自然语言到三维模型的工程化落地

text-to-cad 实战:从自然语言到三维模型的工程化落地 1. 从一句话到三维模型text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词我脑子里蹦出来的画面是对着电脑敲一行字比如“一个 80x60x10mm 的法兰盘中心开 30mm 通孔四角各一个 M6 沉头孔”然后软件直接吐出一个能用的三维模型文件。这个画面在几年前还属于科幻范畴但现在已经有一批工具和方案在往这个方向走了。text-to-cad 本质上是一类把自然语言描述转换成 CAD 几何模型的技术方案集合它的输出通常落在 STEP、GLB、STL 这几种格式上分别对应不同的下游用途。先说清楚这三种格式各自是干嘛的因为很多人一上来就搞混。STEP是工业界通用的边界表示格式保留完整的实体几何信息能精确表达圆柱面、倒角、螺纹孔这些特征适合后续在 SolidWorks、中望 CAD、FreeCAD 这类软件里继续编辑或者出工程图。STL是三角网格格式只有表面的一层三角面片没有实体拓扑关系适合 3D 打印切片但你要拿它去改尺寸就很痛苦。GLB是 glTF 的二进制版本主打轻量和渲染友好适合在网页、移动端做可视化展示比如产品配置器或者在线预览。所以 text-to-cad 的输出格式选择直接决定了这个模型后面能干什么。那这个方向到底解决了谁的痛点我观察下来主要是三类人。第一类是不会 CAD 但需要简单模型的开发者比如做机器人仿真的工程师他需要一个机械臂的连杆模型来测试运动学算法但让他去学 SolidWorks 画图时间成本太高。第二类是产品经理或者设计师在早期概念阶段想快速把脑子里的想法变成可视化的三维形状用来跟团队沟通。第三类是做批量建模的团队比如参数化零件库他们希望用文本描述来驱动模型生成而不是一个个手动画。这三类人的共同点是他们要的是“够用的几何”而不是“完美的工程图”。但这里必须泼一盆冷水。text-to-cad 目前的能力边界非常清晰它能处理的是结构化的、参数明确的、几何相对简单的零件比如方块、圆柱、法兰、支架、简单的装配体。你让它生成一个复杂的自由曲面汽车外形或者一个包含几百个零件的完整装配它做不到至少现在做不到。所以我在实际使用中的策略是把 text-to-cad 当成一个“快速原型生成器”而不是“替代 CAD 工程师的万能工具”。这个定位想清楚了后面的技术选型和实操才不会跑偏。2. 技术路线拆解从文本到几何到底走了哪几步2.1 自然语言解析与参数抽取text-to-cad 的第一步永远是把人话变成机器能理解的参数。这一步看起来简单实际上是最容易翻车的地方。比如你说“一个长 100 宽 50 高 20 的板子四个角倒 R5 圆角中间挖一个直径 30 的孔”人类工程师一看就懂但机器需要把它拆成基体是长方体尺寸参数是 length100, width50, height20特征操作是 fillet(radius5) 作用于四条竖直边cut_hole(diameter30) 作用于上表面中心。目前主流的做法有两种。一种是基于规则模板的解析就是预先定义好一套语法模式比如“长 X 宽 Y 高 Z”对应长方体“直径 D 的孔”对应圆柱切除。这种方式的优点是稳定可控缺点是只能处理预设好的句式稍微换个说法就识别不了。另一种是基于大语言模型的语义解析把自然语言丢给模型让它输出结构化的 JSON 或者 DSL领域特定语言。这种方式灵活度高但存在幻觉问题模型可能编造出不存在的参数或者搞错单位。我在实际项目里用的是混合方案先用大语言模型做初步的意图识别和参数抽取然后用一套校验规则去检查参数的合理性。比如模型输出“直径 30mm 的孔”我会检查这个直径是否小于基体的最小边长如果大于就报错让用户确认。这个校验层非常关键因为大语言模型在数值推理上并不可靠你不加约束它真的会给你生成一个直径 500mm 的孔打在一个 100mm 的板子上。注意单位问题是 text-to-cad 里最隐蔽的坑。用户说“长 100”到底是毫米还是厘米我的做法是在系统提示里强制要求模型输出时带上单位并且在最终生成前做一次单位归一化统一转成毫米。这一步不做后面生成的模型尺寸可能差十倍。2.2 几何内核与建模操作参数抽出来之后就需要一个几何内核来执行建模操作。这里的选择直接决定了你能生成什么级别的模型。目前常见的几何内核有 OpenCASCADE、CGAL、Manifold 这几类。OpenCASCADE 是工业级的内核支持 B-rep 实体建模能精确表达曲面和实体布尔运算FreeCAD 底层用的就是它。CGAL 更偏计算几何适合做网格处理和布尔运算但在 CAD 特征建模上不如 OpenCASCADE 完整。Manifold 是近几年兴起的轻量级内核主打快速布尔运算适合做实时预览。如果你要输出 STEP 格式基本只能选 OpenCASCADE 这条路因为 STEP 本身就是 B-rep 格式需要内核支持精确几何。如果你只要 STL 或者 GLB那可以用更轻量的方案比如用 trimesh 或者 vedo 这类 Python 库直接做网格级别的布尔运算。我个人的经验是如果下游需要编辑或者出工程图老老实实上 OpenCASCADE如果只是 3D 打印或者可视化用网格方案更省事。建模操作这一层核心就是几个基本特征的组合拉伸extrude、旋转revolve、扫掠sweep、放样loft再加上布尔运算union、cut、intersect和修饰特征fillet、chamfer。text-to-cad 系统需要把解析出来的参数映射到这些操作上。比如“法兰盘”这个语义对应的操作序列可能是画一个圆拉伸成圆柱再画一个同心圆拉伸切除成通孔再在四周阵列小圆孔。这个映射关系需要预先定义好或者让大语言模型来生成操作序列。2.3 格式导出与下游对接模型建好之后导出这一步看似简单其实也有不少讲究。STEP 导出要注意精度设置公差太大会导致曲面破面公差太小文件体积爆炸。STL 导出要控制三角面片的密度太粗的话 3D 打印出来表面全是棱角太细的话文件几百兆。GLB 导出要注意材质和法线不然在网页里显示出来黑乎乎一片。我一般会提供多个精度档位让用户选预览级STL 面片数控制在 5 万以内、打印级面片数 20 万左右、工程级STEP 精度 0.001mm。这样用户可以根据自己的用途来选不用每次都生成最高精度的版本。另外导出路径和文件命名也要规范我习惯用{项目名}_{版本号}_{格式}.{扩展名}的格式比如flange_v1.step避免文件多了之后找不到。3. 实操落地搭一个能跑的 text-to-cad 流水线3.1 环境准备与依赖安装假设你现在要自己搭一个 text-to-cad 的原型我推荐的技术栈是 Python OpenCASCADE通过 cadquery 或者 build123d 来调用 一个大语言模型 API。cadquery 是我用得最顺手的它的 API 设计很接近人类的建模思维写起来像在描述几何而不是在调底层接口。安装这块cadquery 的依赖比较多建议用 conda 来管理环境pip 装有时候会缺系统库。核心依赖包括cadquery几何建模、trimesh网格处理和 STL 导出、pygltflibGLB 导出、openai或者你用的其他模型 SDK语义解析。如果你不想用 conda也可以用 pip 装 cadquery但需要确保系统里有 OpenCASCADE 的动态库Linux 下一般是libocct-*系列包Windows 下 cadquery 会自带预编译的库。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install trimesh pygltflib openai装完之后跑一个简单的测试确认几何内核能正常工作import cadquery as cq result cq.Workplane(XY).box(80, 60, 10).faces(Z).workplane().hole(30) cq.exporters.export(result, test.step) print(STEP 导出成功)如果这一步能跑通说明基础环境没问题。接下来就是接大语言模型做语义解析。3.2 语义解析模块的实现语义解析模块的核心任务是输入一句自然语言输出一个结构化的建模指令 JSON。我设计的 JSON schema 大概长这样{ base_shape: box, dimensions: {length: 80, width: 60, height: 10, unit: mm}, features: [ {type: hole, diameter: 30, position: center, through: true}, {type: fillet, radius: 5, edges: vertical} ] }然后写一个 prompt 模板把用户的输入和这个 schema 一起丢给模型。prompt 里要明确告诉模型只输出 JSON不要输出解释文字所有尺寸必须带单位如果某个参数不明确用 null 表示而不是瞎猜。我试过好几个版本的 prompt最后发现给模型几个 few-shot 示例效果最好比单纯描述规则管用得多。拿到 JSON 之后写一个解析器把它翻译成 cadquery 的操作链。比如base_shape: box对应cq.Workplane(XY).box(l, w, h)hole特征对应.faces(Z).workplane().hole(d)。这个映射层需要处理各种边界情况比如孔的位置不在中心怎么办倒角作用在哪些边上多个特征之间的顺序会不会互相干扰。实操心得特征顺序很重要。如果你先倒角再打孔孔可能会切掉倒角的一部分导致结果跟预期不符。我的做法是在 JSON 里给每个特征加一个order字段让模型或者用户来指定执行顺序默认按“先加料后减料”的原则排。3.3 几何生成与格式导出几何生成这一步cadquery 的 API 已经封装得很好了大部分操作就是几行代码的事。但有几个地方需要特别注意。第一是工作平面的选择cadquery 默认在 XY 平面上操作如果你要在侧面打孔需要先切换工作平面这个逻辑在自动生成时容易搞错。第二是布尔运算的容差有时候两个面看起来贴合了但布尔运算会报错这时候需要手动设置一个小的容差值。导出 STEP 用cq.exporters.export(result, output.step)就行导出 STL 需要先转成网格import trimesh # 从 cadquery 结果导出 STL cq.exporters.export(result, output.stl, tolerance0.01, angularTolerance0.1) # 或者用 trimesh 做后处理 mesh trimesh.load(output.stl) mesh.export(output.glb)GLB 导出我一般用 trimesh 来做因为它对材质和法线的处理比较完善。如果你需要带颜色的模型可以在 cadquery 里给不同的面赋材质然后导出时保留这些信息。不过 cadquery 的材质系统比较简单复杂材质还是得在 Blender 或者 Three.js 里后期加。3.4 一个完整的端到端示例我把整个流程串起来用一个具体的例子走一遍。假设用户输入“生成一个 100x50x20mm 的安装板四角各有一个直径 6mm 的通孔孔中心距边缘 10mm板子四个角倒 R3 圆角。”第一步语义解析输出{ base_shape: box, dimensions: {length: 100, width: 50, height: 20, unit: mm}, features: [ {type: hole, diameter: 6, pattern: rectangular, count: 4, offset: 10, through: true}, {type: fillet, radius: 3, edges: vertical} ] }第二步生成 cadquery 代码import cadquery as cq L, W, H 100, 50, 20 hole_d 6 offset 10 fillet_r 3 result ( cq.Workplane(XY) .box(L, W, H) .edges(|Z).fillet(fillet_r) .faces(Z).workplane() .rect(L - 2*offset, W - 2*offset, forConstructionTrue) .vertices() .hole(hole_d) ) cq.exporters.export(result, mounting_plate.step) cq.exporters.export(result, mounting_plate.stl, tolerance0.01)第三步检查生成的模型。我一般会用 cadquery 的result.val().Volume()算一下体积跟手算的理论值对比。这个板子的理论体积是 100x50x20 减去四个孔的圆柱体积再减去倒角去掉的部分大概在 98000 立方毫米左右。如果算出来差太多说明某个特征没生效或者参数错了。4. 踩坑记录与常见问题排查4.1 语义解析阶段的典型问题问题一模型把“直径”理解成“半径”。这个坑我踩过好几次。用户说“打一个直径 10 的孔”模型输出radius: 10结果孔大了一倍。解决办法是在 prompt 里明确区分 diameter 和 radius并且在 JSON schema 里强制要求字段名写清楚是diameter还是radius不允许用模糊的size。问题二单位丢失。用户说“长 100”模型输出length: 100但没带单位。如果系统默认是毫米那还好但如果用户心里想的是厘米那就差了十倍。我的做法是在 prompt 里强制要求所有尺寸字段必须带unit并且在解析后做一次单位检查如果发现没有单位的数值直接报错让用户补充。问题三相对位置描述无法解析。用户说“在板子的左上角打一个孔”这个“左上角”是相对于哪个坐标系距离边缘多少这种模糊的空间描述目前的大语言模型处理得不好。我的策略是对于位置描述要求用户给出明确的偏移量比如“距左边 10mm距上边 15mm”如果用户不给就默认放在中心或者按均匀分布处理并在返回结果里注明“位置为默认推断请确认”。4.2 几何生成阶段的典型问题问题四布尔运算失败。这是最常见的几何错误通常是因为两个实体没有正确相交或者面重合导致内核无法判断内外。排查方法是先把两个实体分别导出 STL 看一眼确认它们的空间关系是对的。如果确实是面重合的问题可以给其中一个实体做一个微小的偏移比如 0.001mm通常就能解决。问题五倒角导致后续操作失败。倒角会改变边的拓扑结构如果后面的操作依赖于原来的边就会找不到目标。解决办法是调整特征顺序把倒角放在最后做。如果必须中间倒角那就需要在代码里重新选择边而不是依赖之前的引用。问题六STL 导出后文件太大。默认的导出精度可能过高导致一个简单的零件 STL 文件几十兆。调整tolerance和angularTolerance参数可以控制面片密度。我一般用tolerance0.05, angularTolerance0.3做预览tolerance0.01, angularTolerance0.1做打印。4.3 常见问题速查表问题现象可能原因排查方法解决方案模型尺寸差十倍单位解析错误检查 JSON 里的 unit 字段强制单位归一化孔的位置偏了坐标系理解错误导出 STL 目视检查明确工作平面和偏移基准布尔运算报错面重合或不相交分别导出两个实体检查加微小偏移或调整位置倒角后模型破面倒角半径过大检查倒角半径是否超过边长一半减小半径或调整顺序STL 文件过大导出精度过高查看文件体积调整 tolerance 参数GLB 显示全黑法线方向错误在查看器里检查法线重新计算法线或翻转面避坑技巧每次生成模型后不要直接导出就完事。先在一个轻量级的查看器里过一眼确认形状、尺寸、特征位置都对再导出最终格式。我习惯用trimesh的show()快速预览或者导出 GLB 丢到网页查看器里看。这一步花不了几秒钟但能省掉很多返工。5. 输出格式选择与下游工具链对接5.1 STEP、STL、GLB 的适用场景对比这三种格式不是随便选的选错了下游工具可能根本打不开或者丢信息。我做了一个对比表方便你根据用途来选格式几何类型可编辑性文件体积典型下游工具适用场景STEPB-rep 实体高保留特征中等SolidWorks、中望 CAD、FreeCAD工程编辑、出图、CNC 加工STL三角网格低只能整体缩放大切片软件、3D 打印3D 打印、快速成型GLB三角网格材质低适合展示小网页查看器、Three.js、Unity可视化、产品展示、AR如果你要做的是机械设计STEP 是唯一正确的选择。如果你只是要 3D 打印一个外壳STL 就够了。如果你要在网页上给客户看模型GLB 最合适。我见过有人拿 STL 去 SolidWorks 里编辑结果发现所有曲面都变成了小平面根本没法标注尺寸这就是格式选错了。5.2 与常见 CAD 软件的对接STEP 文件的兼容性整体很好但不同软件对 STEP 版本的支持有差异。SolidWorks 对 AP214 和 AP242 支持都不错中望 CAD 也没问题FreeCAD 用的是 OpenCASCADE 内核理论上最兼容。如果你导出的 STEP 在某个软件里打不开先检查一下 STEP 的版本用 AP214 通常最保险。STL 导入 3D 打印切片软件比如 Cura、PrusaSlicer基本不会出问题但要注意单位。有些切片软件默认单位是毫米有些是英寸导入后先看一眼尺寸对不对。另外 STL 没有实体信息如果模型有内部空腔或者薄壁结构切片软件可能会误判内外这时候需要在切片软件里手动修复。GLB 在网页端的兼容性最好Three.js、Babylon.js、model-viewer 这些库都原生支持。如果你要在 GLB 里保留材质和颜色导出时确保勾选了材质选项。cadquery 本身对材质的支持有限复杂材质建议在 Blender 里做好再导出 GLB。5.3 批量生成与自动化流水线如果你需要批量生成模型比如根据一个 CSV 参数表生成几百个不同尺寸的法兰盘那就需要把整个流程自动化。我的做法是写一个 Python 脚本读 CSV 的每一行把参数填进模板调用 text-to-cad 流水线生成模型然后按规则命名导出。这个脚本可以挂到 CI/CD 上每次参数表更新就自动重新生成所有模型。import csv import cadquery as cq def generate_flange(od, id_, thickness, bolt_count, bolt_d): result ( cq.Workplane(XY) .circle(od/2).extrude(thickness) .faces(Z).workplane().hole(id_) .faces(Z).workplane() .polarArray(od/2 - 10, 0, 360, bolt_count) .hole(bolt_d) ) return result with open(flange_params.csv) as f: reader csv.DictReader(f) for row in reader: model generate_flange( float(row[od]), float(row[id]), float(row[thickness]), int(row[bolt_count]), float(row[bolt_d]) ) cq.exporters.export(model, fflange_{row[id]}.step)这个模式的好处是你只需要维护一张参数表模型生成完全自动化改一个参数重新跑一遍就行。对于做系列化产品的团队来说效率提升非常明显。6. 能力边界与后续扩展方向6.1 目前做不到的事情text-to-cad 不是万能的有些事你现在别指望它能做好。第一是复杂自由曲面比如汽车外形、涡轮叶片、人体工学手柄这些需要 NURBS 曲面建模或者细分曲面建模自然语言很难描述清楚模型也生成不出来。第二是大型装配体几百个零件的装配关系、配合约束、运动干涉检查这些目前还是得靠人工在 CAD 软件里做。第三是工程标注尺寸公差、形位公差、表面粗糙度这些制造信息text-to-cad 基本不涉及生成出来的模型只是几何形状离能加工的图纸还有距离。所以我的建议是把 text-to-cad 用在概念设计阶段和简单零件批量生成这两个场景不要试图用它替代完整的 CAD 工作流。它的价值在于快速把想法变成可视化的三维形状降低建模的门槛而不是取代专业工程师。6.2 可以继续扩展的方向如果你已经搭好了一个基础版本后面可以往几个方向扩展。第一是增加特征库把常见的机械特征键槽、螺纹孔、中心孔、退刀槽做成模板用户说“加一个 M6 螺纹孔”就能直接生成。第二是接入参数化约束求解器让模型支持尺寸驱动改一个参数自动更新所有相关特征。第三是增加装配功能支持多个零件之间的定位和配合比如“把轴承装到轴上同心配合”。第四是对接 3D 打印切片软件生成模型后直接调切片参数输出可打印的 G-code。我个人最看好的方向是特征库参数化这个组合。因为大部分机械零件都是标准特征的组合你把特征库做丰富了覆盖 80% 的常见零件类型再配合参数化驱动就能实现“说一句话生成一个可用零件”的效果。这个目标比追求通用 AI 建模要现实得多也更有工程价值。最后分享一个小技巧如果你在调 prompt 的时候发现模型总是理解错某些术语不要反复改 prompt直接把那个术语加到 few-shot 示例里。比如模型总是把“沉头孔”理解成“通孔”你就在示例里加一个沉头孔的例子一次就能纠正过来。这比在 prompt 里写一大段解释管用得多。
返回列表