实战:心电图信号处理

做这个小项目的想法来源于 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:画图

选择数据

我这里选了编号为100这个病人的数据(也就是第一个病人)。

wfdb.rdrecord() 是 WFDB 库的核心函数,用于读取生理信号记录(如 ECG 心电信号)。它返回一个record对象:

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'

其中,record.p_signal是存储生理信号数据的 numpy 数组,通常为 (样本数, 通道数)

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

给原始数据画图

创建一个时间轴t,用signal采样点的序号除以频率(1/周期),就是该采样点的时间。

1
t = [i/fs for i in range(len(signal))]

再画前五秒的图:

1
2
3
4
5
plt.figure()
plt.plot(t[:5*fs], signal[:5*fs])
plt.xlabel("时间(s)")
plt.ylabel("电压(mV)")
plt.show()

去噪

带通滤波

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

1
b, a = butter(order, [low/nyq, high/nyq], btype='band')

这个是巴特沃斯带通滤波器,

陷波滤波

去除50Hz的工频干扰

1
2
3
def notch_filter(x, fs, f0=50, Q=30):
b, a = iirnotch(w0=f0, Q=Q, fs=fs)
return filtfilt(b, a, x)

R波检测

算心率

异常检测