如何通过ECG/PPG传感器计算心率与SPO2?求Xamarin跨平台处理方案
处理ECG/PPG心率、SPO2计算及Xamarin Forms跨平台数据解析方案
一、心率(Heart Rate)与SPO2的计算逻辑
1. 基于PPG传感器的计算
- 心率:PPG通过光电容积描记捕捉血液流动引发的光强变化。先对原始信号做滤波(去除基线漂移、高频噪声),再检测信号峰值点,统计单位时间内的峰值数量,乘以60即可得到每分钟心率。
- SPO2:利用氧合血红蛋白、脱氧血红蛋白对红光(660nm)和红外光(940nm)的吸收差异,计算两个波长下的光强比值
R,再通过传感器厂家提供的校准公式(如SPO2 = a*R + b,a、b为校准系数)换算出SPO2值。
2. 基于ECG传感器的计算
- 心率:ECG信号的R波是核心特征,通过检测R波间隔(RR间期),用60除以平均RR间期(单位秒)得到心率。常用检测方法包括阈值法、差分法,或经典的Pan-Tompkins算法。
二、IoT传感器Byte[]数据解析
传感器返回的byte[]必须对照传感器通信协议文档解析,常见场景:
- 固定帧格式:一般由帧头+数据段+校验位组成,数据段中会用指定字节存储心率、SPO2数据(需注意字节序:大端/小端)。
- 示例解析逻辑(C#):
// 假设协议规定:第3-4字节为心率(小端),第5-6字节为SPO2(小端) public (int HeartRate, int SpO2) ParseSensorData(byte[] rawData) { // 先校验帧合法性(如帧头、校验和),此处省略校验步骤 int heartRate = BitConverter.ToInt16(rawData, 2); // 从索引2开始取2字节 int spO2 = BitConverter.ToInt16(rawData, 4); return (heartRate, spO2); }
注意:不同传感器的字节偏移、数据类型(有符号/无符号)差异极大,必须以官方协议文档为准,无通用解析逻辑。
三、Xamarin Forms跨平台实现
Xamarin Forms需通过**依赖服务(Dependency Service)**实现跨平台适配,步骤如下:
1. 定义共享接口(共享项目)
public interface ISensorDataProcessor { (int HeartRate, int SpO2) ParseRawData(byte[] rawData); int CalculateHeartRateFromPPG(List<float> ppgSignals, float sampleRate); int CalculateSpO2FromPPG(List<float> redSignals, List<float> irSignals); int CalculateHeartRateFromECG(List<float> ecgSignals, float sampleRate); }
2. Android平台实现(Android项目)
[assembly: Dependency(typeof(AndroidSensorProcessor))] namespace YourApp.Droid { public class AndroidSensorProcessor : ISensorDataProcessor { public (int HeartRate, int SpO2) ParseRawData(byte[] rawData) { // 按Android端传感器协议解析,示例为蓝牙BLE传感器数据 int hr = rawData[1] & 0xFF; // 假设第2字节为心率 int spo2 = rawData[2] & 0xFF; // 假设第3字节为SPO2 return (hr, spo2); } public int CalculateHeartRateFromPPG(List<float> ppgSignals, float sampleRate) { // 简化峰值检测逻辑,实际需增加滤波步骤 int peaks = 0; float prev = ppgSignals[0]; for(int i = 1; i < ppgSignals.Count - 1; i++) { if(ppgSignals[i] > prev && ppgSignals[i] > ppgSignals[i+1]) { peaks++; } prev = ppgSignals[i]; } float duration = ppgSignals.Count / sampleRate; return (int)(peaks / duration * 60); } public int CalculateSpO2FromPPG(List<float> redSignals, List<float> irSignals) { float redAvg = redSignals.Average(); float irAvg = irSignals.Average(); float R = redAvg / irAvg; // 示例校准公式,需替换为传感器厂家提供的系数 return (int)(-12.6 * R + 110); } public int CalculateHeartRateFromECG(List<float> ecgSignals, float sampleRate) { // 简化Pan-Tompkins算法实现 List<int> rPeakIndices = new List<int>(); // 差分处理 var diff = new List<float>(); for(int i = 1; i < ecgSignals.Count; i++) { diff.Add(ecgSignals[i] - ecgSignals[i-1]); } // 平方+滑动窗口平滑 var squared = diff.Select(d => d*d).ToList(); int windowSize = (int)(0.15 * sampleRate); // 150ms窗口 var smoothed = new List<float>(); for(int i = 0; i < squared.Count - windowSize; i++) { smoothed.Add(squared.GetRange(i, windowSize).Average()); } // 阈值检测R波 float threshold = smoothed.Max() * 0.5f; for(int i = 1; i < smoothed.Count - 1; i++) { if(smoothed[i] > threshold && smoothed[i] > smoothed[i-1] && smoothed[i] > smoothed[i+1]) { rPeakIndices.Add(i + windowSize/2); } } if(rPeakIndices.Count < 2) return 0; // 计算平均RR间期 float avgRR = 0; for(int i = 1; i < rPeakIndices.Count; i++) { avgRR += (rPeakIndices[i] - rPeakIndices[i-1])/sampleRate; } avgRR /= (rPeakIndices.Count -1); return (int)(60 / avgRR); } } }
3. iOS平台实现(iOS项目)
[assembly: Dependency(typeof(iOSSensorProcessor))] namespace YourApp.iOS { public class iOSSensorProcessor : ISensorDataProcessor { public (int HeartRate, int SpO2) ParseRawData(byte[] rawData) { // 按iOS端传感器协议解析,示例为CoreBluetooth获取的数据 int hr = BitConverter.ToUInt16(rawData, 0); int spo2 = BitConverter.ToUInt16(rawData, 2); return (hr, spo2); } // 心率、SPO2计算逻辑与Android一致,直接复用即可 public int CalculateHeartRateFromPPG(List<float> ppgSignals, float sampleRate) { int peaks = 0; float prev = ppgSignals[0]; for(int i = 1; i < ppgSignals.Count - 1; i++) { if(ppgSignals[i] > prev && ppgSignals[i] > ppgSignals[i+1]) { peaks++; } prev = ppgSignals[i]; } float duration = ppgSignals.Count / sampleRate; return (int)(peaks / duration * 60); } public int CalculateSpO2FromPPG(List<float> redSignals, List<float> irSignals) { float redAvg = redSignals.Average(); float irAvg = irSignals.Average(); float R = redAvg / irAvg; return (int)(-12.6 * R + 110); } public int CalculateHeartRateFromECG(List<float> ecgSignals, float sampleRate) { List<int> rPeakIndices = new List<int>(); var diff = new List<float>(); for(int i = 1; i < ecgSignals.Count; i++) { diff.Add(ecgSignals[i] - ecgSignals[i-1]); } var squared = diff.Select(d => d*d).ToList(); int windowSize = (int)(0.15 * sampleRate); var smoothed = new List<float>(); for(int i = 0; i < squared.Count - windowSize; i++) { smoothed.Add(squared.GetRange(i, windowSize).Average()); } float threshold = smoothed.Max() * 0.5f; for(int i = 1; i < smoothed.Count - 1; i++) { if(smoothed[i] > threshold && smoothed[i] > smoothed[i-1] && smoothed[i] > smoothed[i+1]) { rPeakIndices.Add(i + windowSize/2); } } if(rPeakIndices.Count < 2) return 0; float avgRR = 0; for(int i = 1; i < rPeakIndices.Count; i++) { avgRR += (rPeakIndices[i] - rPeakIndices[i-1])/sampleRate; } avgRR /= (rPeakIndices.Count -1); return (int)(60 / avgRR); } } }
4. 共享项目中调用
// 获取跨平台处理器实例 var processor = DependencyService.Get<ISensorDataProcessor>(); // 解析传感器byte[]数据 var (hr, spo2) = processor.ParseRawData(rawSensorBytes); // 计算PPG心率(示例采样率100Hz) var ppgSampleData = new List<float> { ... }; // 从传感器获取的原始PPG数据 int heartRate = processor.CalculateHeartRateFromPPG(ppgSampleData, 100);
四、关键注意事项
- 必须获取传感器官方通信协议文档,否则无法正确解析
byte[]数据; - 原始ECG/PPG信号需做专业滤波(如巴特沃斯滤波),示例逻辑为简化版本,实际项目需优化;
- SPO2计算的校准系数需向传感器供应商获取,不可直接使用示例公式;
- 跨平台需适配权限:Android需申请蓝牙、位置权限;iOS需在
Info.plist中添加蓝牙权限描述。
内容的提问来源于stack exchange,提问作者Rajat Panjavani
相关产品推荐
相关产品推荐

