1、什么是心率血氧传感器
心率传感器是一种用于测量人体心跳频率的设备或传感器。由于脉搏或者心率是生命体征的重要参数之一,所以心率测量是目前可穿戴产品必备的一个测量和健康监控功能。
而血氧传感器是一种用于测量人体血液中氧气饱和度的设备或传感器。血氧饱和度具体是指血液中与氧气结合的血红蛋白含量占比,即血液中血氧的浓度。一般而言,若血氧饱和度在94%以下,就会被视为供氧不足,因此血氧检测对于临床医学而言十分重要。
图1 血氧检测数值的参考性
在当前智能穿戴产品中,心率和血氧的检测几乎已经成了标配功能,通常由一颗集成了多种传感器和处理功能的芯片完成。
图2 智能穿戴设备配备心率血氧检测功能
2、心率血氧传感器是如何工作的
心率测量在医疗领域通常式使用心电图ECG来测量生理电信号来实现心率和心脏活动的检测。但是测量ECG信号,要在身体多个部位连接传感器电极,很不方便,目前普遍应用在智能设备上的测试方法是光电容积脉搏波描记法PPG,简单说就是利用光测量脉搏的一种技术,通过发射一定波长的光波射向皮肤,比如绿光,由于血管组织在脉搏跳动过程中对于光线的吸收率不同,那么透过皮肤组织反射回的光被光敏传感器接受并转换成电信号并数字化后,根据血液的吸光率即可算出心率。
图3 光电容积脉搏波描记法PPG原理
而随着临床医学的发展,在血氧测量方面也普遍采用无创式血氧测量,只要为患者佩戴一个指压式光电传感器,就能实现连续性的血氧检测。
图4 指压式光电传感器测量血氧
其工作原理是利用人体中含氧血红蛋白HBO2以及血红蛋白HB对红光和红外光的吸收率不同的特点,使用波长660nm的红光和940nm的近红外光作为光源,测定通过测定人体组织的光传导强度,来计算血氧浓度及血氧饱和度。
图5 HBO2及HB对红光和红外光的吸收率
用智能手表及其他智能设备测血氧的原理类似于指压式测量,但不同的是,手表发射光源所照射的部分是手腕,并不像手指那样“透明”,可见光与红外光无法穿透,并且设备佩戴在手腕的位置也常常不固定,有时会受到外来光源以及汗水等因素的干扰,所以目前的智能穿戴的心率和血氧检测常常设计一个发射极,多个接收级的方式来增加接收数据的稳定性和可靠性。
图6 发射极(紫色)和接收级(绿色)数量对比
3、常见的心率血氧传感器的种类
由于心率和血氧在测量方式上的相似性以及两个参数的紧密相关,目前大多数的解决方案都是将血氧和心率功能测量集成在一块集成电路中提供出来。
图7 MAX86140 心率和血氧传感器芯片
这是一款由 ADI 提供的 MAX86140,专门为穿戴设备设计的心率和血氧传感器芯片。它的外形非常小巧,采用了紧凑的WLP封装,尺寸只有2乘1.8毫米。我们来看下它的原理框图。
图8 MAX86140 原理框图
在框图左侧是光学输出输入部分,它具备了三个独立的 8bit DAC 以及LED驱动模块,通过复用,可以驱动高达12个LED光源。其接收端采用了高分辨率的 19bit ADC 模块,在执行每秒25个采样的情况下,典型功耗只有不到10微安。
4、心率血氧传感器实验演示
我们来演示使用 MCU 读取显示心率血氧传感器的数据。实验使用测试模块中采用的是 ADI 的 MAX30102 心率血氧集成模块。
将手指贴在传感器上,经过一段时间,可以看到屏幕上的心率和血氧数据开始更新为当前测试数值。
这里需要注意手指要和测试点紧密贴合,如果手指和模块贴合不到位,会出现血氧测试数据偏低的可能,当前血氧SpO2的数值掉到了46%。
将手指紧贴模块一段时间,可以帮助传感器更准确的测量血氧,可以看到血氧测试数据开始升高,最终达到了稳定的96%左右,也就是身体正常血氧饱和度数值。
图9 树莓派读取心率血氧传感器展示
main.py:
import uos
import st7789_c as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
import random
import framebuf
from machine import Pin, SPI, ADC,PWM,I2C,SoftI2C,Timer
import time, math,array
from utime import sleep_ms,ticks_diff, ticks_us
import struct
###############################################
from MAX30102 import MAX30102, MAX30105_PULSE_AMP_MEDIUM
from spo2cal import calc_hr_and_spo2
###############################################
st7789_res = 0
st7789_dc = 1
disp_width = 240
disp_height = 240
CENTER_Y = int(disp_width/2)
CENTER_X = int(disp_height/2)
spi_sck=Pin(2)
spi_tx=Pin(3)
spi0=SPI(0,baudrate=40000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
display = st7789.ST7789(spi0, disp_width, disp_width,
reset=Pin(st7789_res, Pin.OUT),
dc=Pin(st7789_dc, Pin.OUT),
xstart=0, ystart=0, rotation=0)
display.fill(st7789.BLACK)
display.text(font2, "EETREE", 10, 10)
display.text(font2, "www.eetree.cn", 10, 40)
######################################################################
BEATS = 0 # 存储心率
FINGER_FLAG = False # 默认表示未检测到手指
SPO2 = 0 # 存储血氧
TEMPERATURE = 0 # 存储温度
def display_info(t):
# 如果没有检测到手指,那么就不显示
if FINGER_FLAG is False:
return
display.text(font1, "Heart Rate=", 10, 100)
display.text(font2, str(BEATS), 100, 90)
display.text(font1, "SpO2=", 10, 140)
display.text(font2, str(SPO2), 50, 130)
def main():
global BEATS, FINGER_FLAG, SPO2, TEMPERATURE # 如果需要对全局变量修改,则需要global声明
# 创建I2C对象(检测MAX30102)
i2c = SoftI2C(sda=Pin(20), scl=Pin(21), freq=400000) # Fast: 400kHz, slow: 100kHz
# 创建传感器对象
sensor = MAX30102(i2c=i2c)
# 检测是否有传感器
if sensor.i2c_address not in i2c.scan():
print("没有找到传感器")
return
elif not (sensor.check_part_id()):
# 检查传感器是否兼容
print("检测到的I2C设备不是MAX30102或者MAX30105")
return
else:
print("传感器已识别到")
# 配置
sensor.setup_sensor()
sensor.set_sample_rate(400)
sensor.set_fifo_average(8)
sensor.set_active_leds_amplitude(MAX30105_PULSE_AMP_MEDIUM)
t_start = ticks_us() # Starting time of the acquisition
MAX_HISTORY = 32
beats_history = []
beat = False
red_list = []
ir_list = []
history = []
while True:
sensor.check()
if sensor.available():
# FIFO 先进先出,从队列中取数据。都是整形int
red_reading = sensor.pop_red_from_storage()
ir_reading = sensor.pop_ir_from_storage()
if red_reading < 1000:
print('No finger')
FINGER_FLAG = False # 表示没有放手指
continue
else:
FINGER_FLAG = True # 表示手指已放
# 计算心率
history.append(red_reading)
# 为了防止列表过大,这里取列表的后32个元素
history = history[-MAX_HISTORY:]
# 提取必要数据
minima, maxima = min(history), max(history)
threshold_on = (minima + maxima * 3) // 4 # 3/4
threshold_off = (minima + maxima) // 2 # 1/2
if not beat and red_reading > threshold_on:
beat = True
t_us = ticks_diff(ticks_us(), t_start)
t_s = t_us/1000000
f = 1/t_s
bpm = f * 60
if bpm < 500:
t_start = ticks_us()
beats_history.append(bpm)
beats_history = beats_history[-MAX_HISTORY:] # 只保留最大30个元素数据
BEATS = round(sum(beats_history)/len(beats_history), 2) # 四舍五入
if beat and red_reading < threshold_off:
beat = False
# 计算血氧
red_list.append(red_reading)
ir_list.append(ir_reading)
# 最多 只保留最新的100个
red_list = red_list[-100:]
ir_list = ir_list[-100:]
# 计算血氧值
if len(red_list) == 100 and len(ir_list) == 100:
hr, hrb, sp, spb = calc_hr_and_spo2(red_list, ir_list)
if hrb is True and spb is True:
if sp != -999:
SPO2 = int(sp)
# 计算温度
TEMPERATURE = sensor.read_temperature()
if __name__ == '__main__':
tim = Timer(period=2000, mode=Timer.PERIODIC, callback=display_info)
main()
屏幕显示和MAX30102心率血氧传感器驱动可以点此查看。