Administrator
发布于 2026-09-09 / 0 阅读
0
0

医学信号处理 TP:自适应滤波去眼部伪差、ICA 与脑连接性(完整解答)

I. 任务描述

目标

我们要解决医学信号处理中的两大问题:

  1. 从复杂的脑电信号中提取癫痫相关信号
    癫痫患者的大脑活动记录通常会被许多其他信号干扰,比如眼睛的运动和肌肉活动。我们要从这些混合信号中分离出对研究癫痫发作最重要的信号。
  2. 研究癫痫相关脑信号之间的连接性
    通过去噪后的数据,研究大脑中不同区域的 交互联系,以更好地了解癫痫发作时的脑网络行为。

数学模型

假设在观察时间 T 内,在一位癫痫患者头皮上使用的 N_c 个传感器(本实验只用两个传感器 N_c=2)来记录信号,最终观测到三种电生理信号的线性瞬时混合。所获取的数据在空间-时间矩阵中表示,记为 X(N_c \times T) ,它满足以下线性瞬时混合模型:

X = A S
  • 其中,A(N_c \times P) 是混合矩阵,表示源信号到传感器信号的传播关系;S(P \times T) 是源信号矩阵,其第 p 行表示第 p 个源信号的时间变化。

  • 注意,在本实验中假设采用无仪器噪声的观测模型。

在第II和第III部分中,会涉及三种生理信号:

  • s_1(n) :癫痫发作间期尖波,是大脑中神经群活动的目标信号。
  • s_2(n) :眼部运动,作为伪差之一。
  • s_3(n) :肌肉活动,作为伪差之二。

其中,s_2(n)s_3(n) 被视为伪差信号,它们干扰了对 s_1(n) 的提取,因此需要在后续处理中被分离并去除。

去伪差与分析步骤描述

  1. 眼部运动去除
    首先通过自适应滤波技术从时空混合信号 X 中分离并去除眼部运动伪差 s_2(n)。自适应滤波器能动态调整自身参数,以适应输入信号的变化,从而有效减小伪差的影响。
  2. 肌肉活动去除
    在去除眼部运动后,剩余信号中的肌肉活动伪差 s_3(n) 将通过独立成分分析(ICA)技术去除。ICA 是一种能够分解混合信号为独立源信号的统计方法,特别适合分离不同来源的生理信号。
  3. 大脑连接性研究
    在去除所有伪差后,提取的目标信号 s_1(n) 被用于研究模拟癫痫发作时的大脑连接性问题。这一部分探索了神经群之间的相互作用,为癫痫的病理研究提供了数据支持。

II. 使用带参考通道的自适应滤波算法去除观测信号中的眼部伪差

这一部分的目标是从两个传感器输出中去除眼部活动(伪差 s_2(n) )。为此,需要考虑两套伪差去除系统(每个通道对应一套,即每个传感器输出对应一个伪差去除系统),下面为了更清晰的描述,我们只详细介绍单个传感器的伪差去除系统,因此考虑另一个传感器的时候,采用相同的步骤即可。

降噪系统中可以观测到两个信号:

  1. 观测信号 y_1(n),包含目标信号 s(n) 和噪声 b_1(n) (要从观测信号 y_1(n) 中去除),满足:

    y_1(n) = s(n) + b_1(n)
  2. 参考信号 y_2(n),直接是噪声信号 b_2(n) 的观测:

    y_2(n) = b_2(n)

这里的假设是:

  • 参考噪声 b_2(n) 和目标噪声/伪差 b_1(n) 相关
  • b_2(n)b_1(n) 的某种滤波版本。

目标是设计一个 自适应滤波器 h,使得通过最小化噪声的估计误差 E\bigl[(b_1 - h(b_2))^2\bigr] ,即均方误差 E\bigl[\bigl(b_1(n) - h^T(n)\,b_2(n)\bigr)^2\bigr] ,最终得到去噪的目标信号估计 \hat{s}(n)

自适应滤波器算法 数学描述及流程

在实际应用中,滤波器 h 是一个具有 M 个系数(本实验中 M=20 )的横向滤波器(FIR),其目的是利用已知的参考信号 b_2 来估计噪声/伪差 b_1 ,从而得到对目标信号 s 的最佳估计 \hat{s} 。下面给出了描述LMS算法的主要公式:

滤波后的输出信号:

自适应滤波器 h 的输出为:

h^T(n)\,b_2(n)
  • h^T(n):滤波器当前的权值向量,表示为 [h_1(n), h_2(n), \dots, h_M(n)]
  • b_2(n):参考信号的延迟窗口,表示为 [b_2(n), b_2(n-1), \dots, b_2(n-M+1)]
  • 利用参考信号 b_2(n) 的延迟窗口(即过去 M 个时刻的值)来估计目标噪声 b_1(n)

估计目标信号:

从观测信号中去除滤波器输出后,得到目标信号的估计:

\hat{s}(n) = y_1(n) - h^T(n)\,b_2(n)
  • 其中,y_1(n) = s(n) + b_1(n)

权值更新公式:

滤波器通过最小化估计误差 \hat{s}(n) 的均方误差(MSE),自适应更新权值:

h(n+1) = h(n) + \mu\,\hat{s}(n)\,b_2(n)
  • \mu:步长因子,控制更新速度和稳定性。
  • \hat{s}(n)b_2(n) 的乘积用于调整权值。

该降噪系统的示意图如下:

image-20250106145426769

其中:

  • Reference noise 代表眼部活动这一参考噪声信号

  • Estimated signal 表示去除了眼部活动后的估计信号

问题

1. 在测试真实癫痫信号之前,我们先在完全模拟的数据上测试此自适应算法。给定的嘈杂模拟数据如下:

  • 信号 s(n) 是振幅 A=2 、频率为100 Hz、采样率为1 kHz的正弦信号。
  • b_2(n) 是均值为0、方差为1的白高斯噪声。
  • b_1(n) 则通过一个传递函数为 1/(1-\alpha z^{-1}) 的滤波器对 b_2(n) 进行滤波得到。

请编写对应的程序,并在 \alpha=0.9 M=20 以及不同 \mu 取值(例如 \mu=0.01 \mu=0.0005 )时进行测试。

我们先生成目标信号 s(n)

n = 0:N-1;         % 生成样本索引序列,范围为 [0, N-1]
A = 2;             % 振幅设为 2
s = A * sin(2*pi*f0*(n/fe));  % 目标信号为正弦信号
  • f_0=100 Hz 为信号频率,f_e=1000 Hz 为采样率。

在生成噪声信号 b_2(n),是一个标准白高斯噪声。

b2 = randn(1, N);  % 生成长度为 N 的白高斯噪声,均值为 0,方差为 1

通过滤波生成 b_1(n)

b1 = filter([1], [1, -alpha], b2);

使用一个 IIR 滤波器对 b_2(n) 进行处理,其传递函数为 H(z) = 1 / (1 - \alpha z^{-1})。滤波器的系数为alpha=0.9 ,它决定了滤波器的“记忆”特性,也即 b_1(n) 的平滑程度。

现在我们要实现 LMS 自适应滤波算法

初始化参数

% 初始化自适应滤波器变量
h = zeros(M, 1);                         % 滤波器系数
s_hat = zeros(1, N);                     % 目标信号估计
e = zeros(1, N);                         % 误差
phi = zeros(1, N);                       % 均方误差(误差平方)
  • M=20 是滤波器的阶数,决定了参考向量的长度
  • 误差e 的定义为 e(n) = s(n) - s_{hat}(n)

核心循环

% LMS 循环
for n = M:N
    % 提取参考信号向量 b2_ref
    b2_ref = flipud(b2(n-M+1:n).');      % 参考信号向量,长度为 M

    % 噪声估计
    b1_hat = h' * b2_ref;

    % 目标信号估计
    s_hat(n) = x(n) - b1_hat;

    % 误差计算
    e(n) = s(n) - s_hat(n);

    % 更新滤波器系数
    h = h + mu * s_hat(n) * b2_ref;

    % 记录误差平方
    phi(n) = e(n)^2;
end

首先我们从第 M 个样本开始,因为要生成参考向量 b_2^{ref}(n) 需要过去 M 个样本。

然后我们提取 从 b_2(n-M+1)b_2(n) 的参考向量b2_ref,并将其翻转为列向量。

    b2_ref = flipud(b2(n-M+1:n).');      % 参考信号向量,长度为 M

然后我们利用滤波器权重 h 和参考向量 b_2^{ref}(n) 的点积得到估计噪声 \hat{b}_1(n)

    b1_hat = h' * b2_ref;

x(n) 中减去估计的 \hat{b}_1(n) ,得到估计的目标信号 \hat{s}(n)

    s_hat(n) = x(n) - b1_hat;

随后计算误差 e(n) = s(n) - \hat{s}(n)

    e(n) = s(n) - s_hat(n);

使用 LMS 算法更新权重:

h = h + \mu \cdot s_{hat}(n) \cdot b_2^{ref}(n)
    h = h + mu * s_hat(n) * b2_ref;

完整代码与结果

% 初始化参数
clear; close all; clc;

% 基本设置
fe = 1000;       % 采样率
f0 = 100;        % 信号频率
N = 20000;       % 样本数
M = 20;          % 自适应滤波器阶数
A = 2;           % 振幅
alpha = 0.9;     % 滤波器系数
mu = 0.001;      % 步长

% 数据生成
s = A * sin(2 * pi * f0 * (0:N-1) / fe); % 目标信号 s(n)
b2 = randn(1, N);                        % 高斯白噪声 b2(n)
b1 = filter([1], [1, -alpha], b2);       % 滤波后的噪声 b1(n)
x = s + b1;                              % 带噪信号 x(n)

% 初始化自适应滤波器变量
h = zeros(M, 1);                         % 滤波器系数
s_hat = zeros(1, N);                     % 目标信号估计
e = zeros(1, N);                         % 误差
phi = zeros(1, N);                       % 均方误差(误差平方)

% LMS 循环
for n = M:N
    % 提取参考信号向量 b2_ref
    b2_ref = flipud(b2(n-M+1:n).');      % 参考信号向量,长度为 M

    % 噪声估计
    b1_hat = h' * b2_ref;

    % 目标信号估计
    s_hat(n) = x(n) - b1_hat;

    % 误差计算
    e(n) = s(n) - s_hat(n);

    % 更新滤波器系数
    h = h + mu * s_hat(n) * b2_ref;

    % 记录误差平方
    phi(n) = e(n)^2;
end

% 绘图
figure('Name', 'LMS Filtering Results');

% (1) 原始目标信号 s(n)
subplot(6, 1, 1);
plot(s, 'LineWidth', 1.2);
title('s(n): 原始目标信号');
ylabel('Amplitude');
grid on;

% (2) 滤波后的噪声 b1(n)
subplot(6, 1, 2);
plot(b1, 'LineWidth', 1.2);
title('b_1(n): 滤波后的噪声');
ylabel('Amplitude');
grid on;

% (3) 带噪信号 x(n)
subplot(6, 1, 3);
plot(x, 'LineWidth', 1.2);
title('x(n): 带噪信号');
ylabel('Amplitude');
grid on;

% (4) 前 200 样本的估计信号 s_hat
subplot(6, 1, 4);
plot(1:200, s_hat(1:200), 'LineWidth', 1.2);
title('\hat{s}(n): 前 200 样本的估计信号');
ylabel('Amplitude');
grid on;

% (5) 最后 200 样本的估计信号 s_hat
subplot(6, 1, 5);
plot(1:200, s_hat(N-199:N), 'LineWidth', 1.2);
title('\hat{s}(n): 最后 200 样本的估计信号');
ylabel('Amplitude');
grid on;

% (6) 误差平方 \phi(n)
subplot(6, 1, 6);
plot(phi, 'LineWidth', 1.2);
title('\phi(n): 误差平方');
xlabel('Sample Index');
ylabel('Error^2');
grid on;

最后结果显示:

  • 第一行:绘制原始目标信号 s(n),用于观察正弦信号的特性。
  • 第二行:绘制滤波后的噪声 b_1(n),模拟眼动伪差。
  • 第三行:绘制带噪信号 x(n),是 s(n)b_1(n) 的叠加。
  • 第四行:绘制估计信号 \hat{s}(n) 的前 200 个样本,用于观察滤波器初期性能。
  • 第五行:绘制估计信号 \hat{s}(n) 的最后 200 个样本,用于观察稳态性能。
  • 第六行:绘制误差平方 \phi(n),展示误差随时间的变化和收敛趋势。

2. \mu=0.01 \mu=0.005 \mu=0.001 \mu=0.0005 的情况下,绘制输出信号在时间点1到100以及5000到5120之间的波形。

3. 同样在 \mu=0.01 \mu=0.005 \mu=0.001 \mu=0.0005 的情况下,针对所有时间点,绘制 \bigl(s-\hat{s}\bigr)^2 这一误差项。

%% 带参考通道的自适应滤波 (LMS) 去噪测试
% 问题 2 & 问题 3 综合示例
clear; clc; close all;

%% 1. 参数设置
fe = 1000;         % 采样率
f0 = 100;          % 信号频率
N = 20000;         % 样本数
M = 20;            % 自适应滤波器阶数
A = 2;             % 目标信号 s(n) 振幅
alpha = 0.9;       % IIR 滤波器系数, 用于生成 b1(n)

% 要测试的不同步长 mu
muSet = [0.01, 0.005, 0.001, 0.0005];

%% 2. 数据生成
n = 0:N-1;
% 目标信号 s(n): 正弦波
s = A * sin(2 * pi * f0 * (n / fe));  
% 白高斯噪声 b2(n)
b2 = randn(1, N);
% IIR 滤波器 H(z) = 1 / (1 - alpha z^-1), 生成 b1(n)
b1 = filter([1], [1, -alpha], b2);
% 带噪信号 x(n) = s(n) + b1(n)
x = s + b1;

%% 3. 针对每个 mu 进行 LMS 滤波,并绘图
for i = 1:length(muSet)
    mu = muSet(i);
    
    % (a) 初始化自适应滤波器变量
    h = zeros(M, 1);        % 滤波器系数,长度 M
    s_hat = zeros(1, N);    % 目标信号估计
    e = zeros(1, N);        % 误差
    
    % (b) LMS 迭代
    for k = M:N
        % 取参考信号向量 (翻转成列向量)
        b2_ref = flipud(b2(k - M + 1 : k).');
        
        % 估计噪声 b1_hat
        b1_hat = h' * b2_ref;
        
        % 估计目标信号 s_hat(k) = x(k) - b1_hat
        s_hat(k) = x(k) - b1_hat;
        
        % 计算与真实目标信号 s(k) 的误差
        e(k) = s(k) - s_hat(k);
        
        % 更新滤波器系数
        h = h + mu * s_hat(k) * b2_ref;
    end
    
    %% 问题 2: 绘制输出信号在 1~100 和 5000~5120 区间的波形
    figure('Name', ['LMS Output - \mu = ', num2str(mu)]);
    
    % 1) 估计信号前 100 个样本
    subplot(2,1,1);
    plot(1:100, s_hat(1:100), 'LineWidth', 1.2); 
    title(['\hat{s}(n) (前 100 个样本), \mu = ', num2str(mu)]);
    xlabel('样本点'); ylabel('幅值'); grid on;
    
    % 2) 估计信号在 5000~5120 区间
    subplot(2,1,2);
    plot(5000:5120, s_hat(5000:5120), 'LineWidth', 1.2);
    title(['\hat{s}(n) (5000~5120 区间), \mu = ', num2str(mu)]);
    xlabel('样本点'); ylabel('幅值'); grid on;
    
    
    %% 问题 3: 针对所有时间点,绘制 (s - s_hat)^2
    figure('Name', ['Error squared - \mu = ', num2str(mu)]);
    plot((s - s_hat).^2, 'LineWidth', 1.2);
    title(['(s - \hat{s})^2, 全部时间点, \mu = ', num2str(mu)]);
    xlabel('样本点'); ylabel('(s - \hat{s})^2'); grid on;
    
    drawnow;  % 更新图像
end

4. 讨论步长 \mu 在稳态时和在收敛速度方面所产生的影响。

在自适应滤波中,步长 \mu 决定了滤波器系数更新幅度。

  • \mu 过大时,滤波器系数更新幅度较大,初始阶段的收敛速度很快,能迅速减小误差,但是由于更新过于剧烈,滤波器的输出有可能在误差附近振荡、不稳定,即稳态时往往残留的误差较大。
  • \mu 较小时,收敛速度变慢,需要更长时间(更多迭代次数)才能达到稳态,但一般会带来更好的稳态精度,获取更小的均方误差 (MSE)

因此步长 \mu 的选择需要平衡 收敛速度 和 稳态误差

5. 通过对最后500个采样点的平均,计算稳态时的误差。

为了观察自适应滤波在接近稳态时的表现,我们取最后500个采样点的误差取平均,从而估计稳态误差大小。真实信号是 s ,估计信号是 \hat{s} ,那么可用以下方式计算稳态误差(以均方误差 MSE 为例):

\text{MSE}_{\text{steady}} = \frac{1}{500}\sum_{n=N-499}^{N}\bigl(s(n) - \hat{s}(n)\bigr)^2

在 MATLAB 中可直接写:

steady_error = mean((s(N-499:N) - s_hat(N-499:N)).^2);

根据收敛后期的平均误差情况,可以对不同步长 \mu 的的稳态性能进行比较。

6. 在本问题中,所关注的信号 s(n) 可以是一串癫痫发作间期尖波。

a) 使用 signals.mat 文件,通过 MATLAB 的 load 命令加载三个信号:

  • 信号 s1 表示 间歇性尖峰 (interictal spike)
  • 信号 s2 表示 眼部运动 (ocular movement)
  • 信号 s3 表示 肌肉活动 (muscle activity)
dataStruct = load('signals.mat');
s1 = dataStruct.s1;  
s2 = dataStruct.s2;  
s3 = dataStruct.s3;  
S = [s1(:)'; s2(:)'; s3(:)'];

信号矩阵 S,大小为 3 \times T,每一行对应一个信号。

b) 构建时空混合矩阵 X(2 \times T) ,其公式如下:

X = \frac{\mathbf{a}_{1}S(1,:)}{\|\mathbf{a}_{1}S(1,:)\|} + \sigma_{1}\frac{\mathbf{a}_{2}S(2,:)}{\|\mathbf{a}_{2}S(2,:)\|} + \sigma_{2}\frac{\mathbf{a}_{3}S(3,:)}{\|\mathbf{a}_{3}S(3,:)\|}

其中:

  • s_i(:,:) 是矩阵 S = [s_1^T, s_2^T, s_3^T]^T 的第 i

  • 向量 A = \left[a_{1}, a_{2}, a_{3}\right] = \begin{bmatrix}1 & 0.3 & 0.5 \\ -0.7 & 0.6 & 0.5\end{bmatrix}

    % 混合系数矩阵 A
    A = [1 0.3 0.5; 
                -0.7 0.6 0.5];
    % 提取矩阵 A 的列向量            
    a1 = A(:,1);  % 第一列向量 a1
    a2 = A(:,2);  % 第二列向量 a2
    a3 = A(:,3);  % 第三列向量 a3
    
  • \sigma_1, \sigma_2 是用来调整信号噪声比 (SNR) 的标量

这里简化假设 \sigma_1 = \sigma_2 = \sigma ,其中:

  • SNR = 20 \log_{10} (\frac{\sigma_s}{\sigma})
  • \sigma_s 是信号的标准差
  • 我们设 \sigma_s = 1 SNR = 5 dB
sigma_s = 1;
SNR_dB = 5;

这里缩放系数 \sigma 的计算公式为:

sigma = \frac{\sigma_s}{10^{SNR/20}}
sigma = sigma_s / (10^(SNR_dB / 20));

构造混合信号矩阵 X

回顾上述混合信号矩阵 X 的构建公式,(别忘了对信号 S(1,:)S(2,:)S(3,:) 进行归一化)

X = \frac{\mathbf{a}_{1}S(1,:)}{\|\mathbf{a}_{1}S(1,:)\|} + \sigma_{1}\frac{\mathbf{a}_{2}S(2,:)}{\|\mathbf{a}_{2}S(2,:)\|} + \sigma_{2}\frac{\mathbf{a}_{3}S(3,:)}{\|\mathbf{a}_{3}S(3,:)\|}
tmp1 = a1 * S(1, :) / norm(a1 * S(1, :));
tmp2 = a2 * S(2, :) / norm(a2 * S(2, :));
tmp3 = a3 * S(3, :) / norm(a3 * S(3, :));
X = tmp1 + sigma * tmp2 + sigma * tmp3;

c) 绘制信号 s_1(n) s_2(n) s_3(n) ,以及混合信号 x_1(n) x_2(n) (即 X(1,:) X(2,:)


% 绘制源信号、带噪信号和去噪信号
figure('Name','5 行结果展示');

% 第一行:原始信号 s1 (b1)
subplot(5, 1, 1);
plot(s1, 'LineWidth', 1.2);
title('Signal s_1 (b1)');
ylabel('Amplitude');
grid on;

% 第二行:原始信号 s2 (spk)
subplot(5, 1, 2);
plot(s2, 'LineWidth', 1.2);
title('Signal s_2 (spk)');
ylabel('Amplitude');
grid on;

% 第三行:原始信号 s3 (ocl)
subplot(5, 1, 3);
plot(s3, 'LineWidth', 1.2);
title('Signal s_3 (ocl)');
ylabel('Amplitude');
grid on;

% 第四行:带噪声混合信号 x1
subplot(5, 1, 4);
plot(X(1,:), 'LineWidth', 1.2);
title('Mixed Signal x_1 (With Noise)');
ylabel('Amplitude');
grid on;

% 第五行:带噪声混合信号 x2
subplot(5, 1, 5);
plot(X(2,:), 'LineWidth', 1.2);
title('Mixed Signal x_2 (With Noise)');
ylabel('Amplitude');
grid on;

7. 根据上述混合模型,每个混合信号 x_i(n) ( i \in \{1, 2\} ) 表示一个 混合信号,它由 间歇性尖峰记录于头皮表面上的眼肌活动 组成。

在本部分中,参考噪声信号(眼部活动) b_2(n) 是与目标眼部活动 b_1(n) (例如记录的实际眼部活动)相关的信号,并已提供于 MATLAB 文件 Ref_Oc1.mat

编写程序,通过自适应 LMS 算法来去除混合信号中眼部活动对间歇性尖峰和肌肉伪影的贡献。

从矩阵 X 中提取两个混合信号 x_1x_2,分别对应第一通道和第二通道。

x1 = X(1,:);
x2 = X(2,:);

加载参考信号 ref_ocl.mat,变量名为 ocl_n,作为参考眼动信号 b_2(n)

refData = load('ref_ocl.mat');  
refOc = refData.ocl_n;  

确保参考信号与混合信号长度一致,若长度不足则补齐或插值,若信号过长,仅截取前 T 个样本。非必要。

if length(refOc) < T
    error('参考眼动信号长度不足,请检查并补齐或使用插值。');
elseif length(refOc) > T
    refOc = refOc(1:T);  
end

将参考信号归一化到 [-1, 1] 区间,避免过大的数值导致算法不稳定。

refOc = refOc / max(abs(refOc));

LMS 算法部分

**通道 1 的去噪过程 **

设置滤波器阶数 M = 20 和步长因子 \mu = 0.05。步长因子影响收敛速度和稳定性。

M = 20;         
mu = 0.05;

初始化滤波器系数 h_1、去噪输出 x_{1D} 和误差信号 e_1

h1 = zeros(M, 1);        
x1_hat = zeros(1, T);    
e1 = zeros(1, T);

下面进入循环部分

for n = M:T

取参考信号中最近 M 个样本,构成长度为 M 的窗口,按时间顺序反转为列向量。

    refWindow = flipud(refOc(n-M+1:n).');

通过当前滤波器系数 h_1 与参考信号窗口相乘,得到估计的眼动噪声 noiseEst

    noiseEst = h1' * refWindow;

从混合信号 x_1 中减去估计噪声,得到去噪信号 x1_hat(n)

    x1_hat(n) = x1(n) - noiseEst;

计算当前误差 e1(n),定义为混合信号与去噪信号的差值。

    e1(n) = x1(n) - x1_hat(n);

根据 LMS 算法公式,调整滤波器系数 h_1,以逐步减少误差。

    h1 = h1 + mu * x1_hat(n) * refWindow;

通道 2 的去噪过程

初始化第二通道的滤波器参数、去噪输出和误差信号。

h2 = zeros(M, 1);
x2_hat = zeros(1, T);
e2 = zeros(1, T);

通道 2 的去噪过程与通道 1 类似,但针对混合信号 x_2 和滤波器 h_2 分别进行。

for n = M:T
    refWindow = flipud(refOc(n-M+1:n).');
    noiseEst = h2' * refWindow;
    x2_hat(n) = x2(n) - noiseEst;
    e2(n) = x2(n) - x2_hat(n);
    h2 = h2 + mu * x2_hat(n) * refWindow;
end

完整代码及结果展示

x1 = X(1,:);
x2 = X(2,:);

% 加载参考眼动信号 (例如来自实际采集的眼动传感器),此处仅做示例
%   假设文件 Ref_Oc1.mat 中包含一个变量 refOc,对应眼动参考信号
refData = load('ref_ocl.mat');  
% 假设文件里面的变量名叫 refOc,也可能是别的名字,请根据实际情况替换
refOc = refData.ocl_n;  

% 为了对齐,这里假设 refOc 的长度与 x1,x2 一样,若长度不同,可酌情裁剪/插值
if length(refOc) < T
    error('参考眼动信号长度不足,请检查并补齐或使用插值。');
elseif length(refOc) > T
    refOc = refOc(1:T);  % 仅截取与 X 长度一致的部分
end

% 如果 refOc 过大或过小,也可以归一化或其他预处理
% 比如简单归一化:
refOc = refOc / max(abs(refOc));

% 使用自适应LMS算法去除眼动伪差 (逐通道处理)
%    对 x1 进行滤波,再对 x2 进行滤波
%    每个通道的流程与前述“带参考通道的LMS去噪”步骤相同

% ========== 设置自适应滤波器参数 ==========
M = 20;          % 滤波器阶数
mu = 0.05;      % 步长因子 (可多次测试不同 mu)

% ========== 通道1: x1 去眼动伪差 ==========
% 初始化
h1 = zeros(M, 1);        % 滤波器系数
x1_hat = zeros(1, T);    % 去噪后输出
e1 = zeros(1, T);        % 误差信号

% LMS迭代
for n = M:T
    % 取参考信号 refOc 的 M 长度向量,并翻转成列
    refWindow = flipud(refOc(n-M+1:n).');
    
    % 估计出的眼动噪声
    noiseEst = h1' * refWindow;
    
    % 去噪输出
    x1_hat(n) = x1(n) - noiseEst;
    
    % 误差: (真值 - 估值),此处若只想最小化 x1 中眼动成分,可将误差定义成 x1_hat(n)本身
    % 但如需对比真值 s1(n) 时,要看已知不已知 s1。这里示例按常见形式:
    e1(n) = x1(n) - x1_hat(n); 
    
    % 更新系数
    h1 = h1 + mu * x1_hat(n) * refWindow;
end

% ========== 通道2: x2 去眼动伪差 ==========
h2 = zeros(M, 1);
x2_hat = zeros(1, T);
e2 = zeros(1, T);

for n = M:T
    refWindow = flipud(refOc(n-M+1:n).');
    noiseEst = h2' * refWindow;
    x2_hat(n) = x2(n) - noiseEst;
    e2(n) = x2(n) - x2_hat(n);
    h2 = h2 + mu * x2_hat(n) * refWindow;
end

X1D = x1_hat;
X2D = x2_hat;

figure('Name','7 行结果展示');

% 第一行:原始信号 s1 (b1)
subplot(7, 1, 1);
plot(s1, 'LineWidth', 1.2);
title('信号 s_1 (b1)');
ylabel('幅值');
grid on;

% 第二行:原始信号 s2 (spk)
subplot(7, 1, 2);
plot(s2, 'LineWidth', 1.2);
title('信号 s_2 (spk)');
ylabel('幅值');
grid on;

% 第三行:原始信号 s3 (ocl)
subplot(7, 1, 3);
plot(s3, 'LineWidth', 1.2);
title('信号 s_3 (ocl)');
ylabel('幅值');
grid on;

% 第四行:带噪声混合信号 x1
subplot(7, 1, 4);
plot(X(1,:), 'LineWidth', 1.2);
title('混合信号 x_1 (含噪声)');
ylabel('幅值');
grid on;

% 第五行:带噪声混合信号 x2
subplot(7, 1, 5);
plot(X(2,:), 'LineWidth', 1.2);
title('混合信号 x_2 (含噪声)');
ylabel('幅值');
grid on;

% 第六行:去噪后的 x1 (记为 x_{1D})
subplot(7, 1, 6);
plot(X1D, 'LineWidth', 1.2);
title('去噪后的信号 x_{1D}');
ylabel('幅值');
grid on;

% 第七行:去噪后的 x2 (记为 x_{2D})
subplot(7, 1, 7);
plot(X2D, 'LineWidth', 1.2);
title('去噪后的信号 x_{2D}');
xlabel('采样点');
ylabel('幅值');
grid on;

第二步:使用 ICA 去除肌肉运动

我们现在的目标是从结果混合信号 \hat{X}_{(2 \times N)} = [\hat{x}_1^T, \hat{x}_2^T]^T 中去除剩余的伪影(即肌肉运动)。 \hat{X} 是由 iEEG(脑电活动)肌肉运动 两种不同的生理信号源混合而成,即 。由于这两种信号在统计上是相互独立的,可以使用 独立成分分析(ICA) 技术来分离它们。这种分离过程是解决盲源分离(Blind Source Separation, BSS)问题的核心任务。

具体实现中,我们使用 SOBI 方法(Second-Order Blind Identification,第二阶盲识别方法)。SOBI 利用信号在不同时间延迟下的统计特性,结合协方差矩阵的联合对角化来实现信号分离。

准备工作 -- 预白化

使用 PCA 对可用观测数据 \hat{X}_{(2 \times N)} 进行预白化:

为了让分离器更高效地工作,我们需要先对输入数据信号 \hat{X}_{(2 \times N)} 进行标准化处理,即预白化。预白化让信号变得更规整、简单,可以让数据变得统计上无关,且每个信号的方差归一化为 1,方便后续处理。具体过程如下:

a. 计算零均值观测 \hat{X}_c 的协方差矩阵:

我们从混合信号 \hat{X} 中减去其均值,得到零均值的信号矩阵 \hat{X}_c ,然后计算其协方差矩阵:

R_{\hat{X}} = E[\hat{X}_c(:, n)\hat{X}_c^T(:, n)] = \frac{\hat{X}_c \hat{X}_c^T}{N}
  • E[\cdot] 表示期望值运算, N 是信号样本的数量,协方差矩阵 R_{\hat{X}} 描述了信号在不同通道之间的相关性。

零均值化

首先,将观测信号矩阵 \hat{X} 的每一列减去其均值向量 \mathbf{m}_{\hat{X}}

\hat{X}_c = \hat{X} - \mathbf{m}_{\hat{X}}
m_X = mean(X, 2);      % 每行(通道)的均值
X_c = X - m_X;         % 零均值信号

协方差矩阵计算

协方差矩阵 R_{\hat{X}} 定义为:

R_{\hat{X}} = \frac{1}{N} \hat{X}_c \hat{X}_c^T
R_X = (X_c * X_c') / N; % 协方差矩阵

这是一个 2 \times 2 的对称矩阵,表示通道之间的相关性和自相关性:

R_{\hat{X}} = \begin{bmatrix} \mathrm{Var}(\hat{x}_1) & \mathrm{Cov}(\hat{x}_1, \hat{x}_2) \\ \mathrm{Cov}(\hat{x}_1, \hat{x}_2) & \mathrm{Var}(\hat{x}_2) \end{bmatrix}
  • \mathrm{Var}(\hat{x}_1) 是第 1 通道信号的方差。
  • \mathrm{Cov}(\hat{x}_1, \hat{x}_2) 是两通道信号的协方差。

b. 计算协方差矩阵 \hat{R}_{\hat{X}} 的特征值分解(EVD):

\hat{R}_{\hat{X}} = U \Lambda U^T = \tilde{A} R_{\tilde{s}} \tilde{A}^T

其中:

  • R_{\tilde{s}} 是新信号源向量 \tilde{s}(n) = [s_1(n), s_2(n)]^T 的协方差矩阵

  • U 是特征向量矩阵, \Lambda 是对角矩阵,其对角线上的元素为对应的特征值

  • 同时,我们将矩阵 U 的列重新排序(按特征值从大到小的顺序),得到矩阵 \tilde{U}

在代码中,计算协方差矩阵的特征值和特征向量,用于对信号进行线性变换

[U, Lambda] = eig(R_X); % 计算特征值和特征向量

对特征值按降序排列,并重新排序对应的特征向量

[lambda_sorted, sort_idx] = sort(diag(Lambda), 'descend'); % 特征值降序排列
U_sorted = U(:, sort_idx);                                 % 排序后的特征向量矩阵
Lambda_sorted = diag(lambda_sorted);                      % 排序后的特征值对角矩阵

显示排序后的特征值和特征向量

disp('排序后的特征值矩阵 Lambda_sorted:');
disp(Lambda_sorted);
disp('排序后的特征向量矩阵 U_sorted:');
disp(U_sorted);

c. 设置预白化算子:

W = Q^{-1}R_{\tilde{s}}^{-1/2}A^{-1} = \Lambda^{-1/2}\tilde{U}^\dagger
  • 其中 \dagger 是伪逆算子(在 Matlab 中可以使用 pinv 命令完成此操作), Q 是一个酉矩阵。

计算特征值矩阵的平方根倒数,用于标准化每个主成分的方差

Lambda_inv_sqrt = diag(1 ./ sqrt(lambda_sorted)); % 特征值的平方根倒数

计算排序后的特征向量矩阵的伪逆,用于构造预白化算子

U_dagger = pinv(U_sorted); % 特征向量矩阵的伪逆

结合特征值矩阵和特征向量矩阵构造预白化算子 W

W = Lambda_inv_sqrt * U_dagger; % 预白化算子

显示预白化算子

disp('预白化算子 W:');
disp(W);

d. 将原始数据矩阵 \hat{X}_c 投影到新子空间:

通过预白化算子 W ,将原始数据投影到新空间,得到预白化后的数据 Z

Z = W\hat{X}_c = Q\tilde{S}
  • 这里 Z 表示新的数据矩阵,已去除通道间的相关性,便于后续分离独立信号。

使用预白化算子将零均值化信号 \hat{X}_c 投影到新子空间,得到预白化信号 Z

Z = W * X_c; % 投影到新空间

绘制预白化信号的每个通道,观察其变化

figure('Name', '预白化后的信号');
subplot(2, 1, 1);
plot(Z(1, :));
title('预白化信号通道 1');
xlabel('样本点'); ylabel('幅值'); grid on;

subplot(2, 1, 2);
plot(Z(2, :));
title('预白化信号通道 2');
xlabel('样本点'); ylabel('幅值'); grid on;

验证预白化后的信号是否达到单位协方差矩阵,即统计无关性和方差归一化

R_Z = (Z * Z') / N; % 计算预白化信号的协方差矩阵
disp('预白化信号的协方差矩阵 R_Z (应接近单位矩阵):');
disp(R_Z);

完整代码及结果

%% 预白化全过程:从协方差矩阵到预白化信号投影
clear; clc; close all;

% 输入信号和初始化
% 模拟一个 2 通道的混合信号矩阵 (零均值化前的原始信号)
N = 1000; % 样本数量
X = [randn(1, N) + 2; 0.5 * randn(1, N) - 1]; % 模拟两个通道的混合信号

% 显示原始信号
figure('Name', '原始信号');
subplot(2, 1, 1);
plot(X(1, :));
title('原始信号通道 1');
xlabel('样本点'); ylabel('幅值'); grid on;

subplot(2, 1, 2);
plot(X(2, :));
title('原始信号通道 2');
xlabel('样本点'); ylabel('幅值'); grid on;

% 计算协方差矩阵
% 计算零均值信号
m_X = mean(X, 2);      % 每行(通道)的均值
X_c = X - m_X;         % 零均值信号

% 计算协方差矩阵
R_X = (X_c * X_c') / N; % 协方差矩阵

% 显示协方差矩阵
disp('协方差矩阵 R_X:');
disp(R_X);

% 特征值分解 (EVD)
%  计算协方差矩阵的特征值和特征向量
[U, Lambda] = eig(R_X);

% 排序特征值和特征向量(按特征值降序)
[lambda_sorted, sort_idx] = sort(diag(Lambda), 'descend'); % 特征值降序排列
U_sorted = U(:, sort_idx);                                 % 排序后的特征向量矩阵
Lambda_sorted = diag(lambda_sorted);                      % 排序后的特征值对角矩阵

% 显示排序后的特征值和特征向量
disp('排序后的特征值矩阵 Lambda_sorted:');
disp(Lambda_sorted);
disp('排序后的特征向量矩阵 U_sorted:');
disp(U_sorted);

% 设置预白化算子
% 计算特征值矩阵的平方根倒数
Lambda_inv_sqrt = diag(1 ./ sqrt(lambda_sorted));

% 计算特征向量矩阵的伪逆
U_dagger = pinv(U_sorted);

% 构造预白化算子 W
W = Lambda_inv_sqrt * U_dagger;

% 显示预白化算子
disp('预白化算子 W:');
disp(W);

% 投影到新子空间
% 使用预白化算子将信号投影到新空间
Z = W * X_c;

% 显示预白化后的信号
figure('Name', '预白化后的信号');
subplot(2, 1, 1);
plot(Z(1, :));
title('预白化信号通道 1');
xlabel('样本点'); ylabel('幅值'); grid on;

subplot(2, 1, 2);
plot(Z(2, :));
title('预白化信号通道 2');
xlabel('样本点'); ylabel('幅值'); grid on;

% 验证预白化信号的协方差矩阵
R_Z = (Z * Z') / N;

% 显示预白化信号的协方差矩阵
disp('预白化信号的协方差矩阵 R_Z (应接近单位矩阵):');
disp(R_Z);

SOBI 方法去除肌肉活动伪影

在预白化之后,我们使用 SOBI 方法 从信号 Z 中分离出 iEEG 信号和肌肉运动信号。SOBI 的核心思想是利用信号在不同时间延迟下的协方差矩阵,找到一组能够分离信号的变换矩阵 Q 。以下是具体的实现过程:

a. 对时间延迟 \tau_k k=1, 2, \dots, 10 )进行二阶矩估计,计算协方差矩阵:

R_Z(\tau_k) = \frac{1}{N} \sum_{n=1}^N z[n]z^T[n+\tau_k], \quad \tau_k = 1:10
  • R_Z(\tau_k) 是时间延迟为 \tau_k 时的协方差矩阵
  • z[n] 是第 n 个采样点的信号

为时间延迟范围 \tau_k = 1, 2, \dots, 10 初始化,准备存储延迟协方差矩阵 R_Z(\tau_k)

tau_max = 10; % 最大时间延迟
N_channels = size(Z, 1); % 信号通道数量
R_tau = zeros(N_channels, N_channels, tau_max); % 存储不同时间延迟下的协方差矩阵

循环计算每个时间延迟 \tau_k 下的协方差矩阵 R_Z(\tau_k),通过信号时间偏移实现延迟计算

% 计算每个时间延迟的协方差矩阵
for tau = 1:tau_max
    R_tau(:, :, tau) = (Z(:, 1:(n - tau)) * Z(:, (tau + 1):n)') / (n - tau);
end

b. 使用提供的联合对角化算法 jad.m 将上述所有协方差矩阵 R_Z(\tau_k) 变换为对角矩阵,从而估计混合矩阵 Q

Q_est = jad(R_tau); % 调用联合对角化算法,返回分离矩阵 Q_est

c. 通过估计得到的混合矩阵 \hat{Q} ,对预白化数据 Z 进行变换,得到包含 iEEG 活动和肌肉运动的源信号向量:

\hat{S} = \hat{Q}^T Z
  • \hat{Q} Q 的估计。
  • \hat{S} 是提取后的信号矩阵,其中每一行对应一个独立的信号源
  • 一行是 iEEG 信号,另一行是肌肉运动信号。
% 使用分离矩阵提取源信号
S_hat = Q_est' * Z; % 提取源信号
disp('分离矩阵 Q_est:'); disp(Q_est);

d. 将提取的信号(即 \hat{S} 的各行)(即 iEEG 和肌肉运动)绘制出来,验证分离结果。通过观察信号的特性,可以大致判断 iEEG 和肌肉运动是否被成功分离。

绘制分离出的信号 \hat{S} 的每一行,观察其特性以判断分离效果

% 绘制分离后的信号
figure('Name', '分离后的信号');
subplot(2, 1, 1);
plot(S_hat(1, :)); title('提取信号 1 (iEEG)'); xlabel('样本点'); ylabel('幅值'); grid on;
subplot(2, 1, 2);
plot(S_hat(2, :)); title('提取信号 2 (肌肉运动)'); xlabel('样本点'); ylabel('幅值'); grid on;

e. 使用 Matlab 中的 corr 命令计算真实的 iEEG 信号 s_1(n) 和提取信号 \hat{s}_1(n) 之间的相关系数,并对结果进行解释说明。

\rho = \text{corr}(s_1(n), \hat{s}_1(n))

使用 MATLAB 的 corr 函数计算真实信号 s_1(n) 和提取信号 \hat{s}_1(n) 的相关系数

% 计算真实信号与分离信号的相关系数
rho = corr(s1', S_hat(1, :)'); % 计算相关系数
disp(['真实信号与提取信号的相关系数 ρ: ', num2str(rho)]);
  • 如果 \rho 接近 1,则说明分离效果良好,提取的信号 \hat{s}_1(n) 很好地反映了真实的 iEEG 信号。

  • 如果 \rho 远小于 1,则可能是分离失败,需调整算法参数或检查预白化和协方差矩阵的计算。

第三步:脑连接性

这部分的目标是通过相干函数(coherence function)来分析和量化两个神经群之间的功能连接性。相干函数 \rho_{uv}(f) 衡量两个随机过程 u(n) v(n) 在频率 f 上的线性相关性,是功能连接性的一个关键指标。

理论部分:相干函数的性质和证明

两个过程之间相干函数的定义:

\rho_{uv}(f) = \frac{\gamma_{uv}(f)}{\sqrt{\gamma_{uu}(f)\gamma_{vv}(f)}}
  • \gamma_{uu}(f) \gamma_{vv}(f) 是过程 u(n) v(n) 的功率谱密度(PSD),表示信号能量在不同频率的分布;
  • \gamma_{uv}(f) 是互功率谱密度,描述了两个信号在频域上的相关性。

现在我们要证明,对于两个中心化的随机过程 u(n) v(n) ,如果它们在二阶上是平稳的,那么有:

0 \leq |\rho_{uv}(f)|^2 \leq 1

从"注意到"角度来讲: 因为分母的平方根是各信号能量的平方根之积,分子是它们之间的交互能量,分子不可能超过分母。

数学证明1 :

我们设计设计过程 z(n) 为:

z(n) = u(n) + \lambda v(n)
  • 其中 \lambda 是一个复数

根据线性性质和互功率谱的定义,有

Z(f) \;=\; U(f) + \lambda\,V(f)

该过程的频域表示为 Z(f),则它的功率谱密度为

\gamma_{zz}(f) \;=\; \mathbb{E}\bigl[\,Z(f)\,Z^*(f)\bigr]

因此

\gamma_{zz}(f) \;=\; \mathbb{E}\bigl[(U(f) + \lambda\,V(f))\,(U^*(f) + \lambda^*\,V^*(f))\bigr]

展开后可得

\gamma_{zz}(f) \;=\; \gamma_{uu}(f) \;+\; \lambda^*\,\gamma_{uv}(f) \;+\; \lambda\,\gamma_{vu}(f) \;+\; |\lambda|^2\,\gamma_{vv}(f)

由于二阶平稳的复共轭对称性,常见约定是

\gamma_{vu}(f) \;=\; \gamma_{uv}^*(f)

从而上式可写作

\gamma_{zz}(f) \;=\; \gamma_{uu}(f) \;+\; \lambda^*\,\gamma_{uv}(f) \;+\; \lambda\,\gamma_{uv}^*(f) \;+\; |\lambda|^2\,\gamma_{vv}(f)

功率谱 \gamma_{zz}(f) 不可能为负,即对所有频率 f

\gamma_{zz}(f) \;\ge\; 0

下面的关键是选择合适的复数 \lambda 使得上式尽可能“紧”,从而给出对 \gamma_{uv}(f) 的强约束。通常做法是让 \lambda 的相位与 \gamma_{uv}(f) 的相位相抵消,使交叉项成为负的实数,从而逼近最小值。

\lambda \;=\; r\,\frac{\gamma_{uv}(f)}{|\gamma_{uv}(f)|}

其中 r 为实数且 r>0(当 \gamma_{uv}(f)=0 时,|\rho_{uv}(f)|本就等于0,直接成立)。这样一来,

\lambda^* \;=\; r\,\frac{\gamma_{uv}^*(f)}{|\gamma_{uv}(f)|}

将此代入 \gamma_{zz}(f),得到

\gamma_{zz}(f) \;=\; \gamma_{uu}(f) \;+\; r\,\frac{\gamma_{uv}^*(f)}{|\gamma_{uv}(f)|}\,\gamma_{uv}(f) \;+\; r\,\frac{\gamma_{uv}(f)}{|\gamma_{uv}(f)|}\,\gamma_{uv}^*(f) \;+\; r^2\,\gamma_{vv}(f)

因为

\frac{\gamma_{uv}^*(f)}{|\gamma_{uv}(f)|}\,\gamma_{uv}(f) \;=\; |\gamma_{uv}(f)|

所以交叉项之和为

r\,|\gamma_{uv}(f)| \;+\; r\,|\gamma_{uv}(f)| \;=\; 2r\,|\gamma_{uv}(f)|

于是

\gamma_{zz}(f) \;=\; \gamma_{uu}(f) \;+\; 2r\,|\gamma_{uv}(f)| \;+\; r^2\,\gamma_{vv}(f)

上式是关于 r 的一个“抛物线”形式。令

\Phi(r) \;=\; \gamma_{uu}(f) \;+\; 2r\,|\gamma_{uv}(f)| \;+\; r^2\,\gamma_{vv}(f)

若要保证 \Phi(r)\ge 0 对所有实 r 都成立,则其判别式 \Delta 必须非正,即

\Delta \;=\; \bigl(2\,|\gamma_{uv}(f)|\bigr)^2 \;-\; 4\,\gamma_{uu}(f)\,\gamma_{vv}(f) \;\le\; 0

因此

4\,|\gamma_{uv}(f)|^2 \;-\; 4\,\gamma_{uu}(f)\,\gamma_{vv}(f) \;\le\; 0

除以4后得到

|\gamma_{uv}(f)|^2 \;\le\; \gamma_{uu}(f)\,\gamma_{vv}(f)

由相干函数定义

\rho_{uv}(f) \;=\; \frac{\gamma_{uv}(f)}{\sqrt{\gamma_{uu}(f)\,\gamma_{vv}(f)}}

因此

|\rho_{uv}(f)|^2 \;=\; \frac{|\gamma_{uv}(f)|^2}{\gamma_{uu}(f)\,\gamma_{vv}(f)}

结合上面得到的

|\gamma_{uv}(f)|^2 \;\le\; \gamma_{uu}(f)\,\gamma_{vv}(f)

可以立刻推出

|\rho_{uv}(f)|^2 \;\le\; 1

另外,由模的定义可知 |\rho_{uv}(f)|^2 \ge 0 始终成立,故最终得证

0 \;\le\; |\rho_{uv}(f)|^2 \;\le\; 1

这就完成了对相干函数的下上界的证明。

数学证明2 : 没有本质差别,就是中间步骤更详细一些,我不舍得删除

我们先计算 z(n) 的功率谱密度并选择:

\lambda = \frac{\gamma_{uv}(f)}{|\gamma_{uv}(f)|}

功率谱密度 \gamma_{zz}(f) 是信号 z(n) 的傅里叶变换后的功率密度分布。对于 z(n) ,它的功率谱密度定义为:

\gamma_{zz}(f) = \text{傅里叶变换} \{ R_{zz}(\tau) \}

其中 R_{zz}(\tau) z(n) 的自相关函数:

R_{zz}(\tau) = \mathbb{E}[z(n)z^*(n+\tau)]

所以我们需要先计算 R_{zz}(\tau)

代入 z(n) = u(n) + \lambda v(n)

z(n)z^*(n+\tau) = \big(u(n) + \lambda v(n)\big) \big(u^*(n+\tau) + \lambda^* v^*(n+\tau)\big)

展开后:

z(n)z^*(n+\tau) = u(n)u^*(n+\tau) + \lambda u(n)v^*(n+\tau) + \lambda^* v(n)u^*(n+\tau) + |\lambda|^2 v(n)v^*(n+\tau)

下面我们对 z(n)z^*(n+\tau) 取期望,根据线性期望运算的性质:

R_{zz}(\tau) = \mathbb{E}[u(n)u^*(n+\tau)] + \lambda \mathbb{E}[u(n)v^*(n+\tau)] + \lambda^* \mathbb{E}[v(n)u^*(n+\tau)] + |\lambda|^2 \mathbb{E}[v(n)v^*(n+\tau)]

利用功率谱和互功率谱的定义:

  • \mathbb{E}[u(n)u^*(n+\tau)] 对应自相关函数 R_{uu}(\tau)
  • \mathbb{E}[v(n)v^*(n+\tau)] 对应自相关函数 R_{vv}(\tau)
  • \mathbb{E}[u(n)v^*(n+\tau)] 对应互相关函数 R_{uv}(\tau)
  • \mathbb{E}[v(n)u^*(n+\tau)] 对应互相关函数的复共轭 R_{uv}^*(\tau)

因此:

R_{zz}(\tau) = R_{uu}(\tau) + \lambda R_{uv}(\tau) + \lambda^* R_{uv}^*(\tau) + |\lambda|^2 R_{vv}(\tau)

下面我们对自相关函数取傅立叶变换的到功率谱密度:

\gamma_{zz}(f) = \mathcal{F}\{R_{zz}(\tau)\}

R_{zz}(\tau) 的各项分别取傅里叶变换:

  • \mathcal{F}\{R_{uu}(\tau)\} = \gamma_{uu}(f),得到 u(n) 的功率谱密度
  • \mathcal{F}\{R_{vv}(\tau)\} = \gamma_{vv}(f),得到 v(n) 的功率谱密度
  • \mathcal{F}\{R_{uv}(\tau)\} = \gamma_{uv}(f),得到 u(n) v(n) 的互功率谱密度
  • \mathcal{F}\{R_{uv}^*(\tau)\} = \gamma_{uv}^*(f),得到互功率谱密度的复共轭

因此:

\gamma_{zz}(f) = \gamma_{uu}(f) + \lambda \gamma_{uv}(f) + \lambda^* \gamma_{uv}^*(f) + |\lambda|^2 \gamma_{vv}(f)

后面做法同方法1。


实验

现在,考虑如下向量自回归模型(参见图 2)对应的两个 iEEG 信号:

\begin{cases} y_1(n) = 0.95\sqrt{2}y_1(n-1) - 0.9025y_1(n-2) + w_1(n) \\ y_2(n) = -0.5y_1(n-1) + 0.25\sqrt{2}y_2(n-1) + w_2(n) \end{cases} \tag{1}

其中, w_j, j = 1, 2 是具有零均值和单位方差的独立白噪声的实现。

在公式 (1) 中, y_1(n) y_2(n) 可以视为 iEEG 信号。

实验步骤:

a. 在第一步中,仅将 y_1(n) 视为方程 (1) 中给出的 AR 模型,同时 y_2(n) y_1(n) 的一个延迟版本,即:

y_2(n) = 0.5y_1(n-1)

计算并绘制两个信号在 4 个长度为 1024 点的块上、50% 重叠情况下的频谱(使用 pwelch 命令)。计算并绘制这两个信号在不同块上的幅度平方相干性(MSC),并得出结论(使用 mscohere 命令)。

对应代码

我们定义AR(2)模型的两个系数a1a2,其中a1=0.95\sqrt{2}a2=-0.9025。同时设置样本长度 N

N = 8192;
a1 = 0.95*sqrt(2);
a2 = -0.9025;

产生均值0、方差1的白噪声w1(n)

w1 = randn(1, N);

接着初始化y1向量,并给定前两个点(起始条件)均为0。

y1 = zeros(1, N);
y1(1) = 0;
y1(2) = 0;

这里使用AR(2)模型的迭代公式生成y1(n)

for n = 3:N
    y1(n) = a1*y1(n-1) + a2*y1(n-2) + w1(n);
end

接下来,我们构造y2(n)=0.5y1(n-1)

y2 = zeros(1, N);
for n = 2:N
    y2(n) = 0.5 * y1(n-1);
end

为了使用 Welch 方法估计功率谱,我们设定分段长度 seg_len=1024以及50%重叠,也就是512点重叠。

seg_len = 1024;
overlap = seg_len/2;

再设置 nfft=seg\_len,并假设采样频率fs=1

nfft = seg_len;
fs = 1;

使用 pwelchy1(n)计算 Welch PSD,并将结果存储在 PSD_y1以及频率向量f里。

[PSD_y1, f] = pwelch(y1, hanning(seg_len), overlap, nfft, fs);

同样地,对y2(n)计算 Welch PSD,保存在 PSD_y2中。

PSD_y2 = pwelch(y2, hanning(seg_len), overlap, nfft, fs);

这里开始绘制两个功率谱图像,第一幅图绘制y1(n)的PSD。

figure('Name','Welch PSD of y1 & y2');
subplot(2,1,1);
plot(f, 10*log10(PSD_y1), 'LineWidth', 1.2);
title('Welch PSD of y_1');
xlabel('频率'); ylabel('PSD (dB)');
grid on;

第二幅图绘制y2(n)的PSD,保持相同坐标轴格式。

subplot(2,1,2);
plot(f, 10*log10(PSD_y2), 'LineWidth', 1.2);
title('Welch PSD of y_2');
xlabel('频率'); ylabel('PSD (dB)');
grid on;

接下来,用 mscohere函数计算y1y2的幅度平方相干性(MSC),返回结果Cxy和对应频率轴 f_c

[Cxy, f_c] = mscohere(y1, y2, hanning(seg_len), overlap, nfft, fs);

这里我们将MSC随频率的变化绘制出来。

figure('Name','Magnitude-Squared Coherence (MSC)');
plot(f_c, Cxy, 'LineWidth', 1.2);
title('MSC between y_1 and y_2');
xlabel('频率'); ylabel('Coherence');
grid on;

完整代码及结果

clear; clc; close all;

% ========== 1. 生成 AR(2) 信号 y1(n) ==========
N = 8192;                  % 样本长度
a1 = 0.95*sqrt(2);         % 系数
a2 = -0.9025;              % 系数

w1 = randn(1, N);          % 白噪声 w1(n), 均值0, 方差1

% 初始值
y1 = zeros(1, N);
y1(1) = 0;  
y1(2) = 0;

% 根据 AR 模型生成 y1(n)
for n = 3:N
    y1(n) = a1*y1(n-1) + a2*y1(n-2) + w1(n);
end

% ========== 2. 构造 y2(n) = 0.5 * y1(n-1) ==========
y2 = zeros(1, N);
for n = 2:N
    y2(n) = 0.5 * y1(n-1);
end

% ========== 3. Welch 功率谱估计 ==========
%  按题意: 分块长度为 1024, 4 个块, 50% (512点) 重叠
seg_len = 1024;
overlap = seg_len/2;
nfft = seg_len;  % 同步长, 以便可视化
fs = 1;          % 设 fs=1

% (a) y1 的 PSD
[PSD_y1, f] = pwelch(y1, hanning(seg_len), overlap, nfft, fs);
% (b) y2 的 PSD
PSD_y2 = pwelch(y2, hanning(seg_len), overlap, nfft, fs);

% 绘图: y1, y2 的功率谱 (dB)
figure('Name','Welch PSD of y1 & y2');
subplot(2,1,1);
plot(f, 10*log10(PSD_y1), 'LineWidth', 1.2);
title('Welch PSD of y_1');
xlabel('频率'); ylabel('PSD (dB)'); grid on;

subplot(2,1,2);
plot(f, 10*log10(PSD_y2), 'LineWidth', 1.2);
title('Welch PSD of y_2');
xlabel('频率'); ylabel('PSD (dB)'); grid on;

% ========== 4. 计算 y1, y2 的幅度平方相干性 (MSC) ==========
[Cxy, f_c] = mscohere(y1, y2, hanning(seg_len), overlap, nfft, fs);

figure('Name','Magnitude-Squared Coherence (MSC)');
plot(f_c, Cxy, 'LineWidth', 1.2);
title('MSC between y_1 and y_2');
xlabel('频率'); ylabel('Coherence'); grid on;

b. 在第二步中,考虑 y_1(n) y_2(n) 为方程 (1) 中给出的两个信号。计算两个信号在 100 个长度为 1024 点的块上、50% 重叠情况下的频谱,以及 MSC。绘制这些 100 个块上计算得到的平均 MSC,并对结果进行分析。

设定信号总长度N=1024\times100=102400,这样可以在 100 个长度为 1024 的块里做频谱分析,并且采用 50% (512点) 重叠。

N = 102400;

为方程 (1) 中的两个 iEEG 信号设置 AR 系数。方程 (1) 给出:

\begin{cases} y_1(n) = 0.95\sqrt{2}\,y_1(n-1) - 0.9025\,y_1(n-2) + w_1(n) \\ y_2(n) = -0.5\,y_1(n-1) + 0.25\sqrt{2}\,y_2(n-1) + w_2(n) \end{cases}

这里先定义好这些系数。

a1_1 = 0.95*sqrt(2);    % y1(n)的一阶系数
a1_2 = -0.9025;         % y1(n)的二阶系数

a2_1 = -0.5;            % y2(n)关于y1(n-1)的系数
a2_2 = 0.25*sqrt(2);    % y2(n)关于y2(n-1)的系数

生成长度为N的独立白噪声w_1(n)w_2(n),均为零均值、方差 1,用于激励这两个 AR 模型。

w1 = randn(1, N);
w2 = randn(1, N);

给两个信号y_1(n)y_2(n)分配空间,并初始化前若干采样值,这里简单设为 0。之后,我们将依次根据方程 (1) 来迭代生成它们。

y1 = zeros(1, N);
y2 = zeros(1, N);

% 初始化
y1(1) = 0;  y1(2) = 0;
y2(1) = 0;  y2(2) = 0;

使用方程 (1) 的迭代形式生成y_1(n)y_2(n)。注意:当

n\ge 3
时,我们可写出

for n = 3:N
    y1(n) = a1_1 * y1(n-1) + a1_2 * y1(n-2) + w1(n);
    
    y2(n) = a2_1 * y1(n-1) + a2_2 * y2(n-1) + w2(n);
end

设置 Welch 法相关参数:分段长度seg_len= 1024,重叠长度 overlap= 512 (即 50%),以及 FFT 点数nfft=seg\_len、采样频率fs=1。这样可以确保我们在整个信号上分到 100 个块(前后有部分重叠),可用于做频谱和相干性分析。

seg_len = 1024;
overlap = seg_len/2;
nfft = seg_len;
fs = 1;

利用

\text{pwelch}
分别对y_1y_2做 Welch PSD 估计,得到功率谱 PSD_y1, PSD_y2,以及对应的频率向量f

[PSD_y1, f] = pwelch(y1, hanning(seg_len), overlap, nfft, fs);
PSD_y2 = pwelch(y2, hanning(seg_len), overlap, nfft, fs);

绘制y_1y_2的 Welch 功率谱,观察它们的幅度随频率分布 (以 dB 标度绘图)。

figure('Name','Welch PSD of y1 & y2');
subplot(2,1,1);
plot(f, 10*log10(PSD_y1), 'LineWidth',1.2);
title('Welch PSD of y_1 (Equation (1))');
xlabel('Frequency'); ylabel('PSD (dB)');
grid on;

subplot(2,1,2);
plot(f, 10*log10(PSD_y2), 'LineWidth',1.2);
title('Welch PSD of y_2 (Equation (1))');
xlabel('Frequency'); ylabel('PSD (dB)');
grid on;

使用\text{mscohere}(y1,y2,\dots)计算幅度平方相干性 (MSC)。该函数会在 100 个块的基础上,把每个块的相干性结果平均起来,从而得到最终的相干函数。

[Cxy, f_c] = mscohere(y1, y2, hanning(seg_len), overlap, nfft, fs);

绘制上面得到的 MSC 曲线,横轴是频率f_c,纵轴是 MSC 值 (介于 0 和 1 之间)。

figure('Name','Magnitude-Squared Coherence (MSC)');
plot(f_c, Cxy, 'LineWidth',1.2);
title('MSC between y_1 and y_2 (Equation (1))');
xlabel('Frequency'); ylabel('Coherence');
grid on;

完整代码及结果

N = 102400;
a1_1 = 0.95*sqrt(2);    % y1(n)的一阶系数
a1_2 = -0.9025;         % y1(n)的二阶系数

a2_1 = -0.5;            % y2(n)关于y1(n-1)的系数
a2_2 = 0.25*sqrt(2);    % y2(n)关于y2(n-1)的系数

w1 = randn(1, N);  w2 = randn(1, N);

y1 = zeros(1, N);  y2 = zeros(1, N);

% 初始化
y1(1) = 0;  y1(2) = 0;
y2(1) = 0;  y2(2) = 0;

for n = 3:N
    y1(n) = a1_1 * y1(n-1) + a1_2 * y1(n-2) + w1(n);
    
    y2(n) = a2_1 * y1(n-1) + a2_2 * y2(n-1) + w2(n);
end

seg_len = 1024;
overlap = seg_len/2;
nfft = seg_len;
fs = 1;   % 若无明确采样率信息,可先假设为 1

[PSD_y1, f] = pwelch(y1, hanning(seg_len), overlap, nfft, fs);
PSD_y2 = pwelch(y2, hanning(seg_len), overlap, nfft, fs);

figure('Name','Welch PSD of y1 & y2');
subplot(2,1,1);
plot(f, 10*log10(PSD_y1), 'LineWidth',1.2);
title('Welch PSD of y_1 (Equation (1))');
xlabel('Frequency'); ylabel('PSD (dB)');
grid on;

subplot(2,1,2);
plot(f, 10*log10(PSD_y2), 'LineWidth',1.2);
title('Welch PSD of y_2 (Equation (1))');
xlabel('Frequency'); ylabel('PSD (dB)');
grid on;

[Cxy, f_c] = mscohere(y1, y2, hanning(seg_len), overlap, nfft, fs);

figure('Name','Magnitude-Squared Coherence (MSC)');
plot(f_c, Cxy, 'LineWidth',1.2);
title('MSC between y_1 and y_2 (Equation (1))');
xlabel('Frequency'); ylabel('Coherence');
grid on;

c. 接下来,引入由生理模型生成的两个信号(提供于 Matlab 文件 ScenarioK1500second1000a.mat 中,并使用变量 LFP_s)。估计这两个信号随时间变化的 MSC。绘制信号的频谱以及 MSC。


评论