ARTICLE DETAIL

资讯详情

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

text-to-cad 实战:从自然语言到 STEP/STL/GLB 的生成链路

text-to-cad 实战:从自然语言到 STEP/STL/GLB 的生成链路 1. 从一段文字到三维模型text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词很多人脑子里浮现的画面大概是对着电脑敲一句“给我画一个法兰盘”然后屏幕上就自动出现一个带螺栓孔的三维实体。这个想象不算离谱但真正落地的时候它解决的问题比“自动画图”要具体得多也琐碎得多。text-to-cad 本质上是一条从自然语言描述到CAD 几何文件的转换链路。它的输入是一段人话比如“一个外径 80mm、内径 40mm、厚度 10mm 的圆环中心开四个直径 6mm 的孔均布在直径 60mm 的圆上”它的输出是一个可以被 CAD 软件打开、被切片软件识别、被渲染引擎加载的几何文件常见格式就是 STEP、GLB、STL 这几种。中间要经过语义解析、参数抽取、几何建模、网格化、格式导出这一整套流程。这件事为什么值得单独拿出来做因为传统 CAD 的工作流是“人操作软件”而 text-to-cad 想做到的是“人描述意图机器负责建模”。对于经常需要批量生成标准件、快速验证结构想法、或者把文字需求转成可视模型的人来说这条链路能省掉大量重复的鼠标点击。尤其是当你需要生成几十个尺寸不同、形状类似的零件时手动画一遍再改参数效率低得让人抓狂。它适合谁来参考三类人最有用。第一类是做机械设计或产品结构的工程师需要快速把需求转成可编辑的实体模型第二类是做 3D 打印和模型处理的玩家经常要在 STL、STEP、GLB 之间来回倒腾第三类是做 AI 应用开发的程序员想把大语言模型的输出接到实际的几何内核上。这三类人的关注点不一样但底层都绕不开同一件事怎么把一段文字变成一个有正确拓扑结构的模型文件。我在这条链路上踩过的坑比想象中多。最开始我以为只要让模型输出一段坐标点就够了结果发现点云根本没法直接变成实体后来改用网格又发现 STL 只有三角面片没有“圆”和“孔”的语义改一个尺寸就得重新生成整个网格。真正靠谱的做法是先用参数化方式建出实体再按需导出成不同格式。这个思路的转变是整件事能不能做成的分水岭。2. 三种输出格式的脾气STEP、GLB、STL 各自适合什么场景在动手写任何代码之前必须先搞清楚一件事你到底要输出什么格式。STEP、GLB、STL 这三个词经常被混着用但它们代表的是三种完全不同的数据模型用错了地方后面全是麻烦。2.1 STEP带语义的实体边界表示STEP 文件后缀通常是 .step 或 .stp用的是B-Rep边界表示模型。它记录的不是三角面片而是“这是一个圆柱面半径 20轴线在这里”“这是一个平面法向朝上”这样的几何语义。这意味着你打开 STEP 文件后可以选中那个圆柱面直接改它的半径孔的位置和数量都还在。这就是为什么工程领域默认用 STEP 做交付格式。它保留了设计意图能被 SolidWorks、中望 CAD、FreeCAD 这类软件正常识别和编辑。text-to-cad 如果目标是“生成可继续修改的零件”那 STEP 就是首选输出。但 STEP 也有它的脾气。它的生成依赖几何内核常见的是 OpenCASCADE简称 OCCT。这个内核功能强但 API 相对重编译和部署都不算轻量。而且 STEP 的解析在不同软件之间偶尔会有兼容性问题尤其是带复杂曲面的时候导入后出现破面并不罕见。2.2 STL只剩三角面片的“哑巴”网格STL 是 3D 打印领域最通用的格式但它本质上只是一堆三角面片的集合。一个圆柱在 STL 里不是“圆柱”而是被近似成几十个甚至上百个细长三角形。你没法在 STL 里选中一个“孔”因为孔这个概念已经不存在了只剩下围成一圈的三角形。这带来两个直接后果。第一STL 没有单位不同软件对它的尺寸解释可能不一样导入时经常要手动缩放。第二STL 的精度完全取决于网格密度密度低了圆柱看起来像多边形密度高了文件体积暴涨。我见过一个直径 100mm 的圆盘为了表面光滑STL 文件被撑到 80MB而同样形状的 STEP 只有 200KB。所以 STL 的定位很明确它是给下游消费用的不是给设计用的。切片软件、渲染引擎、部分仿真工具认它但你别指望在 STL 上做参数化修改。2.3 GLB为渲染和交互而生的场景格式GLB 是 glTF 的二进制版本主打的是“一个文件装下整个场景”。它不仅能装几何还能装材质、贴图、灯光、相机、动画。在网页端做 3D 展示、在游戏引擎里加载模型、在 AR 应用里显示物体GLB 几乎是默认选择。它和 STL 一样是网格格式但比 STL 丰富得多。STL 只描述表面形状GLB 还能告诉你这个面是什么颜色、粗糙度多少、是不是金属。如果你的 text-to-cad 结果要放到网页上给人看GLB 的体验会比 STL 好一大截。不过 GLB 的几何精度通常不如 STEP它更偏向“看起来对”而不是“尺寸精确”。做展示可以做加工不行。格式数据模型能否参数化编辑典型用途生成依赖STEPB-Rep 实体可以工程设计、加工交付OpenCASCADE 等内核STL三角网格不可以3D 打印、快速预览网格化算法GLB三角网格 材质不可以网页展示、AR、游戏glTF 导出库提示如果你的链路既要展示又要加工正确做法是“一次建模多路导出”——用参数化内核建出实体再分别导出 STEP 和 STL/GLB而不是试图在一种格式里满足所有需求。3. 把一句话拆成参数语义解析这一步最容易翻车text-to-cad 最不确定的环节不是几何建模而是从自然语言里把参数抠出来。几何内核再稳输入错了也是白搭。这一步做不好后面全是垃圾进垃圾出。3.1 为什么不能让模型直接输出坐标很多人第一反应是让大语言模型直接吐出一串顶点坐标或者一段建模脚本。我试过效果很不稳定。原因在于语言模型擅长的是“语义”不是“精确数值计算”。你让它算一个均布孔的坐标它可能给你算出角度对不上、半径有偏差的结果而且每次还不一样。更麻烦的是坐标是“死”的。一旦用户说“把外径从 80 改成 100”如果模型输出的是写死的坐标整个模型就得重新生成之前所有的拓扑关系都丢了。正确的做法是让模型输出结构化的参数比如{ type: ring, outer_diameter: 80, inner_diameter: 40, thickness: 10, holes: { count: 4, diameter: 6, pitch_circle_diameter: 60 } }有了这份参数几何生成就是确定性的改一个数字就能重新建出模型拓扑关系完全保留。这就是“参数化”相对“坐标化”的核心优势。3.2 参数抽取的常见坑第一个坑是单位缺失。用户说“直径 80”是毫米还是厘米如果默认按毫米处理遇到英制输入就会差 25 倍。我的做法是在 prompt 里强制要求模型输出单位或者在解析层做一次单位归一化把所有长度统一到毫米。第二个坑是隐含约束。用户说“四个孔均布”但没说均布在哪个圆上。这时候要么追问要么按常见工程惯例补一个默认值比如取外径的 0.75 倍作为分布圆直径并在结果里标注这是推断值。我倾向于后者因为追问会打断流程而标注能让用户知道哪里需要确认。第三个坑是歧义消解。“开个槽”这三个字可能是通槽、盲槽、环形槽、键槽形状差得远。这时候需要模型结合上下文判断或者提供一个候选列表让用户选。纯靠模型猜翻车概率很高。3.3 用结构化输出约束模型让模型稳定输出 JSON比让它输出自由文本靠谱得多。现在主流的大语言模型接口都支持JSON Schema 约束你可以定义一个参数模板强制模型按这个结构填值。这样解析层就不用写一堆正则去容错了。我一般会把参数模板设计成可扩展的基础字段包括类型、尺寸、位置、数量复杂特征用嵌套对象表示。这样新增一种零件类型时只需要扩展 schema不用改解析逻辑。注意即使有 schema 约束也要对模型输出的数值做合理性校验。比如外径不能小于内径孔的数量不能是负数厚度不能为零。这些校验放在几何生成之前能挡掉大部分低级错误。4. 几何内核选型OpenCASCADE 不是唯一答案参数拿到手之后下一步是把它变成真正的几何体。这一步的核心是几何内核它决定了你能建出什么形状、导出什么格式、跑在什么环境里。4.1 OpenCASCADE功能全但门槛高OpenCASCADEOCCT是开源领域最成熟的 B-Rep 内核STEP 的读写、布尔运算、倒角、抽壳这些操作它都支持。Python 里可以通过pythonocc-core调用C 里直接用原生 API。它的优点是“什么都能做”缺点是“什么都得自己写”。建一个带孔的圆盘你需要依次创建圆柱、创建小圆柱、做布尔差集、导出。代码量不小而且 OCCT 的 API 设计偏底层学习曲线陡。另外它的编译依赖比较多在 Windows 上部署经常要折腾环境。4.2 CadQuery把 OCCT 包成顺手的脚本层CadQuery 是我目前最推荐的方案。它建立在 OCCT 之上但提供了一套链式调用的 Python API写起来接近“描述几何”而不是“操作内核”。同样一个带孔圆盘CadQuery 里大概是这样import cadquery as cq result ( cq.Workplane(XY) .circle(40) .circle(20) .extrude(10) .faces(Z) .workplane() .polarArray(30, 0, 360, 4) .hole(6) ) cq.exporters.export(result, ring.step) cq.exporters.export(result, ring.stl)这段代码的可读性比原生 OCCT 高太多而且一行导出就能同时拿到 STEP 和 STL。对于 text-to-cad 这种“参数驱动建模”的场景CadQuery 的表达力和开发效率都很合适。4.3 纯网格方案什么时候该放弃 B-Rep如果你的目标只是生成 STL 或 GLB不需要参数化编辑那用纯网格方案比如 trimesh、numpy-stl会更轻量。它们不依赖重型内核安装快、运行快适合部署在资源受限的环境里。但代价是失去了“实体”的概念。布尔运算在网格上做起来容易出问题尤其是共面、自相交的情况经常产生破面。所以纯网格方案适合形状简单、不需要复杂布尔操作的场景比如生成一些基础几何体或者从已有网格做变形。方案依赖输出格式适合场景上手难度OpenCASCADE 原生OCCTSTEP/STL复杂实体、精确建模高CadQueryOCCTSTEP/STL参数化零件、快速开发中trimesh纯 PythonSTL/GLB简单网格、轻量部署低提示CadQuery 底层就是 OCCT所以它能导出的格式和原生方案一致但开发效率高很多。除非你有极致的性能要求否则没必要直接啃原生 API。5. 从参数到模型一次完整的生成链路拆解把前面几块拼起来一条完整的 text-to-cad 链路大概是这样走的。我用一个具体例子贯穿用户输入“生成一个外径 80、内径 40、厚 10 的圆环中心均布 4 个直径 6 的孔分布圆直径 60”。5.1 第一步语义解析与参数校验模型把这句话解析成结构化参数解析层再做一次校验。校验内容包括数值是否为正、内外径关系是否合理、孔是否落在实体范围内。这一步如果发现问题直接返回错误信息不进入建模环节。我一般会把校验规则写成独立的函数和解析逻辑分开。这样规则可以单独测试也方便后续扩展。比如“孔必须完全在材料内”这条规则需要计算孔边缘到内外径的距离逻辑不复杂但容易漏。5.2 第二步参数化建模用 CadQuery 按参数建出实体。这里的关键是把参数映射到 API 调用而不是写死任何数值。外径对应circle(outer_diameter/2)内径对应第二个circle孔的数量和分布圆直径对应polarArray的参数。建模顺序也有讲究。先做外轮廓拉伸再挖内孔最后打孔。如果顺序反了布尔运算的结果可能不对。尤其是内孔和外轮廓的差集必须在拉伸之后做否则会得到一个空心的薄壁而不是实体。5.3 第三步网格化与格式导出实体建好后导出 STEP 是直接的CadQuery 一行搞定。导出 STL 时需要指定线性容差和角度容差这两个参数决定了网格的精细程度。容差越小网格越密文件越大。我的经验值是线性容差取模型尺寸的 0.1% 到 0.5%角度容差取 0.1 到 0.5 弧度。对于直径 80 的圆盘线性容差 0.1mm 左右能保证圆柱面看起来足够圆文件体积也可控。如果只是预览容差可以放宽到 0.5mm文件能小一个数量级。cq.exporters.export( result, ring.stl, tolerance0.1, angularTolerance0.2 )5.4 第四步结果验证导出之后别急着返回给用户先做一次验证。最简单的验证是重新读取文件检查包围盒尺寸是否和预期一致。如果外径 80包围盒的 X 和 Y 方向应该是 80 左右Z 方向是 10。偏差超过阈值就说明哪里出了问题。更严格的验证可以检查体积。圆环的理论体积是 π×(40²−20²)×10 减去四个孔的体积算出来和实际体积对比能发现布尔运算是否漏做。这个检查我强烈建议加上因为布尔运算失败时经常不报错只是结果不对。6. 实测中那些文档不会告诉你的坑链路跑通只是开始真正上线之后遇到的问题才是决定这套东西能不能用的关键。下面这几个坑我都是踩过之后才明白的。6.1 浮点数精度导致的布尔失败OCCT 的布尔运算对浮点数精度很敏感。当两个面几乎共面时差集运算可能产生极薄的碎片甚至直接失败。我遇到过一次孔的边缘刚好和某个面的边界重合结果布尔运算卡住不返回。解决办法是给关键尺寸加一个微小的偏移比如孔的位置稍微偏 0.001mm避开共面情况。或者在做布尔之前先对实体做一次clean操作合并冗余的面和边。CadQuery 里可以用.clean()调用。6.2 STL 的单位陷阱STL 文件本身不记录单位不同软件导入时默认值不一样。有的按毫米有的按米有的按英寸。我见过一个模型在切片软件里显示成 80 米高就是因为单位被解释错了。规避方法是在导出 STL 时确保模型的实际尺寸就是你想要的数值不要依赖下游软件的单位设置。如果下游明确要求英寸就在导出前做一次单位换算而不是指望对方猜。6.3 大模型的“幻觉尺寸”语言模型有时候会“脑补”参数。用户说“一个标准的法兰盘”模型可能自作主张补上一堆尺寸而这些尺寸未必符合任何标准。这时候如果直接拿去建模出来的东西看着像那么回事实际根本不能用。我的做法是在 prompt 里明确要求只输出用户明确给出的参数缺失的参数标注为 null 或使用默认值并注明。这样至少能知道哪些是用户说的哪些是模型猜的。6.4 并发生成时的资源竞争如果服务要同时处理多个生成请求OCCT 的某些操作不是线程安全的。多个线程同时调用布尔运算可能产生不可预期的结果。稳妥的做法是给几何生成加一把锁或者用进程池隔离每个进程独立处理一个请求。这个坑在单机测试时完全发现不了一上并发就暴露。我当时的现象是偶尔生成的模型缺一个孔排查了很久才定位到线程安全问题。7. 把生成结果接到下游导出之后还能做什么模型生成出来只是半成品真正体现价值的是它怎么被用起来。不同的下游场景对格式和处理方式的要求完全不同。7.1 3D 打印STL 的切片前处理如果目标是 3D 打印STL 导出后通常还要过一遍切片软件。这时候要注意模型的流形性——网格必须是封闭的不能有破面或非流形边。CadQuery 导出的 STL 一般是流形的但如果模型有极薄的壁或者自相交就可能出问题。打印前建议用 trimesh 做一次检查确认is_watertight为真。如果不是可以用fill_holes尝试修复但修复效果有限最好还是从建模阶段避免。7.2 网页展示GLB 的材质与压缩要在网页上展示GLB 比 STL 合适得多。导出 GLB 时可以做两件事提升体验一是加基础材质让模型有颜色和光照反馈二是做网格简化减少面数加快加载速度。trimesh 支持直接导出 GLB也支持简单的材质设置。如果模型面数很高可以用simplify_quadric_decimation做减面把面数降到原来的 20% 到 30%视觉上几乎看不出差别加载速度却快很多。7.3 工程交付STEP 的版本兼容STEP 交付给下游工程师时最大的问题是软件兼容性。不同 CAD 软件对 STEP 的解析有差异尤其是带复杂曲面的模型。我遇到过在 FreeCAD 里正常的模型导入另一个软件后曲面出现裂缝。降低风险的办法是导出时选择较新的 STEP 协议版本比如 AP214 或 AP242并且尽量避免使用过于复杂的曲面特征。如果下游软件比较老可以先在目标软件里试导入一次确认没问题再批量生成。7.4 批量生成参数表的驱动方式text-to-cad 真正高效的地方在于批量。与其一次生成一个不如让用户提供一张参数表每行一组参数批量生成对应的模型文件。这时候文件名要有规律比如用参数哈希或者序号命名方便后续对应。批量生成时要注意错误隔离。某一组参数不合法不应该让整个批次失败。我的做法是逐行处理记录每行的成功或失败状态最后汇总一份报告。这样用户能清楚知道哪些生成了哪些需要修参数。8. 关于这条链路我个人的几点实操体会做了一段时间之后我最大的感受是text-to-cad 的难点不在“AI”而在“CAD”。语言模型负责把话听懂这部分现在的能力已经够用真正决定成败的是几何内核用得对不对、参数设计得合不合理、导出格式选得准不准。把后面这些工程细节做扎实整条链路才稳。另一个体会是不要追求一步到位。一开始就想支持任意形状、任意格式结果往往是每个环节都半吊子。我的建议是先锁定一两类零件比如回转体、板类件把这条链路打磨到稳定再逐步扩展。参数模板、校验规则、导出配置都可以复用扩展成本比想象中低。最后说一个容易被忽略的点给用户反馈。生成失败时不要只返回一个错误码要告诉用户是哪句话没解析对、哪个参数不合理、应该怎么改。这个反馈质量直接决定了用户愿不愿意继续用。我在这上面花的时间比优化建模代码还多但回报也最明显。
返回列表