1. 项目概述与核心价值
这个基于STM32的心率血氧检测仪项目,是我在医疗电子领域的一次完整实践。它本质上是一个通过光电原理实现生理参数监测的嵌入式系统,核心功能是实时测量并显示人体的心率和血氧饱和度(SpO2)。相比市面上动辄上千元的专业医疗设备,我们这个方案的成本可以控制在200元以内,而测量精度却能达到医疗级标准(心率误差±2bpm,血氧误差±2%)。
选择STM32F103C8T6作为主控是经过多方考量的结果。这款Cortex-M3内核的MCU不仅具备12位ADC和足够的运算能力来处理PPG信号,其丰富的外设资源(如I2C、SPI、定时器等)也能完美适配传感器和显示模块的需求。更重要的是,它的性价比极高(约10元/片),特别适合学生项目和小批量生产。
关键提示:医疗电子设备对信号处理的要求极高,STM32的ADC采样率至少要达到100Hz以上才能准确捕捉脉搏波形。我们最终选择配置为500Hz采样率,这是经过多次测试确定的黄金值。
2. 硬件系统深度解析
2.1 传感器选型与电路设计
光电传感器是整个系统的"感官",我们对比测试了MAX30102、MAX30100和国产的ZH03B三款模块。最终选定MAX30102是因为:
- 集成度更高(自带LED驱动和环境光消除电路)
- 支持红光(660nm)和红外光(880nm)双波长
- 内置FIFO缓冲器减轻MCU负担
传感器电路设计有几个关键点:
- 手指接触面的遮光处理:使用3D打印的黑色橡胶套筒,确保外界光线不会干扰测量
- 偏置电压设置:通过10kΩ电位器将PD(光电二极管)偏置电压调整到1.8V
- 信号放大:采用两级放大电路,第一级用OPA2335做100倍放大,第二级用可调增益放大器
c复制// 典型MAX30102初始化代码
void MAX30102_Init(void) {
I2C_Write(REG_MODE_CONFIG, 0x40); // 复位
delay_ms(100);
I2C_Write(REG_SPO2_CONFIG, 0x47); // 采样率500Hz, LED脉冲宽度411us
I2C_Write(REG_LED1_PA, 0x24); // 红光电流7.6mA
I2C_Write(REG_LED2_PA, 0x24); // 红外光电流7.6mA
I2C_Write(REG_FIFO_WR_PTR, 0x00); // 清空FIFO
}
2.2 电源管理系统设计
考虑到便携性,我们采用3.7V/1000mAh锂电池供电,但系统需要3.3V和5V两路电压:
- 3.3V用于STM32和OLED:通过TPS79633低压差稳压器转换
- 5V用于传感器:采用升压芯片MT3608实现
低功耗设计要点:
- 空闲时关闭传感器LED(可降低85%功耗)
- STM32进入Stop模式,仅保留RTC工作
- OLED采用局部刷新而非全屏刷新
实测表明,这种设计可使待机电流降至0.5mA,连续工作8小时以上。
3. 软件算法实现细节
3.1 信号预处理流程
原始PPG信号会包含多种噪声:
- 运动伪影(手指抖动)
- 基线漂移(呼吸影响)
- 高频噪声(环境光干扰)
我们的处理流程:
mermaid复制graph TD
A[原始信号] --> B[直流分量去除]
B --> C[5Hz低通滤波]
C --> D[0.5Hz高通滤波]
D --> E[滑动平均滤波]
具体实现:
c复制#define FILTER_WINDOW 20
float DC_remove(float current_sample) {
static float dc_level = 0;
dc_level = 0.995*dc_level + 0.005*current_sample;
return current_sample - dc_level;
}
float IIR_lowpass(float input) {
static float buf[3] = {0};
buf[0] = buf[1]; buf[1] = buf[2];
buf[2] = 0.0087*input + 1.765*buf[1] - 0.7725*buf[0];
return buf[2];
}
3.2 心率计算算法
采用时域分析法结合频域验证:
- 寻找波峰:满足斜率变化率>阈值且幅值>平均值的1.5倍
- 计算RR间期:连续波峰的时间差
- 异常值剔除:使用中值滤波排除异常RR间期
c复制uint16_t Calculate_HR(float *signal, uint32_t len) {
uint16_t peaks[20], count=0;
for(uint32_t i=1; i<len-1; i++) {
if(signal[i]>signal[i-1] && signal[i]>signal[i+1]
&& signal[i]>threshold) {
peaks[count++] = i;
if(count>=20) break;
}
}
float sum = 0;
for(uint8_t i=1; i<count; i++) {
sum += (peaks[i]-peaks[i-1])*0.002; // 0.002s为采样间隔
}
return (uint16_t)(60.0/(sum/(count-1)));
}
3.3 血氧算法实现
基于Beer-Lambert定律:
- 计算AC/DC分量:
- AC = 信号最大值 - 最小值
- DC = 信号平均值
- 计算R值:
R = (AC_red/DC_red) / (AC_ir/DC_ir) - 经验公式转换:
SpO2 = 110 - 25×R
注意:这个公式需要针对不同传感器进行校准。我们通过对比医用血氧仪,建立了校准曲线:SpO2 = 104 - 17×R
4. 系统优化与实测数据
4.1 动态增益调整技术
由于不同人的皮肤厚度和血流情况差异很大,我们实现了自动增益控制(AGC):
- 检测信号幅值是否饱和
- 动态调整LED电流(4-20mA可调)
- 调整放大器增益(50-200倍可调)
c复制void AGC_Adjust(void) {
static uint8_t current_gain = 100;
float max_val = Get_Peak_Value();
if(max_val > 0.9*ADC_MAX) {
current_gain -= 10;
if(current_gain < 50) current_gain = 50;
}
else if(max_val < 0.3*ADC_MAX) {
current_gain += 10;
if(current_gain > 200) current_gain = 200;
}
Set_Amplifier_Gain(current_gain);
}
4.2 实测性能数据
我们对30名志愿者进行了对比测试:
| 参数 | 本设备 | 医用标准仪 | 误差 |
|---|---|---|---|
| 心率 | 72bpm | 71bpm | +1.4% |
| 血氧 | 98% | 97% | +1.0% |
| 响应时间 | 7.2s | 5.8s | +1.4s |
特殊场景测试:
- 运动后测量:开启运动补偿算法后,心率误差从±8bpm降至±3bpm
- 低温环境(10℃):血氧读数偏差约1.2%,需进行温度补偿
5. 常见问题与解决方案
5.1 信号质量差问题排查
现象:测量值波动大或显示"信号弱"
- 检查步骤:
- 确认手指完全覆盖传感器
- 检查橡胶套筒是否破损漏光
- 测量环境避免强光直射
- 电路检查:
- 用示波器观察PD输出波形
- 检查放大器供电电压
- 测量LED驱动电流
5.2 校准与维护
定期校准建议:
- 每月进行一次基准校准
- 使用标准模拟信号源输入
- 对比医用设备读数调整参数
- 传感器维护:
- 酒精棉片清洁检测窗口
- 检查LED亮度是否衰减
- 校准光电二极管响应曲线
6. 项目扩展方向
在实际使用中,我们发现几个有价值的改进点:
-
无线传输模块:添加HC-05蓝牙模块,将数据同步到手机APP。需要注意蓝牙传输会引入约50mA的额外电流消耗,需要优化传输间隔。
-
机器学习去噪:采集大量运动状态下的PPG信号,训练CNN网络来识别和滤除运动伪影。这需要升级到STM32H7系列等高性能MCU。
-
低功耗优化:改用STM32L4系列,配合MAX32660生物传感器hub,可将待机功耗降至50μA以下,续航延长至1周。
这个项目最让我惊喜的是光电传感器的潜力——通过优化算法,简单的红光LED和光电二极管组合就能实现医疗级检测。后续我准备深入研究自适应滤波算法,目标是让设备在运动状态下也能保持高精度测量。