
1. 从一句话到三维实体text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词很多人脑子里浮现的画面大概是对着电脑说一句给我画个法兰盘屏幕上就自动长出一个带螺栓孔的零件。这个想象不算离谱但真正落地的时候它解决的问题比省几下鼠标点击要深得多。传统 CAD 工作流的本质是人肉翻译需求方用自然语言描述一个零件直径 80、厚 10、中心一个 30 的通孔、四周均布 6 个 M6 沉头孔工程师在脑子里把这个描述转成几何约束再用手在草图里一条线一条线画出来最后拉伸、打孔、倒角。这个链条里最贵的一环不是画图本身而是从语言到几何的那次翻译——它依赖经验、容易出错、而且几乎无法批量复用。text-to-cad 想干的事情就是把这次翻译交给程序。输入是一段结构化的文本描述输出是标准的 CAD 几何文件常见的目标格式包括STEP通用三维交换格式、URDF机器人描述格式带运动学信息、以及G-code数控加工指令。这三个格式分别对应三类下游场景STEP 给通用 CAD/CAM 软件做后续编辑和出图URDF 给机器人仿真环境做装配和运动验证G-code 直接驱动机床把零件切出来。所以这个项目的核心价值不是炫技而是把重复性的参数化建模变成可编程、可批量、可版本管理的流程。适合谁来参考三类人最受益一是经常要做系列化零件的机械工程师同一结构改尺寸改几十遍那种二是做机器人仿真、需要批量生成连杆和关节模型的开发者三是想把设计流程接进自动化管线、用脚本驱动 CAD 的技术人员。哪怕你只是 CAD 初学者理解这套思路也能帮你建立参数化思维比死记命令有用得多。下面我会把这条链路拆开讲文本怎么变成几何、STEP/URDF/G-code 各自怎么生成、批量处理怎么做、以及我在实操里踩过的那些坑。2. 文本描述怎么变成几何解析层的设计取舍2.1 为什么不能直接让大模型吐 STEP 文件一个很自然的想法是既然有大语言模型直接让它输出 STEP 文件内容不就行了我试过结论是基本不可行。原因在于 STEP 文件ISO 10303-21是一种极其啰嗦的边界表示格式一个简单的圆柱体在 STEP 里可能是几百行CARTESIAN_POINT、DIRECTION、CYLINDRICAL_SURFACE的实体引用而且实体之间的引用关系必须严格自洽ID 不能错、拓扑不能断。语言模型生成这种长程强约束的结构化文本出错率极高而且一旦某个引用 ID 错了整个文件在 CAD 软件里直接打不开你连错在哪都难找。正确的做法是分层让语言模型只负责它擅长的部分——把自然语言解析成结构化的参数字典JSON几何生成交给确定性的建模内核。这样每一层都可测试、可调试、可替换。2.2 参数抽取从直径80厚10到结构化 JSON解析层的核心任务是把一段人话拆成机器能用的字段。我实际用的方案是规则 模型兜底的混合策略纯规则太脆纯模型不稳定。先看一个典型的输入法兰盘外径 80mm厚度 10mm中心通孔直径 30mm 均布 6 个 M6 沉头孔分布圆直径 60mm材料 6061 铝目标是抽出这样的结构{ type: flange, outer_diameter: 80.0, thickness: 10.0, center_hole_diameter: 30.0, bolt_holes: { count: 6, thread: M6, pattern_circle_diameter: 60.0, type: countersunk }, material: AL6061, unit: mm }规则层负责抓数字 单位 关键词的组合比如正则匹配外径\s*(\d(?:\.\d)?)\s*(mm|cm|m)?。模型层负责处理规则抓不到的表述比如稍微倒个角孔别太靠边这种模糊描述让它映射到默认值或追问。这里有个关键设计单位必须显式归一化。我见过太多因为 mm 和 inch 混用导致零件尺寸差 25.4 倍的惨案所以解析完第一件事就是统一到毫米并在 JSON 里保留unit字段做审计。提示解析层一定要输出置信度或缺失字段列表。当关键尺寸缺失时宁可报错让用户补全也不要瞎猜一个默认值——猜错的几何比没有几何更危险因为它可能被直接送进加工。2.3 几何内核选型为什么我最终选了 CadQuery 而不是直接调商业 API几何生成这一层可选路线有几条调用商业 CAD 的 API如某些软件的二次开发接口、用开源内核OpenCASCADE、或者用基于 OpenCASCADE 封装的高层库CadQuery、build123d。我最终选的是CadQuery理由有三条。第一它是 Python 原生和解析层、批量脚本能无缝衔接不用跨语言传参。第二它基于 OpenCASCADE几何内核成熟导出的 STEP 是真正的 B-rep 实体不是网格能被任何主流 CAD 软件正常打开和编辑。第三它的代码即模型code-CAD范式天然适合参数化——一个法兰盘就是一个函数改参数就出新零件。对比一下几条路线的取舍方案优点缺点适用场景商业 CAD 二次开发功能全、出图规范授权成本高、脚本环境封闭、难批量企业内部已有授权OpenCASCADE 裸用完全可控、免费学习曲线陡、样板代码多需要深度定制内核CadQuery / build123d上手快、Python 生态、导出标准复杂曲面能力有限参数化零件、批量生成网格类库如 trimesh轻量、渲染快不是实体、无法直接加工可视化、3D 打印预览对于 text-to-cad 这种以参数化规则零件为主的场景CadQuery 是性价比最高的选择。它的核心建模逻辑就三句话建草图Workplane、加约束、做布尔运算拉伸、切除、倒角。2.4 一个法兰盘的完整生成代码把上面的 JSON 喂给建模函数核心代码大概长这样import cadquery as cq import math def build_flange(p): od p[outer_diameter] th p[thickness] ch p[center_hole_diameter] bh p[bolt_holes] pcd bh[pattern_circle_diameter] n bh[count] hole_d 6.6 # M6 通孔按 6.6 留间隙 # 基础圆盘 part ( cq.Workplane(XY) .circle(od / 2) .extrude(th) ) # 中心通孔 part part.faces(Z).workplane().hole(ch) # 均布螺栓孔 pts [ (pcd / 2 * math.cos(2 * math.pi * i / n), pcd / 2 * math.sin(2 * math.pi * i / n)) for i in range(n) ] part ( part.faces(Z).workplane() .pushPoints(pts) .hole(hole_d) ) # 上下边缘倒角 part part.edges(|Z).chamfer(0.5) return part result build_flange(parsed_json) cq.exporters.export(result, flange.step)这段代码里有两个容易被忽略的细节。一是螺栓孔直径不是螺纹公称直径。M6 的螺纹外径是 6mm但通孔要留间隙通常取 6.6mm中等装配如果要做精密定位可能取 6.4mm做松装配取 7mm。这个值直接决定装配能不能装上是新手最容易翻车的地方。二是倒角放在最后做因为布尔运算之后再倒角边才是最终边如果先倒角再打孔孔口的新边就没有倒角看起来会很突兀。3. STEP、URDF、G-code三种输出格式各自的坑3.1 STEP 导出为什么你的文件在别人电脑上打不开STEP 是 text-to-cad 最常用的输出但导出这一步的坑比想象中多。最常见的问题是单位。STEP 文件内部有自己的单位声明CadQuery 默认按毫米导出但如果你在建模时用了英寸思维比如把 80 当成 80 英寸导出的文件在别人那里就是 2 米大的怪物。我的做法是在导出前强制断言一次包围盒尺寸超出合理范围就报警。第二个坑是精度与文件体积的平衡。STEP 导出时可以设置线性公差和角度公差公差越小文件越大。对于普通机械零件线性公差设 0.01mm 足够如果你做的是光学件或者需要高精度曲面可能要设到 0.001mm。但公差设太小会导致文件里实体数量爆炸打开卡顿。我一般先用默认值导出如果下游反馈面有缝隙再回头收紧公差。第三个坑是命名。STEP 里的实体、面、边都可以带名字但很多导出器默认不写。如果你的下游流程需要按名字识别特征比如自动化装配一定要在建模时给关键面打标签导出时保留。否则下游拿到的是一个无名实体堆只能靠几何位置猜非常痛苦。3.2 URDF 生成几何只是第一步关节才是灵魂URDFUnified Robot Description Format是机器人领域的标准描述格式它和纯几何的 STEP 有本质区别URDF 描述的是带运动学关系的装配体。一个机械臂的 URDF 里每个连杆link是几何体每个关节joint定义了父子连杆之间怎么相对运动——是旋转还是平移、绕哪个轴、行程范围多少。text-to-cad 生成 URDF 的难点不在几何而在关节信息的推断。文本里说两段连杆用铰链连接程序得知道铰链轴在哪、旋转范围多少、两个连杆的坐标系原点怎么对齐。这些信息往往在自然语言里是缺失的所以我的做法是要求输入文本必须显式描述关节或者提供一个关节参数表。一个典型的 URDF 片段link namelink1 visual geometry mesh filenamelink1.stl scale0.001 0.001 0.001/ /geometry /visual collision geometry mesh filenamelink1.stl scale0.001 0.001 0.001/ /geometry /collision inertial mass value0.5/ inertia ixx0.001 iyy0.001 izz0.001 ixy0 ixz0 iyz0/ /inertial /link joint namejoint1 typerevolute parent linklink1/ child linklink2/ axis xyz0 0 1/ limit lower-1.57 upper1.57 effort10 velocity1.0/ /joint这里有几个实操要点。第一mesh 的 scale。URDF 里长度单位是米而 CAD 建模通常是毫米所以 mesh 引用时要加scale0.001 0.001 0.001否则你的机器人会大 1000 倍。这个坑我踩过导入仿真环境后整个模型飞出视野找了半天才发现是单位问题。第二inertial 不能省。很多人只写 visual 和 collision结果仿真时物体没有质量物理引擎直接报错或者行为诡异。质量可以估算惯量张量哪怕用简化公式把连杆近似成圆柱或长方体也比不写强。第三collision 几何可以比 visual 简化。visual 用精细网格好看collision 用包围盒或简化凸包能大幅提升仿真速度这是机器人仿真里的常规优化。3.3 G-code从几何到刀路中间隔着一整个 CAMG-code 是数控机床能直接执行的指令但从 STEP 到 G-code 不是一步转换中间必须经过 CAM计算机辅助制造。这一步 text-to-cad 通常不直接做而是把 STEP 交给 CAM 软件或库如 FreeCAD 的 Path 工作台、或专门的 CAM 引擎生成刀路。为什么不能直接生成 G-code因为刀路依赖太多几何之外的信息用什么刀具、刀具直径多少、切削深度、进给速度、主轴转速、是粗加工还是精加工、材料是什么。这些参数文本里往往没有需要工艺知识。所以合理的架构是text-to-cad 负责生成准确的 STEP 几何CAM 环节单独处理或者提供一个工艺参数模板让用户填。如果你确实想打通到 G-code我的建议是先用 FreeCAD 的 Path 工作台做半自动生成导入 STEP选刀具设参数生成刀路导出 G-code。全自动的难度在于工艺决策那是个比几何生成复杂得多的问题不建议在 text-to-cad 项目里硬啃。4. 批量生成与参数扫描让脚本替你干重复活4.1 参数化建模的真正威力在批量单个零件生成只是入门text-to-cad 真正的价值在批量。假设你要做一系列法兰盘外径从 50 到 200每 10mm 一档共 16 个规格。手工建模要画 16 次用脚本就是一个循环import cadquery as cq base { thickness: 10.0, center_hole_diameter: 30.0, bolt_holes: { count: 6, pattern_circle_diameter: 60.0, type: countersunk }, unit: mm } for od in range(50, 201, 10): p dict(base) p[outer_diameter] float(od) # 分布圆随外径缩放保持边距合理 p[bolt_holes] dict(base[bolt_holes]) p[bolt_holes][pattern_circle_diameter] od * 0.75 part build_flange(p) cq.exporters.export(part, fflange_D{od}.step)这段代码里有个设计决策值得说分布圆直径不是固定值而是随外径缩放。如果固定 60mm外径 200 的法兰盘螺栓孔会挤在中心边缘一大圈没材料既不美观也不合理。按 0.75 倍外径缩放是个经验值保证孔到边缘有足够边距。这种派生参数的逻辑正是参数化建模比手工建模强的地方——规则一旦定好所有规格自动一致。4.2 参数扫描与设计表更进一步你可以把参数组合做成一张 CSV 设计表脚本读表批量生成。这在做系列化产品时特别有用设计表本身就是可版本管理的设计意图。规格代号外径厚度中心孔螺栓数分布圆材料FL-05050820437.5AL6061FL-080801030660AL6061FL-1201201245890AL6061FL-200200168012150AL6061脚本读这张表逐行生成 STEP文件名带规格代号。这样一套流程下来几十个规格几分钟跑完而且每个文件的建模逻辑完全一致不会出现这个画的时候手抖多打了个孔的问题。4.3 批量生成时的资源管理批量跑的时候有个现实问题内存和临时文件。CadQuery 每次建模都会在内存里构建 B-rep 结构如果循环几百次不释放内存会涨到爆。我的做法是每生成一批比如 20 个就显式清理一次或者干脆把批量任务拆成多个子进程跑每个进程处理一批跑完退出释放内存。另外导出路径要规划好。我习惯按输出目录/规格代号/文件名.step的结构组织方便下游按规格检索。如果下游是自动化管线还要同时导出一份清单文件JSON 或 CSV记录每个文件的参数和生成时间方便追溯。5. 实操中踩过的坑与排查链路5.1 孔打在了错误的面工作平面选择问题最早做的时候我遇到过一个诡异现象法兰盘生成出来螺栓孔不在上表面而是跑到了侧面整个零件像被扎了一圈针。排查了半天问题出在workplane()的选择上。CadQuery 里.faces(Z)表示选 Z 方向最靠上的那个面.workplane()在这个面上建立新的工作平面。但如果前面的布尔运算改变了面的方向或者有多个面满足条件选中的可能不是你想要的那个。我的修复方式是显式指定选择器比如.faces(cq.selectors.NearestToPointSelector((0, 0, th)))直接选离某个点最近的面避免歧义。这个坑的教训是在 code-CAD 里选择器比几何本身更容易出错。几何是确定的但选哪个面依赖选择器的语义一旦模型复杂起来隐式选择很容易选错。所以我现在写建模代码凡是涉及面/边选择的都尽量用显式、可验证的选择器并在关键步骤后打印一下选中对象的数量做校验。5.2 导出的 STEP 在某个软件里显示为空心有次下游反馈我导出的 STEP 在他们的软件里打开是空心的只有壳没有实体。我这边用 FreeCAD 打开明明是实心的。排查下来问题出在导出精度和软件容差的差异上。我的模型里有个很小的倒角0.1mm导出时用的默认公差比较大导致那个倒角面在转换时退化B-rep 的拓扑出现了微小裂缝。我的软件容差松自动缝合了他们的软件容差严缝不上就显示成空心。修复方案有两个一是把那个倒角改大一点0.5mm二是导出时收紧公差。我两个都做了问题消失。这件事让我养成了一个习惯导出后一定用至少两个不同的软件打开验证一个宽松一个严格能提前发现这类兼容性问题。5.3 URDF 导入仿真环境后模型乱飞前面提过单位问题这里展开说排查链路。现象是URDF 导入仿真环境后机械臂的连杆位置全乱有的飞出去有的叠在一起。排查步骤先看 URDF 里的 mesh scale确认是不是 0.001。发现没写 scale默认按米处理而 mesh 是毫米建模的大了 1000 倍。加上 scale 后位置还是不对。检查 joint 的 origin发现父子连杆的坐标系原点没有对齐joint 的origin xyz写的是相对位置但我按绝对位置填了。修正 origin 后模型位置对了但一仿真就抖动。检查 inertial发现惯量张量填的是 0物理引擎算不出稳定解。用简化公式补上惯量后仿真稳定。这条链路说明URDF 的问题往往不是单一原因而是单位、坐标系、物理属性三层叠加。排查时要一层一层验证别指望一次改对。5.4 批量生成时文件名冲突覆盖这个坑比较低级但很常见批量循环里文件名只用了外径结果不同厚度但同外径的规格互相覆盖最后只剩最后一个。修复很简单文件名带上所有区分参数或者直接用设计表里的规格代号。我现在一律用规格代号做文件名因为它是设计表的主键天然唯一。6. 把 text-to-cad 接进实际工作流的几点经验6.1 输入文本的规范化比模型能力更重要做了这么多轮我最大的体会是text-to-cad 的瓶颈往往不在生成端而在输入端的规范化。自然语言太自由了大一点的孔差不多居中这种描述再强的模型也没法稳定处理。所以实际项目里我倾向于定义一个受控的输入模板要求用户按字段填而不是随便写一段话。比如法兰盘的输入模板类型: 法兰盘 外径: 80 mm 厚度: 10 mm 中心孔: 30 mm 螺栓孔: 6 x M6, 分布圆 60 mm, 沉头 材料: 6061 铝这种半结构化的输入解析准确率能从看运气提升到基本稳定。语言模型在这里的角色是容错解析器——用户填得不太规范时帮忙纠正而不是从零理解一段散文。6.2 版本管理几何也要进 Gitcode-CAD 的一大好处是几何可以进版本控制。传统 CAD 的二进制文件没法 diff改了什么全靠人记。而 text-to-cad 的输入是文本、建模是代码两者都能进 Git。每次改参数、改建模逻辑都有清晰的提交记录。我的做法是输入参数JSON/CSV和建模脚本分开管理脚本里不硬编码任何具体尺寸全部从参数读。这样改一个尺寸只动参数文件改建模逻辑只动脚本职责清晰。生成的 STEP 文件本身不进 Git二进制、体积大但生成脚本和参数进需要时随时能重新生成。6.3 验证环节不能省自动生成的几何一定要有自动验证。我常用的几个检查包围盒检查生成后的零件包围盒尺寸是否和输入参数一致防止单位错误或缩放错误。体积检查估算体积是否在合理范围防止布尔运算失败导致实体缺失。水密性检查STEP 是否是有效实体solid不是壳shell或面片。孔位检查螺栓孔的实际位置和数量是否和参数一致可以用几何查询验证。这些检查写起来不复杂但能拦住 90% 的低级错误。尤其是批量生成时没有自动验证你根本不知道哪几个文件是坏的。6.4 关于完全自动的预期管理最后说点实在的。text-to-cad 能做到一句话出零件但那个一句话必须是结构良好、信息完整的一句话。指望用户随便说一句模糊需求就出可加工的零件目前不现实也不安全——几何错误可能导致加工报废甚至安全事故。我的定位是text-to-cad 是效率工具不是替代工程师的工具。它把工程师从重复的建模劳动里解放出来但设计意图的定义、关键尺寸的确认、生成结果的审核仍然需要人。把预期放在批量、参数化、可复现上这个项目的价值就非常实在把预期放在全自动无人设计上那大概率会失望。我在实际使用中发现最舒服的工作流是工程师定义参数表和建模规则脚本批量生成工程师抽查验证下游直接用。这个流程里人的价值集中在定义规则和审核结果两件事上中间的重复劳动全部自动化。这比追求全自动务实得多也可靠得多。