1. 项目概述与背景
作为一名长期从事脑电信号处理开发的工程师,我经常需要测试和演示EEG信号处理算法的效果。最近在开发一个基于PyQt5的EEG信号可视化工具时,遇到了一个典型问题:在没有真实硬件设备的情况下,如何快速验证滤波算法的效果?这就是本文要分享的"虚拟数据模式"解决方案。
这个方案的核心价值在于:
- 无需依赖硬件设备即可测试EEG处理流程
- 可以直观对比滤波前后的信号差异
- 方便调整参数观察不同条件下的处理效果
- 为算法开发提供可控的测试环境
2. 虚拟数据模式的设计思路
2.1 需求分析
在EEG信号处理中,我们通常需要:
- 实时显示多通道脑电信号
- 应用数字滤波器去除干扰(特别是50Hz工频干扰)
- 能够灵活调整信号参数进行测试
传统方法需要连接真实EEG设备,但这存在几个问题:
- 硬件设备可能不可用
- 真实信号不可控,难以复现特定测试场景
- 调试过程不够直观
2.2 解决方案设计
基于以上需求,我设计了虚拟数据模式,主要特点包括:
- 使用带噪声的正弦波模拟EEG信号
- 可编程控制信号参数(频率、幅度、噪声等)
- 保留原有硬件接口,可随时切换回真实设备模式
3. 代码实现详解
3.1 SerialThread类的改造
首先对原有的串口线程类进行改造,添加虚拟数据生成功能:
python复制class SerialThread(QtCore.QThread):
data_received = QtCore.pyqtSignal(str)
def __init__(self, com_port: str, log_queue: queue.Queue, virtual_mode=False):
super().__init__()
self.com_port = com_port
self.log_queue = log_queue
self.virtual_mode = virtual_mode # 虚拟模式标志
# 虚拟数据参数
self.virtual_t = 0 # 时间计数器
self.virtual_noise = 50 # 噪声幅度(μV)
self.signal_freq = 10 # 基础信号频率(Hz)
self.interference_freq = 50 # 干扰频率(Hz)
# 原有缓冲区
self.buffer = ChannelBuffer(capacity=DATA_LENGTH)
3.2 虚拟数据生成算法
虚拟数据生成的核心是模拟EEG信号的三个组成部分:
- 基础脑电信号(α波,约10Hz)
- 50Hz工频干扰
- 随机噪声
python复制def _run_virtual(self):
"""虚拟模式:生成8通道正弦波,带50Hz干扰"""
import numpy as np
while self.running:
values = []
for ch in range(8):
# 基础信号:10Hz正弦波(模拟脑电α波)
base = np.sin(2 * np.pi * self.signal_freq * self.virtual_t + ch) * 100
# 添加50Hz工频干扰
interference = np.sin(2 * np.pi * self.interference_freq * self.virtual_t) * self.virtual_noise
# 添加随机噪声
noise = np.random.randn() * 20
value = base + interference + noise
values.append(value)
# 格式化字符串(模拟串口输出)
raw = f"通道数据(μV): {','.join(f'{v:.2f}' for v in values)}"
# 走原有处理流程
self._process_data(raw)
# 模拟200Hz采样率(5ms间隔)
self.virtual_t += 0.005
time.sleep(0.005)
3.3 主界面集成
在主界面中添加虚拟模式开关和参数控制:
python复制class ADCPlotter(QtWidgets.QMainWindow):
def __init__(self, parent=None, virtual_mode=True):
super().__init__(parent)
# 检测串口或强制虚拟
if virtual_mode:
com_port = "VIRTUAL"
InfoBar.info(title='模式', content='虚拟数据模式(带50Hz干扰)')
else:
com_port = self._detect_com_port()
# 创建串口线程
self.serial_thread = SerialThread(com_port, self.log_queue, virtual_mode=virtual_mode)
self.serial_thread.data_received.connect(self._on_serial_line)
self.serial_thread.start()
# 添加虚拟模式控制UI
self._setup_virtual_controls()
4. 关键功能实现
4.1 实时滤波效果对比
在信号处理部分,我们实现了滤波开关,可以实时对比滤波效果:
python复制def _process_serial_data(self, raw: str):
"""处理串口数据(含滤波)"""
values = self._parse_line(raw)
if values is None:
return
# 应用滤波(如果启用)
if self.filter_enabled:
filtered_values = []
for ch, value in enumerate(values):
filtered = self._apply_filter(value, channel=ch)
filtered_values.append(filtered)
values = filtered_values
# 更新缓冲区
self.buffer.put(values)
# 调试输出
if DEBUG_MODE:
print(f"CH0 原始:{values[0]:8.2f} 滤波后:{filtered_values[0]:8.2f}"
if self.filter_enabled else f"CH0 原始:{values[0]:8.2f}")
4.2 滤波器实现
使用IIR陷波滤波器去除50Hz干扰:
python复制def _init_filters(self):
"""初始化各通道的滤波器"""
from scipy.signal import iirnotch, lfilter
self.filters = []
# 设计50Hz陷波滤波器
fs = 200 # 采样率(Hz)
f0 = 50 # 要滤除的频率(Hz)
Q = 30 # 品质因数
b, a = iirnotch(f0, Q, fs)
# 各通道使用相同的滤波器系数
for _ in range(8):
self.filters.append((b, a))
5. 界面优化与交互
5.1 单通道显示模式
针对信号杂乱的问题,实现了单通道显示模式:
python复制def _update_time_plot(self):
"""更新时域波形显示(单通道模式)"""
x, data = self.serial_thread.get_plot_data(PLOT_LENGTH)
if data.size == 0:
return
# 只显示CH1
self.time_curves[0].setData(x, data[0])
# 清空其他通道
for i in range(1, 8):
self.time_curves[i].setData([], [])
# 自动调整Y轴范围
if self.auto_y_box.isChecked():
self._auto_scale_y(data[0])
5.2 参数实时调节
添加滑块控件,可以实时调整信号参数:
python复制def _setup_virtual_controls(self):
"""设置虚拟模式控制面板"""
control_layout = QtWidgets.QHBoxLayout()
# 噪声幅度控制
self.noise_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.noise_slider.setRange(0, 100)
self.noise_slider.setValue(50)
self.noise_slider.valueChanged.connect(self._update_noise)
# 信号频率控制
self.freq_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.freq_slider.setRange(1, 30)
self.freq_slider.setValue(10)
self.freq_slider.valueChanged.connect(self._update_freq)
# 添加到界面
control_layout.addWidget(QtWidgets.QLabel("噪声:"))
control_layout.addWidget(self.noise_slider)
control_layout.addWidget(QtWidgets.QLabel("频率:"))
control_layout.addWidget(self.freq_slider)
self.control_panel.setLayout(control_layout)
6. 实际应用与扩展
6.1 教学演示应用
这个虚拟模式特别适合用于:
- 数字信号处理教学
- 滤波器设计演示
- EEG算法开发测试
6.2 可能的扩展方向
- 多信号模式:添加更多类型的模拟信号(如β波、θ波等)
- 频谱分析:增加FFT频谱显示功能
- 数据记录:添加数据保存和回放功能
- 算法测试:集成更多滤波算法进行比较
7. 开发经验分享
在实现这个虚拟数据模式的过程中,有几个关键经验值得分享:
-
时间同步问题:虚拟模式下的时间推进要与真实采样率匹配,否则会导致波形失真。我采用了固定时间步长+sleep的方式模拟实时采样。
-
线程安全:虚拟数据生成在子线程中运行,但参数调整来自主线程。所有共享变量都需要考虑线程安全,这里使用了简单的Python GIL保护。
-
性能优化:最初使用Python原生math.sin函数生成信号,性能较差。改用NumPy的向量化计算后,性能提升显著。
-
信号逼真度:单纯的sin波太过理想,添加适量随机噪声后,信号更接近真实EEG特征。
这个项目让我深刻体会到,在没有硬件的情况下,通过软件模拟可以大大加速开发流程。虚拟模式不仅解决了设备依赖问题,还提供了更灵活、可控的测试环境。