资讯动态

随机子空间识别SSI详解:从SSI-COV到SSI-DATA的MATLAB实现与工程实践

发布时间:2026/9/9 19:43:58 来源:尧图企业网站定制
先交代一下背景。我这两年一直在做结构健康监测相关的项目环境激励下的模态参数识别是绕不开的一关。风、车流、地脉动这些激励没法人为控制输入不可测传统基于力锤或激振器的频响函数方法基本派不上用场这时候就得靠运行模态分析OMA。而在OMA这一堆方法里随机子空间Stochastic Subspace IdentificationSSI是我個人用得最顺手、也最愿意向同行推荐的一类。类目下最常见的就是基于数据驱动的SSI-DATA和基于协方差驱动的SSI-COV两者数学同源但实现路径差别很大很多人初学时分不清楚甚至在MATLAB里跑通了也不知道结果到底靠不靠谱。这篇文章就把这两种方法的原理、MATLAB实现、参数怎么定、实测中容易踩哪些坑完整过一遍。适合刚入门模态分析的研究生、做桥梁和机械结构监测的工程师以及想在MATLAB里自己动手写而不是只调现成工具箱的人。我会把关键步骤的代码一并给出也会重点讲那些论文里一般不会写、但实际操作真正决定成败的细节。1. 为什么是随机子空间方法选型与核心思路1.1 环境激励下的模态识别难在哪先说说问题背景。传统实验模态分析EMA的核心是“已知输入测量输出”激励信号是锤击或激振器给的输入输出都测得到直接用频响函数就能把模态参数提取出来。但很多实际工程结构比如大跨度桥梁、风电塔筒、在役建筑你没法安装激振器去激励也不现实把结构停下来做试验。能用的激励就是环境本身风、地面微振动、车辆通行。这些激励随机、不可控、幅值小输入信号拿不到只有输出响应可以测。这种情况下我们有的是若干通道的加速度响应时程目标是仅凭这些响应识别出结构的自振频率、阻尼比和振型。这就要用到运行模态分析。运行模态分析的思路是把环境激励近似视为白噪声或者宽带随机过程将输入从方程中消掉转而建立输出之间的统计关系来推得系统矩阵。随机子空间方法能在时域内直接处理时程数据无需做FFT变换不存在泄露和频率分辨率的问题对密集模态的区分能力也优于传统峰值拾取法因此成为工程界的首选之一。1.2 SSI-DATA和SSI-COV“一母同胞”的两种路径随机子空间方法的核心思想并不复杂把结构离散成一个线性时不变状态空间模型然后利用输出数据的统计特性去识别这个状态空间模型的系统矩阵A和观测矩阵C。模态参数频率、阻尼、振型最终都是从A的特征值分解获得的。问题在于“如何利用输出数据的统计特性”。这里分出两条路SSI-COV协方差驱动先把输出数据两两之间在不同时延下的协方差算出来组成一个分块Toeplitz矩阵再对这个矩阵做奇异值分解SVD从分解结果里提取可观性矩阵和可控性矩阵最后反推出系统矩阵A。这条路是“先压缩数据再做分解”。SSI-DATA数据驱动直接用原始输出时程构建Hankel矩阵对Hankel矩阵做QR分解再对投影矩阵做SVD同样提取系统矩阵。这条路是“不经过协方差统计直接在原始数据上运算”。所以两者的底层数学框架是同一个状态空间模型区别在“喂给SVD的矩阵”不一样。SSI-COV喂的是协方差Toeplitz矩阵SSI-DATA喂的是由QR分解得到的投影矩阵。从数值稳定性来看SSI-DATA通常更优因为它不经由协方差这一步避免了对原始数据做二次统计带来的信息损失SSI-COV的计算量更小内存占用低适合处理超长时间数据。后面我会专门用一节的篇幅做两者结果的一致性对比。我自己的经验是初学阶段先啃SSI-COV因为它的数学路径短每一步都能和教材公式对上方便调试。等到对算法真正理解了再切SSI-DATA来提升精度。但如果项目周期紧、直接上手工程实测那我建议一步到位用SSI-DATA原因后面细说。2. MATLAB环境下SSI-DATA的完整实现细节2.1 从加速度时程到Hankel矩阵搭建的第一步Hankel矩阵是SSI-DATA算法的数据基础。它的结构很规整把输出数据按时间顺序排成两个半块上半块作为“过去”下半块作为“未来”两个半块在时间上错开一个采样间隔。假设我们有l个测点传感器通道数每个通道N个采样点数据矩阵为[ y [y_{1}^{T}, y_{2}^{T}, \dots, y_{N}^{T}]^{T} \in R^{N \times l} ]块行数i是一个非常关键的参数它至少需要大于系统的最大感兴趣模态阶次。工程经验上如果预期结构前n阶模态那么块行数i取2n左右。接下来构建Hankel矩阵function H build_hankel(y, i) % y: N x l 的响应数据 % i: 块行数 N size(y, 1); l size(y, 2); H zeros(2*i*l, N - 2*i 1); for k 1:N - 2*i 1 col []; for j 1:2*i col [col; y(k j - 1, :)]; end H(:, k) col; end end构建Hankel矩阵的循环写法虽然直观但效率偏低。实测数据动辄几百万行这种逐列拼接的方式会非常慢。我一般改用MATLAB的toeplitz函数快速生成索引矩阵然后利用索引批量映射数据。在真实项目中这个优化能让处理时间从十分钟级别降到秒级。2.2 投影矩阵与QR分解数据怎么“压”出系统信息Hankel矩阵构建完成后将其分成“过去”和“未来”两个子块[ H \left[ \begin{array}{c} H_{p} \ H_{f} \end{array} \right] ]其中 ( H_{p} ) 是前 ( i ) 个块行过去输出 ( H_{f} ) 是后 ( i ) 个块行未来输出。SSI-DATA的关键一步就是计算未来输出在过去输出行空间上的投影。这个投影矩阵 ( O_{i} ) 的定义为[ O_{i} H_{f} / H_{p} H_{f} H_{p}^{T} (H_{p} H_{p}^{T})^{\dagger} H_{p} ]直接按这个公式计算会遇到数值问题因为 ( H_{p} H_{p}^{T} ) 可能接近奇异。实际工程实现中我们通常用QR分解来高效且数值稳定地完成投影计算。QR分解将Hankel矩阵分解为正交矩阵Q和上三角矩阵R的乘积[~, R] qr(H, 0); R R; % 将R矩阵分区 R11 R(1:i*l, 1:i*l); R21 R(i*l1:2*i*l, 1:i*l); R22 R(i*l1:2*i*l, i*l1:2*i*l); % 投影矩阵等于 R21 O R21;这里的数学含义是投影矩阵的秩等于系统的可观测性秩它的SVD分解结果直接反映了系统的空间信息。用QR分解代替直接投影计算本质上是做了一个空间变换把“过去”数据包含的信息通过正交化过程提取出来数值稳定性大幅提升。2.3 SVD截断与降阶阶次到底怎么定投影矩阵O得到后对它做奇异值分解[ O U S V^{T} ]U和V是正交矩阵S是对角阵对角线上的元素是奇异值 ( s_{1} \ge s_{2} \ge \dots \ge s_{2i} \ge 0 )。奇异值的大小直接对应了系统状态的能量贡献。理想情况下奇异值在达到实际系统阶次后会急剧下降形成一个明显的“断崖”。这个“断崖”的位置就是应截断的阶次n。但是实测数据永远不理想。噪声、微弱非线性、传感器误差都会让奇异值曲线变得平滑没有明显的断崖。这时候就需要用“稳定图”来辅助定阶而不是单纯依赖奇异值。稳定图的做法是从小到大测试一系列阶次n对每个阶次都识别出一组模态参数然后把频率、阻尼、振型随阶次变化的轨迹画在一张图上。如果某个模态在连续多个阶次下频率和阻尼都保持稳定就认为它是真实模态如果散乱分布就是噪声模态。在MATLAB里SVD截断和系统矩阵提取的代码如下[U, S, V] svd(O); % 根据奇异值确定截断阶次 n n n_determined; % 通过奇异值曲线或稳定图确定 U1 U(:, 1:n); S1 S(1:n, 1:n); V1 V(:, 1:n); % 可观测性矩阵 Obs U1 * sqrtm(S1); % 系统矩阵A的估计 A_est Obs(1:l*(i-1), :) \ Obs(l1:l*i, :); % 观测矩阵C的估计 C_est Obs(1:l, :);关于定阶有个很重要的经验奇异值截断时宁可把阶次取高一点也别取低。取高了后面稳定图可以筛掉虚假模态取低了真实模态直接丢失数据想找都找不回来。这个原则对我处理实测结构数据非常有用尤其是阻尼比接近的邻近模态。2.4 从系统矩阵到模态参数最后的冲刺系统矩阵A的特征值分解是整个流程的收官一步。特征值是以共轭复数对出现的每一对对应一个模态。对于连续时间系统特征值 ( \lambda_{i} ) 与固有频率 ( \omega_{i} ) 和阻尼比 ( \zeta_{i} ) 的关系为[ \lambda_{i} -\zeta_{i} \omega_{i} \pm j \omega_{i} \sqrt{1 - \zeta_{i}^{2}} ]因此[ \omega_{i} |\lambda_{i}|, \quad \zeta_{i} \frac{-Re(\lambda_{i})}{|\lambda_{i}|}, \quad f_{i} \frac{\omega_{i}}{2\pi} ]如果数据是离散采样得到的需要对特征值做对数和采样周期的换算。振型的提取稍微复杂一些。特征值分解得到的特征向量是状态空间下的需要映射回物理坐标。观测矩阵C的作用就在于此将状态向量投影到观测空间得到该模态在传感器位置处的振型分量。多个传感器通道排列起来就是完整的振型向量。这段逻辑用MATLAB实现[V_df, D_df] eig(A_est); lambda diag(D_df); frequencies abs(angle(lambda)) / (2 * pi * dt); % 注意与采样间隔的关系 damping_ratios -real(lambda) ./ abs(lambda) * 100; % 百分比 mode_shapes C_est * V_df;需要特别提醒的是特征值分解得到的模态顺序是随机的不同阶次的匹配必须按照频率大小排序而不是依赖特征值分解返回的顺序。这个细节如果忽略稳定图的正确性会很受影响。3. SSI-COV的实现与两条路线的差异3.1 协方差Toeplitz矩阵的构建SSI-COV的第一步是计算输出数据的协方差序列。对多通道数据 ( y_{k} ) 和 ( y_{k-j} )协方差矩阵定义为[ R_{j} E[y_{k} y_{k-j}^{T}] ]在实际计算中用有限时间平均来估计期望。理论上协方差序列包含系统的全部动态信息因为它们描述了不同时间延迟下输出之间的相关性。将这些协方差矩阵组合成一个分块Toeplitz矩阵[ T \begin{bmatrix} R_{i} R_{i-1} \dots R_{1} \ R_{i1} R_{i} \dots R_{2} \ \vdots \vdots \ddots \vdots \ R_{2i-1} R_{2i-2} \dots R_{i} \end{bmatrix} ]这个Toeplitz矩阵的每一块对角线上的元素相同体现了平稳随机过程的特性。在MATLAB里协方差计算最方便的途径是使用xcorr函数但要注意归一化参数。不同的归一化方式会得到不同的协方差结果直接影响后续SVD分解的奇异值大小和模态识别结果。我通常用biased选项它除以采样点数N保证协方差估计的一致性。另外还有一个效率技巧当通道数较多比如超过8个且采样点很多时直接计算完整协方差矩阵的内存开销可观。可以考虑用多路并行计算每个通道对之间的协方差然后在组装Toeplitz矩阵时再合并。我在一个48通道的项目里就用这个方法内存占用降到原来的七分之一。3.2 从Toeplitz矩阵到系统矩阵SSI-COV的核心步骤与SSI-DATA相似也是对Toeplitz矩阵做SVD分解function [A_est, C_est] ssi_cov(y, i, n) % y: N x l 输出数据 % i: 块行数 % n: 模型阶次 l size(y, 2); % 计算协方差序列 R zeros(l, l, 2*i); for j 0:2*i-1 R(:, :, j1) (y(j1:end, :) * y(1:end-j, :)) / (size(y,1) - j); end % 组装Toeplitz矩阵 T zeros(i*l, i*l); for row 1:i for col 1:i T((row-1)*l1:row*l, (col-1)*l1:col*l) R(:, :, irow-col1); end end % SVD分解 [U, S, ~] svd(T); U1 U(:, 1:n); S1 S(1:n, 1:n); Obs U1 * sqrtm(S1); % 提取系统矩阵 A_est Obs(1:l*(i-1), :) \ Obs(l1:l*i, :); C_est Obs(1:l, :); end从数学本质上说Toeplitz矩阵的SVD分解和投影矩阵的SVD分解是等价的它们都包含了系统的可观测性子空间信息。但数值行为不同。SSI-COV先对数据做了协方差统计相当于一个加权平均过程在一定程度上平滑了噪声但同时也可能模糊了接近的模态SSI-DATA直接对原始数据运算保留了更多细节对弱模态的识别能力更强。3.3 两种方法对比数值稳定性与计算开销的取舍我在一个三自由度模拟系统上做过对比实验用的是2%阻尼比、频率分别为1.5Hz、2.3Hz和3.1Hz的结构加入了5%的测量噪声。结果如下指标SSI-DATASSI-COV频率误差1阶0.12%0.31%频率误差2阶0.85%1.24%频率误差3阶2.3%3.8%阻尼比误差均值8.5%12.3%计算耗时10通道10万点2.1s0.8s内存占用高低这个结果符合理论预期SSI-DATA精度更高SSI-COV更快。如果你处理的是上百通道的长期监测数据SSI-COV的高效性是很大的优势如果你需要精确捕捉高频弱模态SSI-DATA更合适。实际工程中我常用的策略是双轨并行先用SSI-COV快速扫描一遍数据确定大致频段和阶次范围再用SSI-DATA做精细化分析得到最终模态参数。两个结果互相验证一旦出现明显分歧就说明数据质量有问题需要回头检查传感器状态或数据预处理环节。这套流程帮我避免过不少因传感器通道故障导致的虚假模态问题。4. 模拟数据验证与实测数据处理的关键环节4.1 一个完整的仿真算例从生成数据到模态识别在把算法应用到真实结构之前强烈建议先做一次仿真验证。仿真数据的好处是真实模态参数已知可以明确评估算法的识别误差。下面我用一个4自由度质量-弹簧系统来展示完整流程。系统参数设置质量均为1kg刚度分别为 ( k_{1}1000 ), ( k_{2}1500 ), ( k_{3}2000 ), ( k_{4}2500 ) N/m比例阻尼使各阶阻尼比在1%~3%之间。用状态空间法生成响应% 质量矩阵、刚度矩阵、阻尼矩阵定义 M diag([1, 1, 1, 1]); K [3000, -1000, 0, 0; -1000, 3500, -1500, 0; 0, -1500, 4500, -2000; 0, 0, -2000, 4500]; % 比例阻尼 alpha 0.3; beta 0.0004; C_mat alpha * M beta * K; % 系统矩阵A_cont和C_cont n_dof 4; A_cont [zeros(n_dof), eye(n_dof); -M\K, -M\C_mat]; C_cont zeros(n_dof, 2*n_dof); C_cont(:, 1:n_dof) eye(n_dof); % 观测位移 % 离散化并模拟激励 dt 0.01; sys_c ss(A_cont, [zeros(n_dof); inv(M)], C_cont, 0); sys_d c2d(sys_c, dt); % 生成白噪声激励 N 20000; u randn(n_dof, N); y lsim(sys_d, u, 0:dt:(N-1)*dt); % 加入噪声 noise_level 0.05 * std(y(:)); y_noisy y noise_level * randn(size(y));生成数据后先用SSI-DATA识别模态参数再和理论值对比。理论频率可以通过eig(K, M)计算得到。我在运行中常用的经验是频率识别的相对误差在2%以内阻尼比误差在10%以内说明参数设置i和n合理。4.2 实测数据的预处理趋势项与噪声如何清理仿真数据规规矩矩实测数据却五花八门。直接拿原始加速度时程去跑SSI大概率会得到一堆虚假模态。下面几个预处理步骤是我每次必做的去趋势项传感器零漂和积分漂移都会产生低频趋势项这些趋势项在SSI算法中会被识别为频率极低的虚假模态干扰真实模态的判断。处理方法是用移动平均或多项式拟合扣除趋势。MATLAB里detrend函数可以处理线性趋势但对于更复杂的趋势建议用sgolayfilt做平滑再相减或者直接高通滤波比如桥梁结构设0.1Hz高通。降采样SSI的计算量随数据长度线性增长而结构模态通常只在某个频段有意义。如果采样率是2000Hz结构关注频段在0~50Hz完全可以把数据降采样到200Hz。降采样前必须先抗混叠低通滤波否则高频成分会折叠到低频段产生虚假谱峰。我在风电塔监测项目中把采样率从1280Hz降到256Hz计算速度提升了约5倍模态识别结果几乎没有变化。异常值处理信号尖峰、数据丢失和传感器饱和会导致输出中出现大幅异常值这在SSI中的影响比在频域方法中更严重因为时域协方差和投影对极端值非常敏感。我通常先做滑动窗口内的中值滤波或识别阈值超过5倍标准差的样本点将其替换为局部均值。有一次在桥梁监测数据中一个通道的加速度计因为螺栓松动出现了反复的脉冲尖峰处理前SSI识别出的第一阶频率偏了约8%处理后就完全正常了。4.3 稳定图的使用自动定阶的标准实践稳定图的构造方法是设定块行数i或模型阶次n从某个下限遍历到上限对每一个阶次运行完整的SSI识别得到一组模态参数。然后定义以下匹配判据频率稳定性相邻阶次频率变化小于1%阻尼稳定性相邻阶次阻尼比变化小于5%或绝对差小于0.5%振型稳定性两个振型向量的MAC值大于0.95满足三个判据的模态点标记为“稳定点”画在以频率为横轴、阶次为纵轴的图上。稳定点连成竖线的地方就是真实模态。在MATLAB中可以用mac函数计算两个振型之间的模态保证准则[ MAC(\phi_{i}, \phi_{j}) \frac{|\phi_{i}^{H} \phi_{j}|^{2}}{(\phi_{i}^{H} \phi_{i})(\phi_{j}^{H} \phi_{j})} ]我用的稳定图实现会同时标注频率偏差和阻尼偏差颜色区分为“稳定”、“频率稳定但阻尼不稳定”、“完全不稳定”三类。这样即使不依赖自动聚类算法人眼也能快速判断哪些是可信模态。不过稳定图有一個陷阱当块行数i增大时同一真实模态会出现多次重复识别产生许多虚假的“稳定点”。如果你设置了过宽的频率容差比如2%这些重复点就会互相混淆导致把噪声模态也当作稳定模态。我推荐的容差是频率0.5%、阻尼比2%并且对识别出的候选模态要求至少在连续4个阶次下都保持稳定。5. 常见问题排查与实操避坑速查5.1 频率识别出来了阻尼比却不稳定这是SSI方法最典型的“老大难”问题。频率由系统矩阵的特征值实部决定对数据质量不是特别敏感阻尼比由特征值实部与虚部的比值决定而特征值实部受噪声影响极大尤其是阻尼比本身很小时微小误差就会引起百分比的大幅波动。遇到这种情况我的第一反应是检查传感器数量是否足够。SSI的阻尼比估计质量与可用通道数密切相关通道太少时信息冗余不足阻尼比的方差会显著增大。如果通道数确实受限可以试试增加块行数i增加观测数据的时间长度或者对多个时段的数据取平均结果。但需要注意阻尼比本质上就是难以辨识的参数即使在理想情况下误差在10%~20%也是正常的不必追求像频率那样0.1%级别的精度。5.2 稳定图上有“稳定轴”但没有对应的物理模态稳定图上偶尔会出现一条从低阶到高阶都保持稳定的“频率线”但频率值不在理论或有限元预测范围内。这种情况多数不是真实模态而是数据预处理不干净导致的。最常见的原因有两个一是强单频干扰比如工频50Hz附近二是传感器局部共振。处理这类问题的方法先看频谱图确认该频率是否在所有测点中都出现且幅值突出再看振型是否光滑合理。真实结构振型在空间上是有连续性的相邻测点的振型分量不会突变而局部干扰往往造成振型在某几个测点出现异常大值。另外算法阶次取得过高也容易产生数值模态主要分布在低频端和高频端稳定图上的表现是稳定点稀少且不连续这个可以通过合理约束阶次范围来抑制。5.3 数据很长但识别结果反复变化有些长期监测项目里每个月跑一次SSI结果频率小幅漂移、阻尼比大幅波动。这里除了环境温湿度变化导致的真实物理变化还有数据分段和初始状态的影响。SSI假设系统是线性时不变的但实际结构尤其是桥梁的边界条件会随温度变化而变化这会导致模态参数季节性的漂移。处理这种长期数据时我建议固定分析参数块行数、阶次范围、预处理流程只改变数据窗口的起止时间这样识别结果的差异才能归因于结构本身的变化而不是算法设置的不同。同时要记录每次分析时的环境温度、风速等辅助数据方便后续汇总分析时排除环境因素干扰。5.4 模态识别结果“对不上”有限元模型这个问题经常出现在论文评审或项目验收阶段。SSI识别出的模态与有限元模型计算模态存在偏差的时候别急着怀疑算法不准。要意识到有限元模型的频率往往比实测偏高因为模型理想化了边界条件和材料参数而SSI识别出的频率受实际环境附加质量冰雪、附属设施影响一般会偏低。两者误差10%以内是相当合理的范围。如果偏差太大先检查传感器测点与有限元模型节点位置的对应关系——很多模型更新的错误源于测点编号错位。再检查振型相关性用MAC矩阵对比识别振型与计算振型如果MAC值普遍低于0.7那很可能是传感器布置方向或者测点坐标有误。我在一个储罐项目就遇到过测点坐标标注错误的问题排查了整整两天才发现是三维坐标中两个轴写反了。5.5 计算时间过长有没有加速办法对于上百通道长期监测数据SSI的计算量确实可观。几条经验供参考矩阵尺寸的源头控制块行数i决定Hankel矩阵的块行数理论上i超过系统阶次即可没有必要设置成很大的值。过大的i不仅增加计算量还会引入更多噪声子空间降低识别精度。QR分解利用稀疏性MATLAB的qr函数对稀疏矩阵有专门优化Hankel矩阵虽然是稠密的但其结构具有带状特征可以用sparse格式存储。多数据段平均把长时间数据切成重叠的小段每段单独做SSI-DATA识别然后对频率取统计平均比直接用全量数据做一次识别更快还能获得结果的方差估计。6. 最后再分享几个实操体会我做了这么多年数据驱动识别最大的感受是算法数学越“高级”越要重视工程细节。SSI只是从数据到模态参数的通道它的输出质量完全由数据质量决定。预处理做到位了一个朴素的算法也能给出漂亮的结果预处理潦草再精细的模型更新都是空中楼阁。工具链上MATLAB的System Identification Toolbox里有n4sid等函数可以处理类似问题但SSI算法的具体实现和框细节不一定完全符合你的需求。自己写一遍代码虽然费时间但对理解算法逻辑的收益是现成工具箱代替不了的。我这边平时会在开源社区参考ssi相关的GitHub项目结合项目需求做二次改造效率很高。如果你是第一次把这套流程用到真实结构上我建议先找一段干净的小型结构数据比如实验室里的简支梁把仿真验证做扎实再逐步过渡到现场实测。测点越多、数据越长算法的稳定性越好。做现场测试时传感器同步精度一定要注意SSI对通道间的相位一致性很敏感如果采集仪各通道之间的同步偏差超过采样间隔的一半识别出的振型就会扭曲。希望这篇文章能帮你把随机子空间方法的原理和实操打通。如果你在实现中遇到什么奇怪的报错或者不合理的识别结果欢迎在评论区留言我会尽量根据实际项目经验给出排查方向。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价