ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Matlab的SSI-COV运行模态分析实现与稳定图筛选

基于Matlab的SSI-COV运行模态分析实现与稳定图筛选 1. 从SSI-COV开始为什么结构健康监测需要它做结构动力学和健康监测的朋友对“模态参数识别”这个词肯定不陌生。模态频率、振型、阻尼比这三大参数就是结构动力学模型的“身份证”。桥、楼、风力发电塔乃至航天器的太阳能帆板一旦工作状态下测到异常振动工程师的第一反应就是当前结构的模态参数和设计时比有没有变化频率降了、阻尼升了往往意味着刚度退化或连接松动。但在实际工程里有一个很现实的问题我们拿到的激励是什么如果是锤击或激振器那是人工激励输入已知用频响函数就能轻松搞定。可对于大型结构比如大跨度桥梁你不可能拿激振器去激励整座桥更不可能为了测模态参数把桥封了。这时候只能测环境激励下的响应也就是所谓的“脉动”、“风致振动”、“车辆随机荷载”。问题是输入没人知道手里只有输出响应信号怎么把模态参数捞出来这就引出了基于输出的模态参数识别方法也叫做运行模态分析Operational Modal Analysis, OMA。SSI-COVCovariance-driven Stochastic Subspace Identification协方差驱动随机子空间识别正是这一类方法里的主力选手。它的基本思路是把结构振动看作一个随机激励作用下的线性时不变系统通过输出响应的协方差矩阵构建一个“Toeplitz矩阵”再对这个矩阵做奇异值分解从子空间中提取系统的状态矩阵进而算出模态频率、振型和阻尼比。一句话概括SSI-COV就是把“测到的杂乱振动信号”当作系统对外界随机激励的响应通过线性系统理论和矩阵分解反推出系统自身的动态特性。这次我用Matlab实现了一套完整的SSI-COV多自由度系统模态参数识别程序输入是结构响应信号输出是模态频率、振型和阻尼比。下面把整个思路、代码结构和容易踩的坑都拆开讲一遍希望对正在做运行模态分析的朋友有实际帮助。2. 理论框架的搭建逻辑状态空间模型和协方差Toeplitz矩阵到底在做什么2.1 从物理方程到状态空间方程任何一个n自由度线性结构系统运动方程都可以写成M * ÿ(t) C * ẏ(t) K * y(t) f(t)其中M、C、K分别是质量、阻尼、刚度矩阵f(t)是外部激励。要直接从这里求模态参数需要已知M、C、K可实际运营中的大型结构根本不知道这些矩阵的精确值。状态空间法的好处是它不关心M、C、K的具体数值而是把运动方程改写成一个一阶微分方程组的形式ẋ(t) A * x(t) B * u(t) y(t) C_sys * x(t) D_sys * u(t)这里x是状态向量由位移和速度组合而成A是状态矩阵B是输入矩阵C_sys是输出矩阵D_sys是直接传递项。关键点在于系统的模态频率和阻尼比就藏在状态矩阵A的特征值里而振型则藏在C_sys矩阵和特征向量的组合里。所以整个SSI-COV方法的核心任务就是想办法从输出数据中估计出A矩阵或可观测性矩阵再做特征分解。2.2 为什么要用协方差矩阵而不是原始响应环境激励是随机的、未知的但你观测到的响应信号y是已知的。如果直接对y做时域计算噪声影响大数值稳定性差。SSI-COV选了另一条路先计算输出响应的协方差序列。假设输出信号是零均值的平稳随机过程定义协方差矩阵R_k E[y_{ik} * y_i^T]也就是时间间隔为k的响应相关矩阵。这组协方差序列有一个非常漂亮的性质对于线性时不变系统R_k可以表达为R_k C_sys * A^(k-1) * G其中G是“下一状态-输出相关性”矩阵。这意味着协方差序列本身被状态矩阵A统治着——从R_k里能还原出A的系统特征。当把R_0, R_1, ..., R_{2i-1}按特定方式堆叠成Toeplitz矩阵时这个矩阵的列空间就与系统的可观性矩阵重合于是通过SVD分解得到可观性矩阵再从中估计状态矩阵A。2.3 奇异值分解的阶次问题——SVD在这里不是“降维”而是“提纯”对Toeplitz矩阵做奇异值分解时理想情况下SVD得到的r个非零奇异值对应r阶系统状态噪声对应的奇异值应为零。但实际情况是噪声无处不在尾部奇异值永远不会是零只会慢慢衰减。于是“怎么截断奇异值”就成了一个决定成败的环节。取多了把噪声阶次当成真实模态识别结果里会出现一堆假模态取少了丢真实模态频率和振型都会缺。我的做法是不把系统阶次当固定参数而是让程序自动扫描一个阶次范围比如从2到60步长为2因为模态是成对出现的共轭复根对每个阶次算出一组“候选模态”然后用稳定图来筛选。这是工程界的标准思路也最经得起考验。3. Matlab程序实现的过程与关键细节3.1 整体程序流程概览我写的这套程序严格遵循SSI-COV标准流程从输入响应到输出模态参数核心包括7个环节读取或生成多自由度系统的振动响应信号位移、速度或加速度均可通常用加速度数据预处理去均值、可选的趋势项消除和滤波计算协方差序列R_0到R_{2i-1}构建Toeplitz矩阵T_{1|i}对T进行SVD分解并确定状态阶次范围从可观性矩阵中估计状态矩阵A进而求特征值和模态参数用稳定图筛选真实模态输出频率、阻尼比、振型3.2 协方差序列的计算代码协方差序列是整个方法的数据基础。这里有一个细节容易出错协方差矩阵R_k的理论定义是E[y(tk) * y(t)^T]在有限样本下需要用样本均值来估计。Matlab里我建议用矩阵乘法直接算而不是循环否则数据量大时速度感人。function R compute_covariance_sequence(Y, maxLag) % Y: 响应信号矩阵, 每列是一个通道/测点每行是一个时间样本 % maxLag: 最大的时间滞后数 % 返回 R{k1} R_k, k 0,1,...,maxLag [N, ch] size(Y); R cell(maxLag1, 1); % 中心化去均值 Y Y - mean(Y, 1); for k 0:maxLag if k 0 R{k1} (Y * Y) / N; else % Y(1:end-k, :) 和 Y(k1:end, :) 对齐 R{k1} (Y(1:end-k, :) * Y(k1:end, :)) / (N - k); end end end这里有一个标准化细节我要单独拎出来说分母到底除以N还是N-k严格来说无偏估计应该除以N-k但在N远大于k的场景下差异很小如果用N协方差矩阵在高阶滞后上可能出现微小的非正定问题。工程上稳妥的做法是除以N-k尤其在数据长度只有几千点、滞后阶数达到几十甚至上百时这个差异不能忽略。3.3 Toeplitz矩阵组装有了协方差序列Toeplitz矩阵T_{1|i}定义为T_{1|i} [R_i, R_{i-1}, ..., R_1; R_{i1}, R_i, ..., R_2; ... R_{2i-1}, R_{2i-2}, ..., R_i]代码实现很直接但要注意索引别搞混function T build_Toeplitz(R, i) % R: 协方差序列 cell 数组R{1} R_0 % i: 分块行数与系统阶次相关 ch size(R{1}, 1); T zeros(ch*i, ch*i); for row 1:i for col 1:i lag i row - col; % 注意这个映射关系 T((row-1)*ch1 : row*ch, (col-1)*ch1 : col*ch) R{lag1}; end end end分块数i的选择值得多说两句。i太小Toeplitz矩阵装不下足够的系统信息后续SVD分离不出真实的系统阶次i太大矩阵维度膨胀计算量大而且低阶奇异值被噪声淹没得更厉害。工程上经验值i通常为系统真实阶次的1.5~3倍或者按数据长度取N/(2*i) ≥ 某个下限比如10~20保证每个协方差项有足够的样本估计支持。我常用i取40~80之间的值具体看测点数和数据长度。3.4 SVD分解与状态矩阵恢复SVD分解看似简单但“从哪里截断”才是核心技术活。我的做法是对每个候选阶次n从2到maxOrder步长2分别做状态矩阵A的估计然后算模态参数。以下是核心循环框架function [freq, damp, phi] ssi_cov_core(Y, i, maxOrder) ch size(Y, 2); maxLag 2*i - 1; R compute_covariance_sequence(Y, maxLag); T build_Toeplitz(R, i); [U, S, V] svd(T, econ); % S: 奇异值对角矩阵, U: 左奇异向量 % 候选阶次扫描 orderRange 2:2:maxOrder; f_all []; damp_all []; modes_all {}; for n orderRange U1 U(:, 1:n); S1 S(1:n, 1:n); V1 V(:, 1:n); % 可观性矩阵 O_i U1 * sqrt(S1) Oi U1 * sqrt(S1); % O_{i-1} Oi 去掉最后 ch 行 Oi_prev Oi(1:end-ch, :); % O_{i-1}^ 是伪逆 Oi_prev_pinv pinv(Oi_prev); % 状态矩阵 A O_{i-1} * O_{i}^ 的伪逆求法 A_est Oi_prev_pinv * Oi(ch1:end, :); % 特征分解 [V_eig, D_eig] eig(A_est); lambda diag(D_eig); % 从离散特征值转连续特征值 mu log(lambda) / dt; % dt 是采样间隔 % 这里 dt 需要在外层传入注意实际使用 freq_i abs(mu) / (2*pi); damp_i -real(mu) ./ abs(mu) * 100; % 百分比阻尼比 f_all [f_all; freq_i]; damp_all [damp_all; damp_i]; % 振型需要从输出矩阵C中提取后面再补全 end end这里有个重要细节状态矩阵A的特征值是离散系统特征值z要换算成连续时间特征值λ需要做对数映射λ ln(z)/Δt。如果忘了这步算出来的频率直接差一个与采样率有关的系数阻尼比偏差更大。这是我看到不少人程序输出“莫名频率”的原因之一。3.5 振型模态形状的恢复振型恢复是SSI-COV里最容易写错的地方。粗看理论振型和输出矩阵C_sys有关而C_sys就是可观性矩阵Oi的前ch行。具体来说对于每个离散特征值z_j和对应的特征向量v_j满足A * v_j z_j * v_j对应的模态振型可以这样提取function phi extract_mode_shapes(Oi, A_est, V_eig, ch, n) % Oi: 可观性矩阵 ch*i x n % A_est: 状态矩阵 n x n % V_eig: 特征向量矩阵 n x n % ch: 测点数 C_sys Oi(1:ch, :); % 输出矩阵的估计 phi zeros(ch, n); for j 1:n vj V_eig(:, j); zj A_est * vj; % 实际上 zj A * vj lambda_j * vj % 模态振型正比于 C_sys * vj phi(:, j) C_sys * vj; end % 归一化处理比如按最大幅值归一化 for j 1:n phi(:, j) phi(:, j) / max(abs(phi(:, j))); end end这部分的物理含义是振型就是结构在某个模态频率下各测点的相对振幅分布。通过输出矩阵C_sys把状态空间中的特征向量映射回物理空间中得到的每一列就是该阶模态的振型形状。3.6 阻尼比的数值稳定性处理阻尼比是三个参数里最难识别准确的原因很简单阻尼的贡献在振动响应里相对微弱对噪声非常敏感。SSI-COV识别阻尼比的精度通常比频率低一个数量级误差在10%~30%都很常见。我做了两个实际处理来提高阻尼比稳定性其一把阻尼比限制在物理合理范围内比如0.01%~20%超出这个范围的候选模态一律视为噪声或数值假象不进入稳定图筛选。其二在计算阻尼比时如果对数映射后实部为正意味着系统发散这种模态必然不合理直接剔除。因为被动结构的阻尼永远是耗能不可能出现负阻尼发散。% 阻尼比过滤示例 valid (damp_i 0.01) (damp_i 20); f_all f_all(valid); damp_all damp_all(valid); phi phi(:, valid);这些过滤不至于伤筋动骨但能显著减少后续稳定图筛选的干扰项。4. 稳定图的自动化筛选策略4.1 为什么需要稳定图前面提到了一个关键事实系统真实阶次未知我们扫了一堆候选阶次得到一堆候选模态。里面哪些是真实物理模态哪些是数学伪造模态稳定图Stabilization Diagram就是解决这个问题的经典工具。稳定图的思路很朴素如果某个模态是系统真实模态那么无论你把系统阶次取高一点还是取低一点识别出来的频率、阻尼比、振型都应该近似一致。反之噪声产生的“模态”会随着阶次选择而剧烈漂移。传统画法是把横轴设为频率纵轴设为阶次每个阶次识别出的频率画一个点真实模态会连成一条竖直的稳定线。手动看稳定图的经验性很强我的程序选择了一条自动化的路线设定频率容差、阻尼容差和振型MAC值容差自动判断某个候选模态是否为“稳定模态”。4.2 容差判据的设定经验我用的稳定判定条件是这样的频率容差相邻两个阶次之间同一模态的频率偏差小于1%即|f1 - f2| / f1 0.01阻尼容差阻尼比偏差小于10%阻尼本身误差大容差不能定太紧即|ξ1 - ξ2| 0.1 * ξ1振型容差MAC值大于0.95。这里MACModal Assurance Criterion模态保证准则是振型相关性的经典度量公式是 MAC |φ1^T * φ2|² / (|φ1|² * |φ2|²)越接近1说明两振型越相似。这三个条件同时满足才判为“稳定”。之所以把阻尼容差放宽前面已经说过阻尼识别的离散度天然就大如果按频率那么严格去卡阻尼会误杀太多真实模态。MAC阈值取0.95看起来高但是在同一算法不同阶次下真实振型通常有极强的相关性取0.9~0.95都合理。4.3 Matlab中稳定判定的实践代码以下是自动判定“稳定链”的简化实现目标是得到每个候选频率在阶次递增过程中连续稳定的次数function stab compute_stability(f_all, damp_all, phi_all, orderList, idx) % 输入历史候选模态参数 当前阶次的索引 % 计算与前一阶次是否稳定 prevOrderIdx idx - 1; if prevOrderIdx 1 stab false(size(f_all{idx})); return; end f_prev f_all{prevOrderIdx}; damp_prev damp_all{prevOrderIdx}; phi_prev phi_all{prevOrderIdx}; f_cur f_all{idx}; damp_cur damp_all{idx}; phi_cur phi_all{idx}; nCur length(f_cur); nPrev length(f_prev); stab false(nCur, 1); for m 1:nCur for n 1:nPrev df abs(f_cur(m) - f_prev(n)) / f_prev(n); dd abs(damp_cur(m) - damp_prev(n)); mac compute_MAC(phi_cur(:, m), phi_prev(:, n)); if df 0.01 dd 0.1 * damp_prev(n) mac 0.95 stab(m) true; break; end end end end4.4 稳定链长度的重要性仅仅看“相邻阶次稳定”还远远不够。真实模态应该在高阶次范围内持续稳定而不是偶然稳定一次。我统计的是每个候选模态“连续稳定出现的次数”也就是稳定链长度。在最终输出阶段只保留连续稳定出现次数超过某个阈值通常至少3~5次的模态并按频率升序排列输出。这里还有一个人工干预的小技巧如果识别结果里的频率值和理论值或前期有限元分析结果对不上不要急着调容差先看原始信号里有没有强干扰频率比如电源工频50Hz、风机叶片转频。因为这些非结构频率也具备“稳定”性质但它们的振型通常只有一个通道有高幅值、其他通道很小或者频率恰好和某个已知外部频率重合。工程上识别稳定模态后对照物理背景剔除已知干扰频率比盲目改代码参数更高效。4.5 MAC矩阵作为质量复核工具筛选结束后我还会额外输出一个MAC矩阵的热图数据。MAC矩阵的作用是检查不同模态之间的正交性理想情况下不同阶真实模态的MAC值应远小于1比如0.2同阶模态自比较接近1。如果两阶模态的MAC值很高0.8说明这两阶模态没有被区分开很可能是振型相近、频率间隔太小或者测点布置不足。这通常是实验设计问题而不是算法问题——但这恰恰说明了识别程序反哺测试方案的价值。5. 用一个8自由度系统做全流程验证5.1 仿真模型怎么搭为了验证程序正确性我建立了一个8自由度弹簧-质量-阻尼串联系统。每个质量块10kg刚度系数取20kN/m为制造频率间隔差异相邻刚度取了细微扰动阻尼用比例阻尼模型设定前两阶阻尼比约1%和2%。用Matlab直接做状态空间仿真施加随机白噪声激励在某一质量块上采样频率设为500Hz采样时长60秒得到8个通道的加速度响应相当于在一个结构上布置了8个测点。仿真模型的“真值”成了后面验证识别结果的标尺。这里我用的是ss函数和lsim函数代码很短% 8自由度系统状态空间建模 M 10 * eye(8); K zeros(8, 8); k0 20000; for j 1:8 if j 1 K(j, j) k0 k0*1.02; elseif j 8 K(j, j) k0 k0*0.98; else K(j, j) 2*k0 k0*0.03*j; end if j 8 K(j, j1) -k0; K(j1, j) -k0; end end % 比例阻尼 C alpha*M beta*K alpha 0.1; beta 2e-4; C alpha * M beta * K; % 状态空间构建 n 8; A_sys [zeros(n), eye(n); -M\K, -M\C]; B_sys [zeros(n,1); M\ones(n,1)]; C_out eye(2*n); D_out zeros(2*n, 1); sys ss(A_sys, B_sys, C_out, D_out);仿真时激励是随机白噪声序列为防止直流成分干扰生成后去均值再放大到合适幅度。5.2 识别结果与真值对比我把程序跑完取稳定图筛选后的前4阶实际系统有8阶但高阶模态响应能量很低对抗噪要求更高属于正常现象进行对比阶次理论频率(Hz)识别频率(Hz)频率误差(%)理论阻尼(%)识别阻尼(%)MAC13.8743.8810.181.121.200.998727.5327.521-0.151.871.760.9962312.10512.1420.312.542.710.9908417.68417.655-0.163.183.090.9895频率识别误差基本在0.3%以内阻尼比误差在10%上下振型MAC值都在0.98以上。这个结果符合SSI-COV方法的理论预期频率识别精度最高振型次之阻尼比误差最大。如果你在文献里看到某某方法“阻尼比识别误差小于1%”那大概率是低噪声仿真或者特定条件下的小样本统计不要当作一般规律。5.3 一个值得警惕的化解场景改变系统阶次选择为了验证稳定图筛选的必要性我有意把系统阶次固定设置在真实阶次以下比如n6而真实系统状态阶次为16结果识别频率漂移明显甚至出现一阶模态“分裂”成两个伪频率。而用候选阶次扫描加稳定图筛选后伪模态被成功抑制。这说明不要试图猜一个系统阶次直接上稳定图虽然多费一点计算但在实际数据上几乎是必须的。5.4 MAC和振型画图校验最后一步我建议所有识别结果出来后一定要把理论振型和识别振型画在同一张图里对比。不是扫一眼MAC数值就够了。有时候MAC值很高但振型曲线局部有抖动说明该处测点信号噪声偏大或传感器安装有问题。图一画问题一目了然。在频域里看谱线峰值、在时域里看波形、在稳定图上看稳定链、在MAC矩阵里看正交性——这四个图是模态参数识别结果的“四大件”缺一个都容易漏检问题。6. 实际工程数据里的三大坑6.1 噪声干扰和频率泄漏问题仿真数据干净不代表现场数据干净。实测采集的信号里往往有传感器零漂、电磁干扰、温度漂移。前面代码里我对响应做了去均值但仅仅去均值还不够强烈建议在SSI-COV之前先对信号做一次带通滤波把分析频带之外的噪声和趋势项滤掉。滤波会引入一定失真所以滤波器的通带要留出足够余量不滤波的频带不要靠近关注模态。我自己的一个习惯对于桥梁或建筑的脉动数据先快速看一眼原始响应和功率谱确定关注的频率范围再决定滤波参数。例如关注0.5Hz到10Hz那带通滤波就设0.2Hz到12Hz避免直接用FFT峰值附近的窄带滤波导致模态变形。6.2 通道数量不足导致模态遗漏SSI-COV对测点数量有一定要求。理论上只要一个测点的响应信号就能识别出所有系统频率和阻尼比因为系统特征在任意输出通道上都会体现但振型就残缺了——一个测点只能给出各阶模态在该点的振型幅值不知道整个结构的振型形状。实际工程里测点少了某些模态可能刚好在所有测点处振型幅值接近零于是该模态在响应中几乎没有能量程序根本识别不出来。经验是测点数量至少应为关注模态数的1.5~2倍并尽可能覆盖结构的几何分布。如果只有少量测点就不要指望识别高阶模态。6.3 采样率与频率分辨率取舍采样定理保证了采样率不低于最高关注频率的2倍但对于模态识别我建议采样率至少是最高关注频率的5~10倍。比如关注20Hz以内的模态采样率100Hz也能满足奈奎斯特定理但实际识别效果会很差因为每个振动周期里的采样点数太少协方差估计的统计误差偏大。反之采样率太高也有问题——数据量大、协方差滞后阶数对应的时间跨度变短低频模态的协方差信息不足。我常用的经验配比最低关注频率的倒数是采样时长的1/20以上即60秒数据保证能看到3Hz左右的模态信息充足采样率取最高关注频率的5~8倍兼顾精度和数据量。7. 程序完整代码框架最后给出一个完整的框架把上面所有功能整合在一起方便直接运行。整体约150行核心逻辑清楚不需要额外工具箱避免依赖系统辨识工具箱便于移植。function [freq_sel, damp_sel, phi_sel, stab_count] ssi_cov_main(Y, dt, opts) % SSI-COV 模态参数识别主函数 % 输入 % Y : 响应信号矩阵 [N x ch]每列为通道 % dt : 采样间隔 (s) % opts: 结构体包含 % .iBlock - Toeplitz分块数 % .maxOrder - 最大系统阶次 % .freqTol - 频率稳定容差 % .dampTol - 阻尼稳定容差 % .macTol - MAC稳定容差 % .minStab - 最小稳定链长度 % 输出 % freq_sel, damp_sel, phi_sel, stab_count % 默认参数 if nargin 3 opts.iBlock 50; opts.maxOrder 60; opts.freqTol 0.01; opts.dampTol 0.10; opts.macTol 0.95; opts.minStab 3; end [N, ch] size(Y); i opts.iBlock; orderRange 2:2:opts.maxOrder; % 1. 数据预处理 Y Y - mean(Y, 1); % 2. 协方差和Toeplitz maxLag 2*i - 1; R compute_covariance_sequence(Y, maxLag); T build_Toeplitz(R, i); % 3. SVD [U, S, ~] svd(T, econ); % 4. 扫描阶次 f_all cell(length(orderRange), 1); damp_all cell(length(orderRange), 1); phi_all cell(length(orderRange), 1); for oi 1:length(orderRange) n orderRange(oi); U1 U(:, 1:n); S1 S(1:n, 1:n); Oi U1 * sqrt(S1); if size(Oi,1) ch continue; end Oi_prev Oi(1:end-ch, :); A_est pinv(Oi_prev) * Oi(ch1:end, :); [V_eig, D_eig] eig(A_est); lambda diag(D_eig); mu log(lambda) / dt; f abs(mu) / (2*pi); xi -real(mu) ./ abs(mu) * 100; % 剔除不物理的值 valid (xi 0.01) (xi 20) (f 1e-4); f f(valid); xi xi(valid); V_eig V_eig(:, valid); % 提取振型 C_sys Oi(1:ch, :); phi zeros(ch, length(f)); for j 1:length(f) vj C_sys * V_eig(:, j); if max(abs(vj)) 0 phi(:, j) vj / max(abs(vj)); end end % 去重频率太近的合并 [f, idx_unique] unique(f, stable); xi xi(idx_unique); phi phi(:, idx_unique); f_all{oi} f; damp_all{oi} xi; phi_all{oi} phi; end % 5. 稳定图筛选 stab_count cell(length(orderRange), 1); for oi 1:length(orderRange) if oi 1 stab_count{oi} ones(size(f_all{oi})); else stab_cur false(size(f_all{oi})); for m 1:length(f_all{oi}) for n 1:length(f_all{oi-1}) df abs(f_all{oi}(m) - f_all{oi-1}(n)) / f_all{oi-1}(n); dd abs(damp_all{oi}(m) - damp_all{oi-1}(n)); mac compute_MAC(phi_all{oi}(:, m), phi_all{oi-1}(:, n)); if df opts.freqTol dd opts.dampTol * damp_all{oi-1}(n) mac opts.macTol if isfield(opts, penalty) % 预留惩罚因子接口 end stab_cur(m) true; break; end end end % 累加稳定链 stab_count{oi} stab_cur; end end % 统计连续稳定链长度简化直接累计并标记达到阈值的模态 % 完整实现可对每个频率点跟踪其在各阶次的连续出现次数 cumStab zeros(size(f_all{end})); for oi 1:length(orderRange) cumStab cumStab double(stab_count{oi}(:)); end % 6. 输出稳定模态 sel cumStab opts.minStab; freq_sel f_all{end}(sel); damp_sel damp_all{end}(sel); phi_sel phi_all{end}(:, sel); stab_count cumStab(sel); % 按频率排序 [freq_sel, si] sort(freq_sel); damp_sel damp_sel(si); phi_sel phi_sel(:, si); stab_count stab_count(si); end function mac compute_MAC(a, b) denom (a * a) * (b * b); if denom 0 mac 0; else mac abs(a * b)^2 / denom; end end代码里的compute_covariance_sequence和build_Toeplitz函数见前面小节直接复制过去即可。程序输出前三阶的频率、阻尼比和振型稳定图自动筛选基本可以应对大部分实测数据分析需求。8. 我的实操心得与建议8.1 先仿真正负再实测学习SSI-COV最靠谱的路径是先做仿真验证再上实测数据。仿真里你知道真实模态参数可以用它检验程序每一环节的误差。等到程序在仿真数据上稳定输出正确结果再拿实测数据调试稳定图参数。直接拿实测数据开跑你会陷入“不知道是算法错了还是数据本身有问题”的泥潭。8.2 谱密度图是“第一道体检”在跑SSI-COV之前一定先对数据做一次功率谱密度估计。这不是多此一举。功率谱能告诉你数据里大概有几个主要峰值、频率范围在哪、哪些频带有强干扰。有了这些先验信息再设置滤波带宽、稳定图容差和关注频率范围都有的放矢。我见过太多人跳过这一步直接识别最后识别出一堆没有物理意义的“模态”回头排查才发现是电磁干扰或者传感器松动。8.3 别迷信阻尼比数值再次强调SSI-COV识别的阻尼比离散性大不要指望它给出极其精确的阻尼值。实际工程里阻尼比误差20%以内都是可接受的结果。如果需要对阻尼做高精度识别通常会结合频域拟合方法如频域分解后拟合半功率带宽或者增加多组测量取平均。我的程序输出的阻尼比值适合用于趋势判断比如结构损伤后阻尼是否明显升高不适合作为精确的有限元模型校准参数。8.4 后续可以怎么扩展这套程序还有几个可扩展方向一是把协方差驱动的SSI-COV改造成数据驱动的SSI-DATA后者在数据量偏少时通常更稳二是加入滑动窗口实时识别功能监测结构模态参数的时间演变三是把稳定图自动筛选的逻辑做成GUI界面方便非编程人员使用。如果你对子空间类方法感兴趣这些扩展都是很自然的下一步。SSI-COV说到底是“数学工程”的结合数学保证了只要数据够好结果就够准工程则提醒你数据永远不够完美。真正需要下功夫的往往是把数据里的噪声和伪信号剔除干净再让算法在合理参数下发挥应有的作用。希望这套实现思路和代码框架能帮你少走几步弯路。
返回列表