资讯详情

动态模态分解(DMD)实战:时间序列模态提取与预测

📅 2026/9/15 4:22:20 | 华诺云谱 👁 阅读
动态模态分解(DMD)实战:时间序列模态提取与预测
简介时间序列分析是工程实践中广泛使用的技术从振动信号到流体场数据人们常需要提取主导动态特征并预测未来演变。动态模态分解DMD作为一种数据驱动的模态分析方法基于Koopman算子理论将非线性系统的演化近似为有限维线性映射通过SVD降维求解特征模态、频率和增长率弥补了FFT无法提供增长率的不足。该方法无需显式建立控制方程适用于CFD后处理、结构健康监测、电力系统低频振荡分析等场景。本文以洛伦兹系统为例详细讲解DMD的数学原理、Python编程实现、采样间隔与截断秩等关键参数设置并给出结果验证与工程落地技巧帮助读者快速上手时间序列的模态分析与预测。1. DMD 时间积分到底是什么从 zip 包名到动力学建模DMD 这个缩写放在 IT 工程师面前第一反应往往是投影显示里的 Digital Micromirror Device但标题里紧跟的“时间积分”四个字把方向定了下来——这里讨论的是 Dynamic Mode Decomposition动态模态分解。它解决的问题很具体手里有一批按固定采样间隔记录的时间信号可能是流体仿真输出的压力场可能是电机振动波形也可能是一组传感器录波你希望在不重写仿真器、不显式求解控制方程的条件下提取系统的主要振荡频率、增长率与空间模态并对未来若干时刻做外推预测。DMD 用一套以 SVD 为主线的线性代数流程完成这件事既不需要神经网络的训练数据量也能把非线性系统的短期演化压缩成若干线性模态。这篇文章面向的是手里已经有时间序列数据、正打算做模态分析和预测建模的人不管数据来自 CFD 后处理、实验采集还是监控系统思路都通用。2. 时间积分信号与 DMD 原理从快照矩阵到特征模态2.1 为什么叫“时间积分”数值积分与采样步长是两个概念“时间积分”这个说法在 DMD 语境里很容易被误解。常见做法是用数值积分器求解常微分方程得到一条连续轨迹而 DMD 消费的并不是那个微分方程而是这条轨迹上等时间间隔的离散点。比如用四阶 Runge-Kutta 求解洛伦兹系统积分步长是 0.001但每隔 0.01 才保存一个状态那么 DMD 看到的采样间隔 dt 就是 0.01而不是积分步长 0.001。import numpy as np from scipy.integrate import solve_ivp def lorenz(t, state, sigma10.0, beta8.0/3.0, rho28.0): x, y, z state return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z] dt 0.01 t_span (0.0, 10.0) t_eval np.arange(0.0, 10.0, dt) sol solve_ivp(lorenz, t_span, [1.0, 1.0, 1.0], methodRK45, t_evalt_eval) X sol.y.T # 形状 (N, 3)N 是快照数量这段代码的关键在于t_eval决定了输出快照的等间隔性。DMD 容不下不等间隔的时间序列因为后续所有推导都建立在线性映射x_{k1} A x_k之上这个映射要求后一个快照比前一个快照正好晚一个 dt。如果原始数据是实验仪表采的没有求解器参与那么采集卡的采样周期就是等效的 dt。很多人在这一步出错把仿真积分步长当成 dt导致 DMD 计算出的频率全部偏大或偏小一整倍。正确的做法是dt 取你在时间序列中相邻两点的时间差与积分器内部步长无关。这个区别正是标题里“时间积分”最容易造成困惑的地方。2.2 Koopman 算子与线性动力学DMD 的理论立足点非线性系统很难直接做线性分解但 Koopman 算子理论提供了一个视角存在一个无限维线性算子可以精确地推进观测函数沿着非线性流演化。DMD 的目标是用快照数据构造一个有限维近似找到最优的线性映射A使得x_{k1} ≈ A x_k在所有已知快照对上误差最小。这个A不直接等于系统真实雅可比矩阵它是在数据子空间上对动力学的最优线性逼近。A的特征分解把演化过程拆成若干相互独立的模态。设特征值λ_j和特征向量φ_j那么任意初始状态可以表示成模态的线性组合未来状态写成x(t) ≈ Σ φ_j b_j exp(ω_j t)其中ω_j ln(λ_j) / dt是连续时间下的复频率b_j是模态振幅。读结果时直接看ω_j的实部与虚部即可为了方便对照把它整理成一张表特征量含义判断依据abs(λ)离散特征值的模大于 1 增长小于 1 衰减等于 1 等幅Re(ω)连续时间增长率正数发散负数衰减Im(ω)角频率周期T 2π / abs(Im(ω))abs(φ)空间模态强度用于定位振荡集中的区域这里要特别注意区分离散和连续特征值。λ是从矩阵特征分解直接得到的它对应的是“每过一步怎么变化”ω是把步长归一化到每秒之后的变化率。写论文报告时一般汇报ω而程序内部往往直接操作λ。2.3 快照矩阵、SVD 与截断秩算法主流程标准流程先把时间序列组织成两个快照矩阵。设状态x_k是d维向量共有N1个连续快照则X1 X[:-1].T # 形状 (d, N) X2 X[1:].T # 形状 (d, N)X1和X2是时间上错开一步的配对数据DMD 要学习的正是从X1的每一列到X2对应列的映射。接下来分四步走对X1做 SVD降维截断得到约减矩阵求约减空间里的特征分解再映射回原始高维空间得到 DMD 模态。from scipy.linalg import svd U, S, Vh svd(X1, full_matricesFalse) r 8 Ur U[:, :r] Sr S[:r] Vr Vh.conj().T[:, :r] Atilde Ur.conj().T X2 Vr np.diag(1.0 / Sr) lam, W np.linalg.eig(Atilde) Phi X2 Vr np.diag(1.0 / Sr) WSVD 截断在这里承担了两个作用。第一个是降噪高阶奇异值对应的通常是噪声或数值误差截断后相当于把低能量成分直接丢弃第二个是数值稳定性X1的条件数在截断后会显著改善避免后面做1/Sr时被接近零的小奇异值放大误差。r选多大直接决定 DMD 能分辨出多少个模态这个参数放在后面专门讨论。Phi的表达式来自 exact DMD它比直接把Ur W当模态更精确因为它保留了被截断子空间外的信息量实际使用时优先采用这一种。3. Python 实现 DMD 编程从洛伦兹系统开始跑通完整流程3.1 一个可复用的 Python 脚本exact DMD 实现把上一章的零散步骤合到一起写成一个函数。后续无论换什么数据只要把时间序列按等间隔传进来都能复用这一段代码。import numpy as np def dmd(X, dt, rNone, energy_threshold0.999): X1 X[:-1].T X2 X[1:].T U, S, Vh np.linalg.svd(X1, full_matricesFalse) if r is None: cum_energy np.cumsum(S**2) / np.sum(S**2) r int(np.searchsorted(cum_energy, energy_threshold) 1) Ur U[:, :r] Sr S[:r] Vr Vh.conj().T[:, :r] Atilde Ur.conj().T X2 Vr np.diag(1.0 / Sr) lam, W np.linalg.eig(Atilde) Phi X2 Vr np.diag(1.0 / Sr) W b np.linalg.lstsq(Phi, X[0], rcondNone)[0] omega np.log(lam) / dt return Phi, b, omega, lam逻辑说明函数接受三个参数X是形状为(N, d)的原始时间序列dt是采样间隔r允许手工指定截断秩。如果r不传就按奇异值能量占比自动取默认保留 99.9% 的能量。omega由lam取自然对数再除以dt得到这一步把每个时间步的变化率转换成一秒内的变化率。振幅b用最小二乘拟合要求的就是初始状态X[0]在模态基底Phi下的坐标。需要说明的是lambda是 Python 内置关键字不能直接当变量名这里统一写作lam很多初写 DMD 的人容易在这一行踩语法错误。3.2 从特征值读取频率和增长率omega 的含义拿到omega之后第一件事不是画图而是把主导模态挑出来。洛伦兹系统在标准参数下有一个不稳定鞍点初期轨迹会有快速振荡DMD 分解后会看到一对共轭复模态代表主振荡其余模态要么衰减极快要么对应数值伪影。freq np.imag(omega) / (2.0 * np.pi) growth np.real(omega) order np.argsort(np.abs(growth))[::-1] for idx in order[:6]: print(ffreq{freq[idx]:.4f} Hz, growth{growth[idx]:.4f}, |lambda|{np.abs(lam[idx]):.4f})运行后通常能看到一个增长率接近零、频率在 1 到 2 Hz 之间的主导模态增长率为负且绝对值很大的模态很快消失不需要重点关注。这里是 DMD 与 FFT 最直观的差异FFT 只能告诉你哪些频率成分占主导DMD 额外给出了每个频率成分的增长率这等价于把频谱按“衰减快慢”再做了一次分类。对结构振动分析来说增长率直接对应阻尼比对电力系统低频振荡分析来说增长率正负直接给出稳定性结论。3.3 用训练好的 DMD 做未来状态预测DMD 的预测公式形如x(t) Phi (b * exp(omega * t))一次性把所有模态叠加得到任意时刻的状态。用前 6 秒的数据训练再预测第 6 到第 10 秒的信号与数值积分结果做对比t_train t_eval[:600] train_predict np.real(Phi (b[:, None] * np.exp(omega[:, None] * t_train))) t_test t_eval[600:] test_predict np.real(Phi (b[:, None] * np.exp(omega[:, None] * t_test))) err np.linalg.norm(test_predict - X[600:], axis1) / np.linalg.norm(X[600:], axis1)这段代码先分别在训练段和测试段按连续时间公式求状态再计算逐点的相对误差。洛伦兹系统对初始条件敏感DMD 的有限维线性近似不可能长期维持精度能看到前 0.5 到 1 秒内误差很小、随后快速增长就是正确表现不要因此怀疑程序写错了。DMD 的定位是中短期预测和模态提取不是用来替代混沌系统的长期数值积分。4. dmd 编程中的参数设置与数据准备采样率、截断秩与预处理4.1 dt 怎么选采样定理与 DMD 频谱上限dt的大小决定了 DMD 能观测到的最大频率。离散特征值λ的辐角范围为[-π, π]换算成频率就是fs/2其中fs 1/dt这与奈奎斯特-香农采样定理完全一致。如果数据里存在频率高于fs/2的成分采样时已经发生混叠DMD 无法还原这不是算法能补救的。参数常取范围失衡表现调试方向dt主周期的 1/20 到 1/50过大时频谱混叠、频率偏离 FFT 结果过小时矩阵条件数变大微小噪声被放大用功率谱先看主频反推采样率是否充足r奇异值能量 0.99 到 0.9999过小丢失弱模态过大会把噪声当成模态growth出现大量虚假正值观察奇异值谱拐点或用能量阈值自动选快照数量 N500 到 10000太少时模态估计方差大截断秩不敢取高太多时 SVD 耗时线性上涨数据不足时用分段平均或降采样仿真数据里还有一个常见的坑求解器输出的时间步长往往等于积分步长直接把全量输出喂给 DMDdt极小数据维度极高SVD 计算量白白增大而高频区域全部是数值积分的截断误差。常见做法是先以dt的整数倍对轨迹做等间隔抽样再送入 DMD。抽样会丢掉最高频的信息但只要先做一次快速傅里叶变换确认主频在安全范围内问题就不大。4.2 截断秩 r 怎么选奇异值能量与模态可信度r是 DMD 里最敏感的参数它对结果的影响远大于dt的微小调整。r取太小低频主导模态虽然在但弱模态被强行合并预测时误差快速积累r取太大噪声被当成真实动力学特征值会出现一批增长率略大于零的虚假发散模态。工程上习惯用奇异值能量占比来选择s_energy np.cumsum(S**2) / np.sum(S**2) r_auto int(np.searchsorted(s_energy, 0.9999) 1)这段代码先算每个奇异值占总能量的累积比例再找第一次超过阈值的下标。阈值 0.9999 适合信噪比高的仿真数据实验数据经常要降到 0.99 甚至 0.95。更稳妥的做法是同时打印前若干阶奇异值观察是否存在明显的断层r取到断层附近即可。如果能量曲线平缓下降说明系统本身存在连续谱有限秩只是近似不要把r抬得太高。判断模态是否可信还可以看两个辅助指标模态的增长率是否落在合理范围内以及模态在训练段的重建误差是否显著小于测试段。4.3 常见预处理减均值、分段与噪声抑制DMD 的公式推导隐含了一个前提状态变量可以写成一堆指数项叠加。若信号带有明显直流偏移叠加出的第一模态往往是一个增长率近似零、频率近似零的常量模态它没错但会吃掉一部分秩。减去时间均值再分解得到的就是围绕平衡点的脉动分量物理含义更清晰。减去均值后预测时记得把均值加回去X_mean X.mean(axis0) Xc X - X_mean Phi, b, omega, lam dmd(Xc, dt) restored np.real(Phi (b[:, None] * np.exp(omega[:, None] * t_new))) X_mean对于非平稳信号整个序列只有一段直接 SVD 会把早期和晚期的不同动力学混在同一个模态里。常见做法是按时间窗口切段每段分别跑 DMD观察各段主导频率是否稳定。分段长度取主周期的 5 到 10 倍即可。噪声抑制方面对每个变量的时间序列先做简单滑动平均或小波去噪再做 DMD效果比在 SVD 之后增大截断更直接因为噪声已经先被压低了。5. 结果验证与工程落地三个可执行技巧5.1 用训练段重建误差倒数判断秩是否合适代码跑出一个 DMD 模型后先不要急着预测第一步是检查重建误差。用全部训练快照代入连续时间公式把重建序列和原始序列做相对误差这个误差应该低到 10 的负 4 次方量级甚至更低recon np.real(Phi (b[:, None] * np.exp(omega[:, None] * t_train))) rel_err np.linalg.norm(recon - X[:len(t_train)]) / np.linalg.norm(X[:len(t_train)])如果重建误差大检查三个点r是否设得偏低dt是否与数据实际间隔不符以及时间序列是否等间隔。三者之间不等间隔数据最容易忽视即使个别缺失点也能让误差突增。补点可以用线性插值但插值后的数据相当于人为引入低通滤波两侧留出一定余量丢弃不要用边界点。5.2 与 FFT 频谱对照验证主导频率DMD 算出的频率是否可信最直接的验证武器是对照 FFT。对同一段信号做np.fft得到频谱峰值位置与omega.imag / (2*pi)的散点对比峰值频率应该严格吻合。这里做一次对照比检查任何抽象指标都更直观。仿真数据通常只有个位数主导频率实验数据则会看到宽峰DMD 会把宽峰拆成多个邻近离散模态这属于正常现象说明实际系统能量分散在连续频谱带上。5.3 交付格式与批量应用时的效率优化DMD 模型的最终产物是一组Phi、b、omega这三样东西决定了任意时刻的状态不需要保留原始数据。交付时建议把模态信息存成npz格式文件体积小加载快np.savez(dmd_model.npz, PhiPhi, bb, omegaomega, dtdt, X_meanX_mean)加载后直接做预测比每次现场重新做 SVD 快几个数量级。批量处理很多组数据时优先保证所有组的dt和截断策略一致否则组间频率没法直接对比。最后一件事任何 DMD 模型都应该附带一段测试段误差说明告诉后续接手的人这个模型预测多久可信而不是让他们拿新数据怼上去才发现结果发散。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。