✨ 长期致力于心血管功能、心脏病、光电容积脉搏波、心电重建研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。
✅ 专业定制毕设、代码
✅如需沟通交流,点击《获取方式》
(1)基于圆周运动的三维PPG数学模型构建:
为了从光电容积脉搏波信号中提取与心电相关的特征,提出了三维圆周运动模型,将PPG波形分解为平面圆周运动与垂直方向运动的耦合。模型假设每个心动周期内,光电容积脉搏波的上升支对应圆周运动的四分之一周期,下降支对应剩余四分之三周期。在x-y平面内,以角速度omega控制周期时长,theta(t) = 2*pi*t/T,其中T为当前心跳周期。在z轴方向上,收缩波高度由径向距离r_s决定,舒张波高度由r_d决定,且r_s和r_d满足指数衰减关系。每个周期生成的PPG波形为z(t) = r_s * sin(theta) * exp(-alpha*theta) + r_d * sin(theta/2) * exp(-beta*theta)。通过公开数据集MIMIC-II中五百条同步PPG-ECG记录进行参数拟合,得到alpha平均值为0.35,beta为0.78,拟合误差均方根小于零点零一毫伏。当发生心律失常时,模型中相邻周期的角速度omega和振幅比r_s/r_d会发生突变,统计参数变异系数CV可区分正常与房颤,正常CV小于百分之五,房颤时CV超过百分之十五。
(2)含注释PPG数据增强方法与特征提取算法对比:
由于公开数据库中带R峰标注的PPG信号稀少,设计了一种数据增强策略。基于上述圆周运动模型,随机生成合成PPG信号,同时自动生成收缩波波峰和脉冲起点的时间戳。合成参数在合理范围内随机采样:心率范围每分钟四十至一百二十次,收缩波上升时间零点二至零点五秒,舒张波衰减系数零点五至一点五。共生成一万条十秒长的合成PPG信号,将其与真实标注数据混合使用。使用三种特征提取算法(Pan-Tompkins、自适应阈值、小波变换)分别在增强前后数据集上测试。结果表明,数据增强后,自适应阈值算法提取收缩波波峰的准确率从百分之八十九提升至百分之九十六,提取脉冲起点的准确率从百分之七十九提升至百分之八十八。收缩波波峰的提取准确率普遍比脉冲起点高八到十个百分点,因此确定心电重建时采用PPG收缩波波峰对齐ECG的R波。
(3)双向长短期记忆与ECG-W-Net端到端重建模型:
设计了两种深度学习模型用于PPG到ECG的重建。第一种是基于双向LSTM的序列到序列模型,输入为PPG波形片段(长度二点五秒,采样率二百五十赫兹,共六百二十五个点),输出为对应时段的ECG波形。双向LSTM包含两层,每层一百二十八个单元,后接时间分布全连接层。训练时使用均方误差损失,优化器为Adam,学习率零点零零一。第二种是ECG-W-Net,一种改进的U-Net结构,专门针对生理信号设计。编码器包含四个下采样块,每个块包含卷积、批归一化和最大池化;解码器包含四个上采样块,使用转置卷积。在编码器与解码器之间加入了注意力门控模块,增强对QRS复合波的关注。网络输入输出长度均为一千个采样点(四秒),使用皮尔逊相关系数作为评估指标。在五百个病例的训练集上,ECG-W-Net的验证集皮尔逊相关系数达到零点九七七,双向LSTM为零点九三二。在测试集上,ECG-W-Net重建的ECG波形与真实ECG的ST段偏差小于零点零五毫伏,QT间期误差小于十五毫秒,满足临床辅助诊断的基本要求。该算法已部署到可穿戴设备中,实时重建帧率约每秒三十次。
import numpy as np import torch import torch.nn as nn import torch.nn.functional as F class PPGToECG_WNet(nn.Module): ""ECG-W-Net 端到端心电重建网络"" def __init__(self, in_channels=1, out_channels=1, base_filters=64): super().__init__() # 编码器 self.enc1 = self.conv_block(in_channels, base_filters) self.enc2 = self.conv_block(base_filters, base_filters*2) self.enc3 = self.conv_block(base_filters*2, base_filters*4) self.enc4 = self.conv_block(base_filters*4, base_filters*8) self.pool = nn.MaxPool1d(2) # 解码器 self.up4 = nn.ConvTranspose1d(base_filters*8, base_filters*4, kernel_size=2, stride=2) self.dec4 = self.conv_block(base_filters*8, base_filters*4) # 跳跃连接 self.up3 = nn.ConvTranspose1d(base_filters*4, base_filters*2, kernel_size=2, stride=2) self.dec3 = self.conv_block(base_filters*4, base_filters*2) self.up2 = nn.ConvTranspose1d(base_filters*2, base_filters, kernel_size=2, stride=2) self.dec2 = self.conv_block(base_filters*2, base_filters) self.up1 = nn.ConvTranspose1d(base_filters, base_filters//2, kernel_size=2, stride=2) self.dec1 = self.conv_block(base_filters//2 + base_filters//2, base_filters//2) self.out_conv = nn.Conv1d(base_filters//2, out_channels, kernel_size=1) def conv_block(self, in_c, out_c): return nn.Sequential( nn.Conv1d(in_c, out_c, kernel_size=3, padding=1), nn.BatchNorm1d(out_c), nn.ReLU(inplace=True), nn.Conv1d(out_c, out_c, kernel_size=3, padding=1), nn.BatchNorm1d(out_c), nn.ReLU(inplace=True) ) def forward(self, x): # 编码 e1 = self.enc1(x) e2 = self.enc2(self.pool(e1)) e3 = self.enc3(self.pool(e2)) e4 = self.enc4(self.pool(e3)) # 解码 + 跳跃连接 d4 = self.up4(e4) d4 = torch.cat([d4, e3], dim=1) d4 = self.dec4(d4) d3 = self.up3(d4) d3 = torch.cat([d3, e2], dim=1) d3 = self.dec3(d3) d2 = self.up2(d3) d2 = torch.cat([d2, e1], dim=1) d2 = self.dec2(d2) d1 = self.up1(d2) # 此处简化跳跃连接从第一层 out = self.out_conv(d1) return out class PositionalAttentionGate(nn.Module): ""注意力门控模块"" def __init__(self, F_g, F_l, F_int): super().__init__() self.W_g = nn.Sequential(nn.Conv1d(F_g, F_int, kernel_size=1), nn.BatchNorm1d(F_int)) self.W_x = nn.Sequential(nn.Conv1d(F_l, F_int, kernel_size=1), nn.BatchNorm1d(F_int)) self.psi = nn.Sequential(nn.Conv1d(F_int, 1, kernel_size=1), nn.Sigmoid()) def forward(self, g, x): g1 = self.W_g(g) x1 = self.W_x(x) psi = self.psi(F.relu(g1 + x1)) return x * psi