资讯动态

混沌时间序列相空间重构:MATLAB实现时间延迟与嵌入维数估计

发布时间:2026/10/15 1:36:26 来源:尧图企业网站定制
1. 混沌时间序列分析的核心逻辑与方案选型混沌时间序列分析这件事说白了就是从一串看起来毫无规律的数据里把系统内在的确定性规律给挖出来。不管是机械振动信号、气象数据、心电波形还是金融时间序列只要它背后是混沌系统产生的就存在一个相空间能把一维的时间序列重新映射到高维空间里去让原本被压缩的信息重新展开。这个过程就是相空间重构而重构的两个核心参数就是时间延迟和嵌入维数。我最早接触这块是在做某类非线性振动信号的特征提取当时手头只有一维的加速度传感器数据想判断系统是不是处于混沌状态第一步就卡在怎么选这两个参数上。后来翻了不少文献也踩了不少坑才把主流的几套方法跑通。这篇内容就把我实际用过的MATLAB实现思路完整梳理一遍包括互信息法求时间延迟、虚假最近邻法求嵌入维数、Cao方法做改进以及C-C方法一次性同时估计两个参数。代码都是可以直接跑的参数选择的依据我也会讲清楚。适合谁看如果你手头有一维时间序列想做混沌特性分析、相空间重构、Lyapunov指数计算或者分形维数估计那这篇内容基本能帮你把前置步骤全部打通。不需要你有很深的非线性动力学背景但至少要熟悉MATLAB基本操作知道什么是时间序列。先说清楚一个核心逻辑时间延迟和嵌入维数的选取不是独立的。Takens定理告诉我们只要嵌入维数足够大重构的相空间就能和原系统拓扑等价。但前提是时间延迟要选得合适让重构后的坐标分量之间既不过度相关也不完全独立。这两个参数互相影响所以实际工程中往往需要联合估计而不是各算各的。我个人的经验是对于长度在几千到几万点之间的时间序列先用互信息法确定时间延迟再用虚假最近邻法确定嵌入维数最后用Cao方法验证一下这套组合拳基本能覆盖大多数场景。如果数据量特别大或者对计算效率有要求可以直接上C-C方法一步到位。2. 时间延迟的求法互信息法与自相关法对比2.1 自相关法为什么不够用最直观的想法是算自相关函数当自相关值降到初始值的1-1/e或者第一次过零时对应的延迟就是合适的时间延迟。这个方法计算量小实现简单但有个致命问题自相关函数只能衡量线性相关性。混沌系统的非线性结构它捕捉不到所以选出来的延迟往往偏大导致重构后的相空间轨迹被过度拉伸相邻点之间的关联被破坏。我试过在一个Lorenz系统生成的序列上对比自相关法给出的延迟是18而互信息法给出的是10。用18去重构相空间轨迹明显被拉散了局部结构模糊用10去重构吸引子的形状清晰得多。所以除非你对计算速度有极端要求否则不建议用自相关法。2.2 互信息法的原理与MATLAB实现互信息衡量的是两个变量之间的广义相关性包括线性和非线性。对于时间序列x(t)我们计算x(t)和x(tτ)之间的互信息当互信息第一次达到局部极小值时对应的τ就是合适的时间延迟。这个位置的物理含义是x(tτ)提供了关于x(t)的最少新信息同时又不完全独立。具体计算步骤是这样的把x(t)和x(tτ)分别划分成若干区间统计联合概率分布和边缘概率分布然后算互信息。区间划分的数量一般取数据长度的平方根或者立方根我通常用sqrt(N/5)左右。function tau mutual_information(x, max_tau, n_bins) % x: 输入时间序列列向量 % max_tau: 最大延迟 % n_bins: 划分区间数 % tau: 返回最优时间延迟 x x(:); N length(x); if nargin 2 max_tau floor(N/10); end if nargin 3 n_bins round(sqrt(N/5)); end MI zeros(max_tau, 1); for tau_idx 1:max_tau tau tau_idx; x1 x(1:N-tau); x2 x(1tau:N); % 等宽划分 edges1 linspace(min(x1), max(x1), n_bins1); edges2 linspace(min(x2), max(x2), n_bins1); % 统计联合分布 joint_hist histcounts2(x1, x2, edges1, edges2); joint_prob joint_hist / sum(joint_hist(:)); % 边缘分布 p1 sum(joint_prob, 2); p2 sum(joint_prob, 1); % 计算互信息 mi_val 0; for i 1:n_bins for j 1:n_bins if joint_prob(i,j) 0 mi_val mi_val joint_prob(i,j) * ... log(joint_prob(i,j) / (p1(i) * p2(j))); end end end MI(tau_idx) mi_val; end % 找第一个局部极小值 tau 1; for i 2:max_tau-1 if MI(i) MI(i-1) MI(i) MI(i1) tau i; break; end end end这段代码有几个实操细节要注意。第一n_bins不能太小否则互信息估计偏差大也不能太大否则每个区间里样本太少统计噪声大。我一般取round(sqrt(N/5))对于N5000的数据大约32个区间实测下来比较稳。第二找局部极小值时如果第一个极小值对应的互信息值已经很小接近零说明序列相关性很弱这时候可以直接取τ1。第三如果数据有趋势项建议先差分或者去趋势否则互信息曲线会单调下降找不到极小值。2.3 实操中的坑与经验互信息法最大的问题是计算量。如果max_tau取到100n_bins取32那就要算100次32×32的联合直方图对于长序列来说比较慢。我的优化做法是先用自相关法粗估一个范围比如自相关第一次过零的位置乘以2然后在这个范围内用互信息法精细搜索。这样能把max_tau从100降到30左右速度提升明显。还有一个坑是区间划分方式。等宽划分在数据分布不均匀时效果不好可以考虑等概率划分也就是按分位数来定边界。但等概率划分在MATLAB里实现麻烦一些我一般先用等宽如果互信息曲线不光滑再换等概率。注意互信息法得到的时间延迟是整数但实际系统的最优延迟可能是小数。对于大多数工程应用整数精度足够了。如果确实需要小数延迟可以用插值方法生成非整数延迟的序列但计算量会成倍增加。3. 嵌入维数的确定虚假最近邻法与Cao方法3.1 虚假最近邻法的核心思想嵌入维数的选取逻辑是这样的假设当前嵌入维数是m如果把它增加到m1重构空间中原本相邻的两个点突然变得很远说明这两个点是“虚假邻居”它们之所以在m维空间里看起来近是因为高维空间被压缩到了低维。当嵌入维数增加到某个值后虚假邻居的比例降到很低比如5%以下就认为这个m是合适的嵌入维数。具体判断虚假邻居的准则对于m维空间中的每个点找到它的最近邻然后看这两个点在增加到m1维后距离是不是显著增大。如果增大比例超过某个阈值通常取10到50倍或者新增加的那个坐标分量导致距离变化超过某个标准差倍数就判定为虚假邻居。function [m_opt, fnn_ratio] false_nearest_neighbor(x, tau, max_m, rtol, atol) % x: 时间序列 % tau: 时间延迟 % max_m: 最大嵌入维数 % rtol: 相对阈值默认10 % atol: 绝对阈值默认2 x x(:); N length(x); if nargin 4 rtol 10; end if nargin 5 atol 2; end fnn_ratio zeros(max_m, 1); for m 1:max_m % 构造m维和m1维嵌入 M N - m * tau; if M 10 break; end Y_m zeros(M, m); Y_m1 zeros(M, m1); for i 1:m Y_m(:, i) x((i-1)*tau1 : (i-1)*tauM); Y_m1(:, i) Y_m(:, i); end Y_m1(:, m1) x(m*tau1 : m*tauM); % 计算标准差用于绝对阈值 sigma std(x); n_fnn 0; for i 1:M % 找m维空间中的最近邻排除自身 dist_m sqrt(sum((Y_m - Y_m(i,:)).^2, 2)); dist_m(i) inf; [~, nn_idx] min(dist_m); % 计算m1维空间中的距离 dist_m1 sqrt(sum((Y_m1 - Y_m1(i,:)).^2, 2)); % 判断是否虚假邻居 if dist_m(i) 0 ratio dist_m1(nn_idx) / dist_m(i); if ratio rtol || abs(dist_m1(nn_idx) - dist_m(i)) atol * sigma n_fnn n_fnn 1; end end end fnn_ratio(m) n_fnn / M; end % 找第一个低于5%的m m_opt max_m; for m 1:max_m if fnn_ratio(m) 0.05 m_opt m; break; end end end3.2 Cao方法解决FNN的阈值依赖问题虚假最近邻法有个主观性很强的地方阈值rtol和atol怎么选选10和选50结果可能差好几个维数。Cao方法也叫Cao氏方法解决了这个问题它定义了一个不依赖阈值的量E1(m)当E1(m)趋于饱和时对应的m就是嵌入维数。Cao方法的计算逻辑是对于每个点找最近邻然后计算m1维距离与m维距离的比值取所有点的平均。这个平均值就是E1(m)。当m增加到某个值后E1(m)不再显著变化就认为饱和了。同时Cao还定义了E2(m)用来区分确定性混沌和随机信号如果E2(m)恒等于1说明是随机序列。function [m_opt, E1, E2] cao_method(x, tau, max_m) % Cao方法求嵌入维数 x x(:); N length(x); E1 zeros(max_m, 1); E2 zeros(max_m, 1); for m 1:max_m M N - m * tau; if M 10 break; end Y_m zeros(M, m); Y_m1 zeros(M, m1); for i 1:m Y_m(:, i) x((i-1)*tau1 : (i-1)*tauM); Y_m1(:, i) Y_m(:, i); end Y_m1(:, m1) x(m*tau1 : m*tauM); a zeros(M, 1); for i 1:M dist_m sqrt(sum((Y_m - Y_m(i,:)).^2, 2)); dist_m(i) inf; [~, nn_idx] min(dist_m); dist_m1 sqrt(sum((Y_m1 - Y_m1(i,:)).^2, 2)); if dist_m(i) 0 a(i) dist_m1(nn_idx) / dist_m(i); else a(i) 0; end end E1(m) mean(a); % E2的计算需要找不同邻域的点这里简化处理 if m 1 E2(m) E1(m) / E1(m-1); else E2(m) 1; end end % 找E1饱和点 m_opt max_m; for m 2:max_m-1 if abs(E1(m1) - E1(m)) / E1(m) 0.01 m_opt m; break; end end endCao方法的好处是阈值不敏感但计算量比FNN大因为要算两次距离。我一般先用FNN粗估再用Cao验证。如果两者差1到2维取较大的那个更保险。3.3 实操中的注意事项FNN和Cao方法都有一个共同的前提时间延迟已经确定。如果τ选得不对嵌入维数的估计也会偏。所以顺序不能反必须先定τ再定m。另外数据长度对嵌入维数估计影响很大。N1000和N10000同样的方法给出的m可能差2到3。我的经验是N至少要大于10的m次方量级对于m5N至少要有10000点。如果数据不够长宁可把m取大一点也不要取小因为嵌入维数不足会导致相空间轨迹重叠后续的Lyapunov指数计算会完全失真。还有一个细节FNN方法在m较小时虚假邻居比例可能不是单调下降的会出现波动。这时候不要急着取第一个低于5%的点要看整体趋势取第一个稳定低于5%的m。4. C-C方法一步到位同时估计两个参数4.1 C-C方法的关联积分思路前面说的互信息法加FNN法需要分两步走而且FNN对阈值敏感。C-C方法也叫关联积分法把两个参数放在一起估计核心工具是关联积分。关联积分的定义是在重构空间中距离小于给定半径r的点对占总点对的比例。当嵌入维数和时间延迟变化时关联积分的变化规律能同时反映两个参数的信息。C-C方法定义了一个统计量S(m, N, r, τ)它是关联积分的函数。当S第一次过零点或者达到最小值时对应的τ就是最优时间延迟。同时另一个统计量Scor(τ)的最小值对应的时间窗口τw和嵌入维数m满足关系τw (m-1)τ。所以只要确定了τ和τw嵌入维数m就出来了。function [tau_opt, m_opt, S, Scor] cc_method(x, max_tau, max_m) % C-C方法同时估计时间延迟和嵌入维数 x x(:); N length(x); % 标准差 sigma std(x); % 半径序列 r_seq sigma * [0.5, 1, 1.5, 2]; n_r length(r_seq); S zeros(max_tau, 1); Scor zeros(max_tau, 1); S_mean zeros(max_tau, n_r); for tau_idx 1:max_tau tau tau_idx; % 对每个嵌入维数计算S S_temp zeros(max_m, n_r); for m 2:max_m M N - (m-1)*tau; if M 10 continue; end Y zeros(M, m); for i 1:m Y(:, i) x((i-1)*tau1 : (i-1)*tauM); end % 计算关联积分 for r_idx 1:n_r r r_seq(r_idx); count 0; for i 1:M dist sqrt(sum((Y - Y(i,:)).^2, 2)); count count sum(dist r) - 1; end C count / (M * (M-1)); if C 0 S_temp(m, r_idx) C; end end end % 计算S统计量 for r_idx 1:n_r S_mean(tau_idx, r_idx) mean(S_temp(2:max_m, r_idx)); end S(tau_idx) mean(S_mean(tau_idx, :)); % 计算Scor if tau_idx 1 delta_S max(S_mean(tau_idx, :)) - min(S_mean(tau_idx, :)); Scor(tau_idx) delta_S abs(S(tau_idx)); end end % 找S的第一个零点 tau_opt 1; for i 2:max_tau if S(i-1) * S(i) 0 tau_opt i; break; end end % 找Scor的最小值对应的时间窗口 [~, tauw_idx] min(Scor(2:end)); tauw tauw_idx 1; % 计算嵌入维数 m_opt round(tauw / tau_opt) 1; end4.2 C-C方法的参数选择与实操细节C-C方法里半径序列的选取很关键。我一般取标准差乘以0.5到2之间的几个值至少4个。如果数据方差特别大可以适当扩大范围。max_m一般取到10到20对于大多数混沌系统嵌入维数不会超过这个范围。C-C方法最大的优势是计算效率高因为它只需要算关联积分不需要找最近邻。但它的精度不如互信息法加FNN法的组合。我实测下来C-C方法给出的τ和m和两步法相比τ通常一致m可能差1。所以如果对精度要求高还是建议用两步法C-C方法用来快速验证。还有一个坑C-C方法对数据长度很敏感。N小于2000时Scor曲线可能没有明显的最小值这时候方法失效。所以短序列还是老老实实用互信息加FNN。提示C-C方法里的关联积分计算可以用矩阵运算加速把双重循环改成向量化操作速度能提升10倍以上。具体做法是用pdist2函数算距离矩阵然后统计小于r的元素个数。5. 完整实操流程与参数验证5.1 从数据到参数的完整步骤假设你手头有一维时间序列x长度N5000采样间隔已知。完整流程是这样的第一步预处理。去均值、去趋势如果有明显噪声可以先做小波降噪或者滑动平均。但要注意降噪不能过度否则会把混沌信号的高频成分滤掉影响后续分析。第二步用互信息法求时间延迟。max_tau取N/10500n_bins取sqrt(5000/5)≈32。跑完得到τ。第三步用FNN法求嵌入维数。max_m取20rtol取10atol取2。跑完得到m_fnn。第四步用Cao方法验证。如果Cao给出的m和FNN差不超过2取较大的那个。第五步用C-C方法交叉验证。如果C-C给出的τ和互信息法一致m和前面差不超过1那基本可以确定参数没问题。第六步用确定的τ和m重构相空间画二维或三维轨迹图肉眼观察吸引子形状是否合理。如果轨迹是一条线或者一团乱麻说明参数不对。% 完整流程示例 load(time_series.mat); % 假设数据在x中 x x(:); N length(x); % 预处理 x x - mean(x); x detrend(x); % 第一步互信息法 tau_mi mutual_information(x, floor(N/10), round(sqrt(N/5))); fprintf(互信息法得到时间延迟: %d\n, tau_mi); % 第二步FNN法 [m_fnn, fnn_ratio] false_nearest_neighbor(x, tau_mi, 20, 10, 2); fprintf(FNN法得到嵌入维数: %d\n, m_fnn); % 第三步Cao方法 [m_cao, E1, E2] cao_method(x, tau_mi, 20); fprintf(Cao方法得到嵌入维数: %d\n, m_cao); % 第四步C-C方法 [tau_cc, m_cc, S, Scor] cc_method(x, 100, 20); fprintf(C-C方法得到时间延迟: %d, 嵌入维数: %d\n, tau_cc, m_cc); % 第五步重构相空间并画图 m_final max(m_fnn, m_cao); tau_final tau_mi; M N - (m_final-1)*tau_final; Y zeros(M, m_final); for i 1:m_final Y(:, i) x((i-1)*tau_final1 : (i-1)*tau_finalM); end figure; plot3(Y(:,1), Y(:,2), Y(:,3)); xlabel(x(t)); ylabel(x(t\tau)); zlabel(x(t2\tau)); title(重构相空间轨迹); grid on;5.2 参数合理性验证的三种方法参数选完了怎么知道对不对我一般用三种方法交叉验证。第一种是看吸引子图。对于Lorenz系统重构出来的应该是蝴蝶形状对于Rossler系统应该是单卷或者双卷结构。如果画出来是一团没有结构的点云说明参数不对。第二种是算关联维数。用Grassberger-Procaccia算法算关联维数如果随着嵌入维数增加关联维数趋于饱和说明嵌入维数够了。如果还在增长说明m不够。第三种是算Lyapunov指数。如果最大Lyapunov指数为正说明系统是混沌的。如果算出来是负的或者零要么系统不是混沌的要么参数选错了。这三种方法里看吸引子图最直观算Lyapunov指数最严格。我一般先看图图对了再算指数确认。5.3 常见问题速查表问题现象可能原因解决方法互信息曲线单调下降找不到极小值数据有强趋势或周期性先差分或去趋势再算互信息FNN比例始终高于5%数据太短或噪声太大增加数据长度或先降噪Cao方法E1不饱和嵌入维数上限设得太小把max_m增加到20以上C-C方法Scor无最小值数据长度不足2000改用两步法重构轨迹是一团乱麻τ或m选错了重新用互信息加FNN算关联维数不饱和嵌入维数不够增大m重新算Lyapunov指数为负系统不是混沌的检查数据来源或换其他方法6. 实操心得与避坑指南6.1 数据预处理的分寸感预处理这块我踩过最大的坑就是降噪过度。有一次拿到的振动信号噪声很大我直接用了一个截止频率很低的低通滤波器结果把混沌信号的高频成分也滤掉了互信息法给出的τ从12变成了25完全不对。后来改成小波降噪只去掉最细节的系数才恢复正常。所以我的建议是如果信噪比不是特别差尽量不降噪或者只用很温和的降噪。去均值和去趋势是必须的但差分要谨慎因为差分相当于高通滤波会放大高频噪声。6.2 参数搜索范围的设定max_tau和max_m的设定也有讲究。max_tau太小可能错过最优延迟太大计算量浪费。我一般取N/10对于N5000就是500但实际上互信息法的极小值通常出现在10到50之间所以可以先用N/20粗搜再在极小值附近细搜。max_m一般取20足够了。如果FNN在20时还没降到5%以下要么数据有问题要么系统嵌入维数确实很高这时候可以增加到30但要注意数据长度是否支持。6.3 计算效率的优化技巧互信息法和FNN法的双重循环是性能瓶颈。我的优化做法是把距离计算向量化用pdist2或者手动构造距离矩阵。对于N5000m10距离矩阵是5000×5000内存占用200MB左右现代机器完全能承受。向量化之后FNN的计算时间从几分钟降到几秒。C-C方法的关联积分也可以用类似方法加速。但要注意pdist2算的是欧氏距离如果要用其他距离度量需要自己实现。6.4 多方法交叉验证的必要性我强烈建议不要只用一种方法。互信息法加FNN法是最经典的组合但FNN对阈值敏感Cao方法虽然阈值不敏感但计算量大C-C方法快但精度稍差。三种方法交叉验证如果结果一致那基本没问题如果不一致取较大的m和较小的τ这样更保守不容易漏掉系统的动态特征。最后分享一个小技巧如果你有多个通道的数据比如多传感器采集的同一系统可以分别对每个通道做相空间重构然后比较得到的τ和m。如果差异很大说明数据质量有问题或者系统不是平稳的。这个方法我用来排查过好几次数据采集故障很管用。

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

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

免费获取报价 →
↑