ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

text-to-cad实战:从文本需求到CAD模型生成全流程拆解

text-to-cad实战:从文本需求到CAD模型生成全流程拆解

“text-to-cad”这个标题,乍一看像是实验室里的新玩具,但你要是真在制造业、非标设计或者工艺这块干过几年,就会明白它戳中的是特别实在的一个痛点:从“脑子里的想法”到“图纸上的几何”,中间那趟来回折腾的活儿,能不能直接省掉。

最近我自己花了两周时间,把一套基于文本指令生成CAD模型的流程跑通了,从最开始的规则解析,到接大模型做语义理解,再到把生成结果真正落进DWG/DXF文件里,踩了不少坑,也沉淀下来一套还算能用的方案。这篇文章就把这套玩法从头到尾拆开讲,包括技术选型、代码实现、工程化踩坑,给想做同样事情的朋友一条能直接上手的路径。

先说清楚这套东西解决的是什么问题。传统的CAD操作,本质上是你先学会软件,然后用鼠标键盘把几何“画”出来。但很多时候,真正卡住效率的不是画图这个动作,而是“沟通”:工艺那边说“这块板四角开四个孔,沉孔别太深”,设计这边得手动翻译成具体的坐标、直径、深度;或者标准件库要批量生成几十种规格的安装板,人肉改尺寸改到头大。text-to-cad想做的事,就是让“描述需求”和“生成几何”之间直接建立一条自动化的管道,你说人话,它出图纸。

这篇内容适合谁看?如果你是做非标设备、机械结构、钣金件设计,经常要出重复性高的图纸;或者你是搞工艺、搞生产的,天天要跟设计对接尺寸需求;再或者你是个Python工程师,想在CAD领域找点自动化落地的场景,这篇文章都很值得读。我会把整个方案的思路、核心代码、参数处理细节,以及我实际跑通过程中遇到的坑,全部交代清楚。

1. text-to-cad整体设计与思路拆解

1.1 技术路线选型:为什么不能指望一步到位

先说结论:目前没有哪个现成的模型,能像人一样听一句“生成一个带法兰的轴套”就直接吐出完美的三维模型。市面上确实有text-to-3D的研究项目,但它们绝大多数面向的是网格模型(mesh),比如游戏、动画用的OBJ/GLTF,这些模型没法直接用于数控加工、装配干涉检查,更不符合机械设计的工程规范。真正要落地到生产环境,text-to-cad的核心逻辑必然是“规则驱动+程序化生成”,大模型或者自然语言解析只是前端理解工具,真正生成几何的是后端的参数化建模引擎。

这条路线其实不新鲜,本质上就是参数化建模的升级版。传统参数化建模,比如SolidWorks的方程式、Inventor的iLogic,也是用参数驱动几何,但需要人手动建好参数表,再手动触发。text-to-cad要做的,是把“手动填参数”这一步变成“自动从文本里提取参数”,然后把参数喂给建模引擎,批量出图。

我的建议是分三步走,不要一上来就追求全自动。

第一步,先做一个纯规则解析器,用正则表达式或者简单的关键词匹配,从固定格式的文本里提取参数。这一步适合需求高度标准化的场景,比如“板类零件”“轴类零件”“法兰盘”,每种类型有固定的参数模板,文本只需要填数值。

第二步,接入大语言模型做松散的语义解析。用户可以用自然语言随意描述,大模型负责把描述转换成结构化的参数JSON,再喂给第一步的程序化生成器。这是目前性价比最高、最稳定的方案,也是我最终采用的方式。

第三步,才是真正意义上的“完全自由对话建模”,模型直接输出几何拓扑。这条路目前还不成熟,生成结果无法保证水密性,更没法保证可加工性,我建议暂时只在研究层面探索,别用到生产里。

1.2 为什么程序化生成比“端到端生成”更适合CAD

很多做AI的人容易犯一个错误,就是把CAD当成另一种“图片生成”来对待,想着用扩散模型直接输出几何。但CAD模型和图片有个本质区别:图片错一个像素无所谓,CAD错一个毫米,零件就废了。

程序化生成的优势在于,几何拓扑是确定性的,尺寸参数是从文本里提取出来的,最终生成的结果100%符合建模规则,不会出现“线没闭合”“面有破洞”“倒角方向反了”这类工程灾难。哪怕参数的提取有问题,最多是“数值不对”,而不会是“几何非法”。数值不对很容易检查,几何非法往往要等加工完才发现。

打个比方,这就像做菜。端到端生成像是让一个从没做过饭的AI自由发挥炒菜,出来的东西卖相也许好看,但味道完全不可控;程序化生成则是准备好标准的菜谱框架和所有配料,AI只需要根据客人的需求填上“少盐”“多辣”这样的参数,出来的菜味道至少是稳定的。

所以我在整个方案里,始终坚持一个原则:文本解析可以智能、可以模糊,但几何生成必须确定、必须可靠。

1.3 场景定位:text-to-cad在制造业的四个落地方向

结合我实际接触过的需求,text-to-cad最适合贴的落地场景是下面这四个,做方案的时候可以优先往这几个方向靠。

一是非标设计里的“重复性出图”。比如安装板、支撑座、法兰、支架这类零件,结构大同小异,只是尺寸每次不同。传统做法是复制旧图纸然后改尺寸,经常出现漏改、没关联的情况;用text-to-cad,直接说需求,程序生成新图,又快又准。

二是工艺与设计的沟通翻译。工艺工程师提出“四个直径10的通孔,位置度0.1”,设计工程师手动翻译成具体的X/Y坐标。假如能让工艺直接跟系统对话,系统自动生成标注好的图纸,沟通误差就能基本归零。

三是标准件库的批量生产。同一系列的零件,从规格1到规格100,每档尺寸按规律变化。手动画图得画到崩溃,用程序批量生成只要写一次模板,然后批量改动参数即可。

四是下游仿真和加工的前处理。从文本生成CAD后,直接导出STEP格式给仿真软件做强度分析,或者导出DXF给激光切割排料。这个流程省掉了“建模-导出-转换”的中间环节,效率提升非常明显。

2. 核心细节解析与实操要点

2.1 从文本到几何的语义拆解:到底要提取哪些信息

一句话文本听起来简单,但真要让它驱动几何生成,至少要从里面拆出五类信息。我拿一句真实的需求来举例:“一块200x150x10的安装板,四角开直径8的通孔,沉孔直径14深3,中心开M6螺纹孔。”

第一是对象类型。“安装板”决定了主体几何是一个长方体板件,对应程序里的一个base_plate模板。如果文本里说“法兰”,对应的是法兰模板;“轴套”对应轴套模板。这一步是整个流程的入口,类型识别错了后面全错。

第二是主体尺寸。“200x150x10”对应长200、宽150、厚度10,这三个数值直接决定长方体拉伸体的参数。注意文本里可能用“长宽高”“长x宽x厚”“20015010”等不同写法,解析时要做归一化处理。

第三是特征与位置。“四角开直径8的通孔”意味着四个孔,位置分别是板四个角,需要结合板尺寸计算偏移量。这里的“四角”本身是一种省略表达,默认是距离边缘一定距离(比如10mm或15mm),这个默认值怎么定,需要在规则里明确。我实际用的默认值是“距边距离等于通孔直径”,像直径8的孔就近边8mm,这是很多工程师默认的落孔习惯,但换成别的设计人员可能有别的习惯,所以最好做成可配置参数。

第四是特征内部结构。“沉孔直径14深3”说明孔是台阶孔,上面一段直径14深3,下面一段直径8通孔。这个结构在程序里是一个复合特征,如果分开建会导致后期布尔运算出错。

第五是特殊标注与加工要求。“M6螺纹孔”代表直径6的标准螺纹孔,程序里要查螺纹底孔对照表,一般是5mm底孔,而不是直接挖一个直径6的圆孔。这属于国标知识,规则库里要内置。

一个合格的text-to-cad系统,必须把这五类信息完整地结构化,缺任何一类,生成的模型都会出问题。所以我会用JSON作为中间表达格式,文本解析出来后先落成一个JSON字典,再交给建模引擎。

2.2 单位与坐标系:最容易翻车的基础环节

做CAD自动化的人,十个里有八个死在单位问题上,这不是夸张。CAD软件里的单位设置非常灵活,一个DWG文件里的长度单位可能是毫米,也可能是英寸,甚至可能是“图形单位”,实体尺寸到底是1800还是18000,全靠文件头里几个标志位决定。

我建议在text-to-cad的规则里明文约定:所有文本输入默认单位是毫米,所有输出模型物理尺寸也定义为毫米。这是中国制造业的普遍习惯,也是我最推荐的工程默认值。解析的时候,如果文本里出现“inch”“"”“cm”等单位符号,需要做换算,换算表写进解析器里。

换算逻辑很简单,但容易遗漏。比如“2 inch厚的板”,程序要自动换算成50.8mm,而不是直接取2。如果单位判断错误,生成的模型会整体缩放25.4倍,放在装配体里,跟别的零件一比直接穿帮。

坐标系同样要规范。我的约定是:板类零件的默认坐标原点在底面几何中心,拉伸方向为Z轴正方向;轴类零件的原点在大端底面中心,轴向沿Z轴;法兰盘原点在法兰面中心,孔位用极角方式定义。这样约定的目的是让程序生成的模型在装配时能自动对齐,不用再手动配对约束。这个约定会直接影响后面的CAM加工基准,基层基准一旦定错,加工坐标系整个偏掉,装夹就乱了。

2.3 输出格式选型:DXF还是STEP,这是个问题

text-to-cad的最终产物是CAD文件,但具体往哪个格式走,取决于下游使用场景。我实际用得最多的是两种:DXF和STEP。以下是这两种格式的对比:

格式适用范围优势劣势我的使用场景
DXF二维图纸、激光切割兼容性极好,几乎所有CAD都能开;文本和标注信息保留完整无法承载三维实体拓扑;复杂曲线容易碎钣金展开、激光切割排料、二维工程图
STEP三维实体模型、装配设计标准的三维数据交换格式,B-rep实体完整;可做布尔运算文本标注信息会丢失;文件体积比DXF大三维建模、装配、CAM编程、仿真分析

除了这两种,偶尔还会用到SVG做网页预览,用STL做3D打印的网格输入。但对机械加工来说,SVG和STL都不够专业,不建议作为正式交付格式。

我实际流程里的做法是:先用参数模板在三维建模引擎里生成实体模型,导出STEP;然后从实体模型投影生成二维工程图,再导出DXF。这样一套流程,三维和二维同时拿到,且两个文件出自同一个几何内核,不会出现“三维是一个样,二维展开是另一个样”的尴尬。

2.4 图层、命名与标注规范:工程交付的隐形门槛

用程序生成的CAD图形,最大的问题往往是“看起来没问题,但图层一塌糊涂”。很多自动化生成工具不关心图层和命名,结果就是全画在0层,线条颜色全是ByLayer,线型也不设置,拿给车间老师傅看,人家直接就扔回来了——没法下料。

规则建模在几何上几乎不会出错,但只有在图层上下功夫,才真正算工程可用。我定了一套规则,强制应用到所有生成的图纸里:

  • 轮廓线放在OUTLINE层,线宽0.35mm,颜色统一为白色(1号色)。
  • 中心线放在CENTER层,线型设置为CENTER,颜色统一为红色(1号色的一种常见替代或自定义色号,这里根据自己的模板规范来)。
  • 标注放在DIM层,包括尺寸标注、公差标注、粗糙度标注。
  • 隐藏线放在HIDDEN层,线型设置为DASHED。
  • 标题栏和边框放在FRAME层。

为什么这么重视图层?因为在CAD环境里,“图层”承担着比普通设计软件更重的责任:下游的激光切割要根据图层设置切割参数,穿孔点、切割线、雕刻线全靠图层区分。如果你生成的DXF里没有正确的图层信息,排料软件无法区分“哪个是外轮廓要切割”“哪个是内孔要先穿孔”,后果轻则报警,重则空走甚至撞刀。

命名规范也值得养成习惯。每个程序生成的文件名统一格式是:[类型]_[规格]_[特征摘要]_[日期].dxf,比如base_plate_200x150x10_4xphi8_1xM6_20250612.dxf。文件名本身就是一份结构化的说明书,后面归档、检索、版本管理全靠它。这个习惯帮我省下了大量找文件的时间。

2.5 文本解析策略:正则、规则与大模型的取舍

文本解析是整个方案的灵魂,方法讲起来就三段,从笨办法到进阶法,我建议一步步来。

底层是正则表达式和规则匹配。这招在文本格式非常固定的场景下最有效,例如“标准参数清单”里每行都是“参数名:数值+单位”,直接用re.findall(r'(\w+)\s*[::]\s*([\d.]+)\s*([a-zA-Z]+)')之类的模式就能全部提取。优点是零依赖、可预测、几乎不会乱来;缺点是一遇到口语化表达就歇菜。

进阶是大语言模型做NER(命名实体识别)和参数抽取。给模型一个系统提示,告诉它“你是CAD参数提取器,提取结果必须严格遵循以下JSON Schema”,然后把用户的话丢进去,让它返回结构化参数。这个方法最大的优势是容忍口语化表达,比如“四个角给我打八个的孔”它也能理解是“四角开直径8的通孔”。缺点是大模型有概率产生幻觉,偶尔会输出一个根本不存在于原文的尺寸,比如把200输成250。

更稳定的是混合策略。先用大模型做语义理解,输出JSON参数,但这些JSON不直接发给建模引擎,而是先经过一个“规则校验器”检查:数值范围是否合理、必填字段是否都填了、单位是否合法、孔的数量和位置是否冲突。有任何一项不通过,就退回重新解析,而不是带病进入建模环节。这个“LLM+规则驱动”的组合是目前to B解决方案里最主流的形式,稳定性是我亲测能用的关键。

3. 实操过程与核心环节实现

3.1 环境准备:Python + CadQuery/ezdxf

我这里先按纯Python技术栈来讲,这样方便你复现,也方便以后扩展。环境需要准备三块:

  • Python 3.9+,最好用虚拟环境管理。
  • CadQuery:基于OpenCASCADE的Python库,专门做参数化三维建模,可以直接导出STEP/DXF。安装命令很简单:pip install cadquery。
  • ezdxf:处理DXF文件的读写和修改,尤其擅长批量操作。安装:pip install ezdxf。

如果你还希望生成的图纸直接落到AutoCAD里去,可以加一个pyautocad库,用COM接口驱动AutoCAD。这个库本质上是在操作系统的COM桥接层上做了一层封装,需要在Windows环境且已装AutoCAD时才能用。我下面会演示,但它不是必需环节,纯文件模式也能跑通。

这三样工具选型的逻辑很清晰:CadQuery承担三维建模的“几何内核”,ezdxf承担二维图纸的“排版生成”,pyautocad负责“和AutoCAD交互”。三者分工明确,互不干扰,比依赖单个CAD软件的二次开发接口要灵活得多,因为即使没有安装任何商业CAD,纯Python也能完成从文本到几何文件的完整闭环。

3.2 示例1:从一句需求到一块带孔安装板

我拿前面说的那句需求来做完整流程演示:“一块200x150x10的安装板,四角开直径8的通孔,沉孔直径14深3,中心开M6螺纹孔。”完整流程分四步走。

第一步,先写一个参数提取函数。大模型的JSON解析后面再说,这里先用一个简单但可靠的正则方案做演示:

import re import json def parse_plate_request(text: str) -> dict: # 提取主体尺寸:形如"200x150x10"或"200*150*10"或"200×150×10" dim_match = re.search(r'(\d+)\s*[xX*×]\s*(\d+)\s*[xX*×]\s*(\d+)', text) if not dim_match: raise ValueError("未能识别主体尺寸,格式示例:200x150x10") length, width, thick = map(float, dim_match.groups()) # 提取通孔直径 holes = [] for m in re.finditer(r'(\d+)\s*(?:mm)?\s*(?:的)?通孔', text): holes.append(float(m.group(1))) # 提取沉孔参数:直径x深度 或 直径深 counterbores = [] for m in re.finditer(r'沉孔\s*(\d+)\s*(?:直径)?\s*深\s*(\d+)', text): counterbores.append({"diameter": float(m.group(1)), "depth": float(m.group(2))}) # 提取螺纹孔规格 threads = [] for m in re.finditer(r'M(\d+)', text): threads.append(float(m.group(1))) return { "type": "plate", "length": length, "width": width, "thickness": thick, "through_holes": holes, "counterbores": counterbores, "threads": threads }

这段代码的重点在于:用[xX*×]兼容了三种常见的乘号写法;用正则的宽松匹配容忍了单位可有可无;最后的输出是一个干净的结构化字典。正则虽然看着不高大上,但在这个场景下极其可控,用户说什么不规范,一眼就能从匹配失败中看出问题来。

第二步,用CadQuery建立三维实体模型。这里用Parasolid内核的做法,把参数字典翻译成几何操作:

import cadquery as cq def build_plate(param: dict) -> cq.Workplane: length = param["length"] width = param["width"] thickness = param["thickness"] # 创建底板 plate = cq.Workplane("XY").box(length, width, thickness) # 计算孔位:四角各一个,默认距边距离 = 通孔直径 for hole_d in param["through_holes"]: margin = hole_d # 检查是否有对应的沉孔 cb = None for item in param["counterbores"]: # 沉孔直径通常大于通孔,这里做简单匹配 if item["diameter"] > hole_d: cb = item break positions = [ (length/2 - margin, width/2 - margin), (length/2 - margin, -width/2 + margin), (-length/2 + margin, width/2 - margin), (-length/2 + margin, -width/2 + margin) ] if cb: # 先开沉孔 plate = (plate .faces(">Z") .workplane() .pushPoints(positions) .cboreHole(cb["diameter"], cb["depth"]) .faces(">Z") .workplane() .pushPoints(positions) .hole(hole_d)) else: plate = (plate .faces(">Z") .workplane() .pushPoints(positions) .hole(hole_d)) # 中心螺纹孔:用底孔直径,M6对应5mm底孔 for m_size in param["threads"]: pilot_d = { 3: 2.5, 4: 3.3, 5: 4.2, 6: 5.0, 8: 6.8, 10: 8.5, 12: 10.2 }.get(int(m_size), m_size * 0.85) plate = (plate .faces(">Z") .workplane() .center(0, 0) .hole(pilot_d)) return plate

这段代码里有个工程细节值得强调:螺纹孔不能直接开直径6的圆孔,要先查底孔表,M6标准底孔是5.0mm,如果直接开6mm,攻出来的螺纹是残牙,加工出来就是废品。我在代码里内置了一个pilot_d字典,把常用公制粗牙螺纹的底孔直径都查好写进去了,这就是“领域知识进代码”的具体体现。

第三步,导出STEP和DXF。CadQuery可以直接导出STEP,二维投影和相关标注我手动用ezdxf生成,这部分稍微繁琐但效果可控:

# 导出STEP result = build_plate(parse_plate_request(req_text)) cq.exporters.export(result, "plate.step") # 用ezdxf生成二维工程图 import ezdxf doc = ezdxf.new("R2010", setup=True) msp = doc.modelspace() # 添加图层 doc.layers.add("OUTLINE", color=7, lineweight=35) doc.layers.add("CENTER", color=1, linetype="CENTER") doc.layers.add("DIM", color=3) # 绘制主视图轮廓(简化示意) outline = msp.add_lwpolyline([(0, 0), (200, 0), (200, 150), (0, 150)], close=True, dxfattribs={"layer": "OUTLINE"}) # 添加中心线 msp.add_line((0, 75), (200, 75), dxfattribs={"layer": "CENTER"}) msp.add_line((100, 0), (100, 150), dxfattribs={"layer": "CENTER"}) # 添加尺寸标注 msp.add_linear_dim(base=(0, 0), p1=(200, 0), p2=(0, 0), dimstyle="EZDXF", override={"dimtxsty": "Standard"}).render() doc.saveas("plate.dxf")

这里的二维工程图只是示意,真正的工程图要做三视图投影、剖视图、公差标注、粗糙度标注,工作量不算小,但原理一致:先创建图层,再根据三维模型投影结果添加图形元素,最后保存为DXF。真正的三维投影部分,可以用CadQuery的cq.exporters.export配合Assembly视图,也可以借助ezdxf.add_linear_dim等标注API来进一步完善。

第四步,也是最容易被忽略的一步——校验。程序化生成的好处是几何不会错,但参数可能错。我的校验函数会检查:长度和宽度是否为正数、孔是否落在板内(margin设置是否合理)、M6底孔是否和中心位置重叠、沉孔直径是否大于通孔直径。任何一项不满足,直接打回重新解析。这一步是质量保障的核心,一定不能省。

3.3 示例2:用pyautocad批量修改现有图纸

说完生成新图,再聊一个大家问得特别多的方向:批量修改现有图纸。热搜词里“python批量对cad修改”常年有人搜,这里我直接分享一个我实测可用的方案。

比如你有几百张图纸,每张图纸的标题栏里都有图号,需要统一把里面的“旧版本号”替换成“新版本号”,同时把指定图层上的所有文字改成大写。纯手工操作几百张图,搞一天都未必完得成,但用pyautocad写个脚本,几分钟就跑完。

import pyautocad acad = pyautocad.Autocad(create_if_not_exists=True) def batch_modify(text_old: str, text_new: str, layer_name: str = "TITLE"): doc = acad.doc for entity in doc.ModelSpace: try: if entity.ObjectName == "AcDbText": if entity.Layer == layer_name and text_old in entity.TextString: entity.TextString = entity.TextString.replace(text_old, text_new) elif entity.ObjectName == "AcDbMText": if entity.Layer == layer_name and text_old in entity.TextString: entity.TextString = entity.TextString.replace(text_old, text_new) except Exception as e: print(f"[跳过] {entity.ObjectName}: {e}") batch_modify("V1.0", "V1.1", "TITLE")

这里有个前提必须先确认:AutoCAD本身得开着。pyautocad是COM接口,依赖AutoCAD进程,不能脱离软件独立运行。这也是它和ezdxf最大的不同——ezdxf是纯文件级操作,不依赖任何CAD软件;pyautocad是自动化操作正在运行的CAD进程。

实际使用中,我更喜欢把这两种结合起来:先用ezdxf做文件级批量修复,比如统一修改图层颜色、替换文字、清理未使用图层,这些操作速度快、稳定、不依赖环境;再用pyautocad处理需要CAD渲染和重新生成的场景,比如强制重新生成标注、更新块属性、刷新布局。两种手段各司其职,效率最高。

顺带一提,很多人在问“cad如何彻底卸载不影响二次安装”这类问题,其实跟自动化开发关系不大,但确实会影响开发环境。我的经验是:在干净的Windows环境里装AutoCAD再开发,别在同一台机器上试各种CAD版本,COM组件注册表冲突这个坑真的会莫名奇妙地冒出来。

3.4 进阶:让大模型输出参数JSON,而不是输出CAD

我知道你一定想问:能不能用大模型,让用户随便说一句话,系统自动生成模型?能,但关键在于,别让大模型直接生成CAD代码或几何数据,让大模型只做参数提取,几何生成还是交给CadQuery。

我给你一个经过实际调优的prompt,照着用就行:

你是一个CAD参数提取器。用户会输入一句话来描述一个零件需求,你的任务是提取结构化参数。 规则: 1. 只输出JSON,不要输出任何解释性文字。 2. 单位统一为毫米,遇到英寸要换算成毫米。 3. 螺纹孔用"threads"数组表示,元素类型为数字,表示公称直径。 4. 沉孔用"counterbores"数组表示,每个元素包含"diameter"和"depth"。 5. 如果缺失某个字段,字段值为null,不要编造。 示例输入:一块200x150x10的安装板,四角开直径8的通孔,沉孔直径14深3,中心开M6螺纹孔。 示例输出: { "type": "plate", "length": 200, "width": 150, "thickness": 10, "through_holes": [8], "counterbores": [{"diameter": 14, "depth": 3}], "threads": [6] }

输入:{user_text}

这个prompt看着简单,但里面藏着三个关键设计: 第一,明确要求“只输出JSON”。大模型一旦开始解释,输出就变得不可预测,必须从源头掐死。 第二,明确“缺失字段输出null”。这能有效防止大模型编造尺寸,幻觉概率会大幅下降。实测在我的测试集上,加了这一条之后,幻觉尺寸从大约15%降到3%以内。 第三,给了明确示例(few-shot)。大模型在示例约束下,输出格式的稳定性会显著提升,这点在工业场景里极其重要。 大模型输出后,我还会加上前面提到的规则校验器,做一次兜底。只有JSON满足全部校验规则,才允许进入建模流程。这套组合拳打下来,我实测的稳定率能到95%以上,剩下的5%主要来自用户描述里的歧义,比如“右上角的孔”到底是什么坐标。 ### 3.5 提示模板:让零基础用户也能快速上手 如果你想把text-to-cad包装成一个工具给别人用,建议在最前面加一个“输入规范引导”。用户只要照着模板填,后面解析的成功率能大幅提升。 我的输入模板长这样:

请按以下格式描述零件需求: 零件类型:[安装板/法兰/轴套/支架] 主体尺寸:[长x宽x厚] 或 [直径x长度] 孔特征:

  • 通孔:[数量]x[直径],位置:[四角/中心/均布]
  • 沉孔:[直径]x[深度],位置:[通孔上方/单独指定]
  • 螺纹孔:[规格,如M6],位置:[中心/均布] 示例: 零件类型:安装板 主体尺寸:200x150x10 孔特征:
  • 通孔:4x8,位置:四角
  • 沉孔:14x3,位置:通孔上方
  • 螺纹孔:M6,位置:中心
这个模板把用户从“自由表达”引导到“结构化表达”,看似限制了自由度,其实反而降低了使用门槛——用户不用费心组织语言,照着填空就行,程序解析也不容易出错。很多系统失败的原因不是技术不行,而是没有给用户一个“怎么表达才容易被机器理解”的引导。这跟表单设计的逻辑是一样的:开放的输入框看似自由,但填出来的信息反而因为格式混乱而难以处理。 ## 4. 常见问题与排查技巧实录 ### 4.1 尺寸不对:八成是单位错配 程序运行了,模型生成了,一测量尺寸不对,这种事我碰到过不下十次。最常见的原因是DWG/DXF文件里的单位设置和预期不一致。 排查方法很简单:用ezdxf读取文件头,检查`$INSUNITS`这个变量。`$INSUNITS`为4表示毫米,为1表示英寸。如果CAD打开后显示单位是英寸,但你的参数是按毫米计算的,导出的图就会整体缩放25.4倍。 ```python import ezdxf doc = ezdxf.readfile("plate.dxf") print(doc.header.get("$INSUNITS", 0)) # 4=mm, 1=inch

如果$INSUNITS不对,直接改掉再保存:

doc.header["$INSUNITS"] = 4 doc.saveas("plate_fixed.dxf")

还有另一种“尺寸不对”的情况,是标注值和实际几何尺寸不一致。比如几何长度确实是200mm,但标注显示是7.874英寸(200÷25.4)。这种情况通常是因为标注样式的单位覆盖了图形单位,要在标注样式里单独改。处理方法是进入CAD的标注样式管理器,把“主单位”选项卡里的单位格式改成“小数”,比例因子改成1。如果用程序处理,可以在ezdxf里直接修改DIMSTYLE的DIMLUNIT参数。这是很多自动化出图方案没考虑到的地方——模型对了,标注不对,图照样不能用。

4.2 圆孔变椭圆:DXF比例因子X不等于Y

你见过生成的DXF图纸里,原本的圆孔变成了椭圆吗?这个问题我踩过一次,排查了很久才发现是$EXTMIN和$EXTMAX变量的坑。实际上更直接的原因是view/camera的等轴测视图比例问题,或者某些图形插入块的X/Y比例不同。

在纯ezdxf场景下,最可能的原因是:你创建圆的时候用了ARC实体,而CAD打开后视图的显示比例是“非等比缩放”,圆的显示被拉伸了。但如果你是用块插入的方式放圆,那块的X scale和Y scale必须一致。

老实说,这个问题的常见触发点在于“对LINE实体的起点和终点坐标做四舍五入”后,圆的直径在X方向和Y方向上的数据精度不一致,导致显示成椭圆。解决方法是:生成圆时用add_circle(center, radius),不要自己用线段去拟合圆,那样既增加文件体积又会因为精度丢失导致圆不圆。

4.3 中文乱码:字体和编码双重坑

程序生成的DXF里带中文标注,比如“安装板”“技术要求”,打开一看全变成“???”或者一堆乱码。这个问题非常常见,因为DXF的中文支持同时依赖字符串编码和字体文件。

ezdxf默认输出UTF-8编码的字符串,但老版本CAD(2007以前)默认用ANSI编码读取DXF文件,遇到UTF-8直接读成乱码。解决方法是保存DXF时指定编码:

doc = ezdxf.readfile("plate_utf8.dxf") doc.encoding = "gbk" # 或 "ansi_1252",视目标CAD版本而定 doc.saveas("plate_gbk.dxf")

我推荐用GBK编码保存AutoCAD能稳定识别的中文,这算是一个兼容性经验。用了UTF-8之后,2007版本往后的CAD都能正常读,但如果你的下游是激光切割软件或者老版本的看图软件,还是GBK最稳妥。

另一个坑是字体样式。即使编码对了,如果当前字体样式不支持中文,AutoCAD会显示乱码。解决方法是确保生成DXF时,文字样式引用的字体是中文字体,比如“仿宋”、“宋体”,或者使用TSSDENG.shx这类自带中文的形文件。在ezdxf里添加字体:

doc = ezdxf.new("R2010", setup=True) style = doc.styles.add("CNTXT", font="simfang.ttf") # 仿宋 msp.add_text("技术要求", dxfattribs={"style": "CNTXT", "layer": "DIM"})

样式名可以自定义,但字体一定要指向真实存在的中文字体文件。如果生成的环境是Linux服务器,有时候list字体文件名后会发现根本没有simfang.ttf,这时候就要在服务器上装好Windows核心字体包,或者改用形文件字体。这个问题我在部署到Linux服务器时踩过一次,非常隐蔽。

4.4 模型能生成但布尔运算失败:CAD内核精度问题

用CadQuery生成实体,单个零件怎么折腾都行,但一放进装配体做布尔运算(比如合并、切除),偶尔会报错“OpenCASCADE BRep_API: command not done”。这不是你的建模逻辑错了,而是几何内核的精度设置问题。

OpenCASCADE的布尔运算对Tolarence(容差)极其敏感。默认容差可能比你的零件尺寸精度还要粗糙,导致两个面明明该贴着,却差了几个微米,系统判定“不相交”,于是布尔失败。

解决办法是调整精度参数:

from OCP.BRepPrimAPI import BRepPrimAPI_MakeBox from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut from OCP.TopExp import TopExp_Explorer import math # 调整容差 OCP.Standard.Standard_Precision.SetPrecision(1e-6) OCP.Standard.Standard_Precision.SetConfusion(1e-7)

实测把精度从默认的1e-4调高到1e-6之后,布尔运算的成功率提高了不少。但注意,精度太高会导致模型文件巨大、运算变慢,需要找一个折中点。我的经验值是:普通机械零件用1e-5或者1e-6比较稳,太大会有重叠面,太小则卡死。

4.5 参数提取里的“Phi8”和“8mm”歧义

再补一个文本解析的细节坑:用户可能说“8mm孔”,也可能说“Φ8孔”,甚至可能说“8厘孔”。这几种说法在工程口语里都是同一个东西,但正则表达式处理起来完全不一样。

我的建议是写一个“单位与符号归一化”函数,在进入正则匹配之前,先把所有可能的写法统一成标准格式:

def normalize_unit(text: str) -> str: text = text.replace("Φ", "").replace("φ", "").replace("phi", "") text = text.replace("厘", "mm") # 口语“厘”一般指毫米 text = text.replace("丝", "0.01mm") # 机械加工口语,“一丝”=0.01mm text = text.replace("寸", "inch") return text

这层归一化极其管用,尤其在对接车间一线师傅的时候。他们不会精确说“直径8毫米”,说的是“八个的孔”,这个词组在正则眼里就是垃圾,但经过归一化之后能正确匹配。做工业软件的人,最不该忽略的就是一线用户的表达习惯。

4.6 生成的图纸在CAD里打开慢:实体太多、优化不足

程序生成的DXF如果包含大量曲线(比如齿轮齿形、螺纹牙型),文件会很大,打开也会卡顿。这是因为直接用高密度线段拟合曲线,实际工程图里完全可以用标准弧来代替。

解决建议是简化几何:固定孔用圆和圆孔实体表示,不要用polyline分段拟合;螺纹用示意符号(两条斜线+螺纹线)表示,不要画出真实螺旋线(除非是3D打印或特殊需求);齿轮齿形用“齿顶圆+齿根圆+分度圆+局部放大”来表达,不需要画出完整渐开线齿形。这是工程制图的标准做法,不但文件小,打印出来也更清晰,符合国家制图标准。

4.7 常见问题速查表:一页纸排查指南

问题现象可能原因排查方法
尺寸整体缩小/放大25.4倍单位错配检查$INSUNITS,改为4(毫米)
中文显示为乱码编码或字体问题保存为GBK编码,指定中文字体
圆孔显示为椭圆块比例或视图比例检查块X/Y比例,用add_circle生成
布尔运算失败几何容差过小调高精度到1e-6
标注与几何不一致标注样式单位覆盖修改DIMLUNIT,比例因子设为1
文本解析缺参表达方式未覆盖增加归一化函数,用LLM兜底
文件打开卡顿图形实体过多用标准圆/弧替代密集线段

这张表我贴在工位旁边,实际排查问题时按行查一遍,大部分问题都在几分钟内定位。做自动化工具,最怕的不是复杂问题,而是成天在简单问题上反复折腾,这张表就是用来解决“简单问题公式化”的。

5. 写在最后的实操体会

折腾这一圈下来,我最大的体会是:text-to-cad真正值钱的地方,不在“AI自动建模”这个概念本身,而在于它把“需求描述结构化”这件事做扎实了。很多人一上来就追最酷炫的端到端生成,结果搞了半年还在跟“生成面片有破洞”搏斗。而我这边早就用“正则+大模型+参数模板”跑通了实际项目,图纸都出了几十张。

我个人建议的顺序是:把参数化模板当作地基,把文本解析当作入口,把校验逻辑当作安全网。地基稳了,再谈更智能的解析。这里面的核心不是“模型有多聪明”,而是“流程有多不依赖于模型的聪明”。工程软件最怕的就是不可预测,哪怕解析笨一点,只要结果可控,就能用。

最后分享一个小技巧:我在生成每一份DXF图纸时,会在文件属性或者额外图层里塞一份原始参数JSON,相当于图纸的“元数据”。下次无论过多久,拿到图就能知道当初是哪些参数生成的,需要改的时候直接把JSON调出来改一处,再重新生成一次。这个习惯帮我避免了无数“这个图当初怎么画的来着”的追悔时刻。对于长期维护的图纸库来说,这个“参数留痕”的做法比任何文档都靠谱。如果你想做类似的事情,这就是最好的起步点。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进