跳转至

说明

PP — Peak Picking:在频谱上找尖峰

最简单的频域方法。计算各通道的自动率谱密度(PSD),取平均,在平均谱上找局部峰值作为模态频率,以 PSD 幅度平方根作为振型。

算法直觉

核心思路

想象一座桥在风中微微晃动。如果你在桥上装一个加速度计并记录数据,频域里看:某些频率处的振动幅度会特别大——这些尖峰对应的就是结构的固有频率。

PP 就是干这件事的 :在频谱上找出那些"特别突出的尖峰"。

为什么这能工作?

环境激励(风、交通)的频谱近似平坦(白噪声),结构就像一个滤波器——在固有频率处"放大"输入。因此输出的频谱尖峰就是结构的模态。

PP 的强项和弱项

-** 强** :速度最快(5ch×3000 点约 50 ms),实现简单,适合快速扫描 -** 弱** : - 分不开靠得太近的模态(< 2 个频点间距) - 不估计阻尼 - 依赖平均 PSD 的质量(数据短时分辨率不够)

算法流程

1. Welch 平均 PSD(每通道独立)
   ├─ 信号分成重叠段(50% overlap)
   ├─ 每段加 Hann 窗 → FFT → 功率谱
   └─ 所有段平均 → 各通道 PSD

2. 所有通道 PSD 取平均 → psd_avg[]

3. 峰值检测
   ├─ 局部最大值掩码(比相邻 2 bin 都大)
   ├─ 噪声基底 = 全频段 1/4 分位数
   ├─ SNR 阈值:psd[k] > 4× noise_floor(≈ 6dB)
   ├─ 按幅度降序排列
   └─ 最小间距约束(≥ 5 bins)

4. 振型提取:sqrt(psd[ch][peak_bin]) → 归一化

设计详解

1. Welch 分段平均

为什么不用周期图(直接对整个信号做 FFT)?因为单一帧的 FFT 方差太大。Welch 将信号分成重叠段后平均,以分辨率换取方差降低:

策略 段数 分辨率 方差降低
周期图 1 段 \(f_s / N\)
Welch 50% overlap ~3 段 \(f_s / seg\_len\) ~3×
Welch 更多段 更多 更差(seg_len 更小) 更多

代码实现选择

int target_seg = (n < 128) ? n : (n / 3 < 512 ? n / 3 : 512);
- n/3:保证至少 3 段(50% overlap → (n-seg)/seg + 1 ≥ 3) - 512 cap:ESP32 上 512 点 FFT 约 0.8ms,1024 点约 1.8ms。cap 在 512 保证全流水线 < 50ms - 向下取 2^n:tiny_fft 要求 FFT 长度为 2 的幂

为什么 50% overlap 而不是 75%? 75% 重叠可降更多方差但计算量翻倍。50% 在 ESP32 上是经过验证的最佳平衡点。

2. Hann 窗

for (int i = 0; i < seg_len; i++)
    seg[i] = data[ch * n + offset + i] * hann_window[i];

在源文件 tiny_sysid_common.c 中预计算为静态数组。Hann 窗的主瓣宽度是矩形窗的 2 倍(旁瓣降低 ~30dB),这是 PSD 估计的标准选择。Hamming 旁瓣更低但主瓣稍宽→过渡带更大,对密集模态识别不利。

3. 峰值检测策略

// 局部最大掩码(±2 bins)
is_peak[k] = (psd[k] >= psd[k-2] && psd[k] >= psd[k-1] &&
              psd[k] >= psd[k+1] && psd[k] >= psd[k+2]);
// 噪声基底 = 1/4 分位数(非中位数/最小值)
// SNR 阈值:6 dB (4× noise_floor)

±2 bins 比 ±1 更保守:对于良好的分离模态,峰值至少横跨 3-5 个 bin。

为什么噪声基底用 ¼ 分位数? - 最小值 → 受频谱谷值影响太大,不稳定 - 中位数 → 如果模态很多(频谱被填满),中位数偏高,漏掉弱模态 - ¼ 分位数 → 稳健估计:假设至少 75% 的频点不含模态,取第 25% 作为背景噪声的保守估计

6 dB 阈值(4×)的统计意义:在 Welch 平均后,PSD 估计的置信区间约为 ±30%(对 3 段而言)。6 dB ≈ 4× 意味着峰高至少是背景的 4 倍,超出 95% 置信区间。

4. 内存 & 计算(ESP32 视角)

  • PSD 缓冲区:n_ch × n_bins = 5 × 1024 = 5120 float ≈ 20 KB → SRAM 可容纳
  • FFT 缓冲区(复用):fft_len × 2 = 2048 float ≈ 8 KB
  • 总耗时:5 通道 × 3 段 × (seg_len/2) FFT ≈ 5×3×0.4ms ≈ 6ms → PP 实际耗时 < 10ms

关键参数

参数 默认 说明
seg_len min(N/3, 512) 向下取 2^n 分段长度
overlap 50% 段间重叠
window Hann 窗函数
SNR threshold 4× noise_floor 峰值检出阈值

何时使用 PP

  • 第一次看数据时快速扫一眼频率分布
  • 数据很短(≥ 500 点即 10s)
  • 作为更精确方法的前置参考