1. 从一句话到三维实体:text-to-cad 到底在解决什么问题
第一次听到 "text-to-cad" 这个词,很多人脑子里浮现的画面大概是:对着电脑说一句"给我画个法兰盘",屏幕上就自动长出一个带螺栓孔的三维模型。这个想象不算离谱,但真正落地的时候,它解决的问题比"省几下鼠标"要深刻得多。
传统 CAD 工作流的本质是手工几何建模。你要画一个零件,得先想清楚基准面在哪、草图怎么约束、拉伸多少、倒角多大,然后一个特征一个特征地堆。这个过程对熟练工程师来说是肌肉记忆,但对三类人极其不友好:一是刚入门的机械/建筑专业学生,二是需要快速验证结构的算法工程师,三是做仿真、机器人、3D 打印但不想学 CAD 软件的开发者。text-to-cad 的核心价值,就是把这批人从"学软件"里解放出来,让他们用自然语言或结构化文本直接描述几何意图,由程序生成可用的 CAD 文件。
这里必须先厘清一个常见误解:text-to-cad 不是"AI 画图"。它更准确的定位是文本驱动的参数化建模管线。文本是输入,CAD 文件(STEP、STL、URDF、G-code 等)是输出,中间靠的是参数化内核(如 OpenCASCADE、CadQuery、build123d)或者大模型生成建模脚本。理解这一点非常关键,因为它决定了你该怎么用它、能期待什么精度、以及什么时候它会翻车。
从热搜词能看出真实需求的分布:STEP、URDF、G-code这三个词暴露了 text-to-cad 最主要的三个下游场景——通用三维交换(STEP)、机器人仿真(URDF 导入 CoppeliaSim)、增材/减材制造(G-code)。而cad下载、cad安装、cad如何彻底卸载、cad激活页面脚本发生错误这一大串词,则说明大量用户其实卡在"软件本身都还没跑起来"的阶段。这两类需求看似无关,实际上指向同一个痛点:CAD 工具链太重、太封闭、太依赖手工操作。text-to-cad 想做的,就是把这条链子拆开,让几何生成变成一个可以脚本化、可以批量、可以版本管理的工程问题。
所以这篇文章不打算停留在概念层面。我会把 text-to-cad 拆成几条可复现的路径:怎么用代码生成 STEP、怎么把生成的模型喂给机器人仿真、怎么从文本参数一路走到 G-code,以及在这个过程中我踩过的那些坑——尤其是坐标系、单位、拓扑修复这些"文档里不写、但一定会遇到"的问题。适合的读者是:有基本编程能力、想绕开重型 CAD 软件、需要批量或自动化生成几何的工程师和学生。
2. 文本到几何的三条技术路线:选错了会白干一周
在动手之前,必须先选路线。text-to-cad 目前主流有三条实现路径,它们的适用场景、精度上限和维护成本差异极大。我见过太多人一上来就用大模型直接生成建模代码,结果卡在语法错误和几何自交上,一周都没跑出一个能用的 STEP 文件。先把路线选对,比写代码重要得多。
2.1 路线一:参数化脚本内核(CadQuery / build123d)
这是目前最稳、最可控的路线。核心思路是:你用 Python 写建模逻辑,文本参数(尺寸、孔位、数量)作为变量传入,内核(OpenCASCADE)负责生成精确的 B-rep 几何,最后导出 STEP/STL。
import cadquery as cq # 文本参数:法兰盘外径、内径、厚度、螺栓孔数、孔分布圆直径 od, id_, th = 80.0, 30.0, 10.0 n_holes, pcd = 6, 60.0 result = ( cq.Workplane("XY") .circle(od / 2) .circle(id_ / 2) .extrude(th) .faces(">Z") .workplane() .polarArray(pcd / 2, 0, 360, n_holes) .hole(6.0) ) cq.exporters.export(result, "flange.step")这段代码的价值在于:几何是精确的、可参数化的、可版本管理的。你把od从 80 改成 100,整个模型自动更新,螺栓孔依然均匀分布。这就是 text-to-cad 最朴素的形态——文本(参数)驱动 CAD。
选这条路的理由很直接:STEP 是 B-rep 格式,要求拓扑严格闭合,只有参数化内核能稳定产出。大模型生成的代码如果语法有误,你还能调试;但如果几何本身有自交或非流形边,导出的 STEP 在别的软件里打开就是一堆破面。
2.2 路线二:大模型生成建模脚本
这条路线是"真·text-to-cad":用户输入"一个长 50、宽 30、高 20 的盒子,顶部中心挖一个直径 10 深 5 的圆槽",大模型输出对应的 CadQuery 或 OpenSCAD 代码,再执行生成模型。
它的优势是自然语言门槛极低,劣势是可靠性不稳定。我实测下来,对于简单几何(盒子、圆柱、孔、阵列),大模型生成的 CadQuery 代码一次通过率大概在六成左右;一旦涉及倒角、抽壳、复杂曲面,通过率断崖式下跌。原因不复杂:大模型对 OpenCASCADE 的拓扑规则理解是"统计性"的,它知道常见写法,但不理解为什么某个操作会导致StdFail_NotDone。
所以我的建议是:把大模型当成"代码草稿生成器",而不是"几何生成器"。让它写第一版,你负责调试和加约束。真正决定成败的是后面的验证环节,而不是生成环节。
2.3 路线三:网格生成 + 转换(适合快速原型)
如果你只是要一个能看、能打印、能进仿真的模型,不要求精确 B-rep,那可以直接生成网格(mesh),再转成 STL 或 OBJ。典型工具是 trimesh、numpy-stl,或者用 OpenSCAD 的 CSG 布尔运算。
import trimesh box = trimesh.creation.box(extents=[50, 30, 20]) cyl = trimesh.creation.cylinder(radius=5, height=10) cyl.apply_translation([0, 0, 10]) result = box.difference(cyl) result.export("part.stl")这条路快、依赖轻,但代价是丢失精确几何信息。STL 只有三角面片,没有"这是一个直径 10 的孔"这种语义。如果你后续要做参数化修改、要出工程图、要导入需要 B-rep 的仿真软件,这条路会把你堵死。
三条路线的对比如下:
| 路线 | 输出格式 | 精度 | 上手难度 | 适合场景 |
|---|---|---|---|---|
| 参数化内核 | STEP/STL | 精确 B-rep | 中 | 工程零件、需交换 |
| 大模型生成脚本 | 取决于内核 | 不稳定 | 低 | 快速草稿、简单件 |
| 网格生成 | STL/OBJ | 网格近似 | 低 | 3D 打印、可视化 |
提示:如果你的下游是 URDF 机器人仿真或 STEP 交换,直接选路线一,别在网格上浪费时间。网格转 B-rep 的逆向重建是个无底洞。
3. 从文本参数到 STEP:一条能跑通的完整管线
选定参数化内核路线后,接下来要解决的是"文本怎么变成参数、参数怎么变成几何、几何怎么变成文件"这条完整链路。这一节我把每个环节的关键决策点讲透,尤其是那些看起来不起眼、但会直接决定成败的细节。
3.1 文本解析:别急着上大模型,先想清楚输入结构
很多人一上来就想用自然语言直接驱动建模,结果发现"长 50 宽 30"这种描述歧义太多——是外尺寸还是内尺寸?单位是毫米还是英寸?圆角是内圆角还是外圆角?
我的经验是:在自然语言和建模内核之间,加一层结构化参数层。也就是说,text-to-cad 的"text"最好先是结构化的(JSON/YAML),再由大模型负责把自然语言翻译成这个结构。这样调试和验证都容易得多。
{ "type": "flange", "outer_diameter": 80.0, "inner_diameter": 30.0, "thickness": 10.0, "bolt_holes": {"count": 6, "diameter": 6.0, "pcd": 60.0}, "unit": "mm" }这个 JSON 就是你的"中间表示"。大模型的职责是生成它,你的建模脚本的职责是消费它。好处是:参数错了你能一眼看出来,而不是等到几何生成失败才发现。
3.2 单位与坐标系:90% 的"模型导入后尺寸不对"都出在这里
这是我最想强调的一点。CAD 内核默认单位通常是毫米,但 STEP 文件本身可以携带单位信息,而不同软件读取时的处理方式不一样。我遇到过太多次:CadQuery 生成的模型在 FreeCAD 里打开是 80mm,导入某个仿真软件后变成 80m 或者 0.08mm。
根本原因是单位在文件头里,但很多解析器会忽略或错误解释。解决办法有两个:一是生成时显式声明单位,二是导入后立刻用已知尺寸校验。
# CadQuery 导出时确保单位一致 cq.exporters.export(result, "flange.step", cq.exporters.ExportTypes.STEP) # 导入后校验:量一下外径,确认是 80 而不是 0.08 或 80000坐标系同理。CadQuery 默认 Z 轴向上,但很多机器人仿真环境(如 CoppeliaSim)和游戏引擎默认 Y 轴或 Z 轴向上,导入后模型可能躺倒。这不是 bug,是约定差异。在生成阶段就把坐标系对齐目标环境,比导入后再旋转省事得多。
3.3 拓扑修复:为什么你的 STEP 在别的软件里是破面
参数化内核生成的几何理论上应该是干净的,但一旦涉及布尔运算(尤其是相切、共面、微小间隙),就可能产生非流形边或自交面。这些在生成软件里看不出来,导出 STEP 后在别的软件里就暴露成破面。
我踩过的典型坑:两个圆柱做布尔差集,如果它们的轴线完全共线且半径接近,OpenCASCADE 有时会生成极薄的残留面。解决办法是避免退化布尔运算——要么让几何有明确的重叠量,要么用clean()清理拓扑。
result = result.clean() # 清理冗余边和面 # 导出前检查有效性 assert result.val().isValid()isValid()这个检查非常关键。它会在导出前告诉你几何是否合法,避免你拿着一个坏文件去下游折腾半天。
3.4 批量生成:text-to-cad 真正的杀手锏
单件建模用不用 text-to-cad 差别不大,但批量生成是它碾压手工建模的地方。比如你要生成 50 个不同规格的法兰盘,手工建模要重复 50 次,脚本只需要一个循环。
specs = [ {"od": 80, "id": 30, "n": 6}, {"od": 100, "id": 40, "n": 8}, # ... 更多规格 ] for i, s in enumerate(specs): part = build_flange(s["od"], s["id"], s["n"]) cq.exporters.export(part, f"flange_{i}.step")配合参数表(CSV/Excel),你可以让非程序员也能通过改表格来生成模型。这才是 text-to-cad 在工程上的真实价值——把几何生成变成数据驱动的流水线。
4. 当 CAD 遇上机器人:URDF 导入 CoppeliaSim 的那些坑
热搜词里urdf导入coppeliasim出现得很频繁,这说明大量 text-to-cad 的用户下游是机器人仿真。这条链路比单纯的 STEP 交换复杂得多,因为 URDF 不只是几何,还包含运动学树、关节、惯性、碰撞体。几何生成对了,URDF 照样可能跑不起来。
4.1 URDF 不是 CAD 格式,别指望直接转换
URDF(Unified Robot Description Format)本质是 XML,它描述的是机器人的连杆和关节关系,几何只是其中一部分(visual 和 collision)。所以你不能把一个 STEP 直接"另存为"URDF,必须显式定义运动学结构。
典型做法是:用 text-to-cad 生成每个连杆的几何(STL 或 STEP),然后在 URDF 里引用这些几何文件,并定义关节。
<link name="base_link"> <visual> <geometry> <mesh filename="base.stl" scale="0.001 0.001 0.001"/> </geometry> </visual> <collision> <geometry> <mesh filename="base.stl"/> </geometry> </collision> <inertial> <mass value="1.0"/> <inertia ixx="0.01" iyy="0.01" izz="0.01" ixy="0" ixz="0" iyz="0"/> </inertial> </link>注意那个scale="0.001 0.001 0.001"。这是 URDF 最经典的坑:URDF 默认单位是米,而 CAD 默认是毫米。如果你生成的 STL 是毫米单位,导入 CoppeliaSim 后模型会大 1000 倍,看起来像一座山。加 scale 缩放是最直接的修法,但更好的做法是生成时就按米输出。
4.2 碰撞体与视觉体:别用同一个高精度网格
新手常犯的错误是 visual 和 collision 用同一个精细网格。结果仿真时碰撞检测慢得离谱,因为精细网格有几十万个三角面。
正确做法是:visual 用精细网格,collision 用简化包围体(盒子、圆柱、凸包)。text-to-cad 的优势在这里体现得很明显——你可以在生成阶段就同时输出两套几何。
# 精细视觉体 visual = build_detailed_part() visual.export("link_visual.stl") # 简化碰撞体(用凸包或包围盒) collision = visual.convex_hull() collision.export("link_collision.stl")4.3 惯性参数:手填会出事,用代码算
URDF 里的惯性张量如果手填,几乎一定会错。质量分布不对,仿真里的机器人会像喝醉一样乱晃。正确做法是用几何和质量密度自动计算。
# 假设均匀密度,用 trimesh 计算惯性 mesh = trimesh.load("link_visual.stl") mesh.density = 1000 # kg/m^3 inertia = mesh.moment_inertia mass = mesh.mass把算出来的值填进 URDF,仿真稳定性会好很多。这一步很多人偷懒跳过,然后在 CoppeliaSim 里看着机器人抽搐,还以为是控制器的问题。
4.4 导入 CoppeliaSim 后的坐标系对齐
CoppeliaSim 的坐标系约定和 URDF 不完全一致,导入后经常需要调整。我的经验是:先在 URDF 里把 base_link 放在原点、Z 轴向上,导入后再用 CoppeliaSim 的模型属性微调。如果一开始就乱,后面越调越乱。
另外,CoppeliaSim 导入 URDF 时对 mesh 路径很敏感。相对路径、绝对路径、大小写,任何一个不对都会导致模型加载失败但又不报明确错误。建议统一用相对路径,并把所有 mesh 放在和 URDF 同级的meshes/目录下。
5. 一路走到制造:从参数到 G-code 的最后一公里
text-to-cad 的终点往往不是屏幕上的模型,而是真实的零件。这就涉及 G-code 生成。热搜词里G-code的出现说明不少用户的目标是 3D 打印或 CNC 加工。这条链路的关键在于:几何精度、切片参数、机床约束三者必须匹配。
5.1 从 STEP 到 STL:网格密度决定打印质量
3D 打印切片软件(如 Cura、PrusaSlicer)吃的是 STL,不是 STEP。所以你需要把 B-rep 转成网格。这里的核心参数是网格细分精度(linear deflection 和 angular deflection)。
# CadQuery 导出 STL 时控制网格精度 cq.exporters.export( result, "part.stl", tolerance=0.01, # 线性偏差,越小越精细 angularTolerance=0.1 # 角度偏差 )tolerance=0.01意味着网格和真实曲面的最大偏差是 0.01mm。对于大多数 FDM 打印够用了。但如果你的零件有很小的圆角,这个值可能让圆角变成多边形。经验值是:最小特征尺寸的 1/10 作为 tolerance 上限。
5.2 切片参数与几何的配合
G-code 的质量不只取决于模型,还取决于切片参数。text-to-cad 生成的几何如果壁厚小于喷嘴直径,切片软件会直接忽略或产生空洞。所以在建模阶段就要考虑制造约束。
| 制造工艺 | 最小壁厚 | 最小孔径 | 关键约束 |
|---|---|---|---|
| FDM 打印 | 0.8mm | 1mm | 悬垂角 < 45° |
| SLA 打印 | 0.4mm | 0.5mm | 需支撑 |
| CNC 铣削 | 1mm | 刀具直径 | 内圆角 ≥ 刀半径 |
这些约束应该写进你的参数校验逻辑里。比如生成前检查壁厚,不满足就报错,而不是等切片失败。
5.3 批量生成 G-code 的自动化
如果你要打印一批不同规格的零件,可以串起整条链路:参数表 → 生成 STEP → 转 STL → 调切片软件命令行 → 输出 G-code。
# PrusaSlicer 命令行切片示例 prusa-slicer --export-gcode --load config.ini part.stl -o part.gcode这样你就能实现"改一个参数表,自动产出所有 G-code"的流水线。这才是 text-to-cad 在制造场景的完整形态。
6. 那些文档不写但一定会踩的坑
前面讲的都是"正确路径",但真实项目里,时间往往花在排错上。这一节我把几个高频坑单独拎出来,都是我自己踩过、并且看到别人反复踩的。
6.1 环境依赖:OpenCASCADE 装不上怎么办
CadQuery 依赖 OpenCASCADE,而 OpenCASCADE 在不同系统上的安装体验差异很大。Windows 上最常见的问题是 C++ 运行库缺失(热搜词里的c++2005cpi错误就是这类)。我的建议是:直接用 conda 装 cadquery,别自己编译 OpenCASCADE。
conda install -c conda-forge cadqueryconda 会把所有二进制依赖一起装好,省掉 90% 的环境问题。如果你非要用 pip,做好折腾半天的准备。
6.2 几何有效性:导出前必须校验
前面提过isValid(),这里再强调一次。我见过太多人拿着一个拓扑有问题的 STEP 去下游,然后在完全无关的地方报错,排查方向全错。导出前校验,是省时间的最有效手段。
solid = result.val() if not solid.isValid(): # 尝试修复 solid.fix() if not solid.isValid(): raise ValueError("几何无效,检查布尔运算")6.3 文件命名与版本管理
批量生成时,文件命名混乱是灾难。建议用"类型_参数_版本"的命名规则,比如flange_od80_id30_v2.step。配合 git 管理你的建模脚本和参数表,几何文件本身可以不进版本库(太大),但生成脚本必须进。
6.4 大模型生成代码的验证策略
如果你用大模型生成建模代码,一定要建立验证流程:先生成 → 语法检查 → 执行 → 几何校验 → 尺寸校验。任何一步失败就回退重试。不要相信大模型一次生成的代码能直接用。
def validate_and_build(code_str, expected_dims): try: exec(code_str, globals()) except Exception as e: return None, f"执行失败: {e}" if not result.val().isValid(): return None, "几何无效" bbox = result.val().BoundingBox() # 校验包围盒尺寸是否符合预期 if abs(bbox.xlen - expected_dims[0]) > 0.1: return None, "尺寸不符" return result, "OK"这套验证逻辑看起来啰嗦,但它能把大模型的不稳定性挡在下游之前。
7. 我实际用下来的一些体会
text-to-cad 这个概念听起来很新,但它的内核其实很朴素:用代码和参数替代手工建模。真正让它有价值的不是"AI"这个标签,而是它把几何生成变成了可编程、可批量、可版本管理的工程活动。
我用它做过几件事:批量生成法兰盘系列、给机器人仿真生成连杆、给 3D 打印生成参数化支架。最深的体会是——前期在参数结构和校验逻辑上多花的时间,后期会十倍地省回来。那些想一步到位、直接自然语言生成复杂零件的尝试,基本都卡在了几何有效性和尺寸校验上。
如果你刚开始接触,我的建议是从最简单的参数化脚本入手,先把"参数 → STEP → 校验"这条最小链路跑通,再逐步加复杂度。别一上来就追求全自动自然语言建模,那是个容易让人挫败的起点。等你把基础管线摸熟了,再引入大模型做自然语言前端,这时候它的不稳定性你也有能力兜住了。
最后分享一个实用习惯:每次生成模型后,除了isValid(),我都会顺手导出一张截图或者量一下关键尺寸。这个动作花不了几秒,但能挡住绝大多数"看起来对、其实错了"的问题。几何这东西,眼睛看和实际量,经常是两回事。