在脑机接口(Brain-Computer Interface,BCI)技术的蓬勃发展中,稳态视觉诱发电位(SSVEP)作为一种重要的脑电信号范式,为实现大脑与外部设备之间的交互提供了有力途径。而典型相关分析(Canonical Correlation Analysis,CCA)算法在 SSVEP 信号识别方面展现出了独特的优势。今天,我们就来深入探讨一下如何使用 Python 编程语言实现基于 CCA 算法的 SSVEP 识别,并对其实现效果进行详细分析。
SSVEP 信号的产生基于当人的视觉系统受到特定频率的周期性视觉刺激时,大脑枕叶视觉皮层会产生与之相关的周期性电活动响应。准确识别这些 SSVEP 信号对于将大脑意图转化为外部设备的控制指令至关重要。CCA 算法通过衡量脑电信号与对应视觉刺激的参考信号之间的线性相关性,能够有效地从复杂的脑电信号中提取出 SSVEP 相关信息,进而实现信号识别。Python 作为一种功能强大且易于使用的编程语言,拥有丰富的科学计算库,为我们实现 CCA 算法并应用于 SSVEP 识别提供了便捷的工具。
具体内容可以参考上一篇文章。
对于不同频率的视觉刺激(例如常见的 8Hz、10Hz、12Hz 等),我们需要构建相应的参考信号。通常采用正弦波和余弦波来构建,因为 SSVEP 信号具有周期性,与正弦、余弦函数的性质相符。以下是一个简单的 Python 代码示例,用于构建特定频率的参考信号(假设采样频率为 fs
,信号时长为 t
):
import numpy as np
def generate_reference_signal(frequency, fs, t):
"""
生成特定频率的参考信号(正弦波和余弦波)
参数:
frequency:视觉刺激频率,单位 Hz
fs:采样频率,单位 Hz
t:信号时长,单位秒
返回:
reference_signal:形状为 (n_samples, 2) 的二维数组,包含正弦波和余弦波信号
"""
n_samples = int(fs * t)
time_vector = np.arange(n_samples) / fs
sine_wave = np.sin(2 * np.pi * frequency * time_vector)
cosine_wave = np.cos(2 * np.pi * frequency * time_vector)
reference_signal = np.column_stack((sine_wave, cosine_wave))
return reference_signal
通过调用这个函数,我们可以为每个视觉刺激频率生成对应的参考信号,这些参考信号将与采集到的脑电信号一起用于 CCA 算法的计算。
首先,我们需要导入 Python 中常用的科学计算库,如 numpy
(用于数值计算和矩阵操作)和 scipy
(提供了线性代数和统计分析等功能,方便我们进行矩阵运算及特征值分解等操作):
import numpy as np
from scipy.linalg import eigh
下面是实现 CCA 算法的核心 Python 函数代码,它接受脑电信号数据和参考信号数据作为输入,并返回典型向量系数以及典型相关系数:
def cca(X, Y):
"""
实现典型相关分析(CCA)算法的函数
参数:
X:形状为 (n_samples, n_channels) 的二维数组,表示多通道脑电信号数据
Y:形状为 (n_samples, n_reference_signals) 的二维数组,表示对应视觉刺激的参考信号数据
返回:
a:形状为 (n_channels, n_canonical) 的二维数组,脑电信号的典型向量系数矩阵
b:形状为 (n_reference_signals, n_canonical) 的二维数组,参考信号的典型向量系数矩阵
rho:形状为 (n_canonical,) 的一维数组,典型相关系数向量
"""
n_samples = X.shape[0]
# 计算协方差矩阵
Q_xx = np.cov(X.T)
Q_yy = np.cov(Y.T)
Q_xy = np.cov(X.T, Y.T)[0:X.shape[1], X.shape[1]:]
Q_yx = Q_xy.T
# 构建矩阵 M 和 N
M = np.linalg.inv(Q_xx) @ Q_xy @ np.linalg.inv(Q_yy) @ Q_yx
N = np.linalg.inv(Q_yy) @ Q_yx @ np.linalg.inv(Q_xx) @ Q_xy
# 求解特征值和特征向量
eigenvals_m, eigenvecs_m = eigh(M)
eigenvals_n, eigenvecs_n = eigh(N)
# 对特征值和特征向量进行排序
sorted_indices_m = np.argsort(eigenvals_m)[::-1]
sorted_indices_n = np.argsort(eigenvals_n)[::-1]
eigenvals_m = eigenvals_m[sorted_indices_m]
eigenvals_n = eigenvals_n[sorted_indices_n]
eigenvecs_m = eigenvecs_m[:, sorted_indices_m]
eigenvecs_n = eigenvecs_n[:, sorted_indices_n]
# 计算典型相关系数
rho = np.sqrt(eigenvals_m)
# 选择前几个典型向量(这里选择全部,实际应用可根据需要选择)
n_canonical = rho.shape[0]
a = eigenvecs_m
b = eigenvecs_n
return a, b, rho
在这个函数中,首先计算了脑电信号和参考信号的协方差矩阵,接着按照 CCA 算法的数学原理构建了用于特征值分解的矩阵 M
和 N
,通过 eigh
函数求解特征值和特征向量后,对结果进行排序整理,最终计算并返回典型向量系数 a
、b
和典型相关系数 rho
。
为了初步验证算法的实现效果,我们先创建模拟的脑电信号数据和参考信号数据。假设我们设置了三个不同频率(8Hz、10Hz、12Hz)的视觉刺激,以下是生成模拟数据的示例代码(这里简化了脑电信号的模拟,仅体现核心逻辑,实际情况会更复杂):
import numpy as np
# 设置模拟参数
fs = 250 # 采样频率,单位 Hz
t = 5 # 信号时长,单位秒
n_channels = 8 # 脑电信号通道数
# 生成模拟脑电信号(简单添加不同频率成分模拟 SSVEP 信号)
simulated_eeg = np.zeros((int(fs * t), n_channels))
for freq in [8, 10, 12]:
reference = generate_reference_signal(freq, fs, t)
amplitude = 10 # 模拟 SSVEP 信号幅值,可调整
simulated_eeg += amplitude * np.random.rand(int(fs * t), n_channels) @ reference.T
# 生成对应参考信号
reference_signals_8Hz = generate_reference_signal(8, fs, t)
reference_signals_10Hz = generate_reference_signal(10, fs, t)
reference_signals_12Hz = generate_reference_signal(12, fs, t)
reference_signals = np.concatenate((reference_signals_8Hz, reference_signals_10Hz, reference_signals_12Hz), axis=0)
在上述代码中,我们首先定义了采样频率、信号时长以及脑电信号通道数等参数,然后通过循环为每个设定频率生成模拟的 SSVEP 成分添加到模拟脑电信号中,最后生成对应的所有参考信号(将不同频率的参考信号按顺序拼接在一起)。
使用前面定义的 cca
函数对模拟数据进行 SSVEP 识别,代码如下:
a, b, rho = cca(simulated_eeg, reference_signals)
# 查看不同频率对应的典型相关系数
print("8Hz 对应的典型相关系数:", rho[0:2])
print("10Hz 对应的典型相关系数:", rho[2:4])
print("12Hz 对应的典型相关系数:", rho[4:6])
运行上述代码后,我们可以得到不同频率参考信号对应的典型相关系数。在理想情况下,由于我们在模拟数据中添加了对应频率的 SSVEP 信号成分,应该可以看到与添加成分频率一致的参考信号对应的典型相关系数相对较高。例如,在这个例子中,如果模拟添加的 10Hz 成分相对更突出,那么 rho[2:4]
对应的典型相关系数可能会在三个频率中最大,这表明通过 CCA 算法能够在一定程度上识别出模拟的 SSVEP 信号,验证了算法在模拟场景下的可行性。
在实际应用中,我们可以替换上述模拟数据为真实采集的脑电信号数据(通常通过专业的脑电采集设备获得,并且需要按照实验设计呈现不同频率的视觉刺激给被试者)。以下是一个简单的应用示例流程:
mne
(Python 中的脑电数据分析库)等工具来实现(Brduino模组自带的代码资料也可以进行相关处理)。cca
函数进行计算,得到典型相关系数。例如,在一个实际的实验中,若采集到的脑电信号经过 CCA 算法计算后,发现 10Hz 参考信号对应的典型相关系数超过了设定阈值,而其他频率的系数未超过,就可以推断被试者的大脑对 10Hz 的视觉刺激产生了 SSVEP 响应,可将此解读为对应的控制意图,比如控制轮椅转弯或者操作智能家居设备的某个功能等。
通过上述在模拟数据和实际应用示例中的测试,我们可以看到基于 Python 实现的 CCA 算法在 SSVEP 识别方面具有一定的有效性。目前基于上述算法,实现Brduino脑控无人机,实现上、下、左、右、前、后、起飞、降落8个指令运动可以达到几乎100%识别率;脑控小车具有同样效果。
优势方面:
局限性方面:
本文详细介绍了基于 Python 实现 CCA 算法进行 SSVEP 识别的方法,包括算法原理、Python 代码实现以及在模拟数据和实际应用中的实现效果分析。尽管 CCA 算法在 SSVEP 识别领域有着诸多优势,但也面临着计算复杂度和非线性处理能力等方面的局限。
未来,我们可以探索多种途径来进一步优化基于 CCA 的 SSVEP 识别方法,例如结合非线性变换方法将脑电信号进行预处理后再应用 CCA 算法等,希望这篇博客能够帮助到大家(Brduino出品)。