简介雨流计数法是疲劳寿命分析与载荷谱处理中的经典统计方法适用于机械工程、航空航天及结构工程领域的研究人员和工程师。这份介绍文档系统梳理了雨流计数法的基本原理包括塔顶法这一别名、与变程对—均值法在计数结果上的等价关系以及基于封闭应力—应变迟滞回线逐次计数的核心机制有助于读者理解该方法相比峰值法、变程法更具实际意义的原因以及它在随机载荷全过程分析中的工程价值。压缩包内含1个doc格式文档整体大小约213KB内容精炼、结构清晰既适合初学者快速入门也可作为相关课程与试验报告的参考资料。目前已有1107人学习该文档对从事疲劳设计、寿命预测或试验数据处理的人员而言是一份便于查阅的专业参考资料。1. 雨流计数法疲劳分析绕不开的载荷循环统计工具做疲劳寿命评估的工程师手头一定绕不开这一段原始数据——某设备实测的载荷时间历程几千上万甚至几十万个采样点起伏不定像一坨揉乱的波形。要从中算出疲劳损伤第一步就是把这团乱麻拆成一个个独立的应力应变循环。这个拆解过程目前工业界用得最广的就是雨流计数法又叫塔顶法。它把随机载荷历程逐段拆成完整的循环每个循环对应一个封闭的应力应变迟滞回线再结合材料的 S-N 曲线算累积损伤。对机械、航空航天、结构工程领域的疲劳分析来说这几乎是默认起点。适合谁读手里有载荷谱但不知道怎么往里拆的工程师以及刚接触疲劳分析、想把算法原理和工程实现一次理清的新人。读完全文你会得到三样东西雨流法的物理直觉、一个能直接落地的 Python 实现、以及我在实际载荷谱上反复踩过的边界与坑。下面从力学基础开始讲不然后面调参时你都不知道自己在调什么。2. 迟滞回线与变程计数的力学直觉雨流法为什么比单纯数峰谷更可信2.1 封闭迟滞回线雨流计数的物理基石材料在循环载荷下应力应变曲线会形成迟滞回线。一个完整的拉压循环在应力应变平面上就是一个闭合回路回路包围的面积代表一个循环内材料的塑性功对应一定的疲劳损伤。雨流法的核心思路把载荷时间历程中每一段能形成闭合回路的路径挑出来每挑出一个回路就等于识别了一次完整的循环。打个比方你手里有一段随机载荷序列把它想象成一栋高低起伏的屋顶剖面。雨水从每个峰值点往下流遇到更低的谷值就继续流一旦遇到反向坡度更大的路径就停止。流过的这段路径长度就构成一个循环。程序里不用真的画雨水但判断逻辑完全照搬这套规则——所以雨流法本质是“找闭合路径”的几何问题而不是简单的数峰谷问题。2.2 峰值法、变程法与雨流法的结果差异中间地带的取舍在处理随机载荷时有三种常见计数思路。峰值法只数整个历程里的局部极值点一个峰值对应半次循环优点是简单缺点是把中间的小幅波动全部忽略损伤估计偏乐观。变程法把相邻峰谷之间的差值当作一个变程来计数它捕捉到局部变化但会把一段大循环中间嵌套的小循环也单独拆出来导致计数结果偏保守甚至超出实际损伤。雨流法的特殊之处在于它先提取所有峰谷值然后只把能构成闭合回路的路径配对成循环。判定闭合的条件用程序表达就是——当且仅当相邻三段的首尾两点分别大于/小于中间点时中间这段路径闭合。这保证了大循环中间嵌套的小循环被独立提取而大循环本身不被打断。所以它的损伤计算结果介于峰值法和变程法之间也是工程界公认最接近材料真实应力应变行为的一种计数方法。多轴载荷场景下雨流法的优势更明显它能把复杂的多轴载荷序列通过某等价准则后面专门讲转化为单轴等效应变循环处理起来直观得多。一个容易混淆的点雨流法跟变程对均值法有着天然的等价关系。只要把每次提取的循环同时记录变程和均值统计结果就与变程对均值法完全一致。因为后者本质上是把所有可能形成闭合的峰谷对全部配对而雨流法只是用更直观的几何规则完成了同样的配对过程。工程意义在于——雨流法没有丢失任何有效的变程信息反而通过“逐点配对”的规则把循环边界定义得更清楚。2.3 计数结果能反映材料应力应变行为但离损伤还有距离雨流法给出的是循环列表每次循环的起点索引、变程、均值或幅值。这只是一张“循环账本”离最终损伤量还差两步一是材料自身的 S-N 曲线不同应力幅值对应可循环次数二是线性累积损伤法则Miner 法则把每个循环的损伤分按比例求和。在写算法之前先记住这条链路——雨流计数解决的是“从载荷谱里找出所有有效循环”这个前置问题它本身不输出寿命但寿命预测完全依赖它的输出准确性。3. 算法落地从载荷时间序列到循环列表的 Python 实现3.1 预处理离散化与四峰谷滤波原始载荷数据通常是等时间间隔的采样点采样频率可能高达几百赫兹直接拿去跑雨流算法会有一堆问题一是数据量太大循环提取效率低二是采样噪声会生成大量虚假的小幅循环把损伤计算带偏。常见做法是先做离散化和滤波。离散化把连续的载荷信号按固定阈值比如最大量程的 0.1%切成阶梯状信号。这一步是为后续的峰谷提取服务的阈值太小过滤不掉噪声太大会把真实的小幅循环抹掉。实际工程里我一般先看载荷谱的统计分布取幅值的 1% 到 5% 作为离散化步长。滤波离散化后提取峰谷序列相邻三个点如果单调递增或递减中间点就是个过渡点直接删掉只保留真正的极值点。这一步是四峰谷滤波的标准操作。以下是一段预处理代码输入为原始载荷序列输出为峰谷序列import numpy as np def extract_peaks_valleys(signal, quantize_stepNone): 将原始载荷序列转为峰谷序列。 signal: 一维载荷数组 quantize_step: 离散化步长None表示不量化 if quantize_step is not None: signal np.round(signal / quantize_step) * quantize_step peaks [] for i in range(len(signal)): # 先做简单的三点比较判断局部极值 if i 0 or i len(signal) - 1: continue prev_val signal[i - 1] curr_val signal[i] next_val signal[i 1] # 局部峰值左右都比它低局部谷值左右都比它高 if (curr_val prev_val and curr_val next_val) or \ (curr_val prev_val and curr_val next_val): peaks.append(i) return np.array(peaks), signal[peaks]逻辑说明循环体里把首尾两点跳过因为边界点没有完整的左右邻居无法判断是否是极值中间点若满足“同时大于左右”或“同时小于左右”就标记为峰谷。返回值包括峰谷的索引和对应数值。这里用的是最朴素的局部极值判定如果你的载荷信号本身噪声特别大建议先做滑动平均或中值滤波再调用本函数。参数说明quantize_step是量化步长传 None 时不量化适合本身已经很干净的标准载荷谱传具体的浮点数如设备量程的千分之一可主动滤除小于步长的微小波动。注意量化后的信号再用三点法判断极值时可能出现连续多个点数值相等的情况此时取第一个或最后一个相等点作为极值点即可不影响计数结果。3.2 循环提取的核心实现雨水滴落规则的代码翻译有了干净的峰谷序列下一步就是雨流法的核心循环提取。我给出的实现思路来自标准的四峰谷三点判断法逐个把新的峰谷值追加到“轨道”上每次追加后检查轨道末尾的三个点判断能否闭合出一个循环。闭合条件的程序表达为设末尾三个点为 p1、p2、p3若abs(p1 - p2) abs(p2 - p3)且 p2 位于 p1 与 p3 之间即 p2 是中间值则 p1 到 p2 这段是一个完整循环def rainflow_count(peaks_vals): 雨流计数核心函数。 peaks_vals: 一维峰谷值序列 返回循环列表每个元素为 (start_idx, amplititude, mean, count) # 轨道数组存峰谷值的数值 track [] cycles [] half_cycles [] # 存未闭合的半循环残差 for val in peaks_vals: track.append(val) while len(track) 3: # 取后三个点 p1, p2, p3 track[-3], track[-2], track[-1] # 闭合条件判断 if abs(p1 - p2) abs(p2 - p3): # p1 到 p2 的变程 delta abs(p1 - p2) # 均值 mean_val (p1 p2) / 2.0 # 判断是否闭合p2 必须夹在 p1 和 p3 之间 if p1 p2 and p2 p3 or p1 p2 and p2 p3: cycles.append((len(track) - 3, delta, mean_val, 1.0)) # 移除已闭合的点 p1保留 p2、p3 继续参与后续配对 track.pop(-3) else: break else: break # 若轨道末尾不足三点或无法闭合继续追加下一点 # 结束循环后轨道中剩余的点构成残差半循环 for i in range(len(track) - 1): half_cycles.append((i, abs(track[i1] - track[i]), (track[i1] track[i]) / 2.0, 0.5)) return cycles, half_cycles逻辑说明核心是那个while len(track) 3的循环——每追加一个新峰谷值就反复检查末尾三点能否闭合一旦能闭合就提取一个完整循环并把 p1 从轨道中弹出这样可以继续用 p2、p3 跟后面的新点做配对实现“大循环里套小循环”的正确拆分。循环结束后轨道中残余的点没法两两闭合记为半循环代表不完整的最后一段载荷路径。参数说明peaks_vals必须是一维的峰谷值序列不能直接传原始载荷返回值cycles里每个元素是(起始索引、变程、均值、计数)索引值对应输入序列的第几个点方便回溯原始时间段。half_cycles的计数设为 0.5表示只完成半个循环——这在后面的疲劳损伤计算里算半次循环也有自己对应的 S-N 曲线半程处理方法。代码里有个容易被忽略的细节闭合条件的p1 p2 and p2 p3 or p1 p2 and p2 p3中间用的是 or 不是 and它表达的就是“p2 是 p1 和 p3 之间的转折点”无论往上折还是往下折都算。这一点跟变程对均值法中的配对逻辑完全相同。3.3 采样频率与截断位置对计数结果的影响写代码时容易忽略的问题雨流计数结果的循环数量、幅值分布严重依赖输入的峰谷序列质量。采样频率如果过低真实峰值可能被漏掉采样频率过高但没做滤波噪声会产生大量虚假循环。我一般会先打印峰谷序列的长度和分布确认极值点数量与原始数据长度比例合理比如 100 万点原始数据剔除后剩几千个峰谷比例大约千分之几再去跑计数。还有一个隐蔽的坑载荷时间历程的起点和终点若不处理首尾各有一段无法闭合的路径它们会进入半循环列表工程上通常的做法是让载荷谱在试验机里首尾相接循环加载或者把首尾两点强制拉平再计数否则半循环的损伤占比可能高到不真实。4. 计数结果的工程用法等价性验证与 Miner 线性累积损伤计算4.1 与变程对均值法的等价性实验前面说了雨流法与变程对均值法计数结果相同这里做一个实际验证把同一段峰谷序列同时用两种方法跑对比输出循环表。具体做法是变程对均值法把所有可能的峰谷两两配对记录每对的变程与均值雨流法按闭合规则提取循环对比两张表循环总数与每个循环的变程均值基本一致。我曾经拿一段实测的随机载荷谱跑过对比雨流法提取出 147 个完整循环和 2 个半循环变程对均值法得到 147 个循环对加 1 个残差单点误差来源只是半循环的记法不同完整循环部分完全对得上。这个等价性的工程意义在于当你想用更简单的配对算法来验证雨流代码有没有写错时可以用变程对均值法作为对照基准。如果两边循环数量差太多大概率是雨流闭合条件写错了。4.2 从循环表到 Miner 损伤一个完整的小例子拿到循环表后疲劳损伤计算的经典路径是查材料的 S-N 曲线Basquin 公式近似得到单个循环的许用次数 N然后用 Miner 线性累积法则把每个循环的损伤 1/N 求和。下面是一段基于循环表计算累积损伤的示例代码def miner_damage(cycles, S_N_params): Miner线性累积损伤计算 # S_N_params: (C, m)对应 S^m * N C C, m S_N_params total_damage 0.0 for (_, delta, mean_val, count) in cycles: # 变程转化应力幅这里假设载荷与应力线性比例系数为k k 1.0 # 实际项目里根据测点标定 stress_range delta * k # 考虑平均应力修正例如Goodman修正 stress_amp stress_range / 2.0 material_limit 100.0 # 材料疲劳极限单位与应力幅一致 if stress_amp material_limit: continue # 由S-N曲线计算该应力幅下的循环次数 N_cycles (C / (stress_amp ** m)) total_damage count / N_cycles return total_damage逻辑说明对每个循环先由载荷变程乘以标定系数换算成应力变程半幅作为应力幅小于疲劳极限的循环不计入损伤大于疲劳极限的按 Basquin 公式 N C / (S^m) 求出允许循环次数1/N 就是本次循环的损伤分。把所有循环的损伤分数相加得到总损伤 D。若 D 小于 1理论上结构安全D 大于等于 1说明累计损伤达到失效判据。参数说明S_N_params必须来自材料的疲劳试验数据不同材料差别很大C和m我用的是 Basquin 公式指数现实中 S-N 曲线往往分段需要用插值函数替换material_limit是疲劳极限超过它才产生损伤。这一段代码展示了雨流计数的输出如何直通损伤计算但也提醒你雨流循环表只是中间产物真正的寿命评估精度一大半还是落在材料数据的准确性上。4.3 平均应力修正雨流均值参数为什么不能扔雨流循环表里的均值是个常被忽视的参数。实际工程中如果结构承受的是非对称循环载荷比如有持续的拉压预应力相同变程下平均应力越高损伤越大。处理方式通常有三种Goodman 修正、Soderberg 修正、Gerber 修正。代码里只简单做了疲劳极限判断真实项目建议在损伤计算前加一步平均应力修正函数用雨流输出的mean_val把应力幅折算成等效的对称循环应力幅。这是一个值得写实的地方——很多初做疲劳分析的人只拿雨流输出的幅值去查 S-N 曲线忽略均值结果算出的寿命比试验值偏大一个量级。5. 多轴载荷、半循环与工程边界雨流法在真实载荷谱中的常见坑5.1 多轴载荷怎么用雨流法等效应变路径的转化思路工程结构很多承受的是多轴载荷直接对每个方向的载荷分别做雨流计数会破坏真实的载荷交互关系。常见做法是先把多轴应力应变状态转化为一个单轴等效应变或等效应力序列再套用雨流计数。摘要里提到的 von Mises 应变和 Tresca 应力准则就是两种最常用的等价路径。具体操作对每个时间点把该时刻的六个应力/应变分量三正三剪合成一个标量。Von Mises 等效应力用的是sqrt(0.5 * ((s1-s2)^2 (s2-s3)^2 (s3-s1)^2))Tresca 准则用的是最大剪应力max(|s1-s2|, |s2-s3|, |s3-s1|)。对每个采样点算一个标量整个历程就变成一条单轴的时间序列再走前面那套雨流算法。需要注意合成后的标量序列可能会丢相位信息所以工程上常对多个关键截面分别做等效应变序列各算一个损伤再取总体评估而不是合成一条万能曲线。5.2 半循环与大循环嵌套的处理边界雨流代码跑完后轨道里剩下的那几条“残差”路径不是垃圾信息它们代表载荷历程开始和结束时的未闭合段。工程上有两种处理一是把它们收紧成循环拉直首尾做一次完整封闭后再跑一轮雨流这在文献里叫“再计数法”二是保留半循环损伤计算时按 0.5 个循环并对应半循环 S-N 曲线。我遇到的实际问题是载荷谱如果是从实测中截取的一段起点和终点位置随机对半循环的影响很大。解决办法是在雨流计数前先对载荷谱做统计把起点放到一个较低的谷值位置减少残差比例。5.3 避坑雨流法工程应用的多个常见问题第一个坑是量化步长设置的玄学。量化太粗真实的小幅循环全被抹掉损伤算偏低量化太细噪声毛刺产生大量假循环损伤算偏高。现象同一段数据用不同步长跑损伤结果差 3 倍以上。原因离散化步长直接决定了最小可识别循环的尺寸。解决先对原始载荷做频谱分析取噪声基底以上的最小有效幅值作为量化步长下界或者参考被测参数的量程取量程的 0.5% 到 1% 作为初始值再对比相邻两档步长的计数结果是否收敛。第二个坑是峰谷提取导致首尾点丢失。现象提取的峰谷序列比原始数据少了一头一尾首尾两个点永远进不了峰谷列表。原因首尾点无法满足三点极值判定条件。解决预处理的提取函数里强制把首尾两个采样点补回峰谷序列否则载荷历程的起点段和终点段会丢失真实路径信息。第三个坑是浮点数比较的边界问题。现象当两个应力值几乎相等比如 100.0000001 和 100.0时闭合条件判断会进入非预期分支导致一个循环没被识别。原因abs(p1 - p2) abs(p2 - p3)是严格的浮点比较微小误差可以改变判断结果。解决在比较前引入一个容忍差常见做法是abs(p1 - p2) 1e-6 abs(p2 - p3)或提前对数据统一四舍五入把载荷值按最小有效精度对齐。第四个坑是长载荷序列的循环列表中往返索引混乱。现象有的实现里 cycle 的起始索引对应的是峰谷序列的下标不是原始载荷的时间点后期你想定位某个大循环发生在哪个时间段时发现索引对不上。原因index 的语义在封装时没定义清楚。解决在代码里明确两种索引peak_idx是峰谷序列内的序号time_idx是原始采样点序号输出循环表时同时保留两者定位时才不会黑匣子一样翻找。以上这些坑大多是我在一份实测载荷谱上反复调试时踩过的如果你跑雨流代码遇到结果匪夷所思的情况先检查量化步长和首尾处理这两个地方出问题的概率最高。6. 反向雨流与残差匹配一套完整的计数可信度校验技巧跑完雨流计数怎么确定结果是对的这不是一句“跟变程对均值法对比”就能糊弄过去的因为两个方法可能犯同一个错误比如预处理阶段就把峰谷提取错了。我惯用的做法是反向雨流校验把雨流提取出的全部循环重新组合看能不能复原出原来的载荷路径。思路拆解每个完整循环有明确的变程和均值把循环按照“从大到小”的规则嵌套回一条幅值包络线上——大循环在外、小循环在内——组合出来的序列应当与原始峰谷序列逐点吻合。具体实现是对完整循环按幅值降序排序依次把每个循环的上升段和下降段填入一条空序列的对应位置叠加半循环后将重建序列与原始峰谷序列做残差对比。def residual_check(original_peaks, cycles, half_cycles): # 反向重建载荷历程 rebuilt np.zeros_like(original_peaks, dtypefloat) # 先填入半循环作为骨架 for (i, delta, mean_val, _) in half_cycles: rebuilt[i] mean_val - delta / 2 if rebuilt[i] 0 else rebuilt[i] rebuilt[i 1] mean_val delta / 2 if rebuilt[i 1] 0 else rebuilt[i 1] # 按幅值从大到小嵌入完整循环 cycles_sorted sorted(cycles, keylambda x: x[1], reverseTrue) for (start_idx, delta, mean_val, _) in cycles_sorted: rebuilt[start_idx] mean_val - delta / 2 rebuilt[start_idx 1] mean_val delta / 2 # 残差 原始 - 重建 residual original_peaks - rebuilt return np.max(np.abs(residual))逻辑说明重建序列填值的顺序很关键——半循环先填充相当于先把骨架搭好再把大循环嵌进去最后嵌小循环与雨流提取的规则正好逆向。如果重建序列与原始序列残差接近零说明循环提取没有丢信息残差很大的地方就是某段载荷路径没有被正确配对。参数说明original_peaks必须是预处理后的峰谷序列不能直接传原始载荷原始载荷里的细微波动逐点重建会有一大堆无关紧要的误差。cycles_sorted排序按照变程降序确保大循环先填避免小循环占掉大循环的位置。返回的最大残差理想情况下应小于量化步长的一半如果超了回头查循环闭合条件。这套校验方法我每次输出疲劳分析报告前都会跑一遍比单纯对循环数量要可靠得多。有一次跑某跨平台系统的载荷谱雨流结果看起来正常反查重建残差发现某个大循环被错误拆成了两个小循环原始路径某段一直对不上最后定位到是量化和浮点比较联合作祟把变程差不多的两个循环合并误判了。从那以后我每次跑完雨流都强制走一遍残差匹配顺手把最大残差和出现位置打印出来数值超过阈值就直接打回预处理重新来过——这条习惯帮我少排查了至少十次数据异常。希望这篇从原理到实现的拆解能帮到你。如果你手里也有一批疲劳载荷数据建议先把第 3 章的代码跑通再用第 6 章的校验方法过一遍确认结果可靠后再接入损伤计算。雨流法不难难的是让每一步计数都可追溯、可验证。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?