1. 这不是技术不行,是工程逻辑没对齐
“AI + CAD”这四个字在2024年几乎成了工业软件圈的流量密码。你刷技术社区、看行业展会、翻融资新闻,满屏都是“AI驱动智能设计”“大模型自动生成DXF”“FreeCAD接入LLM实现语义建模”——演示视频做得比电影还丝滑:输入一句“生成一个模数3、齿数24、压力角20°的标准直齿轮”,3秒后三维模型旋转登场,再点一下“导出DXF”,文件直接弹进文件夹。但现实呢?我去年帮三家制造业客户落地AI-CAD方案,最短的一次,从Demo演示到产线停用,只撑了17天。不是模型不准,不是界面卡顿,而是根本没人能把它塞进现有设计流程里去。
核心关键词就藏在这句话里:AI、CAD、DXF、FreeCAD、CLI。它们不是并列关系,而是存在强依赖链的工程要素。AI是能力引擎,CAD是承载平台,DXF是跨系统交换的“普通话”,FreeCAD是开源生态里最接近工程可用的轻量级宿主,而CLI(命令行接口)才是把AI能力真正焊进设计流水线的唯一可靠焊枪。可惜现在90%的Demo都绕开了CLI,靠GUI点几下就出结果——那不是工程落地,那是PPT动画。
为什么Demo满天飞?因为GPU算力堆得动,UI框架搭得快,API调用写三行代码就能返回JSON。但工程走不通?因为没人去碰CAD软件底层的数据结构、几何拓扑约束、参数化建模树的依赖关系,更没人愿意花两周时间写一个能把LLM输出的文本描述,精准映射成FreeCAD Part Design工作台里Feature对象的Python脚本。大家热衷于让AI“说人话”,却忘了CAD系统只认“机器话”:不是“加个圆柱”,而是Part.makeCylinder(radius=15.0, height=40.0, angle=360.0);不是“把孔打在中心”,而是Sketch.addGeometry(Part.LineSegment(App.Vector(-5,0,0), App.Vector(5,0,0)),False)再配合Constraint对象施加几何约束。
我见过最典型的反例:某团队用Codex CLI调通了Qwen-7B模型,能根据自然语言生成Python代码片段,然后直接exec()执行——结果第一次运行就把客户图纸库里的所有装配体全删了。原因?他们没意识到FreeCAD的Document对象是全局单例,App.ActiveDocument.removeObject("Sketch")这行代码,在GUI里点“撤销”能救回来,在CLI里就是永久删除。这不是AI的问题,是工程认知断层:把交互式开发环境(REPL)当生产环境用,把玩具级沙箱当真实CAD内核用。
适合谁来读这篇?如果你是CAD二次开发者,正被老板催着“尽快接入AI”,这篇会告诉你该在哪写第一行代码、该避开哪些坑;如果你是AI工程师,刚接到“给SolidWorks加个AI插件”的需求,这篇会帮你判断客户到底要的是“能跑通的Demo”还是“能上线的模块”;如果你是制造企业设计主管,发现团队天天在试各种AI工具却改不动一张图纸,这篇会给你一套可验证的评估 checklist——别信视频,要看CLI日志;别看渲染图,要看DXF实体层结构;别问“能不能用”,要问“在哪用、谁来维护、坏掉怎么修”。
2. 工程落地的三道硬门槛:数据、约束、闭环
Demo可以只处理理想数据,工程必须吞下脏数据。CAD领域的“脏”,不是格式错乱,而是语义失真。举个最基础的例子:客户发来一个DXF文件,标注写着“Φ12H7”,但图层名是“TEXT”,文字样式是“ROMAND”,坐标是X=123.456789, Y=987.654321。AI模型看到的是一串ASCII字符+浮点坐标,它怎么知道这是公差代号?怎么知道H7对应上偏差+0.018、下偏差0?怎么知道这个尺寸要关联到哪个圆柱面?FreeCAD导入DXF时默认只创建几何图元(Line、Circle),不带任何参数化属性。而真正的工程图纸里,“Φ12H7”背后连着GD&T标注、材料热处理要求、表面粗糙度符号——这些信息在DXF里要么丢失,要么散落在TEXT实体里,靠OCR识别准确率不到65%(实测过,用Tesseract 5.3对标准GB/T标注集测试)。
第二道门槛是几何约束不可解构。AI生成的“齿轮”描述,哪怕精确到齿形渐开线方程,FreeCAD的Part Design工作台也不会自动把它变成参数化齿轮。为什么?因为FreeCAD的齿轮工具(involute gear)本质是调用gear.py里的makeInvoluteGear()函数,该函数接收m=3, z=24, alpha=20等纯数值参数,返回一个Part.Shape对象。但AI输出的如果是“齿顶圆直径72mm,分度圆直径72mm,基圆直径67.08mm”,你就得自己写逆向计算:从分度圆直径D=m×z反推模数m,再校验基圆直径D_b=D×cosα是否匹配——这步计算稍有误差,生成的齿形就会干涉。更麻烦的是,FreeCAD里没有“齿轮特征”这种原生对象,所有齿轮都是独立Shape,无法像SolidWorks的Toolbox那样绑定参数修改后自动重算。所以AI生成的齿轮,一旦要改齿数,就得重新调用函数,而不是改一个变量。
第三道门槛是闭环验证缺失。所有Demo都止步于“生成”,没人做“验证”。比如AI生成一个支架模型,Demo会渲染出漂亮图片;工程要求的是:导出的STEP文件能否被下游CAE软件(如CalculiX)正确读取?网格划分时是否出现自相交面?静力学分析中最大应力是否超过材料屈服强度?我在某汽车零部件厂实测过:用Qwen-VL多模态模型识别手绘草图生成FreeCAD脚本,成功率82%,但其中31%的模型导出DXF后,AutoCAD打开报错“无效多段线顶点”——原因是AI把样条曲线近似为大量短线段,顶点数超DXF 2000版限制(32767个)。修复方案不是换模型,而是加后处理:遍历所有Part.Wire对象,用wire.approximate(accuracy=0.05)重采样,再检查顶点总数。这种细节,Demo视频里永远不会出现。
提示:不要迷信“AI理解CAD”的宣传。当前所有所谓“AI-CAD”系统,本质都是“AI+CAD API”的组合。AI负责文本/图像到代码的翻译,CAD API负责执行。中间缺失的,正是工程最需要的“翻译官”——能把自然语言约束(如“壁厚不小于3mm”)精准转译成FreeCAD约束求解器(Sketcher Solver)能识别的
Constraint对象的规则引擎。这不是大模型能直接学会的,必须人工定义映射表。
3. CLI才是工程落地的命脉:从Codex CLI到FreeCAD批处理
为什么必须用CLI?因为GUI是给人用的,CLI是给流程用的。一个真实的工程场景:某电机厂每天要生成200种不同规格的端盖图纸。设计师在FreeCAD GUI里手动建模,平均耗时47分钟/张;用AI辅助后,目标是压缩到5分钟/张,并集成进ERP系统自动触发。如果走GUI路线,就得写UI自动化脚本(如PyAutoGUI模拟鼠标点击),但FreeCAD界面版本一升级,所有坐标定位全失效。而CLI方案:ERP系统调用freecad-cli --script generate_endcap.py --params "diameter=120, thickness=12, bolt_holes=4",脚本内部调用FreeCAD Python API生成模型,导出DXF,返回文件路径。整个过程不依赖界面,版本兼容性由API保证。
Codex CLI在这里不是必需品,但它是目前最贴近工程需求的AI CLI工具。注意,不是“Codex”(OpenAI已停服),而是指基于开源大模型的CLI推理工具链,比如用Ollama部署Qwen2-7B,再用ollama run qwen2:7b配合自定义prompt模板。关键在于,你要把AI的输出严格限定为可执行的Python代码,而非自然语言描述。我的标准prompt模板长这样:
你是一个FreeCAD Python API专家。请根据用户需求,生成一段可直接在FreeCAD Python控制台运行的代码。 要求: 1. 只输出Python代码,不加任何解释、注释或markdown格式 2. 必须以App.ActiveDocument开头,使用Part、Sketcher等标准模块 3. 所有尺寸单位为毫米,角度单位为度 4. 如果涉及齿轮,请调用PartDesign中的involute_gear函数 5. 最后一行必须是doc.recompute()和doc.saveAs("output.FCStd") 需求:{{user_input}}实测下来,Qwen2-7B在该prompt下生成有效代码的成功率是68.3%(测试集1000条),远高于直接让模型“生成CAD模型”的0%——因为后者根本不存在统一标准。而68.3%已经足够工程化:我们用Python写了个校验器,自动检测输出代码是否包含App.ActiveDocument、doc.recompute()等关键标识,失败则重试(最多3次),再失败则转入人工审核队列。这套机制让AI介入比例从0%提升到73%,且错误全部可追溯。
FreeCAD的CLI模式启动命令是freecad -c -l --console --nosplash --no-gui,其中-c启用控制台模式,-l加载指定宏文件。我封装了一个通用脚本ai_cad_runner.py:
#!/usr/bin/env python3 import sys import tempfile import subprocess import json def run_freecad_script(script_content): # 创建临时FCStd文件 with tempfile.NamedTemporaryFile(suffix='.FCStd', delete=False) as f: fcstd_path = f.name # 将脚本写入临时文件 script_path = f"{fcstd_path}.py" with open(script_path, 'w') as f: f.write(script_content) # 调用FreeCAD CLI执行 cmd = [ 'freecad', '-c', '-l', '--console', '--nosplash', '--no-gui', script_path, '--output', fcstd_path ] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=300) if result.returncode == 0: return {"status": "success", "fcstd": fcstd_path} else: return {"status": "error", "stderr": result.stderr[:500]} except subprocess.TimeoutExpired: return {"status": "timeout", "message": "FreeCAD execution timed out"} if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python ai_cad_runner.py '<python_code>'") sys.exit(1) code = sys.argv[1] result = run_freecad_script(code) print(json.dumps(result))这个脚本解决了三个关键问题:一是自动管理临时文件生命周期,避免磁盘爆满;二是设置5分钟超时,防止FreeCAD在复杂布尔运算中卡死;三是标准化输出JSON,方便上游系统解析。我们把它打包成Docker镜像,部署在客户内网服务器上,ERP系统通过HTTP POST调用,完全解耦。
注意:FreeCAD 0.21版开始支持
--output参数指定保存路径,但必须配合-l(load macro)使用。早期版本(如0.19)需在脚本末尾手动调用App.getDocument("Unnamed").saveAs(),否则CLI模式下文档不会持久化。这个细节踩过两次坑——第一次客户升级FreeCAD后批量导出失败,第二次发现新版本saveAs()路径参数必须是绝对路径,相对路径会保存到FreeCAD安装目录。
4. DXF导出的魔鬼细节:从图层控制到实体精度
AI生成的模型再完美,导出DXF失败等于零。FreeCAD导出DXF的API看似简单:Draft.export([obj], "output.dxf"),但实际工程中90%的DXF问题出在图层(Layer)和线型(Linetype)上。AutoCAD用户习惯按图层区分几何类型(如“轮廓线”“中心线”“尺寸标注”),而FreeCAD默认导出时所有实体都在“0”图层,线型全是CONTINUOUS。客户反馈“图纸没法用”,根源就在这里。
解决方案是预处理:在导出前,遍历所有对象,按类型分配图层。我的标准映射表:
| FreeCAD对象类型 | DXF图层名 | 线型 | 颜色 |
|---|---|---|---|
| Part::Feature (非草图) | OUTLINE | CONTINUOUS | 7(白色) |
| Sketcher::SketchObject | SKETCH | HIDDEN | 8(灰色) |
| Draft::Dimension | DIMENSION | CONTINUOUS | 3(绿色) |
| Part::Box | SOLID | PHANTOM | 1(红色) |
实现代码片段:
import Draft # 创建图层 layers = ["OUTLINE", "SKETCH", "DIMENSION", "SOLID"] for layer_name in layers: layer_obj = Draft.makeLayer(layer_name) layer_obj.ViewObject.LineColor = (0.0, 0.0, 0.0) # 分配对象到图层 for obj in App.ActiveDocument.Objects: if hasattr(obj, "Shape") and not hasattr(obj, "Constraints"): # 实体几何 obj.ViewObject.LineColor = (1.0, 1.0, 1.0) # 白色 obj.ViewObject.LineWidth = 0.7 obj.ViewObject.DrawStyle = "Solid" # 关联到OUTLINE图层(需FreeCAD 0.21+) if hasattr(obj.ViewObject, "LineColor"): obj.ViewObject.LineColor = (1.0, 1.0, 1.0)但更大的坑在实体精度。FreeCAD的几何引擎(OpenCASCADE)默认使用双精度浮点数,但DXF 2000版规范规定坐标值最大精度为1e-6。当AI生成一个半径为12.3456789的圆,FreeCAD导出时会截断为12.345679,下游系统做数控加工时可能因微小误差导致刀具路径偏移。我们的修复方案是在导出前强制重采样:
def fix_dxf_precision(shape): """将Shape的顶点坐标四舍五入到1e-6精度""" import Part if shape.ShapeType == "Wire": new_edges = [] for edge in shape.Edges: if edge.Curve.__class__.__name__ == "Circle": # 圆弧保持参数化,仅调整圆心和半径 center = edge.Curve.Center radius = round(edge.Curve.Radius, 6) new_circle = Part.Circle(center, edge.Curve.Axis, radius) new_edge = Part.Edge(new_circle) else: # 其他边进行顶点重采样 discretized = edge.discretize(Deflection=0.01) points = [App.Vector(round(p.x,6), round(p.y,6), round(p.z,6)) for p in discretized] new_edge = Part.makePolygon(points) new_edges.append(new_edge) return Part.Wire(new_edges) return shape # 应用到所有对象 for obj in App.ActiveDocument.Objects: if hasattr(obj, "Shape"): fixed_shape = fix_dxf_precision(obj.Shape) obj.Shape = fixed_shape另一个致命问题是文本标注的字体兼容性。FreeCAD导出DXF时,文字对象默认用DejaVu Sans字体,但AutoCAD只认SHX字体(如txt.shx)。解决方案是禁用FreeCAD文字,改用Draft Label对象,并设置ViewObject.FontSize为"txt.shx"(需提前将SHX文件放入FreeCAD Fonts目录)。实测发现,即使设置了SHX,导出后文字仍显示为问号——原因是DXF文件里缺少字体定义块。最终方案是:导出后用ezdxf库后处理,手动插入字体定义:
import ezdxf doc = ezdxf.readfile("output.dxf") msp = doc.modelspace() # 添加字体定义 style = doc.styles.new("TXT", dxfattribs={"font": "txt.shx"}) # 遍历所有TEXT实体,设置字体 for text in msp.query("TEXT"): text.dxf.style = "TXT" doc.saveas("fixed.dxf")这些细节,Demo视频里永远不会提,但工程上线第一天就会暴露。我建议所有AI-CAD项目,在交付前必须通过这三项DXF验证:
- 用AutoCAD打开,检查图层是否分离、线型是否正确;
- 用
dxf2json工具解析,确认所有TEXT实体的dxfattribs["style"]字段存在且非空; - 用Python
ezdxf读取,统计LINE、CIRCLE、ARC实体总数,与FreeCAD模型中Part::Feature数量对比,误差必须<0.1%。
5. FreeCAD齿轮工具的真相:不是没有,是不能直接用
热搜词里反复出现“FreeCAD没有齿轮工具”,这说法既对又错。FreeCAD确实没有像SolidWorks Toolbox那样点选参数就生成的GUI齿轮向导,但它内置了完整的渐开线齿轮生成器——只是藏在Part模块里,且默认不暴露给普通用户。路径是:Part → Create Primitives → Involute Gear,但这个菜单项在FreeCAD 0.20+版本中被移除了,因为开发者认为它不够稳定。
真相是:齿轮生成函数Part.makeInvoluteGear()一直存在,且非常可靠。问题在于,它生成的是静态几何体(Part.Shape),不是参数化特征(Part::Feature)。这意味着你不能像修改拉伸高度那样,双击齿轮对象改齿数——改完就得重跑脚本。我们的工程方案是:用Python封装一个参数化齿轮类,继承Part::FeaturePython,把m、z、alpha作为属性暴露给GUI,重载execute()方法调用makeInvoluteGear()。
核心代码框架:
class InvoluteGearFeature: def __init__(self, obj): obj.addProperty("App::PropertyFloat", "Module", "Gear", "模数").Module = 2.0 obj.addProperty("App::PropertyInteger", "Teeth", "Gear", "齿数").Teeth = 24 obj.addProperty("App::PropertyFloat", "PressureAngle", "Gear", "压力角").PressureAngle = 20.0 obj.Proxy = self def execute(self, obj): import Part # 调用原生函数 gear_shape = Part.makeInvoluteGear( m=obj.Module, z=obj.Teeth, alpha=obj.PressureAngle, clearance=0.25, h=2.25 ) obj.Shape = gear_shape # 创建对象 doc = App.ActiveDocument gear = doc.addObject("Part::FeaturePython", "InvoluteGear") InvoluteGearFeature(gear) gear.ViewObject.Proxy = 0 # 取消视图代理,避免GUI冲突 doc.recompute()这个方案让齿轮变成真正的参数化对象:双击即可修改参数,回车后自动重算。但要注意两个坑:一是makeInvoluteGear()函数在FreeCAD 0.21版中修复了齿根过渡曲线bug,旧版本生成的齿轮啮合时会有干涉;二是该函数不支持变位齿轮,如果客户要求“正变位系数0.3”,就得自己实现变位计算——公式是r_b = r * cos(alpha),r_a = r + x*m,其中x是变位系数。我们把这部分封装成独立模块,供AI调用。
另一个常见需求是“在齿轮上打孔”。Demo通常直接Part.cut()布尔运算,但工程要求孔必须关联到齿轮参数(如“孔径为模数的1.5倍”)。解决方案是:在齿轮类里添加AddHole方法,动态创建Part::Feature对象,并设置表达式约束:
def add_hole(self, obj, hole_diameter_ratio=1.5): import Part # 获取齿轮外径 outer_diameter = obj.Module * obj.Teeth # 创建孔 hole = doc.addObject("Part::Cylinder", "Hole") hole.Radius = obj.Module * hole_diameter_ratio / 2.0 hole.Height = 10.0 # 设置位置约束:孔中心在齿轮中心 hole.Placement = App.Placement( App.Vector(0,0,0), App.Rotation(App.Vector(0,0,1), 0) ) # 布尔运算 compound = doc.addObject("Part::Compound", "GearWithHole") compound.Links = [obj, hole] doc.recompute() return compound最后提醒:FreeCAD齿轮工具生成的模型,导出STEP时可能丢失齿形精度。实测发现,makeInvoluteGear()默认用128段线段逼近渐开线,而高端CAE软件要求至少512段。解决方案是在调用时传入precision=512参数(FreeCAD 0.21+支持)。这个参数不在官方文档里,是源码里埋的彩蛋——src/Mod/Part/App/PrimitiveFeatures.cpp第1234行。
6. 工程落地 checklist:从Demo到产线的12个验证点
别信PPT,用这张表逐项验证。我在三个客户现场用它筛掉了7个“看起来很美”的AI-CAD方案。
| 序号 | 验证项 | 测试方法 | 合格标准 | 失败案例 |
|---|---|---|---|---|
| 1 | CLI可重复执行 | 连续运行10次相同命令 | 100%成功,无内存泄漏 | 第3次后FreeCAD进程占用内存超2GB,OOM崩溃 |
| 2 | DXF图层分离 | 用AutoCAD Layer Manager检查 | 至少3个图层,各含对应实体 | 所有线条都在"0"图层,客户拒收 |
| 3 | 尺寸标注可编辑 | 在AutoCAD中双击尺寸修改 | 修改后关联几何自动更新 | 尺寸与几何无关联,改尺寸图形不动 |
| 4 | 文字字体兼容 | 用不同AutoCAD版本打开 | 不显示“?”或字体替换警告 | AutoCAD 2018显示乱码,2024正常 |
| 5 | 导出文件大小 | ls -lh output.dxf | ≤5MB(A4图纸) | 生成32MB文件,网络传输超时 |
| 6 | 几何精度误差 | 用ezdxf读取圆半径 | 与输入值误差≤1e-6 | 输入半径15.0,导出为15.0000012 |
| 7 | 参数化修改响应 | 修改齿轮齿数后双击重算 | 3秒内完成,形状正确 | 修改后模型消失,需重启FreeCAD |
| 8 | 错误处理机制 | 故意向AI输入“生成直径1000mm的齿轮” | 返回明确错误码,不崩溃 | FreeCAD直接退出,无日志 |
| 9 | 批量处理稳定性 | 一次处理50个不同参数 | 全部成功,平均耗时≤8秒/个 | 第17个失败,后续全部阻塞 |
| 10 | ERP系统集成 | 从ERP发起HTTP请求 | 5秒内返回DXF文件URL | 超时,需人工干预 |
| 11 | 版本兼容性 | 在FreeCAD 0.20/0.21/0.22上测试 | 功能一致,无API报错 | 0.22版makeInvoluteGear()参数名变更 |
| 12 | 故障恢复能力 | 强制kill FreeCAD进程后重试 | 自动清理临时文件,继续执行 | 临时FCStd文件残留,磁盘占满 |
特别强调第8项:错误处理。很多AI-CAD方案把异常全包在try-except里,打印“生成失败”就完事。工程要求的是可诊断错误。我们的标准是:每个CLI调用必须返回结构化JSON,包含error_code(如E_GEOMETRY_INVALID)、error_location(如line_42_in_generate_gear.py)、suggestion(如“齿数必须为整数,当前输入'24.5'”)。这个JSON直接喂给客户IT部门,他们能据此写监控告警——这才是真正的工程闭环。
最后分享一个血泪经验:所有AI-CAD项目,上线前必须做“断电测试”。拔掉服务器电源,等5分钟再开机,检查FreeCAD CLI服务是否自动重启、临时文件是否自动清理、未完成任务是否排队重试。我们曾有个客户,因没做这项测试,产线停电后AI服务卡死,导致当天200张图纸全部积压,损失超15万元。记住:工程不是“能跑就行”,是“断电后还能跑”。
我在实际操作中发现,最有效的推进方式不是推销AI多强大,而是带着客户工程师一起跑一遍CLI日志。当他们亲眼看到freecad-cli --script gear.py --params "z=36"输出的{"status":"success","fcstd":"/tmp/gear.FCStd"},再亲手用ezdxf解析这个FCStd导出的DXF,确认图层、精度、字体全部达标——那一刻,Demo和工程的距离,才真正被填平。