
简介这份ECG噪声数据集面向生物医学工程、信号处理方向的学生与研究者以及心电分析算法开发者主要解决含噪心电信号过滤、去噪与特征提取等实际问题。压缩包共102个文件包含dat格式的原始心电信号、hea格式的参数头文件、atr格式的节拍注释以及MATLAB脚本和mat数据文件整体约47.46MB结构清晰便于批量读取与复现实验。目前已有393人学习下载。借助这些数据用户可练习低通、高通、带通等经典滤波方法对比去噪前后QRS波群识别效果也可进一步开展心率变异性分析、异常搏动检测等研究适合从基础实验到算法优化等不同层级的实践需求。1. ECG_noise_data.zip 是做什么的带噪声的ECG数据为什么值得单独存一份从压缩包命名就能看出这份数据不是常规的“干净心电信号”而是把ECG原始信号与噪声叠加后的产物。噪声在这里不是脏数据而是可用的样本它可以在信号处理阶段作为滤波器设计的验证输入也可以在深度学习阶段充当“被污染样本”来训练去噪模型。常见做法是把噪声单独存成一份.mat或.csv记录采样率和信号长度方便 MATLAB 直接 load。这份数据适合正在做生物医学信号处理、需要真实噪声成分而不是纯合成噪声的人。核心难点不在读数据而在怎么把噪声水平量化以及去噪后如何证明有效。2. 用 MATLAB 解析 ECG_noise_data.zip从解压到波形还原2.1 解压前先检查压缩包完整性拿到ECG_noise_data.zip第一步不是直接unzip而是确认压缩包没有损坏。MATLAB 的unzip函数遇到截断的 zip 文件会直接抛出unable to open file错误且不会告诉你哪个文件损坏。用系统命令先看文件大小与发布说明是否一致是个低成本的预检手段。如果压缩包是从下载链接直接保存的还要检查浏览器是否把网页存成了.zip后缀的 HTML 文件这种情况unzip会报 “not a zip archive”。unzip(ECG_noise_data.zip, ./ecg_unzip); filelist dir(fullfile(./ecg_unzip, *.mat)); for k 1:length(filelist) fprintf(%s - %d bytes\n, filelist(k).name, filelist(k).bytes); end这段代码先解压到./ecg_unzip目录再用dir收集所有.mat文件并打印文件名和字节数。.bytes字段用于判断文件是否为空或明显偏小。一个包含 10 秒 ECG、采样率 250Hz 的.mat文件通常在几 KB 到几十 KB 之间如果只有几十字节说明信号可能被保存成空数组或者数据另有存储位置例如嵌套 zip。2.2 解压后的文件字段与典型结构ECG_noise_data.zip解压后常见文件有ecg_clean.mat、ecg_noise.mat、noise_source.mat以及一个说明文档。ecg_noise.mat是加噪后的完整信号ecg_clean.mat是对应的干净版本noise_source.mat可能单独存放叠加前的噪声。加载时要留意变量名不同数据集作者命名习惯不同用who查看变量名可以避免踩坑。who(-file, ./ecg_unzip/ecg_noise.mat) loaded load(./ecg_unzip/ecg_noise.mat); fs loaded.fs; ecg_raw loaded.ecg;who(-file, ...)打印 mat 文件中所有变量名避免你猜测字段名。如果fs字段不存在可以用默认 250 先跑通但最好回头查说明文档。采样率错误会直接影响滤波器截止频率的计算例如把 360Hz 的数据当 250Hz 处理设计截止 40Hz 的滤波器实际起作用的地方就在 57Hz整个频带都会偏移。表ZIP 内常见文件与变量对照文件名常见变量说明ecg_clean.matecg, fs去噪前的参考信号ecg_noise.matecg, fs, noise_type混合噪声后的信号noise_source.matnoise, noise_type单独存放的噪声源info.txt / README-采样率、设备、采集环境2.3 波形可视化与幅值单位校验读取后不要急着去噪先把信号画出来看。ECG 信号在正常窦性心律下有清晰的 P 波、QRS 波群和 T 波RR 间期在 0.6 到 1 秒之间。用时间轴和幅值轴双重观察能过滤掉两类问题采样率设错导致时间轴压缩以及幅值单位从 int16 转 mV 时漏掉增益系数。t (0:length(ecg_raw)-1) / fs; figure(Color, w, Position, [100 100 800 300]); plot(t, ecg_raw); xlabel(时间 (s)); ylabel(幅值 (mV)); title(原始 ECG 含噪信号); grid on; xlim([0, min(10, length(ecg_raw)/fs)]);如果画的图完全看不出周期结构先怀疑fs不对而不是噪声太大。例如实际采样率是 360Hz你按 250Hz 处理时间轴会被压缩滤波器频率也会整体偏移。另一个常见问题是幅值范围异常ECG 通常落在 ±2mV 区间如果看到 ±1000 的数值说明保存时做了整型放大需要除以增益系数。2.4 用 fullfile 管理解压路径当 zip 解压到子目录后直接load(ecg_noise.mat)会提示文件不存在因为 MATLAB 当前路径不在解压目录。建议用fullfile拼接路径降低跨平台时路径分隔符不一致的问题。data_folder fullfile(pwd, ecg_unzip); mat_file fullfile(data_folder, ecg_noise.mat); loaded load(mat_file);pwd是当前工作目录fullfile会自动处理 Windows 的\和 Linux 的/差异。如果你的数据在其他机器上生成优先用这种方式组织代码避免硬编码绝对路径。另一个相关坑是文件名含空格或中文load本身支持但unzip后如果文件名被操作系统转码建议先rename成纯 ASCII 再继续处理。2.5 非 .mat 格式的 ECG 数据读取不是所有ECG_noise_data.zip都用.mat存储有的数据集为了兼容性会导出成.csv或.txt。读取这类文件需要注意分隔符和首行是否为字段名。常见做法是用readtable它自动识别分隔符和变量名比csvread更稳——csvread遇到带表头的 csv 会直接报错。tbl readtable(ecg_noise.csv); ecg_raw tbl.ecg; % 列名形式 fs tbl.fs(1);读取后同样要做长度和幅值校验防止列错位导致信号被截断。如果 csv 是“时间, 幅值”两列还需要检查时间列的间隔是否均匀间隔不均匀的数据不能直接按等间隔滤波。3. 用 MATLAB 滤除 ECG 噪声的三种方法参数与边界3.1 先分清楚噪声类型再选滤波器ECG 噪声主要分三类基线漂移、工频干扰和肌电噪声。基线漂移来自呼吸运动和电极接触不良频率集中在 0.05Hz 到 0.5Hz表现为整段波形的缓慢起伏工频干扰来自市电耦合在中国是 50Hz 及其谐波频率固定且窄肌电噪声来自肌肉收缩频带在 20Hz 到 500Hz波形上表现为高频毛刺。处理顺序和滤波器参数必须根据噪声类型决定否则会削掉 QRS 波的高频分量。用pwelch看功率谱密度是最直接的分辨手段[pxx, f] pwelch(ecg_raw, hann(1024), 512, 1024, fs); figure; plot(f, 10*log10(pxx)); xlabel(频率 (Hz)); ylabel(功率谱密度 (dB/Hz)); xlim([0, 100]);如果频谱在 50Hz 处有尖锐的窄峰对应工频干扰0.5Hz 以下能量明显高于中频段说明基线漂移严重全频段平铺且高频无衰减则是肌电噪声占主导。这一项检查决定了 3.2 到 3.4 节使用哪些滤波器组合。3.2 带通滤波保留 ECG 主频段ECG 信号的主要能量集中在 0.5Hz 到 40Hz 之间设计一个四阶巴特沃斯带通滤波器可以同时压制低频漂移和高频肌电。之所以用巴特沃斯而不是切比雪夫是因为巴特沃斯在通带内没有纹波对波形形态影响更小而切比雪夫虽然过渡带更陡但通带纹波会让 ECG 的 ST 段出现假性偏移。fc_low 0.5; fc_high 40; [b, a] butter(4, [fc_low/(fs/2), fc_high/(fs/2)], bandpass); ecg_bp filtfilt(b, a, ecg_raw);参数说明fc_low设为 0.5Hz 用于抑制呼吸漂移但如果你后续要做 ST 段分析建议降到 0.05Hz因为 ST 段本身包含低频成分0.5Hz 会改变 ST 段形态。fc_high设为 40Hz 能保留 QRS 波的主要能量但婴儿 ECG 或某些病理性 QRS 波的高频成分可能超过 40Hz这时需要结合数据实际情况调到 45Hz。filtfilt做零相位滤波滤后波形没有群延迟QRS 波起点不会被平移。提示filter和filtfilt的区别在于相位处理。filter会引入群延迟滤波后 R 波位置会偏移filtfilt是双向滤波相位零偏移是离线处理 ECG 的首选。实时处理无法使用filtfilt那时只能接受延迟或在算法上做延迟补偿。3.3 工频干扰用陷波器精确切除带通滤波器对 50Hz 的抑制不够彻底因为它不是陷波结构。为单独去除工频干扰使用iirnotch设计一个窄带陷波器。wo 50 / (fs/2); bw wo / 35; [b_notch, a_notch] iirnotch(wo, bw); ecg_notch filtfilt(b_notch, a_notch, ecg_bp);wo是归一化中心频率bw是陷波带宽这里取中心频率的 1/35大约 1.4Hz。这个带宽既能覆盖电网频率 ±0.7Hz 范围内的波动又不会伤害 QRS 波中接近 50Hz 的成分。如果数据集来自 60Hz 电网标准的设备将第一行改为wo 60/(fs/2)。注意iirnotch返回的是零极点增益形式直接用filtfilt没问题但不要把它应用在实时流式数据上因为filtfilt需要整段信号。3.4 基线漂移严重时用中值滤波估计基线带通滤波对低频漂移的抑制依赖截止频率但截止频率越低边缘效应越明显。当漂移幅度超过 R 波幅值一半时更推荐用中值滤波估计基线后相减。window round(fs * 0.5); baseline medfilt1(ecg_raw, window); ecg_base_corrected ecg_raw - baseline;窗口长度取 0.5 秒的采样点数是经验值。它大约是单个 QRS 波宽度的 2 到 3 倍既能跟踪呼吸引起的慢漂移又不会抹掉 QRS 波本身。如果窗口小于 0.2 秒会把 P 波和 T 波也当作基线滤掉大于 1 秒则追不上幅值变化快的漂移残差中仍保留低频波动。中值滤波的另一个优势是对脉冲型噪声电极松动瞬间不敏感不会像均值滤波那样把单点毛刺扩散成一片。表ECG 噪声处理方案速查噪声类型频率范围处理方法推荐参数基线漂移0.05~0.5Hz中值滤波/高通median 窗口 0.5s工频干扰50Hz 或 60Hz陷波滤波带宽 1~2Hz肌电噪声20~500Hz带通/低通截止 40Hz实际处理时按“先基线、再陷波、最后带通”的顺序执行。如果反过来先带通低频漂移经过滤波器边缘会产生振铃波形起点和终点出现明显畸形。每步处理完都建议画一次对比图确认没有引入新的相位畸变。4. MATLAB 定量评估 ECG 去噪效果SNR、RMSE 和相关系数怎么算4.1 有干净参考时计算 SNR 和 RMSEECG_noise_data.zip通常自带干净版本这为量化去噪效果提供了基础。将原始含噪信号与干净信号之差定义为噪声即可计算信噪比。noise ecg_raw - clean_ecg; snr_raw 10 * log10(sum(clean_ecg.^2) / sum(noise.^2)); snr_after 10 * log10(sum(clean_ecg.^2) / sum((ecg_denoised - clean_ecg).^2)); fprintf(去噪前 SNR %.2f dB, 去噪后 %.2f dB\n, snr_raw, snr_after);snr_after比snr_raw提升 5dB 以上说明滤波有效。但 SNR 对基线漂移敏感漂移幅值大但能量集中少量漂移残留就会让 SNR 明显下降。因此还需要计算 RMSErmse sqrt(mean((ecg_denoised - clean_ecg).^2));RMSE 的单位是 mV能直观反映平均误差幅度。一般小于 0.03mV 可以接受小于 0.01mV 属于优秀。注意计算 RMSE 前要对齐信号长度如果滤波导致信号延迟用filter而不是filtfilt时常见需要先做时间对齐否则 RMSE 会被相位差抬高误判为滤波效果差。4.2 无干净参考时用相关系数观察形态变化临床数据往往没有干净参考这时可以计算去噪信号与原始信号的相关系数观察滤波是否整体改变了波形形态。相关系数接近 1 说明滤波器基本是保形的接近 0 则说明波形结构被破坏。corr_orig corr(ecg_raw, ecg_denoised); fprintf(去噪前后波形的相关系数 %.3f\n, corr_orig);这个方法只能判断形态变化不能判断噪声是否被去除。因为一个恒等于原始信号的“去噪结果”相关系数是 1 但没有去噪。所以相关系数必须配合频域指标或模板一致性一起看。在实际写论文或实验报告时建议同时给出 SNR 和相关系数才能确认既降了噪又保了形。4.3 按心拍评估逐拍 SNR 更接近临床视角整体 SNR 会把 P 波、T 波和 QRS 波混在一起平均无法反映局部去噪效果。更细致的做法是检测 R 峰后截取单拍分别计算每个心拍的 SNR再取中位数。这样能避免个别早搏或异常心拍拉低整体评分。[qrs_peaks, locs] findpeaks(ecg_denoised, ... MinPeakHeight, 0.3 * max(ecg_denoised), ... MinPeakDistance, round(0.4*fs)); beat_len round(0.3*fs); for k 1:length(locs) if locs(k)-beat_len 0 locs(k)beat_len length(clean_ecg) seg_clean clean_ecg(locs(k)-beat_len : locs(k)beat_len); seg_den ecg_denoised(locs(k)-beat_len : locs(k)beat_len); beat_snr(k) 10 * log10(sum(seg_clean.^2) / sum((seg_den-seg_clean).^2)); end end median_snr median(beat_snr);findpeaks的MinPeakDistance设为 0.4 秒防止把 T 波误检为 R 峰。MinPeakHeight取最大值 0.3 倍避免把噪声尖峰当 R 峰。对每个心拍单独算 SNR 后取中位数能反映大多数心拍的真实去噪水平。这个方法的前提是干净的参考信号和去噪信号已经做了时间对齐否则逐拍计算没有意义。表去噪效果评估指标适用场景方法需要干净参考适用场景局限全局 SNR/RMSE是数据集自验证对基线漂移敏感逐拍 SNR是算法对比需要准确 R 峰检测相关系数否临床数据不能单独使用5. 进阶批量处理 ECG 噪声数据与参数自适应当数据集从单个 zip 扩展到上百个文件时逐个调参不现实。我在处理多中心采集的 ECG 数据时会先扫描所有文件的频谱特征再按特征分组设定滤波器参数而不是对每个文件都套用同一组滤波器。5.1 自动检测工频干扰并选择性启用陷波逐文件计算功率谱判断 50Hz 附近是否存在明显能量峰只有存在时才启用陷波器。这样可以避免在工频干扰本就微弱的文件上过度滤波。[pxx, f] pwelch(ecg_raw, hann(1024), 512, 1024, fs); pl_band pxx(f49 f51); bg_band mean(pxx(f5 f45)); if max(pl_band) 3 * bg_band wo 50/(fs/2); [b_n, a_n] iirnotch(wo, wo/35); ecg_raw filtfilt(b_n, a_n, ecg_raw); end工频能量超过邻近频段平均值 3 倍时才启用陷波。这个阈值可以根据数据源调整采集环境差时工频干扰幅度大阈值可降到 2 倍采集环境良好时保持 3 倍以上能避免过度处理带来的波形损耗。5.2 批量打包结果并保存参数表处理完所有文件后除了保存去噪信号我还会保存一份处理参数表方便复现或回溯。这比只存信号更有价值因为处理参数本身就是实验记录的一部分。output_dir ./ecg_processed; if ~exist(output_dir, dir); mkdir(output_dir); end save(fullfile(output_dir, ecg_denoised.mat), ecg_denoised, fs); save(fullfile(output_dir, ecg_clean.mat), clean_ecg); params table({filename}, fc_low, fc_high, window, ... VariableNames, {source,fc_low,fc_high,median_win}); writetable(params, fullfile(output_dir, processing_params.csv)); zip(ecg_processed.zip, output_dir);打包后的结果可以直接用于后续的机器学习训练参数表则记录每个文件的处理条件。这个方法的价值在于当你想比较不同滤波参数对结果的影响时不需要重新处理原始数据直接按source字段查表就能知道每个文件用了什么参数。在多中心数据中这尤其重要——不同设备采集的 ECG 噪声特征不同统一参数反而会伤害部分文件。批处理脚本里给每个文件额外加一个noise_type标签如果压缩包自带该字段就沿用不带就用频谱检测结果自动打标这个标签在后续按噪声类型分别评估算法时非常有用比临时回头看文件名高效得多。本文还有配套的精品资源点击获取