CycloidAI

开发案例 · 我们做过的五类计算工具

从"这道题该用什么模型"到"这个数凭什么信" —— 五个案例,都附我们自己踩的坑

这五个案例都是我们自己的工程项目,不是客户项目。 客户的东西不放在这里 —— 能公开的只有我们自有的模型与代码。 页面上每个数字都取自归档的结果文件(下方各案例末尾标了出处), 没有归档结果的数字一律不引用,即使我们记得它是多少(见案例④ 最后一行)。

找人做定制计算工具,最难判断的不是"他会不会写代码",而是这三件事:
  1. 他会不会挑模型。能用闭式解的地方上有限元,是在替你烧钱; 该上有限元的地方用经验公式,是在替你埋雷。
  2. 他怎么知道自己算错了。一个数看着合理和它是对的,是两回事。
  3. 交付的是什么。一份 PDF,还是一套你自己能重跑、能改参数、能验证的东西。
下面五个案例,一个回答一件。

案例 ①把两天的仿真排期,压成一次点击

摆线针轮多针齿接触力 · 解析式降阶模型(ROM)· Julia

问题是什么

多针齿接触力的时域分析,在通用多体软件里是"建模型 → 排队 → 等结果"的流程。 单次几分钟不算慢,但设计阶段真正要做的是扫参数 —— 偏心距挪 0.1 mm、针齿数加两个,都要重来一遍。 于是实际发生的事情是:没人扫参数,靠经验拍一个方案往下走。

怎么做的

先不写代码,先看这道题的结构。载荷分配的核心是扭矩平衡 Σ Fi·li = T_out,配上赫兹线接触 Fi = K·δi10/9 —— 这是一个关于 δ 的单调非线性方程, 通常就迭代解了。但把 δi 按力臂线性分配代进去以后, δ 是有闭式解的δ = (T_out / (K·Σ win·li))1/n

于是把和转速无关的那部分(720 点曲柄角网格 × 各针齿的归一化力臂与扭矩容量) 离线构表缓存,在线只剩相邻两格线性插值 + 一次幂运算。 缓存键是量化后的 (N, E, Rp) —— 改转速不重建表,因为转速只进标量缩放。

⚠ 我们踩的坑

差点把性能对比做成了自证。 为了让降阶模型有个参照,我们同时保留了一条老老实实每步牛顿迭代的物理求解路径。 写的时候顺手把迭代初值取成了那个闭式解 —— 结果是残差一进循环就满足, 迭代次数恒为 0。两条"不同路径"其实在做同一份计算, 而页面上那个漂亮的加速比,是在和自己比。

现在初值故意取一个通用量级估计(δ = 0.01 mm),默认参数下约 2 次/步、240 步累计约 480 次。 这行代码上面有一段注释,专门写明"不许改回去"。

压缩率虚高了 3 倍多。 测 gzip 效果时用的是"同一条曲线重复 N 遍"的合成数据,得到 14× —— 那测的是重复度,不是我们的响应。换成线上真实响应重测:24.1 KB → 5.97 KB, 4.1×。凡是要写进对外材料的数字,一律以线上实测为准。

现在能验的数

结果
两条路径的接触力偏差< 峰值的 0.3%
在线推理复杂度O(1)(查表 + 一次幂运算)
页面显示的耗时响应里的 elapsed 实测值,不是写死文案
gzip(线上真实响应)24.1 KB → 5.97 KB(4.1×)

自己验:打开 多针齿接触力时域分析, 同一组参数点一次"物理求解"、再点一次"降阶模型", 对比曲线与状态栏里的耗时、迭代次数、是否命中缓存。 耗时是每次真算出来的,所以第二次点会比第一次快 —— 这也是它没作假的证据。

案例 ②24 个型号一键出模型与图纸,过夜跑完

谐波减速器系列批产 · NX 二次开发 + Python 参数契约

问题是什么

一个谐波减速器系列,6 个机号 × 4 个速比 = 24 格, 每格要出柔轮、刚轮、波发生器等一整套零件模型、装配、二维图纸和 PDF。 手工做一遍是周级的工作量,而只要改一个系数,前面全部作废。 这不是"效率问题",是"改不动"——改不动就没人敢改,系列参数就一直将就着用。

怎么做的

核心不是"批量点按钮",是型谱同源:所有格子的模数由 m = Dp / 2i 反解,配 R2 系数链与轮缘自愈规则, 写成一份参数契约。之后每格的流程是 参数契约 → 导出槽位 → NX 批量建三件套与装配 → 出图与 PDF → 归档到 output/series/<机号>-<速比>/。 每格自带校验,校验不过不算绿

⚠ 我们踩的坑

批产会临时改写全局的现役参数文件。 每跑一格,都要把 design_params.json 改成那一格的参数。 跑完当然要改回来 —— 问题是中途异常退出的时候不会改回来, 它会把某个中间型号的参数安安静静留在现役位置上。 下一次谁跑一个完全不相干的计算,用的就是错参数,而且不报错、不告警, 结果看着一切正常。

现在还原写在 finally 里,并且还原后强制重刷一次导出槽位。 这类"沉默的错"比崩溃危险得多 —— 崩溃你会去修,沉默的错你会拿去投产。

现在能验的数

最近一次全系跑
型谱格数24(机号 14/17/20/25/32/40 × 速比 50/80/100/120)
通过24 / 24 全绿,校验失败合计 0
总耗时(无人值守)59.4 分钟
单格耗时136 – 163 秒
本次产出文件456 个
库内累计744 个零件/装配模型、264 张 PDF 图纸

出处:output/series/series_result.json(该脚本每次运行都写一份, 含每一格的耗时、文件数与校验结果)。 另有慢走丝加工的 G 代码由同一条链自动生成 (output/CircularSpline_WEDM.nc,路径 467.4 mm、估时 3.9 h)。

案例 ③判据引擎:每一个数都能问一句"凭什么"

RV 减速器放行看板 · 17 道门禁 · 三态判定

问题是什么

拿到一份别人做的计算书,工程师最难判断的从来不是结论对不对, 而是哪条结论有证据、哪条是用默认值凑出来的。 两者在纸面上长得一模一样 —— 都是一个数,都带单位,都在合理范围内。

怎么做的

把校核拆成 17 道独立门禁(G1–G17),每道门输出三种状态PASS / FAIL / N/A(证据未到位)。 第三态是整个设计的重点:N/A 不是 PASS。 算不出来、输入不全、标定过期,都如实报 N/A 并写明缺什么, 而不是套一个"工程上常用"的默认值把门标绿。

每道门都能点开看它用了哪些输入、哪条口径、出自哪次标定。 修形量这类"看起来是常数"的东西,在这里是当前设计点的函数, 设计点一动,标定新鲜度这道门就会掉到 N/A。

⚠ 我们踩的坑

"缺失即通过"会从意想不到的地方钻进来。 有一道门要同时评估多个工况 case。曾经出现的情况是:其中一个 case 因为输入不全被拒评, 而剩下几个 case 全过,于是整道门被标成了绿的。 逻辑上完全说得通,代码也没写错 —— 但它把"有一部分没评"翻译成了"通过"。

现在的规则是:只要有一个 case 拒评,整道门降为 N/A。评不了 ≠ 通过。 这条规则在源码里带着当初的问题编号,防止哪天有人觉得"太严了"再改回去。

现在能验的数

规模
门禁数17(G1–G17)
引擎核心12,759 行 / 55 个模块
测试12,064 行 / 599 个测试函数
作业脚本28,954 行 / 130 个
测试代码 : 核心代码0.95 : 1

自己验:app.cycloidgear.com/gate 可以在线跑一次真校核(不是预录结果),看 17 道门当场判成什么, 并且能点开看每道门的口径与依据。 网页版有一道门(G9「FE 锚点新鲜度」)是永久 N/A —— 无状态服务收到任意参数时无法核验有限元锚点的判据指纹, 报"证据未到位"比假装 PASS 诚实。这条口径也直接印在页面上,而不是悄悄跳过。

案例 ④逆包络求共轭刀条:把 5 µm 的过切按回 0.08 µm

柔轮外齿滚齿展成 · 加工工艺仿真 · 纯 Python + shapely

问题是什么

滚齿是展成法加工:刀具与工件对滚,刀齿扫过的包络从毛坯上啃出齿形。 那么反过来 —— 想加工出某个设计齿形,刀齿该长什么样?

直觉上的答案是"就照设计齿面做刀刃"(业内叫成形刀条)。 这个答案是错的,而且错得不显眼:展成出来的齿形看着很像那么回事, 不拿尺量根本发现不了。展成法的刀具必须与工件共轭, 设计齿面本身并不共轭于它自己。

怎么做的

正向展成的对偶运算:让一个带着真齿形的工件在刀条毛坯上滚过去, 扫掉的部分之外的残留,就是共轭刀齿(241 个展成位置, 每步在工件坐标系里做一次布尔减材)。 然后用同一把尺(同一个偏差测量函数)让两把刀各自正向展成一遍回验, 对照组和实验组走完全相同的后处理,否则比出来的差异可能来自量法而不是刀具。

⚠ 我们踩的坑

坑就是上面那个"直觉上的答案",我们先信了它。 成形刀条的实现更简单、跑得更快、出来的齿形肉眼看不出问题 —— 它一路活到了被真正量一次为止。工作面过切 4.99 µm, 而这个尺寸的啮合预算只有 10 µm 量级,一把刀就吃掉了一半

值得说的是:发现它的不是"我们比较仔细",是流程里有一步叫"量一下对不对"。 没有这一步的话,这个错误会一直跟到工装图上。

现在能验的数

刀条工作面过切rms 偏差判定
成形刀条(直接拿设计齿面)4.99 µm13.28 µm 超(判据 ≤2 µm)
逆包络求出的共轭刀条0.083 µm0.26 µm 合格

出处:sim/cam/hob_conjugate_result.json,双圆弧齿形、速比 80、模数 0.5, 逆包络 + 两次回验合计 0.8 秒。纯 Python + shapely,不依赖任何商业 CAD 许可证

一处主动声明:我们另有一组 S 齿形的记录(成形刀条过切 10.4 µm, 比这里的双圆弧还严重),数字更有冲击力 —— 但那次运行的结果文件没有归档, 只剩源码注释里的一行记载。按本站规矩,没有归档结果的数字不往外写, 所以这一页用的是能被复现的双圆弧那组。

案例 ⑤把有限元接进桌面端:先证明它没解错,再看它算了什么

整圈摆线盘体变形 · 结构化环形网格 + CalculiX · Julia 写 deck / C# 跑求解器

问题是什么

载荷分配算的是"扭矩怎么摊到各个针齿上",而它的每一个接触都坐在刚性支承上 —— 弹性只存在于赫兹接触本构里,盘体本身不会变形。 真实的盘会变形,变形又会反过来改变载荷分配。要补上这条通道, 第一步得先能回答一个更简单的问题:给定这组针齿力,盘到底怎么变形

通常这一步意味着导出几何、开一套商业前后处理、人工划一遍网格。 我们要的不是这个 —— 参数一改就得整条重来, 那和案例① 里"于是没人扫参数"是同一个死法。

怎么做的

几何是参数化算出来的,网格就该跟着算出来,不该是画出来的。 从中心孔到齿面做极坐标映射的结构化环形网格,每个周向站位同样的行数, 最后一列与第一列共用节点。环到底闭没闭合,用自由边计数验: 1 mm 一档数出 830 条,恰好等于 2×415(内外各一圈),缝没有裂开。 载荷取内核解出的针齿力,沿精确接触法向分摊到三个角节点(权重 ¼ ½ ¼)。

分工是刻意的:Julia 内核只产出 deck 文本,不起进程, 于是它仍然是一个零依赖、在没有求解器的机器上也能跑测试的计算库; 找求解器、开临时目录、起进程、读回 .frd 由 C# 客户端做。 求解器用 CalculiX,GPL v2,我们不附带,由用户自己安装 —— 找不到时给出的是三个下载地址,不是一句"求解失败"。

摆线盘在给定针齿力下的变形形状,放大两万倍
桌面端截图:盘体变形形状,放大 20,000 倍。峰值约两微米、盘直径 114 mm, 一比五万七 —— 不放大的话变形与未变形是同一个像素, 所以倍数必须直接画在图上:没有标注的变形图,画的是一个不存在的零件。
⚠ 这张图是未修形工况(40 根针齿同时相切), 与下表那组(修形 20 µm、承载 4 齿)不是同一个工况,两组数不要合并。 图上刻意不带数字 —— 可引用的数在下表,出处见表下。
⚠ 网格里只有盘体:不含针齿、壳体、曲柄轴承,也不含柱销孔,画的是一个实心环。

⚠ 我们踩的坑

坑一:最想报的那个数,恰恰是不收敛的那个。 峰值位移三档网格给出 2.158 / 2.065 / 2.208 µm, 而同一批解里的远场探针是 −0.2137 / −0.2112 / −0.2114 µm。 在最细的两档之间(判据看的就是这一段):峰值变了 6.47%, 探针变了 0.069%差 94 倍。 这不是网格不够细:二维点载荷的位移对数发散,峰值永远坐在载荷底下 (最细一档离载荷 0.28 mm),再细一档它还会变。 ⇒ 判据改看远场;峰值照报,但旁边永远跟着一句它是什么。

坑二:一行长着"无害加固"样子的代码。 .frd 是定宽格式,每个数据块用一行恰好三个字符的终止符收尾, 而读文件时我们顺手加了一句"跳过长度不足五个字符的行"。 终止符被跳过 ⇒ 反力那一整块并进了位移块 ⇒ 牛顿被当成毫米读回来。 其他每一条检查照样通过:块名对、分量里有 D1/D2、节点数对、文件存在。 两份实现逐行对读三轮,一无所获 —— 差别在源码里根本看不出来。 最后抓到它的是一条只看分量名单、不看数值的测试。 这个仓库每一层都有验证,唯独交付给用户的那一层,当时一个测试都没有。

现在能验的数

单元 mm单元数峰值位移 µm远场探针 µm 平衡:力平衡:力矩
2.003,9332.158−0.2137 2.0e−078.5e−08
1.0016,1852.065−0.2112 1.2e−074.1e−08
0.7528,7562.208(细半段变 6.47%) −0.2114(细半段变 0.069%) 9.5e−081.1e−09

三条不共用代码的检查各验一件事: deck 里所有节点力的合力矩与输出扭矩差 0.19%(2 mm)/ 0.35%(1 mm)—— 验的是"deck 复现了内核的解";上表的平衡残差验的是"求解器解的是我们写的那个问题"; 而约束点位移读回来恰好为零(不是"接近零")—— 验的是"定宽文件按写入时的列对齐读回来了"。 第三条以外没有任何东西能发现坑二,因为错位产出的数字看起来完全像位移。

出处:validation/ring/results/disc_study.jsonmesh_and_deck.json。设计点:针齿 40、分布圆 60 mm、偏心 1.2 mm、 针齿半径 4 mm、齿宽 15 mm、中心孔 R16、均匀修形 20 µm、输出扭矩 75 N·m, 该修形量下参与承载的针齿 4 个。1 mm 网格 49,385 节点 / 16,185 单元(415 列 × 39 行)。 求解器 CalculiX ccx,用户自行安装。

这不是一个可以引用的变形量。四条口径随结果一起回传,页面照抄: 针齿力是输入,来自刚性支承的解,不是从这个变形重新解出来的; 中心孔按刚性固定(真机是曲柄轴承,有游隙与柔度)⇒ 本模型低估变形; 没有柱销孔,而那正是真实摆线盘最弱的地方; 峰值位移不随网格收敛(见上)。方向能说,大小不能。

这五件事其实是一件事。

案例①是"该用闭式解就别上迭代",案例④是"该老老实实求共轭就别抄近路", 案例⑤是"闭式解补不上的那条通道,该上有限元就得上"—— 方向各不相同,判断标准是同一个:这道题的结构决定用什么方法,不是习惯决定

案例②③⑤里那几个坑长得也一样:一个把错参数留在现役位置,一个把"没评"标成"通过", 还有一个把反力当位移读了回来。三个都不会报错,三个都看着正常。 做工程计算工具,一大半的工作量是花在让错误发得出声

所以这一页写的是坑,不是成果 —— 成果谁都会写。 能把坑写出来,说明这些东西是真跑过、真量过、真错过一次的。