从"这道题该用什么模型"到"这个数凭什么信" —— 五个案例,都附我们自己踩的坑
这五个案例都是我们自己的工程项目,不是客户项目。 客户的东西不放在这里 —— 能公开的只有我们自有的模型与代码。 页面上每个数字都取自归档的结果文件(下方各案例末尾标了出处), 没有归档结果的数字一律不引用,即使我们记得它是多少(见案例④ 最后一行)。
摆线针轮多针齿接触力 · 解析式降阶模型(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×) |
自己验:打开 多针齿接触力时域分析, 同一组参数点一次"物理求解"、再点一次"降阶模型", 对比曲线与状态栏里的耗时、迭代次数、是否命中缓存。 耗时是每次真算出来的,所以第二次点会比第一次快 —— 这也是它没作假的证据。
谐波减速器系列批产 · 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 诚实。这条口径也直接印在页面上,而不是悄悄跳过。
柔轮外齿滚齿展成 · 加工工艺仿真 · 纯 Python + shapely
滚齿是展成法加工:刀具与工件对滚,刀齿扫过的包络从毛坯上啃出齿形。 那么反过来 —— 想加工出某个设计齿形,刀齿该长什么样?
直觉上的答案是"就照设计齿面做刀刃"(业内叫成形刀条)。 这个答案是错的,而且错得不显眼:展成出来的齿形看着很像那么回事, 不拿尺量根本发现不了。展成法的刀具必须与工件共轭, 设计齿面本身并不共轭于它自己。
正向展成的对偶运算:让一个带着真齿形的工件在刀条毛坯上滚过去, 扫掉的部分之外的残留,就是共轭刀齿(241 个展成位置, 每步在工件坐标系里做一次布尔减材)。 然后用同一把尺(同一个偏差测量函数)让两把刀各自正向展成一遍回验, 对照组和实验组走完全相同的后处理,否则比出来的差异可能来自量法而不是刀具。
坑就是上面那个"直觉上的答案",我们先信了它。 成形刀条的实现更简单、跑得更快、出来的齿形肉眼看不出问题 —— 它一路活到了被真正量一次为止。工作面过切 4.99 µm, 而这个尺寸的啮合预算只有 10 µm 量级,一把刀就吃掉了一半。
值得说的是:发现它的不是"我们比较仔细",是流程里有一步叫"量一下对不对"。 没有这一步的话,这个错误会一直跟到工装图上。
| 刀条 | 工作面过切 | rms 偏差 | 判定 |
|---|---|---|---|
| 成形刀条(直接拿设计齿面) | 4.99 µm | 13.28 µm | 超(判据 ≤2 µm) |
| 逆包络求出的共轭刀条 | 0.083 µm | 0.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,我们不附带,由用户自己安装 ——
找不到时给出的是三个下载地址,不是一句"求解失败"。
坑一:最想报的那个数,恰恰是不收敛的那个。 峰值位移三档网格给出 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.00 | 3,933 | 2.158 | −0.2137 | 2.0e−07 | 8.5e−08 |
| 1.00 | 16,185 | 2.065 | −0.2112 | 1.2e−07 | 4.1e−08 |
| 0.75 | 28,756 | 2.208(细半段变 6.47%) | −0.2114(细半段变 0.069%) | 9.5e−08 | 1.1e−09 |
三条不共用代码的检查各验一件事: deck 里所有节点力的合力矩与输出扭矩差 0.19%(2 mm)/ 0.35%(1 mm)—— 验的是"deck 复现了内核的解";上表的平衡残差验的是"求解器解的是我们写的那个问题"; 而约束点位移读回来恰好为零(不是"接近零")—— 验的是"定宽文件按写入时的列对齐读回来了"。 第三条以外没有任何东西能发现坑二,因为错位产出的数字看起来完全像位移。
出处:validation/ring/results/disc_study.json 与
mesh_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,用户自行安装。
⚠ 这不是一个可以引用的变形量。四条口径随结果一起回传,页面照抄: 针齿力是输入,来自刚性支承的解,不是从这个变形重新解出来的; 中心孔按刚性固定(真机是曲柄轴承,有游隙与柔度)⇒ 本模型低估变形; 没有柱销孔,而那正是真实摆线盘最弱的地方; 峰值位移不随网格收敛(见上)。方向能说,大小不能。
这五件事其实是一件事。
案例①是"该用闭式解就别上迭代",案例④是"该老老实实求共轭就别抄近路", 案例⑤是"闭式解补不上的那条通道,该上有限元就得上"—— 方向各不相同,判断标准是同一个:这道题的结构决定用什么方法,不是习惯决定。
案例②③⑤里那几个坑长得也一样:一个把错参数留在现役位置,一个把"没评"标成"通过", 还有一个把反力当位移读了回来。三个都不会报错,三个都看着正常。 做工程计算工具,一大半的工作量是花在让错误发得出声。
所以这一页写的是坑,不是成果 —— 成果谁都会写。 能把坑写出来,说明这些东西是真跑过、真量过、真错过一次的。