新闻详情

新闻详情

首页 / 资讯中心 / 详情

MIT-BIH心电数据读取与Matlab绘图全流程解析

发布时间:2026/9/25 7:19:52来源:尧图网络
MIT-BIH心电数据读取与Matlab绘图全流程解析
简介MIT-BIH ECG 心电数据与 MATLAB 绘图详解是一份面向 ECG 信号处理初学者与科研人员的实操资源围绕经典 MIT-BIH 心电数据库展开重点讲解 100m.mat 数据的读取方式与基于 plotSTM 的波形可视化并延伸介绍采样率、奈奎斯特定理、P 波/QRS 波群/T 波识别及心率变异性分析等关键知识点适合用于心律失常研究、课程设计或信号处理入门。资源包共 4 个文件以 mat 数据文件、m 脚本、asv 自动保存文件及 info 信息文件为主整体仅 20KB轻量易用可在 MATLAB 环境中快速加载并绘制 ECG 波形。已有 7590 人学习使用内容兼具数据解读与绘图代码演示读者可直接对照脚本理解心电图特征也可在其基础上扩展滤波、R 波检测等后续分析对掌握 ECG 数据特性与 MATLAB 信号处理流程均有实际参考价值。1. MIT-BIH ECG 心电数据先看懂这条波形再谈分析和绘图做心电信号分析绕不开 MIT-BIH 这个数据库。无论是做毕设、写论文还是验证一个算法它都是事实上的基准数据集。但真正拿它上手时你会发现这套数据本身是裸的二进制文件配上 Matlab 绘图才能直观看到心电波形。很多研究者的第一个坎不是算法而是“数据读出来了但画出来的图完全不对”。这份资源的核心价值就是把 MIT-BIH 的数据结构和 Matlab 绘图全流程拆开讲透让你拿到手就能读数据、画波形、标特征点。适合正在做 ECG 信号处理、需要快速复现科研图或验证算法的工程师和研究生。关键是数据里那些坑——单位、增益、时间轴、注释解析——它都帮你踩过了。2. 读懂 MIT-BIH 原始数据文件结构、单位换算与读取脚本2.1 三大文件是什么.hea、.dat、*.atr 各管哪一部分MIT-BIH 每条记录包含三个核心文件缺一个都跑不起来。.hea是文本头文件里面写清了采样率、导联数量、增益、ADC 分辨率、患者基本信息等元数据。.dat是二进制数据文件存实际的心电采样值按 212 格式每 3 字节存 2 个 12-bit 样本打包。.atr是注释文件存心拍类型、节律类型、R 峰位置等专家标注。绘图前先得把这三个文件读全不然只能看到裸信号、不知道哪一个波形对应什么病理特征。我先说头文件因为它决定了后面所有参数怎么设。以 100 号记录为例.hea文件里几百行信息里最关键是几行采样率和增益。我一般会写一个通用读取函数避免每个记录手动改参数。核心逻辑是先读头文件解析出每条导联的增益gain、基线baseline、单位units、采样率fs再根据 ADC 分辨率把 12-bit 数据还原成物理量单位是 mV。这个转换不做画出来的图就是一堆无意义的大数字。function [sig, fs, gain, baseline] read_mitbih(path, record) % path: 数据目录record: 记录名如 100 hdr fopen(fullfile(path, [record .hea]), r); if hdr -1, error(找不到头文件 %s.hea, record); end % 第一行记录名 导联数 采样率 采样点数 info textscan(fgetl(hdr), %s %d %d %d); n_leads info{2}; fs info{3}; n_samples info{4}; % 逐行读导联格式如 100.dat 2 212 200 0 11 -1024 995 leads cell(n_leads, 1); for k 1:n_leads tok textscan(fgetl(hdr), %s %d %d %d %d %d %d %d); leads{k}.file tok{1}{1}; leads{k}.gain tok{4}; leads{k}.baseline tok{6}; leads{k}.units tok{7}; % 通常是 mV leads{k}.adc_res tok{8}; end fclose(hdr); % 这里先读取第一个导联 gain leads{1}.gain; baseline leads{1}.baseline; % 实际读取 dat 文件的部分在 2.2 节实现 sig read_212(fullfile(path, leads{1}.file), n_samples); end这里有个常见的坑.hea文件里导联格式行的第 6 个字段是 baseline、第 7 个是单位、第 8 个是 ADC 分辨率。很多资料里只讲采样率和导联数最容易忽略的就是增益和基线。如果在读取时不做(raw - baseline) / gain这一步画出来的波形幅值会偏大几十倍而不少论文里恰好就是在这个环节吃了亏图一出来幅度全不对。read_212函数的实现思路是每读取 3 字节其中前两个字节直接组成一个样本第二个字节再和第三个字节拼出另一个样本。注意MATLAB 读二进制默认按字节序这里需要手动组合成 12-bit 无符号整数然后再转换回有符号 12-bit正负值。2.2 212 格式解析3 字节拆出 2 个采样点212 格式是 MIT-BIH 最经典的存储方式每 3 个字节打包两个 12-bit 样本。设计初衷是压缩存储空间但也让读取代码多了一层转换。很多人第一次写读取脚本会遇到“波形锯齿状跳跃”“数值全乱掉”的情况根源大概率是 12-bit 符号位解析错了。正确的做法是字节流中第一个字节是样本1的低 8 位第二个字节的高 4 位是样本1的高 4 位第 9~12 bit同时低 4 位是样本2的高 4 位第三个字节是样本2的低 8 位。function sig read_212(filename, n_samples) fid fopen(filename, rb); if fid -1, error(打不开 %s, filename); end raw fread(fid, uint8); fclose(fid); n_bytes length(raw); % 每隔 3 字节解出 2 个样本需要保证字节数足够 n_pairs floor(n_bytes / 3); sig zeros(1, n_pairs * 2); for i 0:n_pairs-1 b1 raw(i*3 1); b2 raw(i*3 2); b3 raw(i*3 3); % 12-bit 样本1 b1 全高8位 b2 高4位 s1 bitshift(b2, 4) b1; % b2 低 4 位丢弃 % 12-bit 样本2 b2 低4位 b3 全低8位 s2 bitshift(b3, 4) bitshift(b2, -4); % 第 12 位是符号位转换到有符号数 if s1 2048, s1 s1 - 4096; end if s2 2048, s2 s2 - 4096; end sig(i*2 1) s1; sig(i*2 2) s2; end % 截掉多余样本防止 n_samples 和实际不一致 sig sig(1:n_samples); end注意bitshift和按位运算在 MATLAB 里效率不算高但当数据量不大360 秒 × 360 Hz ≈ 13 万样本时完全够用。如果你后续要处理几十条记录可以改成typecast或先从内存里预处理成int16数组——我一般会让read_212返回原始整数序列在后面统一做单位转换这样便于批量处理。参数说明s1 2048判断符号位是因为 12-bit 取值范围是 -2048~20472048 以后是负数必须减去 4096 才能得到正确的负值。如果没做这个判断画图时波形负极会翻转QRS 波看起来像倒立数值还特别大。2.3 单位换算为什么画出来的波形幅值偏大几千倍.dat文件里存的其实是 ADC 原始码不是物理单位。头文件中gain字段表示原始码到物理单位的换算系数。以 100 号记录为例增益是 200即 200 ADC 单位对应 1 mV基线是 1024。那么实际电压值 (raw - baseline) / gain单位是 mV。很多新手直接拿read_212的输出去plot波形确实有但 Y 轴数值上千完全不像心电图的 mV 级别。这时候第一反应应该是查头文件里的baseline和gain。不同记录这两个值不一样绝对不能写死。% 读入后做单位转换这一步必须做 sig_mv (raw_sig - baseline) / gain;有些资料里还会用adc_resADC 分辨率比如 11 bit做进一步缩放实际上如果gain已经包含 ADC 电压分辨率再按2^11除一次就画蛇添足了。稳妥的做法是先看.hea文件里有没有ADC Gain字段。MIT-BIH 标准格式下这一步就是单纯的(raw - baseline) / gain不需要额外处理。如果波形幅值在 0.5~2.5 mV 之间说明单位换算正确。2.4 滤波与基线漂移画图前要不要处理毛刺MIT-BIH 数据的采样率是 360 Hz肌电干扰和工频噪声在里面都真实存在。画图前需不需要滤波取决于你画图的优先级。如果只是看整体形态、确认 R 峰位置滤波反而会拖慢节奏且损失细节。但如果你画的是论文图或者需要标注 ST 段、T 波起点就必须做预处理了。我的一般习惯先画原始波形看全貌再用带通滤波0.5~40 Hz画细节图。带通低端 0.5 Hz 是为了去除基线漂移高端 40 Hz 是为了保留 QRS 波的能量同时压掉高频肌电。但注意滤波会带来相位延迟标注 R 峰时如果直接用滤波后的波形找位置峰会移位几个毫秒严重时会偏移数个采样点。血泪经验滤波后的图只做展示不做特征点提取。% 带通滤波示例filtfilt 零相位滤波避免相位偏移 fs 360; [b, a] butter(2, [0.5 40] / (fs/2), bandpass); sig_filt filtfilt(b, a, sig_mv);这里用butter(2, ...)的原因是二阶 Butterworth 在滤波效果和计算复杂度之间比较平衡。如果你想更锐利的 QRS 边缘可以换 4 阶代价是有一点过冲。我做过实验对 MIT-BIH 100 号记录用 4 阶带通后 T 波起点容易发生轻微形变所以还是推荐先用 2 阶零相位滤波。3. Matlab 绘图实战时间轴构建、导线布局与标注覆盖3.1 先画第一张全览图时间轴、Y 轴与基础参数现在数据已经转成 mV 了接下来要解决的是“时间轴怎么对得上”。MIT-BIH 采样率固定 360 Hzt (0:n_samples-1) / fs就是真实时间轴单位是秒。这里有个高频翻车点直接用plot(sig_mv)而不构造时间轴画出来的图 X 轴是采样点编号写论文时一看就知道外行因为没换算成时间。fs 360; t (0:length(sig_mv)-1) / fs; % 秒 figure(Color, w, Position, [100 100 800 300]); plot(t, sig_mv, b, LineWidth, 0.8); xlabel(时间 (s)); ylabel(幅值 (mV)); title(MIT-BIH 记录 100 号导联 MLII); xlim([10 15]); % 只看 5 秒细节才清晰 grid on;一直看到 10 到 15 秒是因为整条记录 30 分钟画出来完全看不清。初学者经常一上来就画全段结果波形挤成一条毛刺带误以为数据处理有问题。画图先选小窗口比如 5 秒足以看清三段完整 QRS 波。窗口长度可以根据心率调整——如果心率 150 bpm5 秒能看 12 个心拍如果心率 40 bpm5 秒只有 3~4 个心拍。3.2 标注文件读取R 峰位置与心拍类型叠加单纯的波形图价值有限。真正实用的图是把.atr注释里的 R 峰位置、心拍类型正常 N、室性早搏 V、左束支阻滞 L 等叠加到波形上。这样能直观看到不同波形形态之间的差别这在做分类算法或写论文“材料与方法”部分时非常有用。.atr文件是二进制格式字段变长最常见的解析方法是逐条读注释记录。每条注释包含time相对记录起点的时间偏移、type心拍类型、subtype等字段。网上有现成的rdann.m脚本也可以用 WFDB 工具包但更可控的是自己解析尤其是只想取 R 峰位置和心拍类型时。function [ann_times, ann_types] read_atr(filename) fid fopen(filename, rb); if fid -1, error(注释文件打开失败); end % MIT-BIH annotation 格式跳过头部若干字节 % 推荐做法直接读取所有字节定位到注释段 raw fread(fid, uint8); fclose(fid); % 简便方案用 WFDB 的 rdann 替代但这里演示直接解析 % 先做定位找 0x00 0x00 段注释头特征 ann_times []; ann_types []; i 1; time_offset 0; while i length(raw) - 1 if raw(i) 0 raw(i1) 0 % 可能是注释起始尝试解析 type 字节 type_byte raw(i2); ann_types [ann_types, char(type_byte)]; % 时间偏移增量复杂通常用累计采样点表示 ann_times [ann_times, time_offset]; % 伪代码实际需要按注释格式逐步推进 end i i 1; end end实际上.atr文件解析没有这么简单它的格式手册有几十页。我不建议自己从零实现除非你是想深入理解格式。作为工程师我实际会直接用 WFDB 软件包的命令行工具rdann然后把结果导入 MATLAB# 在终端中运行生成文本格式的注释文件 rdann -r 100 -a atr -p 100_annotations.txt然后再用 MATLAB 读取输出文件。这比你手写二进制解析快得多而且不容易出错。关于.atr里的时间偏移rdann输出的第一列是采样点序号除以fs就得到时间。这就是很多人困惑的“为什么时间点和波形对不上”的答案——注释文件存的是采样点索引不是直接的时间秒数。3.3 特殊波形的形态差异室性早搏、左束支阻滞与正常心拍把心拍类型叠加到图上能直观看到特殊波形的差异。例如 100 号记录里主要是正常窦性心律而 200 号记录里有大量室性早搏PVC——PVC 的 QRS 波宽大畸形、T 波方向相反和正常心拍差异明显。读者如果做 ECG 分类算法这一步是必不可少的“地面真值”可视化方法。% 假设 ann_times 是采样点索引ann_types 是对应类型 sel ismember(ann_types, {N, V, L}); % 只显示这三种 p_times ann_times(sel) / fs; % 转成时间秒 p_types ann_types(sel); figure(Color, w, Position, [100 100 1000 400]); plot(t, sig_mv, Color, [0.6 0.6 0.6], LineWidth, 0.6); hold on; % 标记不同心拍类型 colors containers.Map({N,V,L}, {g,r,m}); for k 1:length(p_times) if isKey(colors, p_types(k)) xline(p_times(k), --, colors(p_types(k)), LineWidth, 1); end end legend({ECG, 正常心拍, 室性早搏, 束支阻滞}, Location, best); xlim([10 30]);xline是在 MATLAB R2018b 之后才有的函数如果你还在用老版本可以用plot([t t], ylim, --, Color, color)代替。用containers.Map的好处是颜色和类型一一对应代码更整洁。这里有个关键点同一导联下PVC 和正常心拍的 R 峰振幅差异很大把ylim设得太宽会导致正常波形看起来很小。我一般会把 PVC 和正常心拍分两张图来画或做一个 6 秒窗口放大让细节明显。3.4 多导联对比MLII 与 V1/V2 的同步显示MIT-BIH 大部分记录有双导联常用的是 MLII肢体导联和 V1胸导联。同时画两条导联对齐时间轴能看出心电向量在不同投影方向上的形态差异。这对理解心电轴偏移、ST 段抬高这类诊断概念非常有帮助。% 假设 lead1, lead2 都是已经完成单位换算的向量且等长 t (0:length(lead1)-1) / fs; figure(Color, w, Position, [100 100 900 500]); subplot(2, 1, 1); plot(t, lead1, b, LineWidth, 0.8); ylabel(MLII (mV)); xlim([10 15]); grid on; subplot(2, 1, 2); plot(t, lead2, r, LineWidth, 0.8); ylabel(V1 (mV)); xlim([10 15]); grid on; xlabel(时间 (s));注意两个 subplot 的 X 轴范围必须一致否则对比就失真。这也是“玄学”之一两条导联单独看都没问题叠在一起时间轴错位结论就全反了。我习惯先linkaxes锁定或者直接手动xlim这样缩放时两图同步才方便比对。4. 避坑指南MIT-BIH 绘图最容易翻车的五个细节4.1 波形反向或出现了“镜像”——符号位解析错误现象原始波形 QRS 波主峰向下或 T 波方向明显反了。原因212 格式里 12-bit 符号位处理不对读出来的负值变成了正值导致波形上下翻转。解决检查read_212里s1 2048的判断逻辑对照原始二进制用十六进制工具验证前几个样本。例如打开.dat文件头 6 个字节手动算出两个样本的十进制值和脚本输出比对。符号位判断错误往往不只是反向还会让幅值偏移几千个单位。4.2 波形幅值明显偏大或偏小——单位换算没做现象同一记录画出的波形 Y 轴范围是 -500~500甚至更大。原因直接用read_212的原始码画图没做(raw - baseline) / gain。解决读取.hea中第一导联的baseline和gain统一换算。以 100 号记录为例原始码 ±2000 除以gain 200后是 ±10 mV接近真实心电幅值不除就直接画出 ±2000完全不可用。4.3 时间轴对不上——注释时间点是采样点索引而非秒现象xline标记的位置和波形的 R 峰错位差了几百个单位。原因.atr文件记录的时间偏移是采样点计数不是秒。解决先除以采样率fs 360再画垂直线。我建议把注释时间全部转换成秒后统一保存避免后面每一步都重复换算而犯错。4.4 滤波后的波形发生形变——相位失真导致特征点偏移现象用filter非零相位处理后的波形R 峰被移位ST 段形状变化。原因普通 IIR 滤波是非零相位的会引入延迟导致特征点时间偏移。解决使用filtfilt做零相位滤波或不用滤波只做展示。特征点R 峰、T 波终点的定位应在原始波形上进行滤波只用于视觉平滑。4.5 用旧版 Matlab 跑新函数——xline、tiledlayout 报错现象代码在 R2023b 上正常在 R2017a 上报xline未定义。原因函数版本差异。解决用plot([t t], ylim, --)替代xline用subplot替代tiledlayout。如果你在 2023a 之后版本里写了中文注释乱码检查feature(DefaultCharacterSet)设置或直接用英文注释省得折腾编码。5. 高效绘图与验证不逐条画图而是批量出图和检查到这一步数据读取和单条绘图已经通了。接下来是效率问题——你要处理 48 条 MIT-BIH 记录不可能一条一条改参数画图。一个实用的做法是写一个批量绘图脚本把每条记录的波形、标注、H-R 间期变化图一次性导出成.png或.eps。后面做论文或者汇报时直接用不用临时补图。我的习惯是每跑一条记录先看一眼全览图30 分钟整段粗略图确认数据读取正常、幅值范围合理、注释位置正确。这一步能筛掉 90% 的愚蠢错误——比如文件读错、单位漏换算。然后再针对感兴趣的心拍段画精细图。% 批量处理遍历记录名列表生成全览图 records {100, 101, 102, 103, 105, 106}; out_dir figs/; if ~exist(out_dir, dir), mkdir(out_dir); end for i 1:length(records) [sig, fs, gain, baseline] read_mitbih(mitdb, records{i}); sig_mv (sig - baseline) / gain; t (0:length(sig_mv)-1) / fs; % 第一个图原始波形整段密度图 figure(Visible, off); plot(t, sig_mv, LineWidth, 0.1); xlabel(时间(s)); ylabel(mV); title(sprintf(Record %s Full View, records{i})); xlim([0 t(end)]); saveas(gcf, fullfile(out_dir, [records{i} _full.png])); close(gcf); end这个脚本用了Visible, off批量出图时不会弹出一堆窗口干扰操作。LineWidth调成 0.1 是因为整段 30 分钟有 65 万个点线太粗会变成一团黑。细节图建议LineWidth 0.8只在 5 秒窗口内才看得清。批量跑完后下一步是验证特征点标注是否准确。我的做法是把标注的 R 峰位置和波形本身画在一个窗口放大查看错位量。人工抽查 20 个心拍如果全部对齐就说明解析和绘图链路是对的。从那以后我每次拿到新数据第一件事就是跑一遍这个流程先看全览图再抽查标注而不是直接跑算法——这个习惯帮我省掉了大量返工时间。最后补充一个冷门但很实用的技巧把标注心拍类型用不同颜色叠加在波形上可以直接另存为矢量图放进论文。MATLAB 的print导出.eps在 2023a 之后建议用exportgraphics导出.pdf或.eps字体清晰且不会出现中文乱码。希望这篇关于 MIT-BIH 数据读取和 Matlab 绘图的拆解能帮你把数据到可视化这一段路走顺。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Apache Iceberg 完整发布流程指南:从 RC 候选构建、社区投票到版本化文档与候选版本验证 2026/9/25 7:56:01

Apache Iceberg 完整发布流程指南:从 RC 候选构建、社区投票到版本化文档与候选版本验证

数据湖大数据数据存储 【免费下载链接】iceberg Apache Iceberg 项目地址: https://gitcode.com/gh_mirrors/icebe/iceberg 点击查看 免费下载 Apache Iceberg 作为 Apache 顶级项目,其每个正式版本的诞生都遵循一套严谨、可审计的发布流程:…

阅读更多 →
TypeDoc `@license` 标签详解:让许可声明注释自动排除在 API 文档之外 2026/9/25 7:56:01

TypeDoc `@license` 标签详解:让许可声明注释自动排除在 API 文档之外

开发工具文档 【免费下载链接】typedoc Documentation generator for TypeScript projects. 项目地址: https://gitcode.com/gh_mirrors/ty/typedoc 点击查看 免费下载 在生成 TypeScript 项目的 API 文档时,文件头部的 Copyright、许可条款声明&#x…

阅读更多 →
Atlas 300V 24G部署YOLOv5指南:从ONNX到OM的昇腾推理 2026/9/25 7:55:54

Atlas 300V 24G部署YOLOv5指南:从ONNX到OM的昇腾推理

最近在技术群里被问到最多的两个问题,一个是“Atlas 300V 24G是运算加速卡吗”,另一个是“网上说的atlas部署YOLO到底怎么搞”。这两个问题其实指向同一件事:昇腾生态的Atlas系列AI推理设备越来越普及,但大量开发者在第一步就被卡…

阅读更多 →
使用 Flowbite 与 Tailwind CSS 构建网站页脚(Footer)组件的完整指南 2026/9/25 7:55:54

使用 Flowbite 与 Tailwind CSS 构建网站页脚(Footer)组件的完整指南

UI组件前端 【免费下载链接】flowbite Open-source UI component library and front-end development framework based on Tailwind CSS 项目地址: https://gitcode.com/gh_mirrors/fl/flowbite 点击查看 免费下载 页脚(footer)位于每个页面…

阅读更多 →
Atlas 300V部署YOLOv5实战:模型转换与多路视频推理优化 2026/9/25 7:55:54

Atlas 300V部署YOLOv5实战:模型转换与多路视频推理优化

开工之前先把话放到前面:如果你和我一样,第一次听到“Atlas 300V 24G”的时候脑子里冒出来的问题是“这东西到底是不是运算加速卡”,那这篇文章就是为你准备的。是,但不是我们熟悉的“显卡”那种加速卡。它是昇腾生态里专门做推理…

阅读更多 →
AI视频生成镜头语言六维拆解:从Prompt到导演的实操指南 2026/9/25 7:55:48

AI视频生成镜头语言六维拆解:从Prompt到导演的实操指南

1. 为什么光靠Prompt写不出好镜头1.1 从“抽卡”到“导演”的认知转变很多人用AI视频生成工具,习惯把全部精力砸在Prompt的遣词造句上,反复堆砌“4K、超写实、电影感、丁达尔效应”这类形容词,结果生成出来的画面要么像PPT翻页,要…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉