ARTICLE DETAIL

资讯详情

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

text-to-cad实战:从自然语言到STEP/URDF/G-code的完整技术拆解

text-to-cad实战:从自然语言到STEP/URDF/G-code的完整技术拆解 CAD 这行当有个特别拧巴的地方设计师脑子里的三维结构要变成机器能读的几何文件中间隔着一整套鼠标点击、草图约束、拉伸旋转的手工流程。一个中等复杂度的零件从想法到能用的 STEP 文件熟练工也得折腾小半天。而 text-to-cad 想干的事情就是把这套流程压缩成一句话——你用自然语言描述你要什么系统直接吐出可用的 CAD 几何文件。这件事在 2023 年之后突然变得可行核心原因是 LLM 对空间语义的理解能力上来了加上 OpenCASCADE 这类几何内核有了成熟的 Python 绑定。我前后折腾了几个月从最早的让 GPT 写 CadQuery 脚本到后来搭出一套能跑通 STEP、URDF、G-code 三条输出链路的原型踩的坑比想象中多得多。这篇就把整个思路、技术选型、实操细节和那些文档里不会写的经验完整摊开讲一遍。1. 先搞清楚 text-to-cad 到底在解决什么问题1.1 传统 CAD 工作流的瓶颈在哪要理解 text-to-cad 的价值得先看清楚传统 CAD 流程到底卡在什么地方。一个典型的机械零件设计流程是这样的打开 SolidWorks 或 Fusion 360新建草图画轮廓加约束标注尺寸退出草图拉伸或旋转成实体然后倒角、打孔、做阵列最后导出 STEP 给下游做仿真或 CAM。这套流程本身没问题问题在于它的操作密度极高但信息密度极低。一个直径 50mm、高 30mm、中心通孔直径 10mm 的圆柱体在自然语言里是 20 个字在 CAD 里是十几个操作步骤加五六个尺寸标注。这种信息密度差带来的直接后果就是设计意图的传递成本远高于设计本身的价值。你脑子里想清楚了要什么但把它翻译成 CAD 操作的时间可能比想清楚的时间还长。更麻烦的是一旦需求变了——比如孔径从 10mm 改成 12mm——你得重新打开文件、找到对应特征、修改参数、重新生成。对于参数化建模做得好的工程师来说这不算大事但现实中大量 CAD 文件是死的改一个尺寸可能引发连锁报错。text-to-cad 切入的正是这个缝隙。它的核心主张是让描述直接成为建模指令。你说一个法兰盘外径 120mm内径 60mm厚度 15mm均匀分布 6 个直径 12mm 的螺栓孔孔中心圆直径 90mm系统直接生成对应的三维实体并导出 STEP。这里面的关键不是AI 画图这种噱头而是把自然语言的结构化语义映射到几何内核的 API 调用序列。1.2 三条输出链路STEP、URDF、G-code 分别对应什么场景热词里出现了 STEP、URDF、G-code 这三个词它们其实代表了 text-to-cad 的三条典型输出链路对应完全不同的下游场景。STEP是 CAD 领域的通用交换格式ISO 10303 标准几乎所有 CAD 软件都能读写。它的特点是精确的边界表示B-rep保留完整的曲面和实体拓扑信息。text-to-cad 输出 STEP意味着生成的结果可以直接进 SolidWorks、CATIA、NX 做后续编辑或者进 ANSYS、Abaqus 做有限元分析。这是最正统的 CAD 输出。URDF是 Unified Robot Description Format机器人领域的标准描述格式基于 XML。它描述的不是单个零件的几何而是多个连杆link和关节joint组成的运动学树。text-to-cad 输出 URDF典型场景是你说一个两自由度机械臂大臂长 200mm小臂长 150mm关节都是旋转关节系统生成带几何和关节定义的 URDF直接拖进 CoppeliaSim 或 Gazebo 做仿真。热词里urdf导入coppeliasim就是这个链路的下游操作。G-code是数控加工和 3D 打印的指令语言描述的是刀具路径或挤出路径。text-to-cad 输出 G-code意味着从描述直接跳到制造指令。比如一个 50mm 见方的平板四角各一个 M3 沉头孔系统生成几何后自动切片或生成刀路输出可上机的 G-code。这条链路跳过了传统 CAD 到 CAM 的环节对快速原型场景很有吸引力。三条链路的共同点是输入都是自然语言输出都是机器可读的标准化文件。区别在于抽象层级——STEP 是几何层URDF 是运动学层G-code 是制造层。一个成熟的 text-to-cad 系统应该能根据用户意图自动选择输出格式或者至少让用户显式指定。1.3 为什么现在做这件事才靠谱早五年做 text-to-cad基本是死路一条。原因很简单自然语言到几何的映射需要模型同时具备语言理解能力和空间推理能力而这两者在 2020 年之前的 NLP 技术栈里都是短板。传统的规则式解析只能处理模板化的输入稍微换个说法就崩。2023 年之后情况变了。LLM 在两方面有了质变一是对数量、方位、拓扑关系的语义解析足够稳比如均匀分布 6 个孔它能正确理解为圆周阵列而不是随机摆放二是代码生成能力足够强能把解析结果翻译成 CadQuery、OpenSCAD 或 FreeCAD 的 Python 脚本。这就把 text-to-cad 的技术路线从训练一个端到端的几何生成模型数据需求极大、泛化差转向了LLM 做语义解析和代码生成 几何内核做实际建模数据需求小、可解释、易调试。这个路线转变是决定性的。端到端模型的问题是黑盒生成错了你不知道哪一步出的问题而 LLM 生成代码的路线中间产物是可读的 Python 脚本你能逐行检查、手动修改、重新执行。对于工程场景来说可调试性比端到端的优雅重要得多。2. 技术选型几何内核和 LLM 怎么搭2.1 几何内核的候选方案对比text-to-cad 的底座是几何内核它负责把代码指令变成实际的几何运算。选错内核后面全是坑。我实际对比过四个方案列个表说清楚。内核绑定语言优势劣势适用场景OpenCASCADEPython (pythonocc)、C工业级精度B-rep 完整STEP 支持最好学习曲线陡API 冗长文档差需要精确 STEP 输出的场景CadQueryPython基于 OCCTAPI 简洁链式调用友好复杂曲面能力有限依赖 OCCT 版本参数化零件快速建模OpenSCAD自有 DSL语法极简CSG 直观LLM 生成准确率高只有网格无 B-repSTEP 导出需转换3D 打印、简单结构FreeCADPython完整 CAD 功能支持工作台扩展API 不稳定版本间差异大需要 GUI 交互的混合场景我的结论是如果目标是 STEP 输出CadQuery 是性价比最高的选择。它底层就是 OpenCASCADE但把冗长的 OCCT API 封装成了接近自然语言的链式调用。比如画一个带孔的圆柱CadQuery 里就是cq.Workplane(XY).circle(25).extrude(30).faces(Z).workplane().hole(10)一行搞定。LLM 生成这种代码的准确率远高于直接生成 pythonocc 代码。OpenSCAD 的问题是它只有 CSG构造实体几何没有真正的 B-rep。你用它建出来的模型本质上是网格的布尔运算导出 STEP 需要经过网格到 B-rep 的转换精度损失不说转换本身经常失败。所以 OpenSCAD 适合 3D 打印这种只认 STL 的场景不适合需要 STEP 的工程场景。2.2 LLM 的角色边界它该做什么、不该做什么这里有个特别容易踩的坑很多人以为 LLM 要理解几何其实它只需要翻译几何。LLM 不擅长空间推理你让它想象一个复杂装配体的三维结构它大概率会胡编。但它非常擅长把结构化的语义映射成代码——只要你把几何约束用它能理解的方式表达出来。所以正确的分工是LLM 负责自然语言到建模脚本的翻译几何内核负责所有实际的空间运算。LLM 不需要知道两个圆柱体相交后是什么形状它只需要知道这两个圆柱体要做布尔差集然后把cut操作写进代码。实际的相交计算由 OCCT 完成精度和正确性有保障。这个边界划清楚之后prompt 的设计思路就明确了给 LLM 的上下文里要包含几何内核的 API 文档摘要和几个 few-shot 示例让它知道有哪些建模原语可用、怎么组合。不要让它自由发挥要把它限制在一个明确的 API 集合里。我实测下来给 5 到 8 个覆盖常见操作的示例拉伸、旋转、孔、阵列、倒角、布尔运算LLM 生成正确代码的比例能从 40% 左右提到 85% 以上。还有一个细节让 LLM 输出结构化的中间表示而不是直接输出最终代码。比如先让它输出一个 JSON描述零件由哪些特征组成、每个特征的参数是什么然后再用一个确定性的代码生成器把 JSON 转成 CadQuery 脚本。这样做的好处是中间表示可以被校验——参数范围是否合理、特征之间是否有冲突——而且调试的时候能精确定位是语义解析错了还是代码生成错了。2.3 从自然语言到几何参数的映射难点自然语言里描述几何有几个特别难处理的点我在实际项目里都遇到过。第一是尺寸的隐含约束。一个比拳头大一点的盒子——这种描述 LLM 没法直接转成数值。解决方案是建立默认值体系如果用户没给具体尺寸就用一组合理的默认值并在输出里明确标注使用了默认尺寸可修改。不要试图让 LLM 去猜拳头大是多少毫米那是不可靠的。第二是方位和拓扑关系的歧义。在顶面打四个孔——这四个孔是矩形分布还是圆周分布是在顶面中心还是靠近边缘自然语言在这里天然模糊。我的做法是在 prompt 里强制 LLM 对模糊描述做显式假设并把假设写进输出。比如假设四个孔在顶面按矩形均匀分布边距为零件边长的 1/5。用户看到假设不对可以补充描述重新生成。第三是单位和精度。CAD 里单位搞错是灾难性的一个 50mm 的零件按 50 英寸建出来差 25 倍。我的处理是在系统层面强制单位所有输入默认按毫米解析如果用户写了inch或英寸就做转换输出时在文件头标注单位。不要依赖 LLM 自己处理单位它经常搞混。第四是特征顺序的依赖。先打孔再倒角和先倒角再打孔结果可能完全不同。LLM 生成的代码如果特征顺序错了几何就错了。解决办法是在中间表示里显式定义特征树让 LLM 输出的是一个有序的特征列表而不是一堆无序的操作。这样顺序问题在语义层就解决了代码生成器按列表顺序执行即可。3. 实操搭一套能跑通的最小原型3.1 环境准备与依赖安装先把环境搭起来。我用的技术栈是 Python 3.10 CadQuery OpenAI API或任何兼容的 LLM 接口。CadQuery 的安装有个坑它依赖 OCCT而 OCCT 在不同平台上的二进制包差异很大。最稳的方式是用 conda 装pip 装经常出问题。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install openai装完之后验证一下import cadquery as cq result cq.Workplane(XY).circle(25).extrude(30) cq.exporters.export(result, test.step) print(OK)如果这行能跑通并生成 test.step环境就没问题。如果报 OCCT 相关的错大概率是 conda 源的问题换个源或者指定 cadquery 版本重装。注意CadQuery 2.x 和 1.x 的 API 差异很大网上大量教程还是 1.x 的写法。确认你装的是 2.xcq.__version__看一下。2.x 里Workplane的很多方法签名变了照抄老教程会报错。3.2 设计 LLM 的 prompt 模板prompt 是整个系统的核心。我反复迭代了十几版最终稳定下来的结构是这样的SYSTEM_PROMPT 你是一个 CAD 建模助手。用户会用自然语言描述一个三维零件 你需要输出一个 JSON 格式的特征树然后由代码生成器转成 CadQuery 脚本。 可用的特征类型 - box: 长方体参数 length, width, height - cylinder: 圆柱参数 radius, height - hole: 孔参数 radius, depth, position - fillet: 倒圆角参数 radius, edges - chamfer: 倒角参数 distance, edges - pattern_circular: 圆周阵列参数 count, radius, feature - pattern_rect: 矩形阵列参数 count_x, count_y, spacing_x, spacing_y, feature - boolean: 布尔运算参数 operation (union/cut/intersect), target, tool 输出格式 { unit: mm, features: [ {type: cylinder, radius: 25, height: 30, id: base}, {type: hole, radius: 5, depth: 30, position: [0, 0], target: base} ], assumptions: [假设孔位于圆柱中心] } 如果用户描述中有模糊之处在 assumptions 里列出你的假设。 所有尺寸默认单位为毫米。这个模板的关键点明确列出可用特征类型和参数名让 LLM 的输出空间被严格限制。不要给它自由发挥的余地否则它会生成一些你代码生成器处理不了的奇怪结构。few-shot 示例我放了 6 个覆盖简单拉伸体、带孔零件、圆周阵列、矩形阵列、布尔运算、倒角组合。每个示例都是自然语言描述 期望的 JSON 输出。实测下来6 个示例是性价比最高的数量再多了 token 成本上去了收益不明显。3.3 代码生成器从 JSON 到 CadQuery 脚本拿到 LLM 输出的 JSON 之后代码生成器负责把它翻译成可执行的 CadQuery 脚本。这部分是确定性的不涉及任何 AI纯字符串拼接和模板填充。def generate_cadquery(feature_tree): lines [import cadquery as cq, ] lines.append(result cq.Workplane(XY)) for feat in feature_tree[features]: if feat[type] cylinder: lines.append(fresult result.circle({feat[radius]}).extrude({feat[height]})) elif feat[type] box: lines.append(fresult result.box({feat[length]}, {feat[width]}, {feat[height]})) elif feat[type] hole: x, y feat[position] lines.append(fresult result.faces(Z).workplane().center({x}, {y}).hole({feat[radius]})) elif feat[type] pattern_circular: lines.append(fresult result.faces(Z).workplane().polarArray({feat[radius]}, 0, 360, {feat[count]}).hole({feat[hole_radius]})) # ... 其他特征类型 lines.append(cq.exporters.export(result, output.step)) return \n.join(lines)这个生成器的逻辑很直白但有几个细节要注意。第一特征顺序必须严格按 JSON 里的列表顺序执行因为后面的特征可能依赖前面的结果。第二每个特征操作后要更新result变量CadQuery 是链式的不更新就丢了。第三位置参数的处理要小心CadQuery 的坐标系和用户直觉可能不一致比如faces(Z)选的是 Z 方向最大面但用户说的顶面不一定就是它。3.4 跑通第一个端到端案例拿一个具体例子走一遍。用户输入一个直径 60mm、高 40mm 的圆柱中心有一个直径 12mm 的通孔顶面边缘倒 2mm 圆角。LLM 输出的 JSON{ unit: mm, features: [ {type: cylinder, radius: 30, height: 40, id: base}, {type: hole, radius: 6, depth: 40, position: [0, 0], target: base}, {type: fillet, radius: 2, edges: top_circle, target: base} ], assumptions: [通孔贯穿整个圆柱, 倒角作用于顶面外边缘] }代码生成器输出import cadquery as cq result cq.Workplane(XY) result result.circle(30).extrude(40) result result.faces(Z).workplane().hole(6) result result.edges(Z).fillet(2) cq.exporters.export(result, output.step)跑一下STEP 文件生成成功。用 FreeCAD 打开检查几何正确。这个案例从输入到输出全程不到 3 秒。但这里有个隐藏问题edges(Z)选的是 Z 方向最高的边对于这个圆柱来说就是顶面外圆边没问题。但如果零件形状复杂一点Z可能选到多条边fillet就会作用在预期之外的边上。这是 CadQuery 选择器的一个常见坑后面会专门讲怎么处理。4. 那些文档里不会写的坑4.1 CadQuery 选择器的惊喜CadQuery 的选择器selector是它最强大也最容易出问题的部分。faces(Z)、edges(|X)、vertices(Y)这些语法看起来很直观但实际用起来经常选到意料之外的东西。我遇到最典型的一次一个带阶梯的轴类零件我想在最大直径段的端面倒角写了faces(Z)。结果因为阶梯的存在Z 方向最高的面不是最大直径段的端面而是小直径段的端面。倒角倒错了地方。根本原因是选择器是基于几何属性的相对判断不是基于设计意图的绝对定位。Z的意思是Z 坐标最大的那个面而不是我认为的那个顶面。当零件形状复杂时这两个可能不是一回事。解决方案有两个。一是用更精确的选择器组合比如faces(Z).faces(cq.NearestToPointSelector((0, 0, 40)))先按方向筛再按位置筛。二是干脆在建模时给关键面打标签CadQuery 支持tag机制建面的时候打个标签后面用faces(tag)直接引用。第二种方式更可靠但需要 LLM 在生成代码时就规划好标签对 prompt 的要求更高。我的折中做法是在中间表示里让 LLM 显式指定每个特征作用的目标面或边用坐标或相对位置描述而不是用方向选择器。比如在 Z40 的平面上打孔代码生成器就翻译成workplane(offset40)而不是faces(Z)。这样虽然啰嗦一点但确定性高得多。4.2 STEP 导出的精度与单位陷阱STEP 文件有两个特别容易出问题的地方单位和精度。单位问题STEP 文件本身是带单位信息的但不同 CAD 软件对单位的处理方式不一样。CadQuery 默认导出的是毫米但如果你在代码里用了英寸的数值导出的 STEP 会标注为毫米实际尺寸就错了 25.4 倍。我的做法是在代码生成器里强制所有数值都按毫米处理如果用户输入了英寸在 JSON 解析阶段就转换成毫米代码里只出现毫米值。精度问题STEP 的 B-rep 表示涉及浮点数运算理论上精度是足够的但实际中如果建模操作链太长累积误差可能显现。特别是布尔运算多次 cut 和 union 之后面与面之间的缝隙可能大到影响后续加工。我的经验是尽量用最少的布尔操作完成建模能一次拉伸出来的不要分两次能用一个阵列的不要循环打孔。另外导出时可以指定精度参数cq.exporters.export(result, output.step, tolerance0.001)这个 tolerance 控制的是曲面离散化的精度默认值通常够用但对高精度要求的场景可以调小。还有一个坑CadQuery 导出的 STEP 在某些老版本 CAD 软件里打不开。原因是 OCCT 的 STEP 写入器默认用的协议版本可能和某些软件不兼容。如果遇到这种情况试试指定协议from OCP.STEPControl import STEPControl_Writer, STEPControl_AsIs # 或者用 CadQuery 的底层接口指定这个属于比较深的水一般用不到但知道有这回事遇到问题不会抓瞎。4.3 URDF 输出的坐标系对齐问题URDF 这条链路我踩的坑比 STEP 多得多。核心问题是坐标系约定不一致。CAD 软件里Z 轴通常朝上Y 轴朝前。但机器人领域URDF 的坐标系约定是Z 轴沿关节轴线X 轴沿连杆方向。这两个约定不一样直接转换会导致模型在仿真里姿态完全错乱。我第一次做 URDF 输出的时候生成的模型导入 CoppeliaSim 之后整个是躺着的关节轴方向也全错了。排查了半天才发现是坐标系没对齐。解决方案是在生成 URDF 之前对每个连杆的几何做一次坐标变换把 CAD 坐标系转成 URDF 坐标系。具体来说如果 CAD 里连杆是沿 Y 轴延伸的URDF 里要旋转成沿 X 轴。这个变换矩阵要写死在代码生成器里不能让 LLM 去算它算不对。另一个坑是关节原点的定义。URDF 里每个 joint 有一个 origin 标签定义子连杆相对于父连杆的位姿。这个 origin 如果设错了整个运动学树就散了。我的做法是在中间表示里显式定义每个关节的原点坐标和旋转轴LLM 只负责从自然语言里提取这些数值不负责计算变换。joint namejoint1 typerevolute parent linkbase_link/ child linklink1/ origin xyz0 0 0.1 rpy0 0 0/ axis xyz0 0 1/ limit lower-3.14 upper3.14 effort10 velocity1/ /joint这段 XML 里的每个数值都应该是从中间表示直接映射过来的不要有任何智能推断。4.4 G-code 生成从几何到刀路的鸿沟G-code 这条链路是三条里最难的因为从几何到刀路不是简单的格式转换而是一个 CAM 过程。text-to-cad 生成几何之后要输出 G-code中间需要经过几何分析识别加工特征、工艺规划选择刀具、确定切削参数、刀路生成计算刀具轨迹、后处理转成特定机床的 G-code 方言。这四步里前两步需要工艺知识第三步需要几何算法第四步需要机床配置。我的做法是把 G-code 输出限定在 3D 打印场景因为 3D 打印的刀路就是切片逻辑相对简单而且切片器如 Cura、PrusaSlicer都有命令行接口可以直接调用。流程是text-to-cad 生成 STL调用切片器命令行生成 G-code。# 用 PrusaSlicer 命令行切片 prusa-slicer --export-gcode --layer-height 0.2 --fill-density 20% input.stl -o output.gcode如果是 CNC 加工场景那就复杂得多需要专门的 CAM 软件如 FreeCAD 的 Path 工作台来做刀路规划。text-to-cad 在这个场景里的角色是生成几何并预定义加工特征实际的刀路生成还是交给专业 CAM 工具。不要试图让 LLM 直接生成 CNC 的 G-code它不懂切削参数生成的代码上机就是撞刀。注意G-code 直接驱动机床任何错误都可能导致设备损坏或人身伤害。text-to-cad 生成的 G-code 必须经过仿真验证如 CAMotics、NC Viewer才能上机。不要跳过验证步骤。5. 从原型到可用还需要补什么5.1 参数校验与几何可行性检查原型跑通之后第一个要补的是参数校验。LLM 生成的 JSON 里数值可能不合理——半径是负数、孔比零件还大、阵列数量是小数。这些在代码生成阶段就要拦住。我加了一层校验逻辑def validate_feature_tree(tree): errors [] for feat in tree[features]: if feat[type] cylinder: if feat[radius] 0: errors.append(f圆柱半径必须为正: {feat[radius]}) if feat[height] 0: errors.append(f圆柱高度必须为正: {feat[height]}) elif feat[type] hole: if feat[radius] 0: errors.append(f孔半径必须为正: {feat[radius]}) # 检查孔是否超出零件范围 # ... return errors校验不通过就返回给 LLM 重新生成或者直接报错让用户修改描述。不要试图自动修正参数因为你不确定用户的意图是什么自动修正可能改错。除了数值校验还有几何可行性检查。比如布尔差集的两个实体如果没有交集差集操作会失败或产生空结果。这种检查需要在几何内核层面做CadQuery 执行失败会抛异常捕获异常后返回友好错误信息。5.2 多轮对话与增量修改单轮生成只能解决从零开始的场景实际使用中更多是增量修改把刚才那个零件的孔径改成 15mm、再加一个沉头孔。这需要系统维护一个对话状态记住上一轮的 feature tree然后让 LLM 基于上一轮的结果做修改。prompt 里要把上一轮的 JSON 带上让 LLM 输出修改后的完整 JSON。这里有个坑LLM 做增量修改时容易丢失之前的特征。比如你让它改孔径它可能只输出了孔的特征把圆柱忘了。解决办法是在 prompt 里强调输出完整的特征树包含所有未修改的特征并且在代码层面做 diff 检查——如果新输出的特征数量比原来少就报警。另一个坑是特征 ID 的稳定性。如果每轮生成的特征 ID 都变后续引用就乱了。我的做法是让 LLM 在修改时保留未变特征的 ID只给新特征分配新 ID。这个约束要写进 prompt。5.3 批量生成与模板化热词里有python批量对cad修改这其实是 text-to-cad 的一个自然延伸参数化模板 批量实例化。思路是先用 text-to-cad 生成一个参数化的模板feature tree 里用变量代替具体数值然后给一组参数值批量生成实例。比如一个 M3 螺栓的垫圈内径 3.2mm外径 7mm厚度 0.5mm生成模板后把内径参数扫一遍 3mm 到 5mm批量生成 20 个 STEP 文件。import json from string import Template template_tree json.load(open(washer_template.json)) for inner_d in [3.0, 3.2, 3.5, 4.0, 4.5, 5.0]: tree json.loads(json.dumps(template_tree)) # 深拷贝 tree[features][1][radius] inner_d / 2 script generate_cadquery(tree) exec(script) # 重命名输出文件这个模式在标准件库、系列化产品设计里特别有用。一次描述批量产出比手工建模快几个数量级。5.4 与现有 CAD 工具的集成路径text-to-cad 不可能完全替代 CAD 软件更现实的定位是前端生成 后端精修。生成的 STEP 导入 CAD 软件后工程师继续做细节调整、装配、出图。集成路径有几种。一是文件级集成text-to-cad 输出 STEP工程师手动导入 CAD 软件。最简单但断了自动化流程。二是 API 级集成通过 CAD 软件的 API如 SolidWorks API、Fusion 360 API直接把生成的几何注入当前文档。需要写插件工作量大但体验好。三是脚本级集成text-to-cad 输出 CadQuery 脚本工程师在 CAD 软件的 Python 控制台里执行。FreeCAD 对这种模式支持最好因为它本身就是 Python 驱动的。我的建议是从文件级集成起步验证了核心价值之后再考虑 API 集成。不要一上来就搞插件那是无底洞。6. 几个实际案例的完整拆解6.1 法兰盘从一句话到 STEP用户输入一个法兰盘外径 120mm内径 60mm厚度 15mm均匀分布 6 个直径 12mm 的螺栓孔孔中心圆直径 90mm。LLM 输出的 JSON{ unit: mm, features: [ {type: cylinder, radius: 60, height: 15, id: flange}, {type: hole, radius: 30, depth: 15, position: [0, 0], target: flange}, {type: pattern_circular, count: 6, radius: 45, hole_radius: 6, target: flange} ], assumptions: [螺栓孔贯穿法兰厚度, 孔中心圆与法兰同心] }生成的 CadQuery 脚本import cadquery as cq result cq.Workplane(XY) result result.circle(60).extrude(15) result result.faces(Z).workplane().hole(30) result result.faces(Z).workplane().polarArray(45, 0, 360, 6).hole(6) cq.exporters.export(result, flange.step)这个案例里polarArray是 CadQuery 的圆周阵列方法参数是阵列半径起始角度终止角度数量。注意阵列之后直接.hole(6)意思是每个阵列位置都打一个半径 6 的孔。这个写法很简洁但有个前提阵列必须在 workplane 上做而且 workplane 的方向要对。实测下来这个脚本生成的 STEP 在 SolidWorks 里打开正常几何精度也够。整个流程从输入到文件生成大约 2 秒。6.2 两自由度机械臂URDF 输出用户输入一个两自由度机械臂基座是直径 100mm、高 50mm 的圆柱大臂长 200mm、截面 40x40mm小臂长 150mm、截面 30x30mm两个关节都是旋转关节绕 Z 轴旋转。这个案例比法兰盘复杂因为涉及多个连杆和关节。LLM 需要输出的是 URDF 结构而不是单个零件的特征树。中间表示我设计成这样的结构{ links: [ {name: base, geometry: {type: cylinder, radius: 50, height: 50}}, {name: link1, geometry: {type: box, length: 200, width: 40, height: 40}}, {name: link2, geometry: {type: box, length: 150, width: 30, height: 30}} ], joints: [ {name: joint1, parent: base, child: link1, type: revolute, axis: [0, 0, 1], origin: [0, 0, 50]}, {name: joint2, parent: link1, child: link2, type: revolute, axis: [0, 0, 1], origin: [200, 0, 0]} ] }然后代码生成器把这个结构转成 URDF XML同时为每个 link 生成对应的几何文件STL 或 DAE在 URDF 里引用。这里的关键是坐标系对齐。base 的圆柱在 CAD 里是 Z 轴朝上URDF 里 base_link 的坐标系也是 Z 轴朝上这个一致。但 link1 的 box 在 CAD 里如果沿 Y 轴延伸URDF 里要沿 X 轴需要旋转。我在代码生成器里对每个 link 的几何做了统一的坐标变换CAD 的 Y 轴映射到 URDF 的 X 轴CAD 的 Z 轴映射到 URDF 的 Z 轴。这样 link1 的 box 在 URDF 里就是沿 X 轴延伸的和关节轴 Z 垂直运动学正确。导入 CoppeliaSim 之后机械臂的姿态正确两个关节都能正常旋转。这个案例从输入到 URDF 文件生成大约 5 秒。6.3 简单支架G-code 输出用户输入一个 L 形支架底板 80x60x5mm立板 80x40x5mm立板在底板短边一侧垂直向上底板四角各一个 M4 沉头孔。这个案例输出 G-code 走的是 3D 打印路线。先生成几何导出 STL然后调用切片器。生成的 CadQuery 脚本import cadquery as cq result cq.Workplane(XY) result result.box(80, 60, 5) result result.faces(Z).workplane().center(0, 25).rect(80, 5).extrude(40) # 四角沉头孔 for x, y in [(-35, -25), (35, -25), (-35, 25), (35, 25)]: result result.faces(Z).workplane().center(x, y).cboreHole(2, 4, 2) cq.exporters.export(result, bracket.stl)然后切片prusa-slicer --export-gcode --layer-height 0.2 --fill-density 30% bracket.stl -o bracket.gcode这个案例里cboreHole是 CadQuery 的沉头孔方法参数是孔半径沉头半径沉头深度。注意 M4 螺栓的通孔直径约 4.5mm半径 2.25mm我用了 2mm 是近似值实际应该按标准查表。G-code 生成之后我用 CAMotics 做了仿真验证确认刀路没有撞刀、没有空走。这一步不能省尤其是涉及实际加工的时候。7. 我对这套东西的真实判断折腾了几个月我对 text-to-cad 的判断是它在特定场景下已经可用但离替代 CAD 工程师还差得远。可用的场景很明确结构相对简单、参数化程度高、不需要复杂曲面的零件。法兰、支架、垫圈、简单的轴类零件、标准件变体这些用 text-to-cad 生成比手工建模快得多而且改参数重新生成的成本几乎为零。对于需要快速迭代的设计探索阶段这个效率提升是实打实的。不可用的场景同样明确复杂曲面、自由形状、需要精细工艺知识的加工特征。你让 LLM 描述一个涡轮叶片的几何它生成的代码大概率跑不通。你让它设计一个注塑件的拔模斜度和圆角它不懂工艺约束。这些还是得靠人。所以我的定位是text-to-cad 是一个快速原型生成器不是CAD 替代品。它的价值在于把想法到可验证几何的时间从小时级压到秒级让工程师能快速试错。试错之后该用 CAD 精修的还是得精修。技术路线上LLM 生成代码 几何内核执行这个组合是目前最务实的。端到端的几何生成模型听起来更酷但可调试性太差工程场景不实用。而且随着 LLM 代码能力的提升这条路线会自动受益不需要重新训练模型。最后说一个我踩过的最大的坑不要相信 LLM 生成的几何代码能一次跑通。即使 prompt 设计得再好LLM 生成的代码也有 10% 到 15% 的概率有语法错误或 API 误用。所以系统里必须有自动重试机制代码执行失败时把错误信息回传给 LLM让它修正后重新生成。我实测下来加上一轮重试成功率能从 85% 提到 95% 以上。这个重试逻辑是生产环境必备的原型阶段可以手动处理但要做成产品必须自动化。
返回列表