news 2026/10/9 16:15:51

text-to-cad 实战:从自然语言到 STEP 模型的参数化生成链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
text-to-cad 实战:从自然语言到 STEP 模型的参数化生成链路

1. 从一段文字到三维模型:text-to-cad 到底在解决什么问题

第一次听到 "text-to-cad" 这个词,很多做机械设计或者工业建模的朋友第一反应是:又来个炒概念的。毕竟 CAD 这行当,从二维图纸到三维实体,每一步都靠鼠标点出来、靠尺寸约束卡出来,你跟我说打一行字就能出模型?但真上手跑过几个开源方案之后,我的看法变了——它不是要取代工程师,而是把"从想法到第一版可编辑几何体"这段最枯燥的路给铺平了。

所谓 text-to-cad,直译就是"文本转 CAD 模型"。你输入一句自然语言描述,比如"一个外径 80mm、内径 40mm、厚度 12mm 的法兰盘,带 6 个均布 M8 螺栓孔",系统输出一个标准格式的三维模型文件,常见的就是STEP、GLB、STL这几种。STEP 是工业界通用的边界表示格式,能保留精确的曲面和实体拓扑,丢进 SolidWorks、中望 CAD、Fusion 这些软件里还能继续改;GLB 是面向渲染和 Web 展示的轻量格式,带材质和层级;STL 则是三角网格,3D 打印切片软件最认它。这三种格式基本覆盖了"后续要编辑""要在线预览""要直接打印"三类需求。

这件事的价值在哪?我举个自己碰到的场景。做非标设备的时候,前期方案阶段经常要快速比选十几个结构件的外形,传统做法是每个都从头拉伸、打孔、倒角,一个下午就没了。而用 text-to-cad 的思路,把常用件写成参数化描述,改几个数字就能批量出 STEP,拿去装配验证干涉,效率完全不是一个量级。它适合的人其实很明确:做机械结构设计的工程师、搞创客和 3D 打印的玩家、需要批量生成标准件的开发者,以及想把设计流程自动化的团队。哪怕你只是刚学 CAD 制图入门,用它来快速生成一个参考模型,也比对着教程一步步画要直观得多。

但这里必须先泼一盆冷水:text-to-cad 目前的能力边界很清楚。它擅长的是规则明确、参数可枚举的零件——法兰、支架、齿轮毛坯、外壳、连接件。你让它生成一个"造型优雅的汽车曲面",它大概率给你一坨没法用的东西。所以正确的定位是:它负责把 0 变成 1,你负责把 1 打磨成 100。想清楚这一点,后面的所有技术选型和实操才不会跑偏。

2. 拆解 text-to-cad 的技术链路:一句话是怎么变成 STEP 文件的

2.1 自然语言到结构化参数的翻译层

整条链路的第一环,是把人话翻译成机器能算的参数。这一步现在主流有两种做法。一种是基于大语言模型的参数抽取:把用户的描述连同预定义的参数 schema 一起喂给模型,让它输出 JSON,比如{"type": "flange", "outer_diameter": 80, "inner_diameter": 40, "thickness": 12, "hole_count": 6, "hole_diameter": 8.5}。另一种是基于模板和正则的规则匹配,适合描述高度固定的批量场景。

我实测下来,纯靠大模型抽参数有两个坑。第一是单位歧义,"厚度 12"到底是毫米还是厘米,模型不会主动问,你得在 prompt 里强制约定"所有尺寸默认毫米"。第二是数值幻觉,你说了外径 80,它可能给你输出 80.0 也可能输出 8,尤其在描述复杂的时候。所以稳妥的做法是:抽完参数后加一层校验和回填,用 Pydantic 之类的工具做类型和范围检查,超出合理区间就报错让人确认,而不是闷头往下走。

from pydantic import BaseModel, Field, validator class FlangeParams(BaseModel): outer_diameter: float = Field(..., gt=0, le=2000) inner_diameter: float = Field(..., ge=0) thickness: float = Field(..., gt=0, le=500) hole_count: int = Field(0, ge=0, le=64) hole_diameter: float = Field(0, ge=0) @validator("inner_diameter") def inner_less_than_outer(cls, v, values): if "outer_diameter" in values and v >= values["outer_diameter"]: raise ValueError("内径必须小于外径") return v

这段校验看着简单,但它拦住的是后面几何内核直接崩溃的灾难。我踩过一次,内径比外径还大,几何内核直接抛异常,整个批处理任务挂掉,排查了半天才发现是输入描述里"内孔 90、外圆 80"这种明显笔误没人管。

2.2 参数化几何的构建:为什么选内核比选语言重要

拿到结构化参数后,就要真正造几何了。这一步的核心是几何内核,它决定了你能不能输出精确的 STEP。目前开源圈子里最常用的是OpenCASCADE(OCCT),Python 侧通过pythonocc-core或者cadquery调用。CadQuery 的定位很讨喜——它用链式 API 把建模过程写成代码,可读性接近自然语言,特别适合做 text-to-cad 的中间层。

import cadquery as cq result = ( cq.Workplane("XY") .circle(40) # 外径 80 -> 半径 40 .circle(20) # 内径 40 -> 半径 20 .extrude(12) # 厚度 12 .faces(">Z").workplane() .polarArray(30, 0, 360, 6) # 6 个孔均布 .hole(8.5) # M8 底孔直径约 8.5 ) result.val().exportStep("flange.step")

这段代码就是 text-to-cad 的"心脏"。为什么用 CadQuery 而不是直接怼 OCCT 的底层 API?因为底层 API 光是创建一个圆柱就要写十几行,维护成本极高,而 CadQuery 把常用操作封装成了接近工程语言的表达。但要注意,CadQuery 的polarArray参数顺序容易记混,第一个参数是半径、第二个是起始角、第三个是总角度、第四个是数量,写错了孔的位置就全乱。我建议每次生成后都导出 GLB 在浏览器里转一圈看看,肉眼确认比看代码快得多。

2.3 三种输出格式的取舍逻辑

生成完几何,导出格式的选择直接决定了下游能不能用。我把这三种格式的关键差异整理成表,方便对照:

格式本质能否保留精确曲面能否继续参数化编辑典型用途
STEP边界表示(B-Rep)能能(导入后仍是实体)工业设计、装配、CNC 加工
GLB三角网格 + 材质否(已离散化)否Web 预览、AR/VR、渲染
STL纯三角网格否否3D 打印、切片

这里有个很多人踩的坑:STL 转 STEP 是有损且困难的。热搜里经常有人问"sw 中 stl 转 stp",答案基本是——转出来的 STEP 是一堆碎面片,不是真正的实体,没法做布尔运算。所以 text-to-cad 的正确姿势是从参数直接生成 STEP,需要网格时再从 STEP 离散出 STL 或 GLB,而不是反过来。方向搞反了,后面全是坑。

3. 动手搭一个最小可用的 text-to-cad 流程

3.1 环境准备里最容易被忽略的两件事

搭环境这一步,网上教程大多只告诉你pip install cadquery,但真正卡人的是另外两件事。第一是OCCT 的版本匹配,CadQuery 对底层 OCCT 版本有要求,用 conda 装通常比 pip 稳,因为 conda 会把二进制依赖一起解决。第二是中文字体和路径问题,如果你的输出路径或者参数里带中文,某些几何内核的 IO 模块会直接报错,建议工作目录全用英文。

conda create -n t2cad python=3.10 conda activate t2cad conda install -c conda-forge cadquery pip install pydantic openai

装完之后先跑一个最小验证,确认内核能正常导出 STEP,别等写完一大套逻辑才发现底层是坏的。

import cadquery as cq box = cq.Workplane("XY").box(10, 10, 10) box.val().exportStep("smoke_test.step") print("内核正常")

3.2 把描述文本喂给模型并约束输出

参数抽取这一步,关键在于给模型一个明确的输出契约。我习惯在 system prompt 里把可用零件类型、每个类型的必填参数、单位约定全部列清楚,并要求它只输出 JSON,不要任何解释文字。这样解析起来最省事。

import json from openai import OpenAI client = OpenAI() SYSTEM_PROMPT = """ 你是一个 CAD 参数抽取器。用户会用自然语言描述一个零件。 你只能输出 JSON,不要输出任何其他文字。 所有尺寸单位统一为毫米。 支持的零件类型:flange(法兰)、plate(板件)、bracket(支架)。 flange 参数:outer_diameter, inner_diameter, thickness, hole_count, hole_diameter plate 参数:length, width, thickness, corner_radius bracket 参数:base_length, base_width, height, thickness """ def extract_params(user_text): resp = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_text}, ], temperature=0, ) raw = resp.choices[0].message.content.strip() return json.loads(raw)

temperature=0是为了让输出尽量稳定,同样的描述每次抽出来的参数一致,方便复现。这一点在批量生成时特别重要,否则你没法解释为什么昨天生成的模型和今天的不一样。

3.3 从参数到几何的分发逻辑

拿到 JSON 之后,需要一个分发器根据type字段调用对应的建模函数。这里的设计要点是每个零件类型一个独立函数,而不是写一个巨大的 if-else 堆在一起。独立函数便于单独测试,也方便后续扩展新类型。

def build_flange(p): return ( cq.Workplane("XY") .circle(p["outer_diameter"] / 2) .circle(p["inner_diameter"] / 2) .extrude(p["thickness"]) .faces(">Z").workplane() .polarArray(p["outer_diameter"] / 2 - 8, 0, 360, p["hole_count"]) .hole(p["hole_diameter"]) ) def build_plate(p): return ( cq.Workplane("XY") .rect(p["length"], p["width"]) .extrude(p["thickness"]) .edges("|Z").fillet(p.get("corner_radius", 0)) ) BUILDERS = {"flange": build_flange, "plate": build_plate}

注意polarArray里那个outer_diameter / 2 - 8,这是把螺栓孔布置在离外缘 8mm 的位置,属于工程经验值。如果直接放在外径上,孔会切穿边缘,模型直接废掉。这种"常识性约束"最好写死在建模函数里,而不是指望语言模型每次都记得。

3.4 导出与批量处理

单个零件跑通后,批量处理就是套一层循环。但批量场景下有两个细节必须处理:文件名唯一性和失败隔离。一个零件生成失败不能拖垮整批任务,所以每个零件用 try-except 包起来,失败的记录到日志里单独处理。

import os, traceback def batch_generate(descriptions, out_dir="output"): os.makedirs(out_dir, exist_ok=True) report = [] for i, desc in enumerate(descriptions): try: params = extract_params(desc) builder = BUILDERS[params["type"]] model = builder(params) path = os.path.join(out_dir, f"part_{i:03d}.step") model.val().exportStep(path) report.append({"index": i, "status": "ok", "path": path}) except Exception as e: report.append({"index": i, "status": "fail", "error": str(e)}) return report

跑完一批之后,我习惯把 report 里失败的挑出来,看看是描述本身有问题还是抽取出了问题。十有八九是描述太模糊,比如"做个差不多大的板子"——这种没有具体尺寸的输入,任何系统都救不了。

4. 实测中那些文档不会告诉你的坑

4.1 单位、精度与浮点误差的连锁反应

几何内核处理浮点数时,**容差(tolerance)**是个绕不开的东西。OCCT 默认的建模容差在 1e-7 量级,当你输入的尺寸特别小(比如微米级)或者特别大(比如米级)时,布尔运算可能因为容差判断失败而报错。我遇到过一次,生成一个 0.5mm 厚的薄片,倒角半径设了 0.5mm,结果内核认为倒角面退化成一条线,直接抛异常。解决办法是把倒角半径限制在厚度的 40% 以内,或者干脆对薄壁件跳过倒角。

另一个高频问题是孔位与边界的干涉。前面提到的螺栓孔位置,如果hole_count很大而外径很小,相邻两个孔会重叠,布尔减运算后得到一个奇怪的连体孔。稳妥的做法是在建模前做一次几何可行性检查:孔中心所在圆周的周长除以孔数,必须大于孔径加一个安全余量。

import math def check_hole_feasibility(outer_d, hole_count, hole_d, margin=2.0): if hole_count <= 1: return True pitch_circle = outer_d - 16 # 假设孔心圆比外径小 16 circumference = math.pi * pitch_circle spacing = circumference / hole_count return spacing >= hole_d + margin

这个检查不通过就直接拒绝生成并提示用户,比生成一个废模型再让人去 CAD 里修要友好得多。

4.2 语言模型对工程语义的误解

语言模型在工程语境下有个很隐蔽的问题:它会把工艺描述当成几何描述。比如你说"这个面要铣削加工",它可能理解成要在这个面上加一个凹槽。再比如"倒角 2×45°",它可能只抽出一个数字 2,把角度丢了。我的应对办法是在 prompt 里明确区分"几何参数"和"工艺要求",并告诉模型工艺要求一律忽略,只抽几何。

还有一个更麻烦的:相对尺寸。用户说"孔比外径小一圈",这个"一圈"是多少?模型会瞎猜。所以我在 prompt 里加了一条硬规则——遇到相对描述,必须要求用户给出绝对值,否则拒绝生成。宁可多问一句,也不要生成一个尺寸全错的模型。

4.3 导出 STEP 后在其他 CAD 里打不开

这个问题我被问过太多次。STEP 导出后在 SolidWorks 或者中望 CAD 里打不开,通常有三个原因。第一是版本兼容,STEP 有 AP203、AP214、AP242 等不同协议,老软件可能只认 AP203,导出时指定协议能解决大部分问题。第二是模型本身有自相交或非流形边,这种模型在 CadQuery 里能导出,但严格的内核会拒绝加载。第三是文件编码或路径问题,前面提过,中文路径是重灾区。

# 指定 STEP 协议版本,提升兼容性 from OCP.STEPControl import STEPControl_Writer, STEPControl_AsIs from OCP.Interface import Interface_Static Interface_Static.SetCVal("write.step.schema", "AP214")

实测下来,导出 AP214 的兼容性最好,绝大多数主流 CAD 都能正常读取。如果对方用的是很老的版本,再退到 AP203。

4.4 批量生成时的内存与性能

CadQuery 每生成一个模型都会在内存里保留几何对象,批量跑几百个之后内存会明显上涨。我的做法是每生成一个就显式释放,并且把导出和建模分开——建模完立刻导出,导出完立刻丢弃对象引用。

import gc for desc in descriptions: model = build_one(desc) model.val().exportStep(path) del model gc.collect()

另外,几何内核的布尔运算是单线程的,想提速只能靠多进程。但多进程下每个进程都要独立初始化内核,启动开销不小,所以批量规模小于 50 个的时候,多进程反而更慢。这个临界点因机器而异,建议自己压测一下再决定。

5. 这套东西能落到哪些真实场景里

5.1 标准件与非标件的快速出图

机械设计里大量时间花在重复性的标准件建模上。螺栓、垫片、法兰、轴承座,这些零件参数固定、形状规则,正是 text-to-cad 的甜区。把常用件做成描述模板,需要的时候改几个数字,几秒钟出 STEP,直接拖进装配体。我自己的做法是维护一个描述库,每个零件一行文本,配合一个批量脚本,需要哪批就生成哪批。

对于非标件,它的价值在于方案阶段的快速迭代。客户说"这个支架再高一点、底板再宽一点",传统做法是打开 CAD 改草图、重建、重新装配。用参数化描述,改两个数字重新生成,装配关系如果用的是坐标定位,甚至不用重做。这个效率差距在方案反复修改的项目里体现得淋漓尽致。

5.2 3D 打印前的模型准备

玩 3D 打印的朋友对 STL 肯定不陌生。text-to-cad 在这里的用法是:先用参数生成精确的 STEP,确认尺寸无误后,再离散成高精度的 STL 去切片。为什么不直接生成 STL?因为 STL 是网格,改一个尺寸就得重新离散,而 STEP 改参数重新导出即可。而且从 STEP 离散时你可以控制网格精度,打印出来的表面质量更可控。

这里有个经验:STL 的网格精度不是越高越好。精度太高文件巨大,切片软件卡顿;精度太低曲面变成多边形。一般曲面零件用 0.05mm 的弦高容差,平面零件用 0.1mm 就够了。这个参数在 CadQuery 导出 STL 时可以指定。

from cadquery import exporters exporters.export(model, "part.stl", tolerance=0.05, angularTolerance=0.1)

5.3 与现有 CAD 工作流的衔接

很多人担心 text-to-cad 生成的东西和现有工作流脱节。其实 STEP 作为中间格式,衔接得很自然。生成的 STEP 导入 SolidWorks 后是实体,可以继续加特征、做装配、出工程图。导入中望 CAD 也没问题,国产软件对 STEP 的支持这些年做得很到位。唯一要注意的是坐标系约定,CadQuery 默认 Z 轴向上,而有些 CAD 习惯 Y 轴向上,导入后可能需要旋转一下。这个在导出前统一处理掉,比导入后再转要省事。

如果你的团队用 Python 做设计自动化,那 text-to-cad 更是天然契合。把参数抽取、建模、导出、校验串成一条流水线,接上 PLM 或者 ERP 系统,就能实现"下单即出图"的自动化。当然,这条链路要上生产,还得加上权限、版本、审核这些工程化的东西,但技术底座就是前面讲的这些。

6. 几个我反复验证过的实操心得

先说参数校验这件事。我一开始觉得有语言模型兜底,输入随便点没关系,结果被现实教育了好几次。后来我把校验做成了三层:第一层是 JSON schema 校验,保证字段齐全、类型正确;第二层是几何可行性校验,比如内径小于外径、孔不重叠、倒角不超过壁厚;第三层是生成后抽检,随机挑几个模型导出 GLB 在浏览器里看一眼。三层下来,废品率从最初的三成降到了几乎为零。

再说 prompt 的写法。我试过很多版本,最后发现最有效的不是把 prompt 写得多长,而是给几个高质量的示例。在 system prompt 里放两三个"描述到 JSON"的完整例子,模型抽参数的准确率明显提升,尤其是对"均布""对称""带圆角"这类工程词汇的理解。示例比规则管用,这是我在实际调优里最实在的体会。

关于格式选择,我的默认策略是永远先生成 STEP。STEP 是母版,GLB 和 STL 都是派生物。需要预览就现从 STEP 转 GLB,需要打印就现转 STL。这样无论下游要什么,你手里始终有一个精确的、可编辑的源头。反过来,如果你只存了 STL,想改尺寸的时候就得从头再来,这个教训我吃过不止一次。

最后提一个容易被忽视的点:版本管理。text-to-cad 生成的文件往往批量产生,如果不做命名规范和版本记录,很快就会乱成一锅粥。我的做法是文件名里带上零件类型、关键尺寸和时间戳,比如flange_od80_id40_t12_20250101.step,一眼就能看出是什么。配合一个简单的 CSV 台账记录每次生成的参数和文件路径,回溯的时候非常方便。这些看着是小事,但真到了要复现某个历史版本的时候,你会感谢当初多写的这几个字段。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 16:11:59

拆解39页智慧园区方案:五层架构、平台边界与售前落地

简介&#xff1a;这是一份华为与中软联合推出的智慧园区解决方案技术主打胶片&#xff0c;共39页&#xff0c;面向园区管理者、解决方案架构师及售前工程师。内容从传统园区在安全、效率、体验和运营成本上的痛点切入&#xff0c;梳理了从“人防”到“技防”再到“智防”的演进…

作者头像 李华
网站建设 2026/10/9 16:09:08

Blinko AI Provider 模型拉取 Docker 内网连通性测试方案详解

后端前端人工智能大模型RAG知识库桌面应用 【免费下载链接】blinko An open-source, self-hosted personal AI note tool prioritizing privacy, built using TypeScript . 项目地址&#xff1a; https://gitcode.com/gh_mirrors/bl/blinko 点击查看 免费下载 本文以 Blinko 仓…

作者头像 李华
网站建设 2026/10/9 16:09:07

Java行为验证码源码实战:点击中文文字与滑动图片验证码实现

简介&#xff1a;本资源面向Java后端与全栈开发者&#xff0c;提供一套可直接用于生产环境的用户行为验证码方案&#xff0c;涵盖点击中文文字图片验证码与拖动/滑动图片验证码两种形态&#xff0c;适合需要提升登录、注册等场景防刷能力的中高级开发者参考落地。压缩包共178个…

作者头像 李华
网站建设 2026/10/9 16:08:25

正则表达式入门与实战:从匹配到替换的文本处理核心技巧

开头先聊点实在的。上周帮同事处理一份两万行的业务日志&#xff0c;要找出所有下单超过 3 秒的订单号&#xff0c;连带接口路径和耗时。他原本打算把日志导到 Excel 里手工筛&#xff0c;我一听就摇头&#xff0c;用正则表达式两分钟搞定的事&#xff0c;真不用折腾半小时。类…

作者头像 李华
网站建设 2026/10/9 16:08:15

MySQL 5.7.32在ARM64服务器部署全攻略:从glibc检查到踩坑排查

简介&#xff1a;这是为 ARM 64 位架构&#xff08;AArch64&#xff09;编译的 MySQL 5.7.32 二进制发行包&#xff0c;基于 glibc 2.28&#xff0c;面向树莓派、ARM 服务器等场景下的开发与运维人员&#xff0c;免编译、可离线安装部署&#xff0c;适合缺少现成软件源或需要离…

作者头像 李华
网站建设 2026/10/9 16:06:12

166张图跑通工地扬尘YOLO检测:小数据集实战指南

简介&#xff1a;本资源是面向计算机视觉初学者与工程实践者的建筑工地扬尘目标检测专用YOLO数据集&#xff0c;聚焦于施工场景中尘土颗粒物的识别任务&#xff0c;可直接用于YOLOv5至YOLOv13等主流系列模型的训练与验证&#xff0c;助力智能工地环境监测、AI巡检系统开发等实际…

作者头像 李华