ECG/PPG/BP三路同步信号特征提取与批量处理方法

发布时间:2026/9/7 6:17:42
ECG/PPG/BP三路同步信号特征提取与批量处理方法 简介面向生物医学工程研究人员与信号处理开发者这份MATLAB项目为同步采集的PPG、ECG、BP三路生理信号提供完整的特征提取流程。代码覆盖信号预处理、峰值与间期检测、时频域分析及基于参考信号的对齐处理例如R波检测、脉搏波峰谷定位、小波分解、频域样本熵计算等并内置一段.mat实测数据可直接运行主程序适合用于健康监测或疾病特征研究。压缩包共30个文件其中包含28个.m源码、1个.mat示例数据和1个README说明文档整体大小33.31MB目录划分清晰便于按功能模块查阅。目前已有1401人学习对于希望快速上手多模态生理信号特征工程的研究者与开发者是一份可复现、可扩展的实用代码库有助于缩短算法验证与实验开发周期。 做生理信号处理的人应该都有体会采集设备一开三路波形哗哗地录下来看着挺漂亮但真正把数据变成可以进统计模型的特征才是费时费力的地方。我这次整理的bp_features_extraction程序处理的正是同步采集的ECG、PPG和BP三路信号——心脏电活动、外周血流容积、动脉内压力三个维度各讲各的故事又要放在同一个时间轴上比对。这个程序的价值就是把这套从原始波形到特征表格的管线跑通每搏切分、时域/频域特征计算一步到位最终输出标准化的CSV特征表。适合做心血管功能评估、连续血压监测研究、可穿戴生理信号分析的人直接抄作业。1. 项目背景与整体设计思路1.1 三个信号为什么放在一起处理ECG记录的是心肌细胞的电活动体表电极就能拾取到QRS波群的出现代表了心脏一次机械收缩的启动指令PPG利用光电传感器检测组织内血液容积的搏动变化指尖夹个探头就能采到BP则是动脉内压力的直接体现临床常用的连续血压波形能够逐搏给出收缩压、舒张压和平均压。三者从不同层面描述同一个生理过程电兴奋、容积脉动、压力变化。同步处理的价值就体现在“联合”上。最典型的例子就是PTT——心电图R峰到外周搏动波某个特征点PPG波峰或BP波形上升支的时间差。这个时间差与动脉血压之间有较强相关性也是很多无创连续血压估计算法的基础。再比如HRV与血压变异性BPV的频域耦合分析、PPG与ECG之间的相位关系都需要三路信号严格同步。如果各提各的最后拿到的时间轴都对不上再好的特征也没法用。1.2 程序的功能定位与处理流程最初拿到这批数据时我最大的诉求是批量、可复现。实验涉及几十个受试者、每段记录几十分钟手动在软件里一个个数搏动、算特征根本不现实。所以程序从一开始就按流水线设计读取原始数据统一时间轴与采样率对三路信号分别做针对性预处理再以ECG的R峰作为全周期基准把PPG、BP波形切成一个个心搏窗口在每个窗口里依次计算三路信号的时域特征所有搏动特征汇总后再做频域计算和描述统计最后把特征表写成CSV和MAT两种格式方便后续导入Excel、SPSS或Python的统计脚本。这套流程看起来直白但每一步都有细节。比如预处理时滤波器参数怎么选、R峰检测阈值怎么定、坏段怎么筛这些决策会直接决定最终特征的质量。后面几节我会把每个环节的具体做法和踩过的坑都展开说。1.3 为什么选Matlab而不是Python我是有意选Matlab搭的这个程序。原因很实际Signal Processing Toolbox里有几个函数是这个任务的地基比如filtfilt做零相位滤波、findpeaks做峰值检测、iirnotch做工频陷波、pwelch做功率谱估计一套流程下来代码量能比Python少不少。而且我做实验用的采集系统BIOPAC、PowerLab这类导出格式以及很多门内流传的旧脚本都是Matlab生态和同门对数据、对代码时沟通成本低。当然Python现在也很强但如果你只是处理一批离线数据Matlab的交互式调试体验和绘图响应速度确实更顺手。这个选择并不绝对按自己课题组的生态来就好。2. 数据接入与预处理三板斧2.1 数据格式确认与同步对齐拿到数据的第一步永远不是滤波而是把时间轴搞明白。三路信号虽然来自同一个采集系统但不同设备、不同导出软件的坑非常多有的波形文件里时间轴是单独一列有的按起始时间加采样率隐式给出有的三个通道采样率还不一样。先查清楚这些写成硬性校验。常规做法是读进后用统一采样率重建时间轴线性插值到共同网格。Matlab里interp1或resample都行但要注意resample会自动做抗混叠滤波处理阶跃突变时会引入边缘效应没有特殊需求时我一般用resample并在头尾各截掉几十个点来消除过渡带影响。data readtable(record.csv); t data.time; ecg data.ecg; ppg data.ppg; bp data.bp; fs 1000; t_common (0 : length(t)-1) / fs; ppg_r interp1(t, ppg, t_common, linear); bp_r interp1(t, bp, t_common, linear);同步对齐之后最好用ECG的R峰和PPG的搏动波峰做一次交叉验证——两条通道的搏动计数应该一致或者差一个固定的生理延迟如果数目对不上说明时间轴或通道映射出了问题这时候值得回去核对原始数据。2.2 ECG信号的工频陷波与基线校正ECG最典型的干扰是工频噪声国内就是50Hz。采集环境只要接地没做好波形上就是厚厚一层高频毛刺。这里我用的是iirnotch设计陷波器陷波频率wo和带宽BW都按采样率做归一化fs 1000; wo 50 / (fs/2); bw wo / 35; [b_notch, a_notch] iirnotch(wo, bw); ecg_f filtfilt(b_notch, a_notch, ecg); [b_hp, a_hp] butter(2, 0.5 / (fs/2), high); ecg_f filtfilt(b_hp, a_hp, ecg_f);这里两个参数值得说一下。wo是50Hz映射到奈奎斯特频率后的归一化位置bw取wo/35对应的品质因子Q35实测下来既能把50Hz分量压住也不会因为带宽过窄而对真实频率偏移敏感。高通滤波的截止频率选0.5Hz是为了去掉呼吸引起的基线漂移和电极慢漂移同时保证QRS波群能量集中在几Hz到几十Hz完全不受伤。注意连续用了两次filtfilt这是我一直坚持的习惯零相位滤波波形不会整体延迟后面做各通道时间对比时少了很头疼的矫正步骤。2.3 PPG和BP信号的平滑去噪PPG受环境光和运动伪迹的影响大与ECG相比往往更脏。它的主要能量集中在心率基频以及前几次谐波上因此一个10Hz左右的低通滤波器就能把大部分高频噪声压掉。BP连续波形频率范围和PPG接近也是低通处理同时保留动脉压力波本身的形态。这里一个小技巧低通之前先做异常尖峰剔除。有些数据里会突然出现一个孤立的毛刺信号比如电极松动、光线遮挡、体温变化如果直接滤波会把尖峰扩散成一段隆起影响后面的峰值检测。我习惯预先做一个中值滤波medfilt1几十个点的窗口或阶梯式离群值剔除只对明显超出局部均值好几个标准差的点做替换而不是对整个信号做全局平滑这样能最大限度保住真实搏动边缘的陡峭程度。3. 按搏特征提取的核心实现3.1 用ECG的R峰作为整条流水线的锚点特征提取的核心先把“一个搏动”的定义定死。我选择ECG的R峰作为时刻锚点原因很简单R峰是三个信号里信噪比最好、形态最单调的波峰检测算法成熟、误检率低。把R峰位置定位后PPG和BP都以它为基准取窗口——默认取R峰之前0.2秒到R峰之后0.6秒可按心率调整这样一个窗口内的ECG片段、PPG搏动波形、BP压力搏动天然对齐。R峰检测我用findpeaks而不是从零手写Pan-Tompkins[pks, locs] findpeaks(ecg_f, MinPeakHeight, threshold, ... MinPeakDistance, round(0.3 * fs), ... MinPeakProminence, 0.3 * std(ecg_f));MinPeakHeight和MinPeakProminence需要用一段手工标注的数据来试把它们定在“能检出所有正常R峰、同时不把T波当作R峰”的位置。MinPeakDistance设成0.3s对应的样本数这是生理极限成人最小心动周期也就是紧张、剧烈运动时可能到0.3s上下小于这个间隔的峰一定不是相邻心搏。3.2 三路信号的时域特征计算拿到每个搏动窗口后特征计算就是纯粹的数学了。核心循环大致长这样for i 1:length(locs)-1 idx locs(i) : locs(i1); ecg_beat ecg_f(idx); ppg_beat ppg_f(idx); bp_beat bp_f(idx); rr_ms(i) (locs(i1) - locs(i)) / fs * 1000; sbp(i) max(bp_beat); dbp(i) min(bp_beat); map(i) mean(bp_beat); pp(i) sbp(i) - dbp(i); ppg_amp(i) max(ppg_beat) - min(ppg_beat); ppg_pi(i) ppg_amp(i) / mean(ppg_beat) * 100; [~, pkg_peak] max(ppg_beat); [~, bp_peak] max(bp_beat); rise_time_ppg(i) pkg_peak / fs * 1000; end这里有几个细节MAP用的是搏动区间内的时间平均而不是简单的SBP2DBP/3后者是临床估算公式在有完整波形时直接积分平均更准确PPG的PI是搏动分量幅度除以平均光强再乘100%反映灌注水平这个指标受传感器贴合力度影响大横向比较时要小心。信号特征名称计算方式单位ECGRR间期相邻R峰时间差msECGSDNN全部RR间期标准差msECGRMSSD相邻RR差值均方根msPPG脉率60/平均峰值间隔bpmPPGPI搏动幅度/平均光强×100%%PPG上升时间谷到峰时间msBPSBP/DBP窗口最大值/最小值mmHgBPMAP窗口时间平均mmHgBPPPSBP-DBPmmHg3.3 HRV与血压变异性的频域特征时域特征是一拍一拍的而频域特征往往要把一段长时间序列切成若干子段来做。HRV频域分析最常用的做法先把逐搏RR间期序列用三次样条插值重采样到4Hz均匀序列再做Welch功率谱估计然后在标准频带内积分功率ts cumsum(rr_ms) / 1000; tq ts(1) : 1/4 : ts(end); rr_interp interp1(ts, rr_ms, tq, spline); [pxx, f] pwelch(rr_interp, hann(256), 128, 256, 4); lf trapz(f(f0.04 f0.15), pxx(f0.04 f0.15)); hf trapz(f(f0.15 f0.4), pxx(f0.15 f0.4));频带划分是行业标准高频段0.15-0.4Hz一般认为反映副交感神经调节低频段0.04-0.15Hz反映交感与副交感的共同作用LF/HF常被拿来当自主神经平衡指标。需要注意的是短时记录的频域HRV可靠性并不好一般要求至少5分钟我在程序里把长记录切成5分钟子段再分别算LF/HF输出的时候保留子段编号。同样的流程在BP上就是对逐搏SBP序列做血压变异性频域分析。3.4 特征整理与批量导出所有逐搏特征算完后把整个网络的组织工作交给table和timetable。timetable可以直接带时间戳后面画趋势图、做滑动平均都很方便。用writetable直接落盘CSVT table(rr_ms, sbp, dbp, map, pp, ppg_pi, ... VariableNames, {RR_ms,SBP,DBP,MAP,PP,PPG_PI}); writetable(T, features_per_beat.csv);我一般会同时输出两种粒度逐搏明细表每个心搏一行包含ECG/PPG/BP的所有特征汇总统计表每个受试者或每个时间段一行包含均值、标准差、中位数、分位数。后者才是最终送进统计软件做组间比较的数据。4. 常见问题与排查技巧实录4.1 时间轴错位与采样率不一致踩过的第一个坑是同一批记录里部分文件ECG是1000HzPPG和BP导出时被降到了250Hz。如果不做处理直接按相同索引取窗口后面所有特征对不齐。排查办法对两个通道做互相关横轴滞后对应的时间偏移正常情况下应该是一个固定值比如几十毫秒的心电-脉搏延迟如果斜率不对或呈现分段跳变基本就是采样率不一致。解决方式是统一用高采样率那个通道的时间基准对低采样率的做interp1线性插值补齐。4.2 运动伪迹与坏段剔除数据里总有不听话的受试者。轻微身体动作会让PPG和BP波形出现大段基线台阶甚至脱落。我的程序里加了坏段标记逻辑对每个5秒窗口计算PPG搏动幅度序列的变异系数超过阈值就标记为疑似运动段再检查BP的收缩压值是否落在合理生理范围比如30-260mmHg之外就是明确异常。被标记的坏段不进特征统计但在汇总表里会记录坏段占比这一步对后续数据质量说明很有用。4.3 findpeaks参数调优的坑findpeaks虽然好用但参数设置不好也会丑态百出。印象最深的是MinPeakProminence没设好时T波被当成了R峰——心率直接翻倍而MinPeakDistance设太大又会在心动过速时漏检。我的经验是先用一段5分钟、信噪比高的数据手工标注R峰位置把两组候选参数跑出来的输出与手工标注对比让检出率、误检率都在可接受范围再把参数固定下来跑批量。记住一点调参是适配你的数据的不是适配某个理论公式的。4.4 长时数据的性能与存储一小时的三路1kHz原始数据存成mat文件大约百兆级别按搏循环算特征如果每拍都反复用索引取数据几十分钟的记录也可能跑出十分钟的耗时。两个优化点一是循环前把需要重复使用的信号片段全部转成列向量并按区间索引切片避免频繁的矩阵访问二是所有输出变量在循环前先zeros预分配。实测这样几十分钟的记录能在几十秒内算完。另外CSV小了但信息量也小了建议同时保留一个中间MAT数据包含清洗后的信号和所有峰值位置后续想换个特征重算就不用从头读原始文件。最后说点个人体会。这个程序最初版本是三路信号各管各预处理、各算各的特征最后再去合并对齐结果每次都被时间轴问题折磨。后来狠下心重构把预处理完的三路信号放进同一个矩阵再用ECG的R峰作为唯一锚点切窗口之后ECG、PPG、BP的特征天然对齐流程一下子顺了。所以如果参考我这个思路第一条建议就是先解决时间基准再谈特征质量。后续想继续扩展的话可以在这个框架上加入PTT的计算——R峰到PPG波峰的时间差以及利用血压和脉搏波形态估算动脉硬度、心输出量这些更宏观的指标管线不用大动往里加特征就行。本文还有配套的精品资源点击获取