实战:心电图信号处理与ESP32心率血氧计

心电图信号处理

做这个小项目的想法来源于 HPC WEEK 比赛中 CT 图像并行计算的那道题。当时虽然我看不懂题目,但是大受震撼。因为不管是各种OJ还是Leetcode上我平时都不太会看到实际数据的处理,一般都是想象出来的场景,选择合适的算法和数据结构解决(毕竟以我们这种水平也做不了什么复杂的现实场景的东西)。

我后来问了下GPT,了解到了信号处理这个方向。打算从最基础的心电图信号处理做一个小项目进行实战。

环境配置

在项目文件夹下用venv创建python3.12环境,然后安装了numpy,scipy,matplotlib,wfdb库。

1
pip install numpy scipy matplotlib wfdb

其中:

wfdb:读 MIT-BIH 这类心电数据库

scipy:滤波、信号处理

matplotlib:画图

数据获取

MIT-BIH 数据库的文件通常成对出现(.dat 信号文件和 .hea 头文件)。可以使用 wfdb 库来处理它们。wfdb.rdrecord() 是 WFDB 库的核心函数,用于读取生理信号记录(如 ECG 心电信号)。它返回一个record对象,主要包括:

  • 信号数据record.p_signal:一个多维数组,表示 ECG 的多个导联,通常为 (样本数, 通道数)。例如:包含 2 个导联、持续 10 秒、采样率为 360Hz 的信号,返回的数组形状是(3600,2)。

  • 元数据字典:包含采样率(record.fs)、导联名称(record.sig_name)、单位(record.units)等。

1
2
3
4
5
6
record.p_signal    # 生理信号数据(numpy 数组)
record.fs # 采样频率(Hz),MITDB 通常是 360 Hz
record.sig_name # 信号名称,如 ['MLII', 'V1', 'V2', 'V4', 'V5']
record.n_sig # 通道数
record.units # 信号单位,如 ['mV', 'mV']
record.record_name # 记录名 '100'

**MLII (Modified Limb Lead II)**是第1个通道,是最常用的监测导联。问GPT后说这个最常用,所以我就取了这个。

给原始数据画图

根据传入的startduration参数创建一个时间轴time_axis,并使用loader数据画图。

去噪

如果观察刚才画出的原始信号,可能会发现两个问题:

基线漂移 (Baseline Wander):波形整体像海浪一样上下起伏。这通常是由于呼吸或身体晃动引起的(属于低频干扰)。

高频噪声 (High-frequency Noise):波形上有细小的锯齿。这可能是肌肉电信号 (EMG) 或设备电子噪声。

带通滤波

带通滤波只保留需要的频段(0.5–40 Hz的频段)。Google后就可以知道,< 0.5 Hz的低频是基线漂移噪声,> 40 Hz的是高频噪声。

我们构建一个4阶巴特沃斯带通滤波器,截止频率设置为0.5 Hz和40 Hz。使用scipy.signal.butter函数设计滤波器,并用scipy.signal.filtfilt函数进行零相位滤波,避免相位失真。

R波检测

R波是心电图中最显著的特征,代表心脏的收缩。检测R波的位置对于计算心率和分析心律非常重要。

ECG 信号即使滤了波,也还包含 P 波、T 波等。为了让计算机只盯着 R 波看,我们需要放大它们之间的差距,进行平方处理。

我们使用scipy.signal.find_peaks函数来检测R波峰值。设置适当的高度和距离参数,以确保只检测到真正的R波峰值。然后将返回的峰值索引转换为时间,并在图上标注出来。

然后在draw.py的draw_signal函数里添加可选参数rwave,如果传入了R波索引,就在图上用蓝色圆点标注出来。

算心率

心率(BPM)可以通过计算连续R波之间的时间间隔(RR间期)来得到。我们计算每对连续R波之间的时间差,并将其转换为心率(BPM)。同时,我们也计算了整个信号段的平均心率。

异常检测

用前面的instant_heartrate计算出的瞬时心率,统计心动过速(>100 BPM)和心动过缓(<60 BPM)的频次。

HRV(心率变异性)是指连续心跳之间时间间隔的变化程度。我计算了RR间期的标准差(SDNN)作为HRV的一个指标。

Claude Code辅助与完善

下载.atr标注文件,新增load_annotations()方法。用wfdb.rdann()读取标注,过滤掉非搏动标注符号(+, ~, | 等),仅保留 19 种 AAMI 搏动类型。

evaluate.py — 评估核心模块,包含:

  • match_peaks() — 逐一匹配检测点与标注点(150ms 容差,符合 ANSI/AAMI 标准)
  • evaluate_detector() — 计算 Se、+P、F1
  • print_evaluation() — 单条记录详细输出
  • print_summary() — 多记录汇总表

ESP32 心率血氧计