免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

text-to-cad实战:从自然语言到STEP文件的参数化建模流水线

text-to-cad实战:从自然语言到STEP文件的参数化建模流水线 CAD 这行当有个特别拧巴的地方设计师脑子里的三维结构要变成机器能读的几何文件中间隔着一整套手工建模流程。画个法兰盘、支架、齿轮箱熟练工也得在草图、约束、拉伸、倒角之间来回折腾几十分钟。text-to-cad 这个方向想干的事就是把这段流程压缩成一句话——你描述形状程序吐出 STEP 文件。听起来像天方夜谭但拆开看它其实是自然语言解析 参数化建模 几何内核调用三件事的串联每一件都有成熟工具可用难点在于怎么把它们缝成一条顺滑的流水线。这篇内容适合三类人看一是做机械设计、想批量生成标准件的工程师二是搞机器人仿真、需要程序化产出 URDF 和网格的开发者三是单纯好奇AI 到底能不能画 CAD的技术爱好者。我会从整体架构讲到具体代码把参数怎么算、内核怎么调、坑在哪里踩全部摊开说清楚。读完你至少能自己搭一个能跑通的最小原型把一个 M 8 的六角螺栓这种描述变成真实可导入的模型文件。1. 先搞清楚 text-to-cad 到底在解决什么工程问题1.1 传统 CAD 建模的瓶颈不在画图在重复劳动很多人以为 CAD 慢是因为操作复杂其实真正吃掉时间的是重复性建模。一个项目里可能有几十个规格相近的螺栓、垫片、型材接头每个都要重新画草图、标尺寸、加约束。更麻烦的是改需求——客户说孔径从 10 改成 12你得回到特征树里找到对应草图改完还要检查关联尺寸有没有崩。这种场景下参数化建模已经解决了一半问题把尺寸抽成变量改一个数全模型跟着变。但参数化仍然要求你先手工建一次模板。text-to-cad 想再往前一步——连模板都不用手建直接用文字描述生成。它的价值不在于替代复杂曲面设计而在于批量、标准化、可编程这三件事。1.2 一句话描述到三维实体中间要过几道关从一个直径 50、厚 10 的圆盘中心有个直径 20 的通孔到最终 STEP 文件中间至少经过四个环节语义解析把自然语言拆成结构化的几何参数识别出圆盘对应圆柱体、通孔对应布尔减运算。特征映射把几何参数翻译成建模内核能理解的 API 调用序列比如先cylinder(radius25, height10)再cut(hole)。几何内核执行调用 OpenCASCADE 或类似内核真正做布尔运算、生成 B-rep 边界表示。格式导出把内核的内存对象序列化成 STEP、STL 或 URDF 需要的网格格式。这四步里第一步是 LLM 擅长的第二三步是传统 CAD 内核的强项第四步是格式转换的体力活。text-to-cad 的核心工作量其实在第二步的翻译层——怎么把不稳定的自然语言输出变成稳定可靠的建模指令。1.3 为什么现在做这件事比三年前靠谱三年前做 text-to-cad最大的拦路虎是自然语言理解太弱你说倒个角它可能理解成切掉一块。现在 LLM 对工程语义的把握已经够用尤其是配合 few-shot 示例和结构化输出约束之后参数抽取的准确率能到可用水平。另一个变化是几何内核的 Python 绑定成熟了。OpenCASCADE 的 Python 封装、CadQuery 这类声明式建模库让用代码画图的门槛大幅降低。你不需要懂 B-rep 的底层数据结构调几个高层 API 就能生成实体。这两件事凑到一起text-to-cad 才从论文概念变成能落地的工程方案。2. 拆解一条可落地的技术流水线2.1 整体架构LLM 只负责它该负责的部分我见过不少失败的尝试都是让 LLM 直接输出 CAD 脚本代码。这条路能走通但极不稳定——模型可能生成语法正确但几何荒谬的代码比如把半径写成负数或者布尔运算顺序搞反导致实体消失。更稳的做法是分层解耦LLM 只做参数抽取输出一个严格的 JSON 结构中间层做校验和补全最后由确定性的代码生成器调用几何内核。这样即使 LLM 偶尔抽错参数也能在中间层拦截不至于生成一个坏文件。# LLM 的输出契约只允许返回这种结构 { primitive: cylinder, params: {radius: 25.0, height: 10.0}, operations: [ {type: cut, feature: hole, params: {radius: 10.0, depth: through}} ], units: mm }这个 JSON 就是整个系统的接口协议。LLM 的 prompt 里要明确告诉它只输出 JSON不要解释不要 markdown 代码块。配合response_format之类的结构化输出约束稳定性会好很多。2.2 参数抽取环节的 prompt 设计要点prompt 写得好不好直接决定参数抽取的准确率。我的经验是三个原则第一给足示例。至少放 5 到 8 个覆盖常见形状的 few-shot 例子包括圆柱、长方体、带孔件、带圆角件。示例要展示描述文本 → JSON的完整映射让模型学会模式而不是死记。第二明确单位和默认值。工程描述里经常省略单位你要在 prompt 里规定未指定单位时默认毫米未指定厚度时默认 5mm。否则模型可能一会儿按米算一会儿按毫米算出来的模型尺寸差一千倍。第三约束数值范围。明确告诉模型半径、高度必须是正数孔径必须小于外径。这能挡掉一部分明显错误的输出。当然真正的校验还是要在代码层做prompt 约束只是第一道防线。2.3 几何内核选型CadQuery 还是直接调 OCC几何内核这块主流选择有两个方向。一是直接用 OpenCASCADE 的 Python 绑定pythonocc灵活但 API 偏底层写起来啰嗦。二是用 CadQuery 这类声明式封装代码简洁很多。我倾向 CadQuery原因是它的 API 设计天然贴合参数化建模的思路。比如生成一个带孔圆盘import cadquery as cq result ( cq.Workplane(XY) .circle(25.0) # 外圆半径 .extrude(10.0) # 拉伸厚度 .faces(Z) # 选顶面 .workplane() .hole(20.0) # 打直径 20 的通孔 ) cq.exporters.export(result, disk.step)这段代码几乎就是自然语言的直译。LLM 抽取出的 JSON 参数映射到这几个 API 调用上非常自然。相比之下 pythonocc 要手动构造BRepPrimAPI_MakeCylinder、BRepAlgoAPI_Cut这些对象代码量大且容易出错。提示CadQuery 底层就是 OpenCASCADE所以导出的 STEP 文件质量跟直接调 OCC 是一样的不用担心封装层损失精度。2.4 从 JSON 到建模代码的映射逻辑中间层的核心是一个指令翻译器把 JSON 里的 primitive 和 operations 翻译成 CadQuery 调用链。这里有个设计决策是每个形状写一个专门的生成函数还是做一个通用的解释器我的做法是混合。常见基础形状圆柱、长方体、球、圆锥写专门的生成函数保证代码清晰operationscut、fillet、chamfer做成通用处理器因为它们的作用方式跟基础形状无关。def build_from_spec(spec): wp cq.Workplane(XY) p spec[params] if spec[primitive] cylinder: wp wp.circle(p[radius]).extrude(p[height]) elif spec[primitive] box: wp wp.box(p[length], p[width], p[height]) # ... 其他基础形状 for op in spec.get(operations, []): if op[type] cut and op[feature] hole: wp wp.faces(Z).workplane().hole(op[params][radius] * 2) elif op[type] fillet: wp wp.edges().fillet(op[params][radius]) return wp这段代码是整个系统的骨架。实际项目中要处理更多边界情况比如孔不一定在中心、圆角要选特定边但核心思路就是这个。3. 把模型接进仿真和制造链路3.1 STEP 只是起点URDF 才是机器人场景的刚需如果你做的是机器人相关项目光有 STEP 文件不够用。仿真环境比如 CoppeliaSim需要的是 URDF 加网格文件。URDF 描述的是连杆的层级关系、关节类型、惯性参数网格文件提供可视化几何。从 STEP 到 URDF 的转换核心工作是几何简化。CAD 模型往往有大量倒角、螺纹这些细节直接拿去做碰撞检测会拖慢仿真。常见做法是用凸包或者简化网格替代精细模型import trimesh mesh trimesh.load(part.stl) hull mesh.convex_hull # 凸包简化 hull.export(part_collision.stl)然后在 URDF 里visual标签引用精细网格collision标签引用简化网格。这样既保证显示效果又不拖累物理计算。3.2 惯性参数不能瞎填要从几何算URDF 里有个特别容易被忽略的坑inertial标签的质量和转动惯量。很多人随手填个 1kg 就完事结果仿真里机械臂抖得像筛糠。正确的做法是从几何和材料密度算出来。对于规则形状转动惯量有解析公式。比如实心圆柱绕中心轴的转动惯量是I 0.5 * m * r²。对于复杂形状可以用 trimesh 直接算mesh trimesh.load(part.stl) mesh.density 7850 # 钢材 kg/m³ print(mesh.mass) print(mesh.moment_inertia)trimesh 会基于网格体积和密度算出质量再积分得到惯量张量。这个值填进 URDF仿真稳定性会好很多。3.3 G-code 生成从几何到加工路径的最后一公里如果你的目标是 3D 打印或者 CNC 加工那还需要从几何生成 G-code。这一步通常不自己写而是把 STEP 或 STL 丢给切片软件3D 打印或 CAM 软件CNC。但 text-to-cad 可以在这里做一件有价值的事自动生成加工参数。比如根据模型尺寸和材料推荐层高、填充率、进给速度。这些参数有经验公式可循LLM 也能给出合理建议配合模板生成切片软件的配置文件。# 根据模型包围盒推荐打印参数 bbox mesh.bounding_box.extents if max(bbox) 50: layer_height 0.1 # 小件用细层高 else: layer_height 0.2这种规则引擎加 LLM 建议的组合比纯手工调参快得多也比纯 LLM 拍脑袋靠谱。4. 实测中那些文档不会告诉你的坑4.1 布尔运算失败几何内核最脆弱的环节布尔运算尤其是带孔的 cut 操作是几何内核最容易翻车的地方。常见失败原因有三个面重合、切面正好穿过顶点、数值精度不够。举个例子你要在一个圆柱侧面打一个孔孔的轴线正好跟圆柱的某个切面重合内核可能算不出稳定的结果直接抛异常或者生成空实体。解决办法是微调位置——把孔的位置偏移 0.001mm避开退化情况。# 危险孔正好在中心线上 wp wp.faces(Z).workplane().center(0, 0).hole(10) # 安全微小偏移避开退化 wp wp.faces(Z).workplane().center(0.001, 0.001).hole(10)这个 0.001 的偏移在视觉上完全看不出来但能让布尔运算稳定通过。这是我在实际项目里踩了无数次坑才总结出来的经验。4.2 LLM 参数幻觉数值合理但语义错误LLM 有个讨厌的毛病它生成的参数往往看起来合理但实际错误。比如你说一个 M8 螺栓它可能给你半径 8mm 的圆柱——但 M8 指的是螺纹公称直径 8mm半径应该是 4mm。这种错误特别隐蔽因为 8 这个数字本身没错错的是语义理解。对策是建立领域词典。把常见标准件的规格映射表喂给 LLM让它查表而不是猜。M8 对应直径 8mmM10 对应 10mm这些是死知识不该让模型自由发挥。THREAD_SPECS { M3: 3.0, M4: 4.0, M5: 5.0, M6: 6.0, M8: 8.0, M10: 10.0, M12: 12.0, M16: 16.0, M20: 20.0, }在 prompt 里把这个表带上或者在后处理阶段做一次规格校验能挡掉大部分这类错误。4.3 单位混乱毫米和米的战争单位问题是 text-to-cad 最经典的坑。CAD 领域默认毫米但物理仿真和 3D 打印切片有时用米。如果 LLM 抽取参数时没明确单位或者中间层转换时忘了乘系数出来的模型可能大一千倍或者小一千倍。我的做法是全链路统一用毫米只在导出特定格式时做转换。STEP 文件本身不强制单位但大多数 CAD 软件默认按毫米解析。URDF 则强制用米所以导出 URDF 时要除以 1000。# 内部计算全用毫米 radius_mm 25.0 # 导出 URDF 时转成米 radius_m radius_mm / 1000.0这个转换点要写死在代码里不能靠人记。我见过项目因为漏了这一步仿真里机械臂直接飞到外太空。4.4 复杂形状的分解策略别指望一句话生成整个装配体text-to-cad 目前的能力边界很清晰单一零件、规则形状、参数明确的场景它能干得不错。但你要是说生成一个完整的减速器它大概率会给你一堆乱七八糟的东西。正确用法是分而治之。把复杂装配体拆成单个零件每个零件用一句话生成最后用装配约束拼起来。这符合工程实践——真实设计也是先做零件再做装配。parts { flange: 直径 100 厚 10 的法兰盘中心通孔直径 30均布 6 个直径 8 的螺栓孔, shaft: 直径 30 长 200 的轴一端有直径 25 长 40 的键槽段, } for name, desc in parts.items(): spec llm_extract(desc) build_from_spec(spec).export(f{name}.step)这种批量生成标准件的场景才是 text-to-cad 真正能提升效率的地方。一个项目里几十个法兰、支架、接头用脚本批量跑比手工建模快一个数量级。5. 从原型到可用工具还差什么5.1 参数校验层挡住 LLM 的胡言乱语原型跑通之后第一个要补的就是校验层。LLM 的输出不可信必须有一层确定性的代码做检查。校验内容包括数值范围半径、高度、厚度必须为正几何合理性孔径小于外径圆角半径小于最小边长的二分之一单位一致性所有长度参数单位统一特征可执行性cut 操作的目标面必须存在def validate(spec): p spec[params] assert p[radius] 0, 半径必须为正 assert p[height] 0, 高度必须为正 for op in spec.get(operations, []): if op[type] cut and op[feature] hole: assert op[params][radius] p[radius], 孔径不能大于外径 return True校验失败时不要直接报错退出而是把错误信息回传给 LLM让它重新生成。这种生成-校验-重试的循环能显著提升最终成功率。5.2 缓存与复用别每次都重新生成text-to-cad 的调用成本不低LLM 推理要钱几何运算要时间。如果同一个描述反复生成纯属浪费。加一层缓存把描述文本的哈希 → 生成的 STEP 文件路径存起来下次遇到相同描述直接返回。更进一步的优化是参数化模板。如果发现某类描述反复出现比如各种规格的法兰盘就把它固化成一个模板函数LLM 只负责抽参数几何生成走确定性代码。这样既快又稳。5.3 批量处理与错误恢复实际项目里往往是几百个零件一起生成。这时候要考虑并发和错误恢复。我的做法是用任务队列每个零件一个任务失败的进重试队列重试三次还失败就标记为人工处理。from concurrent.futures import ThreadPoolExecutor def process_batch(descriptions): results {} with ThreadPoolExecutor(max_workers4) as executor: futures {executor.submit(generate_part, d): d for d in descriptions} for future in futures: desc futures[future] try: results[desc] future.result(timeout60) except Exception as e: results[desc] {error: str(e)} return results超时设置很重要几何内核偶尔会卡死没有超时保护整个批次都会挂住。5.4 可视化预览让用户确认再导出生成完直接导出文件有风险用户可能拿到一个完全不对的模型。加一个可视化预览环节用 trimesh 或者 matplotlib 渲染个缩略图让用户确认形状对了再导出正式文件。import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection def preview(mesh): fig plt.figure() ax fig.add_subplot(111, projection3d) ax.add_collection3d(Poly3DCollection(mesh.triangles)) plt.savefig(preview.png)这个预览不需要多精细能看出大概形状就行。关键是给用户一个反悔的机会避免批量生成一堆废文件。6. 几个真实场景的完整走查6.1 场景一批量生成法兰盘系列假设你要生成 DN50 到 DN200 的六种法兰盘每种规格的尺寸有标准可查。传统做法是手工建六个模型text-to-cad 的做法是写一个描述模板批量跑。flange_specs [ {dn: 50, od: 165, thickness: 18, bolt_count: 4, bolt_d: 18}, {dn: 80, od: 200, thickness: 20, bolt_count: 8, bolt_d: 18}, # ... ] for spec in flange_specs: desc f外径 {spec[od]} 厚 {spec[thickness]} 的法兰盘中心通孔直径 {spec[dn]}均布 {spec[bolt_count]} 个直径 {spec[bolt_d]} 的螺栓孔 result generate_part(desc) result.export(fflange_DN{spec[dn]}.step)这个场景里LLM 其实可以省掉——参数都是结构化的直接调 CadQuery 就行。但如果你要处理的是客户发来的一段文字描述LLM 抽取就有价值了。6.2 场景二机器人连杆的快速迭代做机器人设计时连杆尺寸经常要改。用 text-to-cad 可以快速生成不同尺寸的连杆导入仿真验证运动学比手工改模型快得多。关键是把连杆的描述参数化长度、宽度、厚度、两端孔的位置和直径。改一个参数重新生成导入 CoppeliaSim 看效果。这个迭代循环从原来的半小时缩短到几分钟。6.3 场景三从图纸文字说明生成模型有些老项目只有图纸的文字说明没有电子模型。把文字说明喂给 text-to-cad能快速重建出可用的三维模型。当然精度取决于文字描述的详细程度但至少能有个可编辑的起点比从零建模快。这个场景要注意的是尺寸链校验。文字描述里的尺寸可能互相矛盾比如总长 100 但分段加起来 110。校验层要能发现这类问题并报错。7. 我对这套方案边界的判断text-to-cad 不是万能药它的能力边界很清楚。规则形状、参数明确、单一零件的场景它能把效率提升五到十倍。但复杂曲面、自由造型、精密配合的场景它目前还替代不了人工设计。我的建议是把它当成标准件生成器和设计起点工具来用。批量标准件用它快速原型用它但最终的设计决策和精密调整还是要人来把关。把它定位成帮你省掉重复劳动而不是替你设计心态就对了。另外提醒一句生成的 STEP 文件在导入正式 CAD 软件之前最好做一次几何检查——确认实体是封闭的、没有自相交、法向一致。这些检查 CadQuery 和 trimesh 都能做几行代码的事但能避免后面加工或仿真时出大问题。最后分享一个我在实际项目里养成的习惯每次生成完一批模型随机抽几个用 CAD 软件打开手工量一下关键尺寸。LLM 和几何内核都可能出意料之外的错误人工抽检是最后一道防线。这个习惯帮我挡过好几次看起来对但实际错的模型省下了不少返工时间。
返回列表