
上礼拜一个做机械加工的朋友给我发了条语音让我帮他把一张草图画成CAD图。他描述得挺清楚一块板长120宽80四角R5圆角中心直径12的孔左下角离左边20、离下边10的地方再放一个直径6的孔最后标好尺寸发给他。我打开CAD新建图纸画矩形倒圆角捕捉中心画圆标尺寸十来分钟过去了。事情做完我就在想这十来分钟里真正在“画”的时间不到两分钟剩下八分钟全花在把他的话翻译成参数、再一个个填进命令里。这种需求就是text-to-cad想解决的问题。这个方向听起来像科幻实际上做起来并不玄乎。它的本质不是让AI替你去画一条线而是把一句自然语言描述解析成一套几何参数再用程序自动落成CAD图元。我花了一个周末用Python写了一个能从中文描述生成DXF图纸的小工具跑了十几个真实场景其中也包括开头朋友那段语音。这篇文章把我整个设计思路、踩过的坑、最后能用的代码骨架都整理出来。如果你是刚接触CAD的制图员、做机械或建筑设计的工程师或者想给自己画图工作做点自动化应该能从中拿到一些可直接抄走的方案。1. 图纸不是画出来的是“翻译”出来的1.1 从一段语音需求到一条多段线中间隔了十次快捷键我截了一下自己画那块板的操作顺序新建文件、输入rec画矩形、输入f倒圆角、输入c画圆、打开对象捕捉找圆心、切换图层、用dim标注线性尺寸、用ddi标直径。每一步都不难但每一步都需要把脑子里的信息转成键盘输入。最烦的是改图比如朋友说“孔径改成10”我得找到那个圆删掉重新画还得重新标注。这种场景在工厂里每天都在发生。很多老师傅画图多年手速并不慢慢的是“确认需求”和“反向修改”的循环。CAD软件本身没有理解能力它只认坐标和参数。一个矩形在软件里本质就是“长120、宽80、左下角坐标(0,0)”这几个数字的组合一个圆就是“圆心坐标半径”。所以画图快不快的分水岭不在于鼠标稳不稳而在于你能不能快速把语言需求变成这些数字。还有一个更隐蔽的痛点热词里大量出现“cad安装、cad字体、cad卸载”这类问题说明大部分人的精力其实先耗在了让CAD跑起来这件事上。好不容易安装好、配好字体真正留给设计和画图的时间已经打了折。text-to-cad这类思路能省下来的恰恰是那些最重复、最不需要“设计感”的操作。1.2 text-to-cad的真实定位不是替代CAD而是把话变成参数网上关于“AI生成图纸”的宣传很容易让人误会以为输入一句话就能吐出一整套装配图。以我现在的经验看那句话离成熟还远。更务实的定位是把text-to-cad当作一个“翻译器”它只负责把“人话”翻译成“结构化参数”最终落地到CAD时用的还是传统图元库。这个思路的好处是确定性强。训练一个AI来直接生成图纸文件难度极高而且结果不可控一个尺寸差了0.1mm就是废图。但如果我们只让程序做“规则解析”把“长120宽80”这样明确的描述提取成长宽参数再由ezdxf这类库去生成DXF那么每一步都是可验证可追溯的。数据对了图就一定对。这其实是编译器思路——自然语言是源代码DXF文件是目标机器码中间是一个解释器。当然这也意味着它的能力边界很清晰能处理套路的、参数化的描述处理不了“给我设计一个好看的花瓶”这种开放性需求。至于怎么在规则解析框架内把话说清楚、把参数提取稳就是下一章要讲的核心。2. 自然语言解析把一句话拆成指令、参数、约束2.1 指令识别关键词需要优先级不能谁先出现谁说了算我第一版解析器非常天真拿到一句话就在里面找关键词。“矩形”就画矩形“圆”就画圆“直线”就画直线。结果第一轮测试就翻车了。朋友的原话里有“四角R5圆角”我的程序先匹配到“R”以为这是个半径参数还去画圆了。更惨的是“倒圆角的矩形”这种句子如果先匹配到“圆”就会画一个圆出来和用户的意图差出十万八千里。后来我吸取教训把指令识别分成三层优先级。第一层是核心图形词包括“矩形”“长方形”“圆”“圆弧”“直线”“多段线”。第二层是修饰词像“圆角”“倒角”“阵列”“镜像”。第三层才是参数和约束。程序先找第一层词确定主干图形再找第二层词确定附加操作最后才去提取数字。这个顺序非常重要。比如“四角R5圆角的矩形”程序会先锁定“矩形”再识别“四角”“R5”为圆角操作。即使一句话里同时出现“矩形”和“圆”两个字也能根据层级关系判断谁是主体、谁是附属。我建议做类似工具的读者在解析层千万别省这个优先级逻辑否则后面每加一组关键词就乱一次。2.2 参数提取数字、单位、前后缀的处理套路数字提取是整个解析层里最容易出bug的地方。中文描述里有各种写法长120宽80、长120、宽80、120×80、直径12的孔、孔径12、D12。“长120宽80”没有分隔符直接正则抓数字会抓出一堆来。我的处理方式是分字段提取而不是一次性把所有数字全掏出来。具体做法是给每个字段单独写一条正则规则。比如提取长度就匹配“长”字符后面跟着的数字提取宽度就匹配“宽”字符后面跟着的数字提取直径匹配“直径”或“D”后面跟着的数字。如果描述里没有显式的字段名再退回到“第一个数字是主尺寸、第二个数字是副尺寸”的默认规则。这样“120×80”这种省略写法也能正确处理。单位方面也要提前约定好。机械加工默认单位是毫米大多数描述里不写单位。如果描述里出现“cm”或者“米”要先识别并换算成毫米再进坐标计算。我实际测试时输入“长1.2m”第一版直接当成1.2毫米处理生成的图纸拿到加工厂差点闹笑话。从那以后我定了规矩任何带单位的描述必须显式换算不换算就直接报错提示。2.3 约束处理中心、偏移、旋转这些相对关系才是图纸的灵魂长宽和直径这种直接参数好提取难的是位置约束。朋友说“中心一个孔”“离左边20、离下边10再放一个孔”这里的“中心”“离左边”“离下边”都是相对关系需要先算出基准点再算圆心坐标。我的做法是把约束抽象成三类第一类是绝对约束比如“左下角在原点”直接把基点定到指定坐标第二类是相对约束比如“距离左边20”就是x20“距离下边10”就是y10第三类是几何约束比如“中心”就是x宽度/2、y高度/2。处理时先扫描描述里的约束词把它们收集成一个字典再在下单生成图元的阶段统一计算坐标。这里有个经验不要边解析边算坐标先收集约束再统一算。因为一句描述里可能有多个对象“中心一个孔”和“距左边20的孔”会互相独立边解析边算会把状态搞得一团糟。我第一版就是边解析边计算结果处理到第三个对象时前面对象的位置数据全被覆盖了。重构后先用中间结构存所有待画对象再统一出图代码清晰了很多也方便后续加图元。3. 为什么我选Pythonezdxf而不是挂着AutoCAD写脚本3.1 几个候选方案的真实对比做这个项目之前我研究了四条路线一是用AutoCAD自带的AutoLISP写二是用pyautocad库通过COM控制AutoCAD三是直接用Python生成DXF文件四是把自然语言丢给大模型让它直接输出结果。我对这四条路都做过测试结论非常明确。方案优点缺点是否推荐AutoLISP深度集成CAD能在AutoCAD里直接跑依赖CAD环境跨版本兼容差语法老旧不推荐pyautocad调用封装好的COM接口能直接控制CAD界面必须安装完整版AutoCAD服务器没法用执行慢看场景Pythonezdxf纯Python库不依赖CAD跨平台可批量生成生成的是DXF不是原生DWG强烈推荐大模型直接出图理解自然语言能力强图纸精度不可控难以工程化校验不靠谱核心结论很简单工程图纸这事确定性压倒一切。pyautocad听起来很酷但它要求本机装着AutoCAD用起来等于让AutoCAD当“绘画机器人”速度上没优势还容易被COM连接问题卡住。ezdxf直接读写DXF文件不需要打开任何软件一台普通笔记本上跑一百张图也就是几秒钟的事。3.2 DXF本质上是一份“图纸说明书”看懂它你就成功了一半很多人一听DXF就觉得是个二进制格式其实它是纯文本。用一个文本编辑器打开DXF文件能看到一堆带数字的段落这些数字是组码每个组码代表一种含义。比如0代表“图元开始”8代表“图层名”10代表“X坐标”20代表“Y坐标”100代表“子类标记”。CAD软件打开DXF时就是逐行解析这些组码还原成屏幕上的图形。我一开始没去了解这个结构直接上手写代码结果生成的文件CAD打不开报错信息也看不懂。后来花了一个晚上把ezdxf生成的DXF文件用记事本打开对照着看才算真正弄明白。写图元时ezdxf的modelspace.add_circle(center(x,y), radiusr)这行代码底层其实就是生成一个圆图元把圆心坐标、半径、图层信息写进ENTITIES段。理解了这层后续调图层、改线型、加标注都有据可循不会瞎试。所以如果你想做类似的工具建议先别急着写代码生成一个最简单的DXF文件打开看看里面长什么样再回去调API。这个底子打好了后面所有问题都只是查文档的事。4. 最小实现一个能跑的text-to-cad脚本4.1 解析器骨架下面这段是我精简后的解析器骨架它负责从描述里提取长、宽、圆心偏移和直径。代码不复杂但固定句式都能正确处理。import re import math import ezdxf def parse_dim(desc, key, defaultNone): 提取关键词后面的数字支持中文冒号和空格 m re.search(r(?:%s)[:]?\s*(\d(?:\.\d)?) % key, desc) if m: return float(m.group(1)) if default is not None: return default # 兜底取整句话第一个数字作为主尺寸 nums re.findall(r\d(?:\.\d)?, desc) return float(nums[0]) if nums else 0.0 def parse_circles(desc, width, height): 识别所有圆孔返回圆心坐标和半径列表 circles [] # 提取所有直径 diameters re.findall(r(?:直径|D)\s*(\d(?:\.\d)?)\s*(?:的)?(?:圆|孔), desc) for d in diameters: radius float(d) / 2.0 x, y width / 2.0, height / 2.0 # 默认中心 # 处理距左边xx和距下边xx m re.search(r距左边\s*(\d(?:\.\d)?), desc) if m: x float(m.group(1)) m re.search(r距下边\s*(\d(?:\.\d)?), desc) if m: y float(m.group(1)) circles.append((x, y, radius)) return circles这个解析器的设计思想是“约定优于识别”。它不强求听懂所有句子只处理“直径多少、距左边多少、距下边多少”这种工程上最常见的固定句式。真实工厂里的口头描述其实九成都是这种套路话所以覆盖度已经非常可观。关于正则里那个(?:直径|D)的非捕获分组我特别提醒一下。如果不加?:re.findall返回的就是括号里的分组内容而不是完整匹配。第一次写时我就是踩了这个坑拿到一堆“直径”“D”而不是数字排了半天错后来才发现是正则分组语义问题。4.2 图元生成与图层分配图元生成我统一封装成一个函数所有对象都从这里走。图层命名和颜色按常见制图标准来结构层用红色、中心线层用灰色CENTER线型、标注层用绿色。def draw_plate_with_holes(desc, filenameplate.dxf): doc ezdxf.new(R2010) # 准备工作层 doc.layers.add(结构层, color1) doc.layers.add(中心线, color8, linetypeCENTER) doc.layers.add(标注层, color3) msp doc.modelspace() width parse_dim(desc, 长) height parse_dim(desc, 宽) # 画矩形外框 pts [(0, 0), (width, 0), (width, height), (0, height)] msp.add_lwpolyline(pts, closeTrue, dxfattribs{layer: 结构层}) # 画圆孔和中心线 for x, y, r in parse_circles(desc, width, height): msp.add_circle((x, y), r, dxfattribs{layer: 结构层}) # 中心十字线 k r * 1.4 msp.add_line((x - k, y), (x k, y), dxfattribs{layer: 中心线}) msp.add_line((x, y - k), (x, y k), dxfattribs{layer: 中心线}) # 标注尺寸 dim_style doc.dimstyles.get(EZDXF) dim_style.dxf.dimtxt 2.5 msp.add_linear_dim(base(0, height 5), p1(0, 0), p2(width, 0), distanceheight 5, dxfattribs{layer: 标注层}).render() msp.add_linear_dim(base(-5, 0), p1(0, 0), p2(0, height), distance-5, dxfattribs{layer: 标注层}).render() doc.saveas(filename) return filename这段代码里有几个地方是经过实测才定下来的。第一矩形要用add_lwpolyline而不是四条add_line因为多段线是一个闭合整体后续做偏移和布尔运算方便得多。第二中心线必须单独放一个图层线型设成CENTER否则一张图里圆孔一多全挤在结构层里根本看不清。第三标注样式里的dimtxt是尺寸文字高度默认值往往很大不调的话标注文字比图形还大图面直接废掉。4.3 尺寸标注自动化里的一个隐藏细节尺寸标注是我调试时间最长的一块。初版代码我调用了add_linear_dim但生成的文件在CAD里打开只有两个尺寸界线的端点没有文字也没有尺寸线。查了官方文档才知道ezdxf创建标注对象后必须调用.render()方法它才会实际计算并生成标注的几何图形。另一个容易忽略的是base参数。线性标注的base表示尺寸线所在的位置点它不应该和p1、p2在同一条直线上否则尺寸线会跟被标注边重叠。我习惯把水平尺寸的base放在yheight5的位置也就是比图形高一点点垂直尺寸的base放在x-5的位置也就是图形左侧。这样标注出来图面干干净净不会糊到一起。这里我再分享一个经验标注的图层最好和结构图层严格分开颜色也区分开来。加工厂拿到图纸后经常要单独关掉标注层来出毛坯图或线切割图图层分好能省很多沟通成本。5. 用三句话实测记录过程与翻车修复5.1 测试一矩形圆孔中心线的完整生成我把开头朋友那段语音整理成文字作为第一个测试用例“一块板长120宽80中心一个直径12的孔距左边20距下边10的位置放一个直径8的圆。”程序跑完之后用CAD打开生成的plate.dxf图形完全符合预期120×80的矩形中心位置一个直径12的孔左下侧距离边缘20和10的位置一个直径8的圆中心线也画上了。这个测试让我最惊喜的不是“跑通了”而是“一次就跑通了”。原因是我在前面设计阶段坚持了“先收集约束再统一计算坐标”的思路所有参数解析完成后坐标计算只是简单加减乘除。如果像第一版那样边解析边算坐标遇到“中心”和“距左边”同时出现时坐标就已经被覆盖掉了。这个用例也验证了一个判断固定句式的解析能力对真实工厂语言已经够用。真正为难的不是语法而是那些省略和默认。比如“中心一个孔”机器需要知道孔直径是多少英寸这里靠的是用户提供完整信息。如果描述里没给直径程序就直接报错提示绝不猜一个默认值。这条原则帮我避免了很多次生成废图。5.2 测试二带标注和旋转的场景暴露了什么问题第二句测试是“画一个长100宽50的矩形顺时针旋转30度四角倒R5标注尺寸。”这句一跑完问题全冒出来了。第一个问题是旋转方向。我说的是“顺时针旋转30度”但CAD的默认角度方向是逆时针为正所以程序必须把角度取负。当时我偷懒没处理方向生成的图转了30度但方向完全反了。只能跟用户解释“你描述的顺时针在CAD里其实是负角度”。当然程序里可以做好约定检测到“顺时针”三个字时自动把角度处理成负值。第二个问题是圆角。矩形四角倒圆R5用多段线画矩形时每个角都是一个顶点直接倒圆需要计算每个角的圆弧起点和终点。最初我用多段线的bulge凸度参数实现试了几次发现角度一复杂就出偏差。后来改成把四角拆成四段直线加四段圆弧再拼接成一个整体逻辑直观多了。第三个问题是标注重叠。矩形旋转之后尺寸标注的base点还放在原来的水平方位结果标注线跟旋转后的图形叠在一起读图完全不行。正确做法是先算出旋转后矩形的最小外接矩形再把标注点放在外接矩形的外面。5.3 这轮迭代我改了三处针对上面暴露的问题我做了三处修改。第一解析层增加方向词检测“顺时针”自动把角度乘以负一“逆时针”保持原值。第二圆角矩形改成直线和圆弧拼接方案。第三旋转场景下的标注基准点改为外接矩形坐标。修改后的程序再跑同样一句话生成的图纸在CAD里打开矩形旋转了30度四角圆弧圆润连续标注线清晰地位于图形外侧。这次实际测试让我更确信一件事text-to-cad的难点不在于画图而在于把你没说明白的隐性规则补全。人看图时靠直觉能自动理解“顺时针”和“尺寸不能压住图形”程序没有直觉只能靠你拆成规则补进去。这轮的教训我写成了注释放在代码里任何影响几何位置的修饰词都必须显式定义一个标志位而不是在计算坐标时临时处理。比如“顺时针”是一个方向标志位“倒圆角R5”是一个圆角标志位“四角”是一个范围标志位。全部统一收集再分发给图元生成逻辑。6. 把这类工具真正放进工作流的几点建议6.1 最实用的场景批处理模板和参数化族库我目前使用这个工具最多的场景不是单张画图而是批量出参数类似的图纸。比如一批板件长度从100到200每档间隔10孔径随板长变化如果手动在CAD里改尺寸再另存一个小时都未必做完。用程序做先读取一个CSV每一行填上长、宽、孔径、孔位偏移循环调用draw_plate_with_holes一分钟不到一百张DXF全部生成。第二个实用落地是参数化族库。某一类零件的图纸结构是固定的区别只在几个尺寸参数。我把这类零件的描述模板和变量定义好用户只需填参数表不用碰CAD。比如一个法兰盘模板是“外径D、内径d、螺栓孔数量n、螺栓孔分布圆直径PCD”程序根据这几个参数自动生成完整图纸并标注。这种工作方式比在CAD里建参数化块更灵活也更方便跟数据库、ERP系统对接。我见过不少工程师一听到“编程”两个字就想绕开其实这类工具真正用起来接触代码的只有开发维护的那一个人其他人都只是填表或者改描述文本而已。边际成本非常低。6.2 从DXF回到DWG用ODA File Converter统一转换很多合作工厂指定要DWG格式而Python生态里直接写DWG的库几乎没有靠谱的。我的解决方案是生成DXF后再用ODA File Converter做批量转换。这个软件是免费的支持命令行调用Windows和Linux都有版本。转换命令大概是这样的形式ODAFileConverter input_dir output_dir ACAD2018 DXF 0 1实际参数含义可以在它的官方文档里查我重点提醒两点一是输出版本选ACAD2018之类的高版本兼容性最好二是转换目录里的DXF文件名如果包含中文最好先做一次编码处理否则转换会静默失败。这个问题我排查了很久才找到原因当时生成的图纸在本地CAD里能打开但转换后总缺文件最后发现是文件名编码问题。如果你只是自己看图或内部评审DXF完全够用大多数CAD软件打开DXF和DWG没有体验差别。对外交付时再统一转一次DWG整个流程更干净。6.3 普通用户能吃到多少红利取决于输入方式不取决于CAD写完这套工具后我让单位里一个不怎么会用CAD的同事试了试。他只需要按我给的格式写描述文本“一块板长200宽100四个角R10圆角中心直径20的孔。”然后运行一下脚本几分钟后拿到一张规范标注的图纸。对他来说这不叫编程这叫“照着模板填字”。但我也得说实话目前这套方案只对“参数明确、结构套路化”的图有效。如果描述变成“这里做个支撑结构看着结实一点”程序完全无能为力。所以普通用户如果想直接享受text-to-cad的便利更现实的是用那些已经集成了自然语言输入的在线工具或AI草图工具它们适合出概念图一旦进入交付环节还是得靠参数化脚本和传统CAD做精确控制。另外有件事我想提醒市面上很多所谓“AI一键出图”服务输出的是图片或者简化模型不是真正的CAD文件拿到生产环节是没法用的。判断一个工具是否靠谱就看它输出的格式是不是DXF或DWG坐标数据能不能精确到毫米级。如果只是生成一张效果图那和text-to-cad完全是两回事。最后说点我自己的使用习惯。我现在维护这套脚本把“描述解析、图元生成、DXF输出”三部分拆成三个独立模块绝不揉在一个文件里。原因特别简单需求一定会改。第一次改解析规则第二次改图层命名第三次改标注样式。拆开之后每次改动只动一个文件回归测试也只需要跑对应模块。如果你也想做类似的自动化画图工具我强烈建议一开始就按这个结构来否则项目做到一半再重构成本会高得多。