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

ERP 波形计算与地形图绘制

把模块 2 洗好的试次一层层叠起来,得到一条属于你自己的 ERP 波形。这一课先讲清 "平均"背后的信噪比直觉,再用 EEGLAB 把波形、ERPimage 和地形图全部画出来。

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

🎯 本节学习目标

  • 能用"信号锁时、噪声随机"的信噪比直觉,解释平均为什么能提取出 ERP
  • 理解 epochs 数据从"通道 × 时间 × 试次"到 ERP 的"通道 × 时间"发生了什么
  • 会用 EEGLAB GUI 与脚本两种方式计算并绘制单被试 ERP 波形
  • 理解 grand average 的意义,能写出跨被试的组平均脚本
  • 会画并解读 ERPimage 与头皮地形图,掌握 topoplot 的关键参数

一、为什么要平均:一场信号和噪声的拔河

先面对一个让人沮丧的事实:单看一个试次,你几乎看不出 ERP。 ERP 成分的典型幅度只有几个微伏,而自发脑电、残留肌电动辄是它的五到十倍—— 信号是被噪声淹没的。

但 ERP 有一个噪声没有的特质:它和刺激锁时。刺激一出现,P300 就在 300ms 左右冒头,一百个试次里次次如此;噪声则与刺激无关,这一试次偏正、 下一试次偏负,完全随机。把试次对齐刺激点后逐点平均:信号每次都往同一个 方向使劲,被完整保留;噪声时正时负,互相抵消。

一个类比:教室里有人小声念课文,全班在随机聊天。只录一遍,念书声听不见; 把一百遍录音的起点对齐、逐点叠加,聊天声正负抵消,念书声一遍遍加强—— 它自己就浮出来了。

📖 概念:信噪比与 √N 法则

平均 N 个试次,信号不变,噪声的标准差缩小为原来的 1/√N——100 个试次能把噪声 压到十分之一。这就是"多收试次"永远不过时的原因:它是提升信噪比最笨、也最有效的手段。

Matlab
% 用模拟数据直观感受"平均"的威力
% 每个试次 = 固定形状的"ERP"(信号) + 随机噪声
fs = 500;                                    % 采样率 (Hz)
t  = -0.2 : 1/fs : 0.8;                      % -200 ~ 800 ms 时间轴
sig = 6 * exp(-((t - 0.3) / 0.05).^2);       % 模拟 300ms 的正波,峰值 6 uV
nTrial = 100;
data = zeros(length(t), nTrial);             % 时间点 x 试次
for k = 1:nTrial
    data(:, k) = sig + 8 * randn(length(t), 1);   % 噪声比信号还大——真实脑电的常态
end

figure;
subplot(3, 1, 1); plot(t, data(:, 1));        title('单个试次:信号被噪声淹没');
subplot(3, 1, 2); plot(t, mean(data(:, 1:10), 2)); title('平均 10 个试次:隐约成形');
subplot(3, 1, 3); plot(t, mean(data, 2));     title('平均 100 个试次:ERP 浮出水面');
xlabel('时间 (s)'); ylabel('幅度 (\muV)');

二、从 epochs 到 ERP:维度发生了什么

模块 2 结束时,你手里的 EEG.data 是一个三维矩阵: 通道 × 时间点 × 试次(例如 64 × 501 × 80)。 所谓"算 ERP",就是沿第 3 维(试次维)求平均,得到一个 通道 × 时间的二维矩阵——试次维被平均掉了,剩下的就是 "这堆试次的典型反应"。

理解这一步的另一个好处:你会明白 ERP 是描述性的——它是平均的结果, 不是某一次"真实发生"的波形。单试次里信号长什么样,要靠 ERPimage(第六节)去看。

三、单被试 ERP:EEGLAB GUI 路径

加载预处理好的 .set 文件后,画 ERP 主要走 Plot 菜单下的三条路:

  • 带地形图的波形Plot > Channel ERPs > With scalp maps——所有通道的 ERP 排成一页,附一张最大方差时刻的地形图;点任意通道的波形,可查看对应时刻的地形图;
  • 按头皮位置排波形Plot > Channel ERPs > In scalp array——每个通道的波形画在它所在的头皮位置上,一眼看出哪个区域在什么时候"动"了;
  • 一串时刻的地形图Plot > ERP map series > In 2-D——输入 0:100:500 这样的时刻列表,画出各时刻的地形图序列。

看图时盯住三件事:极性(正波还是负波)、潜伏期(波峰在刺激后多少毫秒)、 头皮分布(集中在哪个区域)。三件事合起来,才是一个 ERP 成分的"身份"。

(旧版本 EEGLAB 里这些菜单叫 Channel ERPs (popup),功能相同。)

四、单被试 ERP:脚本方式

GUI 适合看图,脚本适合复现。核心只有一行,剩下的都是画图:

Matlab
% 前提:EEG 是模块 2 结束时的分段数据(已基线校正)
ERP = mean(EEG.data, 3);                    % 沿第 3 维(试次维)平均 -> 通道 x 时间
[~, chIdx] = ismember({'Pz'}, {EEG.chanlocs.labels});   % 找到 Pz 的通道编号

figure; plot(EEG.times, ERP(chIdx, :), 'LineWidth', 1.2); hold on;
xline(0, '--');                             % 刺激 onset 参考线
xlabel('时间 (ms)'); ylabel('幅度 (\muV)'); title('单被试 Pz 的 ERP 波形');

% EEGLAB 自带的画 ERP 函数:GUI 中 Plot > Channel ERPs 的脚本等价形式
pop_erpplot(EEG, 1, [chIdx], 0);            % 通道列表;0 表示不叠加标准差
💡 GUI 用出来的命令不用背

想不起 pop_erpplot 的参数顺序?在 GUI 里点一遍 Plot > Channel ERPs, 再到命令行敲 eegh——你在界面里做的每一步都会变成一条可直接复制的命令 (模块 2.4 讲过的老技巧,永远好用)。

五、组平均 grand average:一条属于"这群人"的波形

单被试的 ERP 只是"这个人这一次实验"的结果,被试间的个体差异(头型、皮层折叠、 注意状态……)会原样留在波形里。grand average(组平均)先给每个被试算出 他自己的 ERP,再跨被试平均——个体间的随机差异像试次间的噪声一样被抵消, 剩下的是这群人的典型反应。论文结果图里那条粗线,通常就是它。

📖 概念:grand average(组平均)

"平均的平均":先每人一条 ERP(试次级平均),再跨被试平均(被试级平均)。 两次平均对付的是两种不同的随机性——试次间的噪声,和被试间的个体差异。

Matlab · grand_average.m
%% 组平均:先算每个人的 ERP,再跨被试平均
clear; clc; close all;
dataDir = 'D:\project\processed\';          % 预处理输出目录(英文路径)
subs    = {'sub01','sub02','sub03','sub04','sub05'};

erpAll = [];  times = [];
for s = 1:numel(subs)
    EEG = pop_loadset('filename', [subs{s} '_pp.set'], 'filepath', dataDir);
    erp = mean(EEG.data, 3);                % 这名被试的 ERP:通道 x 时间
    if isempty(erpAll)
        % 用第一名被试初始化尺寸与通道编号
        [~, chIdx] = ismember({'Pz'}, {EEG.chanlocs.labels});
        erpAll = zeros(numel(subs), numel(EEG.times));
        times  = EEG.times;                 % 预处理一致 -> 时间轴一致
    end
    erpAll(s, :) = erp(chIdx, :);           % 收集 Pz 波形:每行一个被试
    fprintf('%s:剩余 %d 个试次\n', subs{s}, EEG.trials);   % 顺手做质控
end
GA = mean(erpAll, 1);                       % 跨被试平均 -> 组平均

figure; hold on;
plot(times, erpAll, 'Color', [0.75 0.75 0.75]);   % 灰色:每个被试
plot(times, GA, 'r', 'LineWidth', 2);             % 红色:组平均
xlabel('时间 (ms)'); ylabel('幅度 (\muV)');
title('灰色为单被试,红色为 grand average');
⚠️ 组平均的两个坑

一是把所有被试的试次直接混在一起平均(伪被试):条件间试次数不等时结果被扭曲, 也做不了被试级统计;二是无视试次数差异:某人只剩 20 个试次、别人有 80 个, 他的 ERP 噪声更大,会把组平均"拉毛"。先看每人的试次数(上面的 fprintf 就是在做这件事),再进组平均。

六、ERPimage:把每个试次摊开看

平均是把双刃剑:它把噪声压下去的同时,也把试次间的差异抹掉了—— 而这些差异可能正是大脑实时工作的线索。ERPimage 把单个通道的所有试次 排成一张试次 × 时间的图像:每一行是一个试次的完整波形,颜色代表电位。 默认按采集顺序排列,也可以按反应时、窗口内幅值等变量排序。

GUI 路径:Plot > Channel ERP image(对应 pop_erpimage), 填通道号和 smoothing(相邻试次平滑宽度)即可;排序可以在弹窗里选事件字段(如反应时 rt)。

Matlab
%% 手工画 ERPimage:imagesc 就够了
X = squeeze(EEG.data(chIdx, :, :))';        % 原为 1 x 时间 x 试次,转置成 试次 x 时间

figure;
subplot(2, 1, 1);
imagesc(EEG.times, 1:EEG.trials, X);        % 每一行 = 一个试次的完整波形
set(gca, 'YDir', 'normal'); colorbar; axis tight;
xlabel('时间 (ms)'); ylabel('试次编号'); title('按采集顺序排列');

% 换一种排法:按 250-350ms 窗口内的平均幅值从小到大排序
subplot(2, 1, 2);
idxWin = EEG.times >= 250 & EEG.times <= 350;
[~, order] = sort(mean(X(:, idxWin), 2));   % 每个试次在窗口内的幅值
imagesc(EEG.times, 1:EEG.trials, X(order, :));
set(gca, 'YDir', 'normal'); colorbar; axis tight;
xlabel('时间 (ms)'); ylabel('试次(按幅值排序)'); title('同一份数据,按窗口幅值排序');

怎么读:竖着的色带 = 与刺激锁时的成分(所有试次同时变红/变蓝); 排序后出现的斜纹 = 成分的幅度或潜伏期随排序变量系统变化—— 这正是平均波形里看不到、却可能最有故事的信息。

七、地形图:电极值如何"铺满"头皮

电极只是头皮上的几十个采样点,像在草坪上插了几十根测湿度的探针。 地形图做的事,是用插值(球面样条)从这些离散点推算整个头皮表面的电位分布, 再用红(正)蓝(负)上色。它回答的问题是:某个时刻,电活动集中在头皮哪里?

Matlab
% 画 300ms 时刻的地形图:给每个电极一个值
[~, tIdx] = min(abs(EEG.times - 300));      % 离 300ms 最近的时间点下标
vals = ERP(:, tIdx);                        % 所有电极在该时刻的平均电位 (uV)

figure;
topoplot(vals, EEG.chanlocs, ...
    'maplimits', 'maxabs', ...              % 颜色范围对称:红=正,蓝=负
    'electrodes', 'labels', ...             % 在头皮图上标出电极名
    'style', 'both', ...                    % 填色 + 等高线
    'numcontours', 6);                      % 等高线条数
title('300 ms 的头皮电位分布');
参数作用常用取值与说明
maplimits颜色范围[min max]'maxabs'(对称);多张图对比时必须手动统一,否则颜色不可比
electrodes电极标注方式'off' / 'labels' / 'numbers'
style绘图风格'both'(填色+等高线,最常用)/ 'straight' / 'fill'
numcontours等高线条数6–10;太多会显得杂乱

GUI 里画一串时刻的地形图用 Plot > ERP map series > In 2-D。 注意地形图的前置条件是电极定位正确(模块 2.2)——没有坐标,插值无从谈起。

八、本节交付物

📌 到这一节结束,你应该有三张图

一张单被试 ERP 波形图(Pz,带 0ms 参考线)、一张组平均图 (灰色单被试 + 红色 grand average)、一张300ms 地形图。 三张图齐了,你的数据才算"算得出、画得好"——下一课开始"测得准"。

📌 本节小结

  • 平均的逻辑:信号随刺激锁时出现、噪声随机起伏,平均后噪声按 1/√N 缩小
  • ERP = mean(EEG.data, 3):试次维被平均掉,三维 epochs 变成二维波形
  • grand average 是"先每人一条 ERP,再跨被试平均";切勿把所有人的试次混在一起
  • ERPimage 是平均的"背面":用排序暴露试次间的差异,检查平均掩盖了什么
  • 地形图 = 电极采样值在头皮表面的插值;多张图对比时必须统一色标

✏️ 课后练习

  1. 把模拟代码里的试次数依次改成 25 / 100 / 400,各画一张平均波形;量一量基线期(0ms 之前)的标准差,验证噪声是否按 1/√N 缩小。
  2. lesson23_preprocessed.set 计算单被试 ERP:画 Pz 波形图(带 0ms 参考线)和 300ms 地形图,分别保存为 PNG。
  3. 用 ERPimage 把同一通道的试次按 250-350ms 幅值排序,与未排序版本对比,写一两句你看到了什么。