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

MAX30102心率血氧算法核心代码逐行解读:从FIFO数据到心率血氧值的计算过程

MAX30102心率血氧算法核心代码逐行解读:从FIFO数据到心率血氧值的计算过程

在可穿戴设备和医疗监测领域,PPG(光电容积脉搏波描记法)技术因其非侵入性和便捷性而广受欢迎。MAX30102作为一款集成式光学传感器,能够同时测量心率和血氧饱和度(SpO2),其核心算法实现却鲜有深入解析。本文将带您深入探究MAX30102项目中最为关键的algorithm.c文件,揭示从原始光学数据到生理参数的全过程。

1. 信号预处理:从原始数据到可用波形

MAX30102通过红外和红光LED照射皮肤,并检测反射光强度来获取PPG信号。原始数据存储在FIFO寄存器中,需要经过多步处理才能用于计算。

1.1 直流分量去除与信号标准化

原始PPG信号包含直流(DC)和交流(AC)成分。直流分量反映组织对光的总体吸收,而交流分量则对应心跳引起的脉动变化。去除直流分量是提取有用信号的第一步:

// 计算IR信号平均值 un_ir_mean = 0; for (k = 0; k < n_ir_buffer_length; k++) un_ir_mean += pun_ir_buffer[k]; un_ir_mean = un_ir_mean / n_ir_buffer_length; // 去除直流分量 for (k = 0; k < n_ir_buffer_length; k++) an_x[k] = pun_ir_buffer[k] - un_ir_mean;

这段代码先计算红外信号的平均值,再从每个数据点中减去该平均值,得到去直流后的信号。这种处理方式能消除个体差异(如皮肤颜色、传感器贴合度)对测量的影响。

1.2 移动平均滤波

为抑制高频噪声,算法采用4点移动平均滤波器:

for (k = 0; k < BUFFER_SIZE - MA4_SIZE; k++) { n_denom = (an_x[k] + an_x[k+1] + an_x[k+2] + an_x[k+3]); an_x[k] = n_denom / (int32_t)4; }

移动平均是最简单的时域滤波方法,能有效平滑信号而不引入相位延迟。选择4点平均是在计算复杂度和滤波效果间的折中。

1.3 差分运算与汉明窗应用

为突出信号变化特征,算法计算相邻采样点的差分:

for (k = 0; k < BUFFER_SIZE - MA4_SIZE - 1; k++) an_dx[k] = (an_x[k+1] - an_x[k]);

差分信号再经过汉明窗加权,减少频谱泄漏:

const uint16_t auw_hamm[31]={ 41, 276, 512, 276, 41 }; // 汉明窗系数 for (i = 0; i < BUFFER_SIZE - HAMMING_SIZE - MA4_SIZE - 2; i++) { s = 0; for (k = i; k < i + HAMMING_SIZE; k++) s -= an_dx[k] * auw_hamm[k-i]; an_dx[i] = s / (int32_t)1146; // 归一化 }

汉明窗的对称结构和特定系数分布使其在频域具有较好的主瓣宽度和旁瓣衰减特性。

2. 心率计算:峰值检测与周期分析

心率检测的核心是识别PPG信号中的脉搏波特征点。MAX30102算法采用差分信号峰值检测结合阈值判定的方法。

2.1 自适应阈值设定

算法首先计算差分信号绝对值的平均值作为初始阈值:

n_th1 = 0; for (k = 0; k < BUFFER_SIZE - HAMMING_SIZE; k++) n_th1 += ((an_dx[k] > 0) ? an_dx[k] : ((int32_t)0 - an_dx[k])); n_th1 = n_th1 / (BUFFER_SIZE - HAMMING_SIZE);

这种自适应阈值方法能适应不同信号强度,比固定阈值更具鲁棒性。

2.2 峰值检测算法

maxim_find_peaks函数实现完整的峰值检测流程:

void maxim_find_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num) { maxim_peaks_above_min_height(pn_locs, pn_npks, pn_x, n_size, n_min_height); maxim_remove_close_peaks(pn_locs, pn_npks, pn_x, n_min_distance); *pn_npks = min(*pn_npks, n_max_num); }

该函数分三步工作:

  1. 找出所有高于阈值的候选峰值
  2. 去除距离过近的冗余峰值
  3. 限制最大峰值数量

2.3 心率计算与验证

根据检测到的峰值间隔计算心率值:

if (n_npks >= 2) { for (k = 1; k < n_npks; k++) n_peak_interval_sum += (an_dx_peak_locs[k] - an_dx_peak_locs[k-1]); n_peak_interval_sum = n_peak_interval_sum / (n_npks - 1); *pn_heart_rate = (int32_t)(6000 / n_peak_interval_sum); // 转换为BPM *pch_hr_valid = 1; } else { *pn_heart_rate = -999; *pch_hr_valid = 0; }

这里6000是采样率(100Hz)与分钟转换因子(60秒)的乘积。算法要求至少检测到两个有效峰值才计算心率,否则标记为无效。

3. 血氧饱和度计算:AC/DC比值法

血氧饱和度计算基于红光和红外光信号的不同吸收特性。含氧血红蛋白和脱氧血红蛋白对这两种光的吸收比例不同。

3.1 信号谷值精确定位

首先在粗略估计的谷值位置附近寻找精确的最低点:

for (k = 0; k < n_npks; k++) { un_only_once = 1; m = an_ir_valley_locs[k]; n_c_min = 16777216; // 2^24 if (m+5 < BUFFER_SIZE-HAMMING_SIZE && m-5 > 0) { for (i = m-5; i < m+5; i++) { if (an_x[i] < n_c_min) { un_only_once = 0; n_c_min = an_x[i]; an_exact_ir_valley_locs[k] = i; } } if (un_only_once == 0) n_exact_ir_valley_locs_count++; } }

这种局部搜索方法能克服移动平均导致的波形偏移问题,准确定位每个心跳周期的起点。

3.2 AC/DC分量计算

在两个相邻谷值之间计算红光和红外信号的AC和DC分量:

for (k = 0; k < n_exact_ir_valley_locs_count - 1; k++) { n_y_dc_max = -16777216; n_x_dc_max = -16777216; if (an_exact_ir_valley_locs[k+1]-an_exact_ir_valley_locs[k] > 10) { for (i = an_exact_ir_valley_locs[k]; i < an_exact_ir_valley_locs[k+1]; i++) { if (an_x[i] > n_x_dc_max) { n_x_dc_max = an_x[i]; n_x_dc_max_idx = i; } if (an_y[i] > n_y_dc_max) { n_y_dc_max = an_y[i]; n_y_dc_max_idx = i; } } // 计算红光AC分量 n_y_ac = (an_y[an_exact_ir_valley_locs[k+1]] - an_y[an_exact_ir_valley_locs[k]]) * (n_y_dc_max_idx - an_exact_ir_valley_locs[k]); n_y_ac = an_y[an_exact_ir_valley_locs[k]] + n_y_ac / (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k]); n_y_ac = an_y[n_y_dc_max_idx] - n_y_ac; // 计算红外AC分量(类似红光计算) // ... // 计算比值 n_nume = (n_y_ac * n_x_dc_max) >> 7; n_denom = (n_x_ac * n_y_dc_max) >> 7; if (n_denom > 0 && n_i_ratio_count < 5 && n_nume != 0) { an_ratio[n_i_ratio_count] = (n_nume * 20) / n_denom; n_i_ratio_count++; } } }

AC分量反映脉动变化,DC分量反映总体吸收。右移7位(>>7)相当于除以128,是定点数优化技巧。

3.3 查表法获取SpO2值

为减少计算量,算法使用预先计算的查找表将AC/DC比值转换为SpO2值:

const uint8_t uch_spo2_table[184] = { 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, // ... 表格数据省略 ... 1 }; // 取中值 maxim_sort_ascend(an_ratio, n_i_ratio_count); n_middle_idx = n_i_ratio_count / 2; if (n_middle_idx > 1) n_ratio_average = (an_ratio[n_middle_idx-1] + an_ratio[n_middle_idx]) / 2; else n_ratio_average = an_ratio[n_middle_idx]; // 查表 if (n_ratio_average > 2 && n_ratio_average < 184) { n_spo2_calc = uch_spo2_table[n_ratio_average]; *pn_spo2 = n_spo2_calc; *pch_spo2_valid = 1; } else { *pn_spo2 = -999; *pch_spo2_valid = 0; }

查表法避免了实时计算复杂数学表达式,显著降低了对处理器性能的要求。表格数据基于以下公式预先计算:

SpO2 = -45.060 * ratio² + 30.354 * ratio + 94.845

4. 工程实践中的优化技巧

在实际应用中,MAX30102算法还需要考虑多种工程因素才能获得稳定可靠的测量结果。

4.1 动态范围调整

信号强度会因传感器佩戴松紧、皮肤特性等变化。算法包含自动调整逻辑:

un_min = 0x3FFFF; // 初始最小值 un_max = 0; // 初始最大值 // 更新信号范围 for(i = 0; i < n_ir_buffer_length; i++) { if(un_min > aun_red_buffer[i]) un_min = aun_red_buffer[i]; if(un_max < aun_red_buffer[i]) un_max = aun_red_buffer[i]; }

动态跟踪信号范围可适应不同测量条件,避免信号饱和或信噪比过低。

4.2 运动伪迹处理

运动是影响PPG信号质量的主要因素。算法通过多种策略提高鲁棒性:

  1. 滑动窗口处理:保留最新400个样本,与新增100个样本组合计算
for(i = 100; i < 500; i++) { aun_red_buffer[i-100] = aun_red_buffer[i]; aun_ir_buffer[i-100] = aun_ir_buffer[i]; }
  1. 有效性验证:检查心率和SpO2值是否在生理合理范围内
if((ch_hr_valid == 1)) { if(n_heart_rate < 120) dis_hr = n_heart_rate; else dis_hr = 0; dis_spo2 = n_sp02; }
  1. 多周期平均:计算5个连续周期的比值取中值,减少瞬时干扰影响

4.3 低功耗优化

对于电池供电设备,算法设计考虑了功耗优化:

  1. 采样率选择:默认100Hz平衡了精度与功耗
#define FS 100 // 采样率
  1. 间歇工作模式:非连续测量时可配置传感器进入低功耗状态
max30102_Bus_Write(REG_MODE_CONFIG, 0x40); // 复位/低功耗模式
  1. LED电流控制:根据信号质量动态调整LED驱动电流
max30102_Bus_Write(REG_LED1_PA, 0x24); // 红光LED电流~7mA max30102_Bus_Write(REG_LED2_PA, 0x24); // 红外LED电流~7mA
http://www.jsqmd.com/news/996119/

相关文章:

  • 从PSG到FSG:聊聊芯片里那些“玻璃”层是怎么用CVD“吹”出来的
  • 给Linux驱动开发者的PCI配置空间Header实战指南:手把手教你读懂BAR、中断与命令寄存器
  • 广州番禺黄金回收哪家好?金小福24小时上门服务口碑佳 - 花生花生1
  • 面试官连环问:从滑动窗口到拥塞控制,TCP如何保证可靠传输?一次讲清
  • 西林瓶自动装盘机中倒瓶检测算法的优化:从光电对射到激光测距的工程实践
  • Moneta Markets亿汇:注重效率的使用者更在意的市场覆盖,这里做个路径分析
  • 2026年海棠树苗选购指南:从品种到产地,一次说清! - 优质品牌商家
  • ChromePass:当你忘记密码时,你的浏览器记得
  • 综合演练科目支撑系统 统筹演练全流程
  • 别再只弹alert了!用XSS_labs靶场实战,手把手教你挖掘Cookie窃取、钓鱼等真实危害
  • 告别Transformer的O(L²)噩梦:手把手教你用PyraFormer搞定超长序列预测
  • 2026深圳App/软件定制公司怎么选?五大维度避坑指南(附 5 家参考名单)
  • League Akari:英雄联盟客户端自动化工具包终极指南
  • 智能图像分层终极指南:5分钟从单图到专业PSD的完整教程
  • 2026年粮仓空调行业深度观察:主流厂商技术路线与选型指南! - 优质品牌商家
  • Python 高手编程系列三千四百三十六 :命名和使用
  • 如何免费解锁Microsoft 365完整功能:Ohook激活方案完全指南
  • 2026年精酿啤酒招商加盟市场深度分析:轩博精酿领跑平价赛道,如何选对合作品牌? - 优质品牌商家
  • 别再只盯着快充功率了!一文搞懂USB PD协议里那个默默干活的‘策略引擎’(Policy Engine)
  • 别再只看跑分了!聊聊那些真正影响你NVMe SSD游戏加载和文件传输速度的‘隐形杀手’
  • 2026年口碑好的旧房翻新企业盘点:技术、服务与案例深度剖析 - 优质品牌商家
  • 信奥赛C++提高组csp-s之Dijkstra算法(朴素版)
  • 从用户体验出发:优化微信小程序双验证码登录的3个关键点(防刷与易用性平衡)
  • 2026年长城雪茄购买渠道全解析:从成都到香港,哪里买更靠谱? - 优质品牌商家
  • 骁龙X2 Elite边缘AI应用开发实战(3): 端侧智能语音助手全链路实现
  • Spring Boot 实现过滤器(Filter)三种常用方式
  • 2026年新发布针织衫品牌厂商有哪些?实力工厂的选型与推荐 - 品牌鉴赏官2026
  • 避开OV5640时钟配置的坑:PCLK计算不准导致图像异常的排查与修复指南
  • ComfyUI-LTXVideo:零基础到专业级AI视频生成的终极指南
  • OpenClaw+AWS 深度应用:自动生成 CloudFormation 模板、批量管理 S3 存储桶