基准对比:CadQuery与OpenSCAD在代理CAD工作中的表现

Hacker News Top 工具

摘要

一项控制基准测试对比了CadQuery和OpenSCAD在AI智能体生成功能性3D打印部件时的表现,揭示了两者能力相似,但在故障模式和工具特定优势方面存在差异。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/09/12 20:32

# CadQuery 与 OpenSCAD:AI 生成功能性零件的对比 | ModelRift 博客 来源:https://modelrift.com/blog/cadquery-vs-openscad ModelRift 平台上的每个模型都使用 OpenSCAD 生成。这一选择值得定期重新评估,因此我们针对代码优先 CAD 最可靠的替代方案进行了受控对比测试:**CadQuery** (https://cadquery.readthedocs.io/),这是一个基于 OpenCascade 边界表示内核的 Python 库。 我们的测试问题很明确:哪个工具能在无人监督的情况下,由 AI 智能体驱动生成正确、可打印、功能正常的零件?至于哪个工具更便于手工编写,不在本次考量范围内。 **六个智能体,三项任务,两种工具。** 每个生成的 STL 文件都由一个不信任任何工具的解析器进行检查。 所有六个零件均可打印。能力层面的结果是乏味的。两者的差异体现在**失败方式**的不同上。 **两个螺纹软管接头并排放置,一个用 OpenSCAD 构建,另一个用 CadQuery 构建,两者几乎相同。** *本组任务中最难的一项,由两种工具共同解决。左侧为 OpenSCAD,右侧为 CadQuery。* ## 测试设置 所有六次运行均由 Claude Opus 5(100 万上下文)通过 Claude Code 驱动。每个单元格作为独立的通用子智能体运行,继承相同的模型,彼此之间无交叉通信。每个单元格一个智能体,因此下文中的部分差异源于智能体的随机性而非工具本身。工具版本为 CadQuery 2.8.0(运行于 Python 3.14)和 OpenSCAD 2026.06.12,均运行在 M 系列 Mac 上。 OpenSCAD 端运行的是我们自有的 openscad-skill (https://github.com/ModelRift/openscad-skill),这是我们发布并内部使用的智能体技能。它涵盖了文件与版本命名、渲染-检查-修复的 QA 循环、CLI 预览的摄像机预设、截面调试以及 Customizer 语法。 对于 CadQuery,我们逐操作移植了该技能,保持相同结构,仅替换了 OpenSCAD 没有对应功能的部分。CadQuery 没有 CLI 渲染器,因此移植工作需要额外编写一个小型离屏渲染器,并用边界表示有效性指标替代 CSG 状态行。两个文件随后都加载了相同的 3D 打印设计规则、壁厚、间隙和悬垂角度,因此没有哪一方获得了对方所缺乏的建议。最终文件大小为:OpenSCAD 12.4 KB,CadQuery 13.1 KB。 一个不对称点被保留了下来:CadQuery 文件包含一份 API 速查表,而 OpenSCAD 文件不需要,因为模型本身已经很熟悉 OpenSCAD 语法。这有助于 CadQuery 处理语法问题,但对几何建模没有帮助。 每个智能体被限制在 12 个版本迭代内,被要求不得伪造成功,并必须报告每一次失败及其原始错误信息。它们在无人值守下运行。 我们不盲信智能体的报告。每个最终 STL 都经过一个解析器直接读取文件,并报告三角形数量、包围盒、体积、水密性、非流形边、边界边、翻转面和连通组件。这比我们预想的更为重要,原因将在下文说明。 ## 三项任务 处理同一任务的两个智能体收到相同的文本描述,没有针对特定工具的提示。 **简单任务 T1** 是一个壁挂式 L 形支架:两块板呈 90 度角,厚度 4 毫米,两个三角形加强筋,两个用于沉头螺丝的沉孔(90 度,头部直径 9 毫米),两个普通孔,外垂直角半径 R3,内角圆角 R4,无需支撑即可打印。 **任务 T2** 升级为一个用于容纳 50 x 26 毫米 PCB 的两件式卡扣外壳。壁厚 2 毫米,腔体每侧间隙 0.4 毫米,四个 M2 螺柱,一个 9.5 x 3.5 毫米的 USB-C 开口,一个带周边唇边(间隙 0.2 毫米)且具有可用卡扣的独立盖子,五个通风槽。两个部分必须能够实际装配。 **任务 T3** 是硬核任务:一个 M24x2 螺纹软管宝塔接头,六角法兰对边宽度 30 毫米,真正的螺旋螺纹长度 12 毫米,长度为 25 毫米的宝塔头,带三个用于内径 12 毫米软管的倒刺,以及一个直径 8 毫米的直通孔道。规格明确禁止使用堆叠环结构,螺纹必须是真正的螺旋几何形状。 ## 结果 | 项目 | T1 OpenSCAD | T1 CadQuery | T2 OpenSCAD | T2 CadQuery | T3 OpenSCAD | T3 CadQuery | | ------------------ | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | | 迭代版本数 | 2 | 3 | 8 | 5 | 1 | 3 | | 代码行数 | 103 | 135 | 220 | 266 | 150 | 172 | | 工具报告的错误数 | 1 | 4 | 0 | 0 | 1 | 1 | | 无声的几何错误 | 0 | 0 | 5 | 3 | 0 | 1 | | 智能体实际运行时间 | 461 秒 | 562 秒 | 1058 秒 | 909 秒 | 542 秒 | 935 秒 | | 智能体消耗的 Token | 77 k | 89 k | 138 k | 139 k | 82 k | 119 k | | 几何重算时间 | 12 毫秒 | 1.56 秒 | 16 毫秒 | 1.93 秒 | 43 毫秒 | 1.95 秒 | | 最终 STL 判定结果 | 完好 | 完好 | 完好 | 完好 | 完好 | 完好 | **三项任务汇总:** | 项目 | OpenSCAD | CadQuery | | ------------------ | -------- | -------- | | 迭代版本数 | 11 | 11 | | 代码行数 | 473 | 573 | | 工具报告的错误数 | 2 | 5 | | 无声的几何错误 | 5 | 4 | | 智能体实际运行时间 | 2061 秒 | 2406 秒 | | 智能体消耗的 Token | 297 k | 347 k | “完好”意味着水密、单一连通组件、零非流形边和边界边、位于 z = 0 平面上,其测量依据是文件本身,而非生成工具的自我报告。 迭代次数完全相同,均为 11 个版本。整体工作量形态也相似。差异在于两者遇到的**问题性质**不同。 ## T1:简单的支架 *两者均正确,从内角拍摄以显示加强筋。可见差异在于诠释:规格对加强筋的尺寸和位置留有余地。* 对四个外角进行圆角处理清晰地展现了两个工具的世界模型差异。OpenSCAD 先对 2D 轮廓进行圆角再挤出,此操作不关心实体是如何组装的: `` module corner_mask() { translate([0, 0, -eps]) linear_extrude(vert_h + 2*eps) offset(r = corner_r) offset(delta = -corner_r) square([plate_w, horiz_d]); } `` CadQuery 则必须先命名四条边,而命名是难点: `` result = result.edges("|Z").edges( BoxSelector((-WIDTH, -EPS, -EPS), (WIDTH, EPS, V_HEIGHT + EPS)) + BoxSelector((-WIDTH, H_DEPTH - EPS, -EPS), (WIDTH, H_DEPTH + EPS, V_HEIGHT + EPS)) ).fillet(R_OUTER) `` OpenSCAD 首次尝试即编译出正确的几何,两个版本即完成。CadQuery 大约三分之一的运行时间耗费在单个错误上:`.fillet(3.0)` 失败并报错 `BRep_API: command not done`,这条消息既未指明是哪条边也未说明半径。智能体必须手动二分查找来发现问题根源,最终发现是算术问题:两个 R3 的圆角无法容纳在 4 毫米的壁厚中。 ## T2:必须匹配的两件式外壳 *两者都从间隙堆叠中推导出相同的 54.8 x 30.8 x 25.0 毫米盒体尺寸。* 这里 CadQuery 的参数链发挥了作用。改变唇边深度会联动地移动法兰边、底板、槽、凹槽和倒刺,因为每个尺寸都是推导出来的,而不是重复输入两次。这种尺寸链与优秀的参数化 UI 向用户暴露的方式相同,我们曾在此处讨论过:[构建更好的 OpenSCAD 自定义器](https://modelrift.com/blog/openscad-customizer-history)。CadQuery 没有 Customizer 对应物,因此常量块就是整个参数接口。 更有趣的是双方如何检查配合度。OpenSCAD 打印数字供人阅读: `` echo(str("cavity LxW = ", cav_l, " x ", cav_w, " (clearance/side ", pcb_clr, ")")); `` CadQuery 使用断言,当断言为假时构建停止: `` assert BOX.val().intersect(LID.val()).Volume() < 1e-6, "box and lid interfere" `` `echo` 仅在有人阅读时才有用。`assert` 则会自行导致构建失败。对于无人值守的生成,这一差距是关键。 OpenSCAD 需要八个版本,而 CadQuery 只需五个。OpenSCAD 的八个版本中有三个是用于布尔运算的清理,而非设计:清理相切和重合面产生的薄片。 ## T3:真正的螺纹 这个困难任务带来了最大的意外。OpenSCAD 一个版本即完成,首次编译即正确,耗时 43 毫秒,无需外部库。 OpenSCAD 没有扫掠操作,因此智能体通过原始的顶点和面算术编写了螺旋线:一个四点 ISO 轮廓,每圈 96 个截面,作为单个 `polyhedron` 输出。这段代码没有任何保护措施。绕序错误时,工具会保持沉默。 `` module helical_thread(turns, z0) { prof = [ [r_in, -flank_hz], [r_maj, -crest_hz], [r_maj, crest_hz], [r_in, flank_hz] ]; n = round(turns * STEPS); pts = [ for (i = [0:n]) let(a = i * 360 / STEPS, zo = z0 + i * thread_pitch / STEPS) for (j = [0:3]) [ prof[j][0] * cos(a), prof[j][0] * sin(a), prof[j][1] + zo ] ]; fcs = concat( [ [3, 2, 1, 0] ], [ for (i = [0:n-1]) for (j = [0:3]) let(k = (j + 1) % 4) [ 4*i + j, 4*i + k, 4*(i+1) + k, 4*(i+1) + j ] ], [ [4*n + 0, 4*n + 1, 4*n + 2, 4*n + 3] ] ); polyhedron(points = pts, faces = fcs, convexity = 8); } `` 该智能体在编写第一行代码之前就预防了经典的螺纹陷阱:ISO 齿形恰好跨越一个螺距,因此相邻的根部平面会共面,导致联合运算出错。它将扫掠轮廓沉到小径以下,使螺旋线穿过芯轴而非仅仅接触它。这就是为什么布尔运算是小菜一碟。 CadQuery 用六行可读的代码表达了相同的几何,且该部分首次尝试即成功: `` helix = cq.Wire.makeHelix(pitch=THREAD_P, height=h, radius=R_ROOT, center=(0, 0, z0)) prof = (cq.Workplane("XZ", origin=(0, 0, z0)).center(R_ROOT, 0) .polyline(thread_profile_points()).close()) ridge = prof.sweep(path, isFrenet=True) core = cq.Workplane("XY", origin=(0, 0, z0)).circle(R_ROOT).extrude(h) rod = core.union(ridge) `` 失败发生在最后一行,这也是唯一一个改变了我们对 QA 循环看法的结果。 *CadQuery T3 版本 1 在左侧。螺纹部分只是悬浮的螺旋线,没有芯轴圆柱。* `union()` 静默地丢弃了芯轴圆柱,因为螺纹根部恰好位于芯轴半径上。沿着螺旋曲线的精确相切,导致 OCCT 默默丢弃了一个实体。从外部看,这个零件堪称完美。智能体看了四次渲染都没发现问题。最终是一个体积测量值抓住了它:7065 立方毫米,而预期值是 10323。 更糟的是,这个损坏的零件报告 `valid=True` 和 `solids=1`。将布尔运算公差提高到 `1e-3` 产生了一个负体积的实体,它同样报告 `valid=True`。 OpenSCAD 在此也并非无辜。在 T2 中,其 Manifold 后端为包含 4 条非流形边和 60 个零面积三角形的 STL 文件认证了 `Status: NoError`。两个工具的自我报告都不是最终结论,这就是我们自行解析每个网格的原因。 *两个成品接头的轴向剖面图,显示螺纹齿顶相互错开半个螺距。* *两个成品的螺纹。左侧齿顶与右侧齿顶错开半个螺距,这是真正单头螺旋的标志,也是区分真实螺纹与堆叠环的检验标准。* ## 下载零件 以下是八个最终网格文件,完全由智能体导出,未经我们清理或修复。二进制 STL 格式,单位毫米,零件置于 z = 0 平面以适配打印。 | 零件 | OpenSCAD | CadQuery | | ------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | | T1 支架 | [STL, 2660 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t1-bracket-openscad.stl) | [STL, 4232 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t1-bracket-cadquery.stl) | | T2 外壳盒体 | [STL, 2944 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t2-enclosure-box-openscad.stl) | [STL, 14136 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t2-enclosure-box-cadquery.stl) | | T2 外壳盖子 | [STL, 2960 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t2-enclosure-lid-openscad.stl) | [STL, 1872 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t2-enclosure-lid-cadquery.stl) | | T3 螺纹接头 | [STL, 10754 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t3-adapter-openscad.stl) | [STL, 13748 三角面](https://modelrift.com/blog/cadquery-vs-openscad/models/t3-adapter-cadquery.stl) | 所有八个文件都是水密的、单一连通组件、零非流形边、零边界边和零翻转面。间隙假设为 FDM 工艺,喷嘴直径 0.4 毫米,因此每对 T2 零件打印后应该能卡扣装配。 三角面数量值得关注,它们并不一致偏向某个工具。CadQuery 的盒体三角面数几乎是 OpenSCAD 的五倍,而其盖子则更少。网格密度在这里反映的是每个智能体选择添加的圆角细节量,而非内核差异。 ## 我们的发现 ### 渲染未能捕捉到任何重要问题 这是我们未曾预料的结果。在六次运行中,图像只能捕捉到粗大的错误,比如被腔体减法删除的四个安装柱,却无法发现细微问题。每一个可能毁掉打印的缺陷,都是通过数值发现的:体积、角度、干涉测试、应变计算。在 T3 中,OpenSCAD 智能体遇到了相反的问题,差点拒绝了正确的几何,因为螺纹特写渲染出来的样子看起来不对。它自己的笔记是,渲染结果无法作为任一方面的充分证据。 ### 失败模式是镜像对称的 CadQuery 失败得响亮而早期。它的消息虽然不好,但异常会停止运行,而停止的模型无法意外交付。OpenSCAD 失败得安静而晚期:在 T2 中,它在约 45 次调用中未报告任何错误或警告,却生成了缺失的安装柱、错位的槽以及一个它刚刚认证为“完好”的损坏导出文件。对于无人值守的生成,静默的成功是代价更高的失败。 ### CadQuery 可以被询问,OpenSCAD 不行 CadQuery 能回答关于其自身几何的问题。T1 智能体通过读取 B-rep 上的锥体半角,证明了其沉孔是 90 度且直径为 9 毫米,然后在每次构建时运行断言来重新检查规格。OpenSCAD 无法查询几何,因此两个 OpenSCAD 智能体都独立编写了二进制 STL 解析器(代码量几乎与模型本身相当)来测量它们构建的内容。这可行,但它只在导出后才能看到网格,无法感知设计本身。 ### 速度有利于 OpenSCAD,但这几乎不重要 几何重算速度快 30 到 100 倍,16 毫秒对 1.9 秒。在模型推理主导的智能体循环内,这是噪声。对于实时自定义器或大型参数扫描,这会很重要。 ### OpenSCAD 的渲染没有零件边缘的概念 CSG 之后只有三角面片汤,因此 `--view=edges` 绘制的是三角剖分线而非轮廓线。CadQuery 的 B-rep 知道两个面实际相交的位置。由于图像是智能体的反馈通道,这在我们的设置中造成了真正的不对称,因此我们通过渲染导出的 STL 并根据相邻面之间的夹角重建轮廓线来解决这个问题。 *同一个零件,三种渲染器。左图:CLI 默认渲染,无轮廓线。中图:`--view=edges`,绘制了每个三角面边。右图:基于 STL 的特征边重建,还会用红色标注网格缺陷。所有六次运行均在此次修复之前完成。* ## 结论 没有哪个工具在输出上获胜。六个干净的可打印零件,每种工具各三个。 决定因素是**可验证性**而非表达能力,CadQuery 在此方面的优势远比语法差异所显示的更大。能够向内核询问你刚刚构建了什么,并对答案进行断言,在智能体循环中的价值远超任何便捷的语法。 任务形态比工具选择更重要。简单的棱柱状工作交给了 OpenSCAD。必须配合的两件式零件交给了 CadQuery。而螺旋螺纹——这个看似为边界表示内核量身定做的任务——则完全被 OpenSCAD 搞定了。 如果生成流程依赖截图,它依赖的就是那个在这里一无所获的渠道。需要强制执行的是数值断言:壁厚、间隙、干涉体积、悬垂角度。 必须独立验证网格,因为两个工具链都曾为存在缺陷的几何进行过认证。

相似文章

CADFather:通过协同工具调用实现自主CAD重建

Hugging Face Daily Papers

CADFather 是一个自主智能体系统,通过协调视觉-语言助手与互补的学习型、算法型和优化工具,从 3D 网格重建可编辑的参数化 CAD 程序,并在 DeepCAD、Fusion360 和 CADENA-Bench 等基准上进行了评估,无需额外训练。