当前位置: 首页 > news >正文

血氧检测原理与算法

血氧饱和度测量原理

        血氧饱和度(SPO2)是指血液中氧和血红蛋白(HbO2)占总可结合血红蛋白(包括氧和血红蛋白和还原血红蛋白,即HbO2+Hb)的比例:

SPO2 = (HbO2/HbO2+Hb) * 100%

        临床上,SpO2的值通常不低于94%。

        血氧饱和度的测量方法可以分为两类:电化学法光学法电化学法通过动脉穿刺采集血液样本,利用血气分析仪分析动脉血中的氧分压,再通过计算得出血氧饱和度。该方法虽然测量精度高,但由于其有创性,操作复杂且实时性差,通常仅用于需要较高精度的特殊场合。与电化学法相比,光学法是一种连续的,无创的血氧饱和度测量方法,广泛应用于临床等领域。其原理是通过血氧探头检测血液对光吸收量的变化,计算氧和血红蛋白占总血红蛋白的百分比,,从而获得血氧饱和度。光学法具有操作简便,实时性强等优点,但其测量精度略低于电化学法。

        本实验采用光学法,用血氧指夹探头测量。

        关于血氧探头测量血氧的具体原理,读者可自行搜索。

血氧检测原理图

设计思路:

血氧探头发光管驱动电路

RED-,RED+,IR_CS,RED_CS均接单片机的引脚

压控恒流源电路

Ic = Ie = UTP11 / R111 = U- / R111,所以当UDA1增大是,IC也会增大

当IC增大时,UTp11也会增大,U-就会增大,但是UDA1没变,UTP10就会减小,进而Ib减小,那么IC就会受到负反馈的影响。

参考电压输出电路

信号放大滤波电路

完整的原理图

算法实现

滤波算法同呼吸和血氧

计算脉率

#define PEAK_MIN_INTERVAL  20  // 最小峰间隔(样本点)
#define DYNAMIC_THRESH_RATIO 0.7  // 动态阈值比例
double last_rate;
int find_peak(double *data,int* peak, int len)
{if(len < 3 || data == NULL) return 0;int count = 0;double max = 0;int16_t last_peak_index = -PEAK_MIN_INTERVAL; // 确保第一个峰值可被检测// 1. 计算全局最大值for(int i = 0; i < len; i++) {if(data[i] > max) max = data[i];}// 动态阈值(心率越高,阈值越低)double dynamic_thresh_ratio = 0.7 - 0.3 * (last_rate / 220.0);  // 线性调整if (dynamic_thresh_ratio < 0.4) dynamic_thresh_ratio = 0.4;      // 保护下限// 2. 动态阈值检测 + 峰间隔限制double dynamic_thresh = max * dynamic_thresh_ratio;for(int i = 1; i < len-1; i++) {if(data[i] > data[i-1] && data[i] > data[i+1] && data[i] > dynamic_thresh &&(i - last_peak_index) >= PEAK_MIN_INTERVAL) {peak[count++] = i;last_peak_index = i;  // 更新最后峰值位置if(count >= RATE_BUFFER/2) break; // 防止数组溢出}}return count;
}
#define MEDIAN_FILTER_SIZE 5     // 中值滤波窗口大小
// 2. 生理范围限制 (30-200bpm)
const int min_interval = (60 * SAMPLE_RATE) / 210; // 最高心率间隔
const int max_interval = (60 * SAMPLE_RATE) / 25;  // 最低心率间隔// 整数比较函数(用于qsort)
int compare_int(const void *a, const void *b) {return (*(int*)a - *(int*)b);
}
double SPO2Rate(int* peak,int count)
{double median_interval = 0.0;// 1. 无效数据检查if(count < 2) return -1;    // 3. 计算有效间隔int valid_intervals[50] = {0};int valid_count = 0;for(int i = 1; i < count; i++) {int interval = peak[i] - peak[i-1];if(interval >= min_interval && interval <= max_interval) {valid_intervals[valid_count++] = interval;}}if(valid_count < 1) return -1;// 4. 中值滤波qsort(valid_intervals, valid_count, sizeof(int), compare_int);if(valid_count%2==0 )median_interval = (valid_intervals[valid_count/2]+valid_intervals[(valid_count/2)-1])/2;elsemedian_interval = valid_intervals[valid_count/2];// 5. 计算心率并滑动平均double heartrate = (60.0 * SAMPLE_RATE) / median_interval;last_rate = heartrate;return heartrate;
}

计算血氧饱和度

typedef enum {FINGER_THIN,      // 细手指FINGER_NORMAL,    // 正常手指FINGER_THICK,     // 粗手指FINGER_UNKNOWN    // 无法识别
} FingerType;
double redADRng ;
double irADRng ;
//movingAverageFilter_Red_max等参考这个函数
double movingAverageFilter_HearteRate(double newSample, double *buffer)
{static int index = 0;static float sum = 0;// 减去最旧的值sum -= buffer[index];// 添加最新的值sum += newSample;// 更新缓冲区buffer[index] = newSample;// 更新索引index = (index + 1) % 10;// 返回平均值return sum / 10;
}
double rate(double red_buffer[],double ir_buffer[],int RR_TABLE[])
{// 初始化(避免依赖 buffer[0])double red_max = -INFINITY;double red_min = INFINITY;double ir_max = -INFINITY;double ir_min = INFINITY;for(int i = 0;i<BUFFER_THEREAD;i++){if(red_max < red_buffer[i]) red_max = red_buffer[i];if(red_min > red_buffer[i]) red_min = red_buffer[i];}for(int i = 0;i<BUFFER_THEREAD;i++){if(ir_max < ir_buffer[i]) ir_max = ir_buffer[i];if(ir_min > ir_buffer[i]) ir_min = ir_buffer[i];}red_max = movingAverageFilter_Red_max(red_max,red_max_buffer);red_min = movingAverageFilter_Red_min(red_min,red_min_buffer);ir_max = movingAverageFilter_ir_max(ir_max,ir_max_buffer);ir_min = movingAverageFilter_ir_min(ir_min,ir_min_buffer);redADRng = red_max - red_min;irADRng = ir_max-ir_min;double red_DC = (red_max + red_min)/2;double ir_DC  = (ir_max + ir_min)/2;if(redADRng < 5.0 || irADRng < 5.0){figurestate = FIGURE_OFF;}else { figurestate = FIGURE_ON;}double light_intensity = get_current_light_intensity(red_DC, ir_DC)*1000.0;double dynamic_offset = get_dynamic_compensation(light_intensity);double R1 = redADRng * 1000.0/ irADRng -dynamic_offset;R1 = movingAverageFilter_R1(R1,R1Buffer);//double spo2 = 110.0 - 25.0 * R1;  // 示例公式,需校准int index = 1; while(R1>=(double)RR_TABLE[index-1]&&index<11){index++;}int spo2 = 101 - index;char str[20];     sprintf(str,"%.02lf,%.02lf,%.02lf",R1,redADRng,irADRng);LCD_WriteAsciiString(50,50,24,(uint8_t*)str,BLACK,WHITE);return spo2;
}
FingerType classify_finger_type() {if (irADRng <35 &&irADRng > 18.5) {return FINGER_THIN;      // 细手指} else if (irADRng <= 18.5 && irADRng >= 13.0) {return FINGER_NORMAL;    // 正常手指} else if ( irADRng <=16) {return FINGER_THICK;    // 粗手指} else {return FINGER_UNKNOWN;  // 异常情况}
}

http://www.dtcms.com/a/359041.html

相关文章:

  • Linux系统直接查询文件或目录绝对路径的方式
  • TensorFlow 深度学习 | 使用底层 API 实现模型训练(附可视化与 MLP)
  • HyperPlonk 的硬件友好性
  • Linux kernel 多核启动
  • LINUX-网络编程-TCP-UDP
  • Python 入门 Swin Transformer-T:原理、作用与代码实践
  • AI + 行业渗透率报告:医疗诊断、工业质检领域已进入规模化落地阶段
  • 通过数据蒸馏打破语音情感识别的资源壁垒
  • 基于单片机音乐喷泉/音乐流水灯/音乐播放器设计
  • 移动零,leetCode热题100,C++实现
  • SpringCloud Alibaba Sentinel 流量治理、熔断限流(四)
  • 【源码】智慧工地系统:智能化施工现场的全新管理方案
  • 第十七章 ESP32S3 SW_PWM 实验
  • 深入解析Nginx常见模块2
  • web渗透之RCE漏洞
  • 针对 “TCP 会话维持与身份验证” 的攻击
  • (二)设计模式(Command)
  • SQL Server 临时表合并与数量汇总的实现方法
  • 大模型不听话?试试提示词微调
  • “可选功能“中找不到 OpenSSH, PowerShell 命令行来安装OpenSSH
  • windows 谷歌浏览器一直提示无法更新Chrome弹窗问题彻底解决
  • Learning Curve|学习曲线
  • 数据库攻略:“CMU 15-445”Project0:C++ Primer(2024 Fall)
  • 【开题答辩全过程】以 “与我同行”中华传统历史数字化平台的设计和分析-------为例,包含答辩的问题和答案
  • Linux软件定时器回顾
  • 本地部署开源媒体服务器 Komga 并实现外部访问( Windows 版本)
  • 容器存储驱动升级:美国VPS文件系统优化全指南
  • 上海我店模式的多维度探究
  • 对于STM32工程模板
  • CRM、ERP、HRP系统有啥区别?