ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

text-to-cad 实战:从自然语言到参数化模型的工程化落地

text-to-cad 实战:从自然语言到参数化模型的工程化落地 1. 从一段文字到一张图纸text-to-cad 到底在解决什么问题第一次听到 “text-to-cad” 这个词很多人脑子里冒出来的画面大概是对着电脑敲一句“给我画个法兰盘”然后 SolidWorks 或者 FreeCAD 就自动把三维模型吐出来了。这个理解方向没错但真正落到工程实践里它要解决的问题比“省几下鼠标”要深得多。传统 CAD 工作流的本质是人肉翻译需求方用自然语言描述一个零件工程师在脑子里把它翻译成尺寸、约束、特征树再用手一根线一根线地画出来。这个过程中信息损耗极大——需求方说的“差不多 50 毫米”到了图纸上可能是 48 也可能是 52需求方说的“能装进去就行”工程师得自己脑补公差配合。text-to-cad 想干的事情就是把这段“人肉翻译”尽可能自动化输入一段结构化的文字描述输出一份机器可读、可编辑、可制造的几何文件。它涉及的核心技术点其实横跨了好几个领域。自然语言理解负责把“直径 80、厚度 10、中心开 20 的通孔”解析成参数字典参数化建模引擎负责把这些参数变成实际的几何体格式转换层负责把内部表示导出成 STEP、URDF、G-code 这些下游工具能吃的格式。这三个环节任何一个掉链子最终产物都是一堆没法用的垃圾数据。适合关注这个方向的人我大致分成三类。第一类是机械设计工程师手上有大量重复性建模任务比如标准件库、系列化产品想用脚本批量生成第二类是机器人方向的开发者需要频繁生成 URDF 模型做仿真手工写 XML 写到吐第三类是做 CAM 和 3D 打印的玩家想把参数化设计直接接到切片和加工路径生成上。如果你属于这三类中的任何一类下面这些内容应该能帮你少走不少弯路。需要提前说明的是text-to-cad 目前还不是“一句话出成品”的魔法。它更像是一个参数化模板引擎——你给它足够明确的输入它给你稳定可复现的输出。模糊的、带歧义的自然语言它处理起来依然很吃力。所以真正好用的方案往往是在“自然语言”和“结构化参数”之间找一个平衡点。2. 整体架构设计为什么不能直接让大模型画图2.1 直接生成几何体的三条死路我见过不少人的第一反应是既然大模型能写代码那让它直接输出 STEP 文件的文本内容不就行了这个思路我实测过结论是基本不可行原因有三。第一STEP 文件ISO 10303-21是一种基于实体边界表示B-rep的格式一个稍微复杂点的零件其 STEP 文本动辄几千上万行里面全是CARTESIAN_POINT、ADVANCED_FACE、EDGE_CURVE这类底层拓扑实体。大模型在生成这种长序列结构化数据时局部一致性极难保证——前面定义的一个点坐标后面引用时很可能就写错了导致整个文件无法解析。第二就算模型勉强生成了一个能打开的 STEP它也是不可参数化的。你拿到的是一个死掉的几何体想改个孔径对不起重新生成一遍而且大概率生成出来的东西和上一次还不一样。工程上要的是可追溯、可修改的模型不是一次性的雕塑。第三URDF 和 G-code 的生成逻辑完全不同。URDF 描述的是运动学树——连杆、关节、惯性矩阵、碰撞体G-code 描述的是加工路径——刀具轨迹、进给速度、层高。让一个模型同时精通这三种格式的底层语法还要保证几何一致性这在当前技术条件下是强人所难。2.2 分层架构把“理解”和“生成”拆开我最终采用的方案是三层分离架构这也是目前比较主流、比较稳的做法。最上层是语义解析层。这一层可以用大模型也可以用规则引擎甚至可以用一个简单的表单。它的唯一职责是把输入文本转成一个中间参数表示Intermediate Parameter Representation我习惯叫它 IPR。IPR 是一个纯 JSON 结构里面只有数字、字符串和布尔值不涉及任何几何实体。比如{ part_type: flange, outer_diameter: 80.0, inner_diameter: 20.0, thickness: 10.0, bolt_hole_count: 4, bolt_hole_diameter: 8.0, bolt_circle_diameter: 60.0, material: steel }中间层是参数化建模层。这一层用代码Python 的 CadQuery、build123d或者 FreeCAD 的脚本接口读取 IPR调用几何内核OpenCASCADE生成实体。这一层是确定性的——同样的 IPR 输入永远得到同样的几何输出。这是整个系统可靠性的基石。最下层是格式导出层。根据目标应用把生成的实体导出成 STEP用于 CAD 交换、URDF用于机器人仿真、STL用于 3D 打印或 G-code用于 CNC 加工。这一层只做格式转换不做几何修改。提示把大模型限制在“语义解析层”是保证整个系统稳定性的关键决策。大模型可以犯错但它的错误只会影响参数提取不会污染几何生成。参数错了改参数就行几何错了整个模型都得推倒重来。2.3 为什么选 CadQuery 而不是直接调 OpenCASCADE几何内核的选择上我对比过三条路线直接调 OpenCASCADE 的 C API、用 FreeCAD 的 Python 脚本接口、用 CadQuery。直接调 OCCT 的 C API 性能最好但开发效率极低一个简单的拉伸特征要写几十行样板代码调试成本太高。FreeCAD 的脚本接口功能全但它的 API 设计比较老旧文档散乱而且 FreeCAD 本身的启动和依赖比较重做服务化部署不太方便。CadQuery 是我最终的选择。它基于 OCCT但提供了一套链式调用的 Python DSL写起来非常接近“说话”import cadquery as cq result ( cq.Workplane(XY) .circle(40) .circle(10) .extrude(10) .faces(Z) .workplane() .polarArray(30, 0, 360, 4) .hole(8) )这段代码生成的就是上面那个法兰盘。可读性极强改参数就是改数字非常适合和 IPR 对接。而且 CadQuery 支持导出 STEP、STL、DXF 等多种格式生态比较完整。3. 核心细节拆解从文本到参数的映射规则3.1 尺寸描述的解析策略自然语言里描述尺寸的方式五花八门“直径 80”、“外径 80 毫米”、“80mm 外圆”、“外圈 80”。如果全靠大模型去理解稳定性不够。我的做法是规则优先模型兜底。先定义一套正则规则覆盖最常见的表达模式import re PATTERNS { outer_diameter: [ r外[径圆]\s*[为是]?\s*(\d(?:\.\d)?)\s*(?:mm|毫米)?, r直径\s*[为是]?\s*(\d(?:\.\d)?)\s*(?:mm|毫米)?, r[Φφ]\s*(\d(?:\.\d)?), ], thickness: [ r[厚高]\s*度?\s*[为是]?\s*(\d(?:\.\d)?)\s*(?:mm|毫米)?, r板厚\s*[为是]?\s*(\d(?:\.\d)?), ], }规则能匹配上的直接提取不走模型。匹配不上的再交给大模型做语义理解但要求它输出固定 schema 的 JSON而不是自由文本。这样即使模型理解有偏差输出格式也是可控的。注意单位统一是踩坑重灾区。用户可能说“8 个的孔”意思是 8 毫米也可能说“0.8 的孔”意思是 0.8 毫米。我的做法是在 IPR 里强制统一为毫米解析时如果发现数值小于 1 且没有明确单位就弹出一个确认提示让人工介入。这个“人机确认”环节看起来笨但能避免大量低级错误。3.2 特征顺序与依赖关系参数提取出来之后还有一个容易被忽略的问题特征的生成顺序。法兰盘是先拉伸外圆再打孔还是先打孔再拉伸结果看起来一样但特征树的结构不同后续修改的便利性也不同。我的原则是从大到小从外到内先做基础实体外轮廓拉伸再做减材料特征孔、槽、倒角。这样特征树清晰而且减材料特征可以随时抑制掉方便调试。对于更复杂的零件特征之间可能存在依赖关系。比如“在凸台上打孔”就必须先有凸台才能打孔。我在 IPR 里用一个features数组来显式表达顺序{ features: [ {type: extrude, profile: circle, diameter: 80, height: 10}, {type: hole, diameter: 20, position: center}, {type: polar_hole, diameter: 8, count: 4, pcd: 60} ] }建模层按数组顺序依次执行逻辑非常直白。3.3 公差与配合信息的处理工程图纸上的公差标注比如Φ20 H7、Φ20 0.021/0在 text-to-cad 里是个难点。严格来说公差信息不影响几何体的标称形状但影响下游的加工和装配分析。我的处理方式是分离存储几何体只生成标称尺寸公差信息作为元数据附加在 IPR 里导出 STEP 时写入TOLERANCE字段如果目标格式支持导出 URDF 时忽略因为仿真通常不需要导出 G-code 时根据公差决定加工策略比如 H7 的孔需要精铰普通孔钻一下就行。{ hole: { nominal_diameter: 20.0, tolerance: {type: H7, lower: 0.0, upper: 0.021} } }这样做的好处是几何模型保持干净公差信息不会污染几何内核但下游需要的时候又能拿到。4. 实操过程从零搭一个可用的 text-to-cad 流水线4.1 环境准备与依赖安装我用的技术栈是 Python 3.10 CadQuery 2.4 FastAPI做服务化 Pydantic做数据校验。安装命令如下pip install cadquery2.4.0 pip install fastapi uvicorn pydantic pip install ocp # OpenCASCADE 的 Python 绑定CadQuery 会自动装CadQuery 的安装在不同平台上坑比较多。Windows 上建议直接用 conda 装pip 装有时候会缺 OCCT 的动态库conda install -c conda-forge cadqueryLinux 上 pip 装一般没问题但需要确保系统里有libgl1和libglu1-mesa否则导入 CadQuery 时会报 OpenGL 相关的错误。提示如果你只是想做原型验证不想折腾环境可以用 CadQuery 的在线编辑器 CQ-editor它把 OCCT 和 Python 环境都打包好了开箱即用。但要做服务化部署还是得在本地把环境搭起来。4.2 语义解析层的实现语义解析层的入口是一个 FastAPI 接口接收文本返回 IPRfrom fastapi import FastAPI from pydantic import BaseModel app FastAPI() class TextInput(BaseModel): text: str class IPROutput(BaseModel): part_type: str params: dict app.post(/parse, response_modelIPROutput) def parse_text(input: TextInput): # 先走规则 ipr rule_based_parse(input.text) if ipr is None: # 规则失败走模型 ipr llm_based_parse(input.text) return ipr规则解析的部分我写了一个rule_based_parse函数用前面提到的正则模式去匹配。匹配成功且所有必填参数都齐了就直接返回否则返回None触发模型兜底。模型兜底的部分我用的是一个本地部署的小模型7B 级别prompt 里明确要求输出 JSON并且给了几个 few-shot 示例。实测下来对于“直径 80、厚 10、中心孔 20、四个 8 个的孔分布在直径 60 的圆上”这种描述模型能稳定提取出正确的参数。4.3 参数化建模层的实现建模层的核心是一个build_part函数接收 IPR返回 CadQuery 的 Workplane 对象import cadquery as cq def build_flange(params): outer_d params[outer_diameter] inner_d params[inner_diameter] thickness params[thickness] bolt_count params[bolt_hole_count] bolt_d params[bolt_hole_diameter] pcd params[bolt_circle_diameter] result ( cq.Workplane(XY) .circle(outer_d / 2) .circle(inner_d / 2) .extrude(thickness) ) if bolt_count 0: result ( result.faces(Z) .workplane() .polarArray(pcd / 2, 0, 360, bolt_count) .hole(bolt_d) ) return result这段代码的逻辑很直白先在 XY 平面上画两个同心圆外圆和内孔拉伸成实体然后在顶面上用极坐标阵列打一圈螺栓孔。polarArray的第一个参数是阵列半径PCD 的一半第二个是起始角度第三个是总角度第四个是数量。4.4 格式导出层的实现导出 STEP 最简单cq.exporters.export(result, flange.step)导出 STL 用于 3D 打印cq.exporters.export(result, flange.stl, tolerance0.01)tolerance控制网格精度值越小网格越密文件越大。对于一般 3D 打印0.01 毫米的精度足够了。导出 URDF 稍微麻烦一点因为 URDF 需要定义连杆和关节。对于单个零件可以把它作为一个连杆导出def export_urdf(shape, name, mass, inertia): urdf_template f?xml version1.0? robot name{name} link name{name}_link visual geometry mesh filename{name}.stl/ /geometry /visual collision geometry mesh filename{name}.stl/ /geometry /collision inertial mass value{mass}/ inertia ixx{inertia[0]} ixy0 ixz0 iyy{inertia[1]} iyz0 izz{inertia[2]}/ /inertial /link /robot with open(f{name}.urdf, w) as f: f.write(urdf_template)惯性矩阵的计算可以用 CadQuery 的Shape.matrixOfInertia()方法拿到但要注意单位换算——URDF 默认用千克和米而 CAD 里通常用毫米需要除以 1000 的平方。导出 G-code 是最复杂的一环因为需要 CAM 规划。我的做法是先用 CadQuery 导出 STL然后用 PyCAM 或者 FreeCAD 的 Path 模块生成刀具路径。这部分内容比较多后面单独展开。4.5 完整流水线的串联把三层串起来就是一个完整的 text-to-cad 服务app.post(/generate) def generate(input: TextInput): ipr parse_text(input) shape build_part(ipr) cq.exporters.export(shape, output.step) cq.exporters.export(shape, output.stl) return {status: ok, files: [output.step, output.stl]}实测下来从发送文本到拿到 STEP 文件整个流程在普通笔记本上大约 2 到 5 秒其中大部分时间花在 OCCT 的几何运算上语义解析只占几百毫秒。5. 常见问题与排查技巧实录5.1 几何内核报错BRep_API: command not done这是 CadQuery 用户遇到频率最高的错误之一。原因通常是布尔运算失败——比如两个面完全重合、或者孔的位置超出了实体范围。排查思路先检查参数是否合理。比如外径 80、内径 20、螺栓孔分布在直径 60 的圆上螺栓孔直径 8。螺栓孔的外边缘在 60/2 8/2 34内边缘在 60/2 - 8/2 26都在外径 40 和内径 10 之间没问题。但如果 PCD 改成 70螺栓孔外边缘就跑到 39离外径 40 只剩 1 毫米几何内核在计算布尔运算时可能因为容差问题失败。解决办法在 IPR 校验层加一个几何可行性检查确保所有特征都在实体范围内且特征之间留有足够的间隙我一般要求至少 0.5 毫米。5.2 STEP 导出后在其他 CAD 里打不开这个问题通常和单位有关。CadQuery 默认用毫米导出的 STEP 也是毫米。但有些 CAD 软件比如某些版本的 SolidWorks默认用米导入时会把模型缩小 1000 倍看起来就像“打不开”。排查方法用文本编辑器打开 STEP 文件搜索SI_UNIT看看单位定义是什么。如果是MILLI和METRE那就是毫米没问题。如果导入后模型尺寸不对在导入设置里手动指定单位为毫米即可。另一个可能的原因是 STEP 版本兼容性。CadQuery 默认导出 AP214 版本有些老 CAD 只认 AP203。可以在导出时指定cq.exporters.export(shape, output.step, opt{write_pcurves: False})5.3 URDF 导入 CoppeliaSim 后模型位置不对URDF 里的坐标系原点和 CAD 里的坐标系原点往往不一致。CAD 里通常以零件底面中心为原点而 URDF 的连杆坐标系默认在关节位置。导入 CoppeliaSim 后模型可能“飘”在空中或者“陷”在地里。解决办法在 URDF 的visual和collision标签里加origin偏移visual origin xyz0 0 -0.005 rpy0 0 0/ geometry mesh filenameflange.stl/ /geometry /visual这里的-0.005表示把模型沿 Z 轴下移 5 毫米假设厚度是 10 毫米原点在顶面中心。具体偏移量需要根据你的建模原点来定。5.4 G-code 生成后加工出来的零件尺寸偏大这是 CAM 环节的经典问题原因通常是刀具半径补偿没做对。如果你用直径 6 毫米的铣刀沿着轮廓走实际切出来的轮廓会比设计轮廓大 3 毫米刀具半径。正确的做法是在生成路径时做刀具半径补偿让刀具中心沿着“设计轮廓向外偏移刀具半径”的路径走。在 FreeCAD 的 Path 模块里这个补偿是自动做的但需要正确设置刀具直径。如果你自己写 G-code 生成器记得在计算路径点时加上补偿。问题现象可能原因排查方法解决方案BRep_API 报错布尔运算失败检查特征是否超出实体范围加几何可行性校验STEP 导入后尺寸不对单位不一致检查 STEP 里的 SI_UNIT导入时指定毫米URDF 模型位置偏移坐标系原点不一致对比 CAD 和 URDF 的原点定义加 origin 偏移G-code 加工尺寸偏大刀具半径补偿缺失检查路径是否沿设计轮廓加刀具半径补偿模型生成速度慢几何运算复杂用 cProfile 分析耗时简化特征树减少布尔运算提示几何内核的报错信息通常很晦涩比如Standard_Failure、StdFail_NotDone。遇到这类错误不要试图去读懂错误信息直接检查参数和特征顺序90% 的问题都出在这两个地方。6. 进阶玩法批量生成与参数扫描6.1 用 Python 批量修改 CAD 参数text-to-cad 真正体现效率优势的场景是批量生成系列化零件。比如你要生成 100 个不同尺寸的法兰盘手工建模得画到天荒地老用脚本就是改个循环的事。import csv with open(flange_specs.csv) as f: reader csv.DictReader(f) for i, row in enumerate(reader): params { outer_diameter: float(row[outer_d]), inner_diameter: float(row[inner_d]), thickness: float(row[thickness]), bolt_hole_count: int(row[bolt_count]), bolt_hole_diameter: float(row[bolt_d]), bolt_circle_diameter: float(row[pcd]), } shape build_flange(params) cq.exporters.export(shape, fflange_{i:03d}.step)这段代码读取一个 CSV 文件每行是一组参数循环生成 STEP 文件。实测下来生成 100 个法兰盘大约需要 3 到 5 分钟平均每个 2 到 3 秒。如果参数变化不大OCCT 还能复用一些中间计算结果速度会更快。6.2 参数扫描与优化更进一步你可以把 text-to-cad 和参数优化结合起来。比如你要设计一个支架要求在满足强度的情况下重量最轻。可以写一个循环扫描不同的板厚和孔径生成模型导出 STL然后用有限元分析工具比如 CalculiX算应力找到最优解。best_weight float(inf) best_params None for thickness in [5, 6, 8, 10, 12]: for hole_d in [6, 8, 10]: params {thickness: thickness, hole_d: hole_d, ...} shape build_bracket(params) weight shape.Volume() * 7.85e-6 # 钢的密度单位 kg/mm³ if weight best_weight: # 这里应该加应力校验省略 best_weight weight best_params params这个思路在轻量化设计里非常实用。传统做法是手工改参数、重新建模、重新分析一轮下来半小时。用脚本自动化一轮几秒钟一晚上能跑几千组。6.3 和 CAD 插件生态的衔接如果你日常用的是中望 CAD、AutoCAD 或者浩辰 CADtext-to-cad 生成的 STEP 文件可以直接导入。但如果你想让生成的模型自动带上图层、标注、标题栏这些“图纸信息”就需要写一些插件脚本。以 AutoCAD 为例可以用 Python 的pyautocad库操作from pyautocad import Autocad acad Autocad(create_if_not_existsTrue) acad.model.InsertBlock((0, 0, 0), flange.dwg, 1, 1, 1, 0)这段代码把生成的 DWG 文件作为块插入到当前图纸里。更复杂的操作比如自动标注尺寸、生成 BOM 表需要调用 AutoCAD 的 ActiveX 接口代码量会大一些但思路是一样的。注意不同 CAD 软件的二次开发接口差异很大。AutoCAD 用 ActiveX 和 .NET中望 CAD 用 ZRX 和 LISPFreeCAD 用 Python。如果你的工作流涉及多个 CAD 平台建议把 text-to-cad 的输出统一成 STEP 或 IGES 这种中性格式然后在各个平台里分别写导入脚本。7. 我踩过的坑和几条实用建议第一个坑是过度依赖大模型。我一开始想让模型直接从文本生成 CadQuery 代码结果生成的代码十次有三次跑不通跑通的里面又有两次几何不对。后来改成“模型只提取参数代码模板固定”稳定性立刻上来了。这个教训让我明白大模型适合做模糊匹配不适合做精确生成。第二个坑是忽略单位换算。有一次生成 URDF 时忘了把毫米转成米导入仿真环境后模型大了 1000 倍整个场景都被撑爆了。后来我在 IPR 里强制标注单位所有数值都带unit字段转换时统一处理再也没出过这个问题。第三个坑是STEP 文件的版本兼容性。我生成的 STEP 在 FreeCAD 里打开正常发给同事用 SolidWorks 打开就报错。后来发现是 AP214 和 AP203 的差异。现在我的导出函数默认生成 AP203兼容性最好需要颜色和图层信息时才用 AP214。几条实用建议参数校验一定要做宁可拒绝生成也不要生成一个错误的模型日志要详细每次生成都记录输入参数、耗时、输出文件路径出问题时能快速定位版本管理要跟上生成的模型文件建议用 Git LFS 或者专门的 PLM 系统管理不然文件一多就乱套了。最后分享一个小技巧如果你经常需要生成相似但不完全相同的零件可以做一个参数预设库。把常用的几组参数存成 JSON 文件生成时直接调用预设改一两个参数就行。这个做法看起来简单但实际用起来能省掉大量重复输入的时间。
返回列表