阶段二 · 核心分析能力 · 模块 4 · 第 9 课 / 共 28 课

功率谱密度(PSD)计算

ERP 看的是"锁时的平均",而大脑的节律活动大多不锁时——它们藏在频谱里。 这一课学会把一段波形"拆解"成频率成分,并正确地计算功率谱。

🕐 45 分钟🎯 难度:进阶✅ 前置:模块 2 预处理

🎯 本节学习目标

  • 建立傅里叶变换的直觉:任何信号都是不同频率正弦波的叠加
  • 理解频谱、功率谱与功率谱密度的关系
  • 掌握 Welch 平均周期图方法及其"为什么"
  • 能用 Matlab 手写 fft 版 PSD,也能规范地使用 pwelch
  • 知道计算 EEG 功率谱时数据长度、采样率、1/f 趋势各有什么讲究

一、为什么要看频率

想象你在听一首歌:示波器看到的是一条不断抖动的空气压力曲线, 但你的耳朵听到的是"低音、中音、高音"。耳朵做的事情就是 频率分解——把混合在一起的波形拆成各个频率的成分。

脑电同理。头皮记录到的信号是无数神经元群体同步振荡的混合物: 有 10Hz 左右的 α 节律、有 20Hz 的 β 活动、也有慢悠悠的 δ 波。 ERP 平均会把不锁时于刺激的节律"平均掉",而这些节律恰恰携带了 大脑状态的关键信息——所以我们需要频域分析。

📌 一句话记住

时域图回答"什么时候发生了什么",频谱回答"这里面有哪些节律、各有多强"。两个视角互补,缺一不可。

二、傅里叶变换:拆解波形的"三和弦"

傅里叶变换的神奇之处在于:任何一段合理信号,都可以唯一地 表示成一组不同频率、不同幅度、不同相位的正弦波的叠加。就像一个和弦—— 你听到的是 C、E、G 三个音的混合,而傅里叶变换能精确告诉你 "这个和弦里有哪几个音、每个音多响"。

先用一段仿真信号建立直觉:

Matlab
% 构造一个"三和弦"信号:5Hz + 10Hz + 40Hz 三个正弦波叠加
fs = 500;                    % 采样率 500 Hz
t  = 0 : 1/fs : 2 - 1/fs;    % 2 秒时间轴
x  = 3*sin(2*pi*5*t) ...     % 5Hz,幅度 3
   + 7*sin(2*pi*10*t) ...    % 10Hz,幅度 7(最强)
   + 2*sin(2*pi*40*t);       % 40Hz,幅度 2

figure; plot(t(1:250), x(1:250));   % 看前 0.5 秒
xlabel('时间 (s)'); title('时域:一团乱麻?');

时域上这就是一团复杂的波形,肉眼看不出任何结构。但做一次 FFT:

Matlab
N  = length(x);
X  = fft(x) / N;            % 除以 N 得到归一化幅值
f  = (0:N-1) * fs / N;      % 每个点对应的频率

half = 1 : floor(N/2);      % 只取一半(对称镜像)
figure; stem(f(half), abs(X(half)), 'filled');
xlabel('频率 (Hz)'); ylabel('幅度');
title('频域:清清楚楚三个峰 —— 5Hz(3) / 10Hz(7) / 40Hz(2)');

频谱上只有三个峰,分别在 5、10、40 Hz,高度恰好是 3、7、2。 混乱消失了——这就是频率视角的力量。

频谱泄漏(leakage)

如果信号不是"整周期截断"(比如 2 秒里恰好装不下整数个 10.3Hz 周期),能量会从真实频率"漏"到邻近频点,谱峰变成矮胖的丘。后面 4.3 会专门处理它。

三、从频谱到功率谱

幅度谱(|X(f)|)衡量每个频率"有多强",但物理和统计上我们更常用 功率——也就是幅度的平方:

Matlab
P = abs(X).^2;              % 功率谱 = 幅度谱的平方
% 平方有两个好处:
% 1) 与能量直接挂钩(物理意义)
% 2) 让小信号更小、大信号更大,对比更鲜明

而当你把信号长度增加一倍、把相邻频点的功率加起来归一化,就得到 功率谱密度(PSD, Power Spectral Density),单位是 功率/Hz(如 μV²/Hz)。它描述的是"每个频率上、每 1Hz 带宽内分布着多少功率", 与数据长度无关,因此可以在不同研究间直接比较。

四、周期图与 Welch 方法:为什么要"分段平均"

最直接的 PSD 估计叫周期图(periodogram):整段数据做一次 FFT 再平方。 它的问题在于方差极大——对同一段平稳信号反复截取不同片段算周期图, 得到的谱会剧烈抖动,像噪声一样不可靠。

Welch 方法是标准解法,三步走:

  1. 分段:把长数据切成若干段(可重叠);
  2. 加窗:每段乘一个窗函数(默认 Hann 窗),压住截断造成的泄漏;
  3. 平均:把各段的功率谱平均,方差被压低到约 1/K(K 为段数)。
💡 直觉类比

周期图像"只问一个人"的民意调查,Welch 像"问了一百个人取平均"——牺牲一点分辨率(每段变短了),换来稳定性(谱不再乱跳)。这个"分辨率换稳定性"的交易,贯穿整个频域分析。

五、Matlab 标准实现:pwelch

Matlab
% pwelch 是科研中最常用的 PSD 估计函数
fs = 500;
[pxx, f] = pwelch(eeg_data, ...
    hann(500), ...          % 窗:500 点 Hann 窗(约 1 秒)
    250, ...                % 相邻段重叠 250 点(50%)
    2048, ...               % nfft:补零到 2048 点,插密频轴
    fs);                    % 采样率

figure; plot(f, 10*log10(pxx));    % 习惯画成对数刻度(dB)
xlabel('频率 (Hz)'); ylabel('功率谱密度 (dB, re 1 \muV^2/Hz)');
xlim([1 80]);                     % 只看有生理意义的频段

参数选择的门道:

参数控制什么经验建议
窗长频率分辨率(≈ fs/窗长)2 秒窗 → 0.5Hz 分辨率,够分辨 α 峰
重叠率段数多少 → 平滑程度50% 是标配
nfft频轴密度(补零插值)≥ 窗长即可,常用 2 的幂;不改变真实分辨率
⚠️ 最常见误解

把 nfft 调大并不会提高真正的频率分辨率——分辨率由窗内数据长度唯一决定,补零只是把频轴插得更密。想要分辨 9Hz 和 9.5Hz,唯一办法是用更长的窗。

六、计算 EEG 功率谱的五个注意事项

1. 数据长度决定频率分辨率

分辨率 = fs / N。1 秒数据只有 1Hz 分辨率;想精细看 α 峰的形态,至少 2-4 秒连续数据。静息态分析常用 2 秒窗。

2. Nyquist 限制:一半以上的频率是"镜像"

采样率 500Hz 的数据,有效频率上限是 250Hz。频谱上半部分只是下半部分的镜像回折,画图时通常只画到 fs/2。

3. 1/f 背景趋势

真实 EEG 功率谱不是平的:低频功率远大于高频(近似 1/f 规律),叠加慢漂移后更明显。直接看绝对功率会"低频独大"。对策:分段前去趋势detrend)或高通滤波 0.5-1Hz,或分析时改用相对功率 / 对数刻度。

4. 典型频段与生理含义

频段频率范围典型状态
δ (delta)1–4 Hz深睡、无意识状态
θ (theta)4–8 Hz困倦、记忆编码、冥想
α (alpha)8–13 Hz闭眼放松、视觉皮层空闲
β (beta)13–30 Hz清醒专注、运动控制
γ (gamma)> 30 Hz高阶认知、注意绑定(幅度小,易受肌电污染)

5. epoch 数据 vs 连续数据

同样的分段窗(如 2 秒),从连续数据切窗和从预处理后的 epochs 切窗,结果口径不同——epochs 已经过基线校正和伪迹剔除,通常更干净;但若在 epochs 边缘切窗会有截断效应。报告方法时必须写清楚用的是哪种、窗长多少、重叠多少。

七、把一课串起来:单通道 EEG 的 PSD 完整流程

Matlab
% 输入:eeg_cont (1 x Ntimes) 连续数据,fs = 500
fs = 500;
eeg_det = detrend(eeg_cont');          % 去趋势,压掉慢漂移

[pxx, f] = pwelch(eeg_det, hann(2*fs), fs, 4096, fs);

% 提取各经典频段的平均功率
bands = struct( ...
  'delta',  [1 4], 'theta', [4 8], ...
  'alpha',  [8 13], 'beta', [13 30], 'gamma', [30 45]);
bn = fieldnames(bands);
for k = 1:numel(bn)
    idx = f >= bands.(bn{k})(1) & f <= bands.(bn{k})(2);
    pw(k) = mean(pxx(idx));            % 该频段平均 PSD
end
disp(table(bn', pw', 'VariableNames', {'band','power'}));

figure; plot(f, 10*log10(pxx)); xlim([1 50]);
xlabel('频率 (Hz)'); ylabel('PSD (dB)');

这段代码就是静息态 EEG 频谱分析的骨架——把它包一层被试循环, 就是你在无数论文方法部分看到的"各频段功率"指标。

📌 本节小结

  • 傅里叶变换把时域混合波形唯一拆解为频率成分:时域乱麻、频域三峰
  • 功率谱 = 幅度平方;PSD = 功率/Hz,与数据长度无关、可跨研究比较
  • 周期图方差大,Welch"分段+加窗+平均"用分辨率换稳定性,是标准做法
  • 频率分辨率由窗内数据长度决定,nfft 补零不提高分辨率
  • EEG 功率谱三件事:去趋势压 1/f、只看到 Nyquist、报告口径写清楚

✏️ 课后练习

  1. 生成叠加 5Hz(幅度3)、10Hz(幅度7)、40Hz(幅度2) 的仿真信号,分别用 fft 手算和 pwelch 计算频谱,对比两者在 10Hz 处的读数差异。
  2. 把窗长从 1 秒改成 4 秒重算 PSD,观察 α 频段(8-13Hz)的谱线是变"锐"还是变"胖",并解释原因。
  3. 写一个函数 bandpower_eeg(pxx, f, [f1, f2]),返回指定频段的平均功率,用它输出五个经典频段的功率表。