基于STM32的心率计(2):R波识别算法设计

原创 电子电路开发学习 2024-06-25 21:13

前言

上一篇文章:基于uFUN开发板的心率计(一)DMA方式获取传感器数据,介绍了如何获取PulseSensor心率传感器的电压值,并对硬件电路进行了计算分析。心率计,重要的是要获取到心率值,本篇文章将介绍一种采样数据处理算法——动态阈值算法,来获取心率值,这种算法来自于一位网友:玩的就是心跳 —— 使用 PulseSensor 脉搏传感器测量心率(http://www.shaoguoji.cn/2017/06/29/use-pulsesensor-with-stm32/),本文部分内容摘自这篇文章。

IBI和BPM

心率,指的是一分钟内的心跳次数,得到心率最笨的方法就是计时一分钟后数有多少次脉搏。但这样的话每次测心率都要等上个一分钟才有一次结果,效率极低。另外一种方法是,测量相邻两次脉搏的时间间隔,再用一分钟除以这个间隔得出心率。这样的好处是可以实时计算脉搏,效率高。

IBI: 相邻两次脉搏的时间间隔,单位:s。

BPM:心率,一分钟内的心跳次数。即 BPM=6000/IBI

例如,在这张心率传感器输出信号的波形图中,可以计算出,两次波峰之间的时间为:0.685s,心率值为:60/0.685 = 87。

从网上找来的 arduino 开源算法复杂的一匹,看了一遍感觉一头雾水(反正我暂时没看懂)。由上面的分析可以得出,我们的最终目的就是要求出 IBI 的值,并通过 IBI 计算出实时心率。既然知道原理了那就自己来把算法实现吧。 有兴趣研究官方算法的朋友,可以下载:PulseSensor资料.rar。

核心操作 —— 识别一个脉搏信号

无论是采用计数法还是计时法,只有能识别出一个脉搏,才能数出一分钟内脉搏数或者计算两个相邻脉搏之间的时间间隔。那怎么从采集的电压波形数据判断是不是一个有效的脉搏呢?

显然,可以通过检测波峰来识别脉搏。最简单粗暴的方法是设定一个阈值,当读取到的信号值大于此阈值时便认为检测一个脉搏。似乎用一个 if 语句就轻轻松松解决。但,事情真的有那么简单么?

其实这里存在两个问题。

问题一:阈值的选取

作为判断的参考标尺,阈值该选多大?10?100?还是1000?我们不得而知,因为波形的电压范围是不确定的,振幅有大有小并且会改变,根本不能用一个写死的值去判断。就像下面这张图一样:

可以看出,两个形状相同波形的检测结果截然不同 —— 同样是波峰,在不同振幅的波形中与阈值比较的结果存在差异。实际情况正是如此:传感器输出波形的振幅是在不断随机变化的,想用一个固定的值去判定波峰是不现实的。

既然固定阈值的方法不可取,那自然想到改变阈值 —— 根据信号振幅调整阈值,以适应不同信号的波峰检测。通过对一个周期内的信号多次采样,得出信号的最高与最低电压值,由此算出阈值,再用这个阈值对采集的电压值进行判定,考虑是否为波峰。也就是说电压信号的处理分两步,首先动态计算出参考阈值,然后用用阈值对信号判定、识别一个波峰。

问题二:特征点识别

上面得出的是一段有效波形,而计算 IBI 只需要一个点。需要从一段有效信号上选取一个点,这里暂且把它称为特征点,这个特征点代表了一个有效脉搏,只要能识别到这个特征点,就能在一个脉搏到来时触发任何动作。

通过记录相邻两个特征点的时间并求差值,计算 IBI 便水到渠成。那这个特征点应该取在哪个位置呢,从官网算法说明可以看出,官方开源 arduino 代码的 v1.1 版本是选取信号上升到振幅的一半作为特征点,我们可以捕获这个特征点作为一个有效脉搏的标志,然后计算 IBI。

算法整体框架与代码实现

分析得出算法的整体框架如下:

  • 缓存一个波形周期内的多次采样值,求出最大最小值,计算出振幅中间值作为信号判定阈值

  • 通过把当前采样值和上一采样值与阈值作比较,寻找到「信号上升到振幅中间位置」的特征点,记录当前时间

  • 寻找下一个特征点并记录时间,算出两个点的时间差值,即相邻两次脉搏的时间间隔 IBI

  • 由 IBI 计算心率值 BPM

代码如下,程序中使用一个 50 长度的数组进行采样数据缓存,在主函数 while (1) 中以 20ms 的周期不断执行采样、数据处理,其中的条件语句 if (PRE_PULSE == FALSE && PULSE == TRUE) 就表示找到了特征点、识别出一次有效脉搏,串口输出心率计算结果。

  1. int main(void)

  2. {

  3. int i;

  4. LED_Init();

  5. delay_init(); //延时函数初始化

  6. UART1_Config(115200); //串口初始化为9600

  7. ADC1_Init();

  8. while(1)

  9. {

  10. preReadData = readData; // 保存前一次值

  11. // readData = GetPulseSensorValue(); // 读取AD转换值

  12. readData = 4095 - ADC_ConvertedValue; // 读取AD转换值


  13. if((readData - preReadData) < filter) // 滤除突变噪声信号干扰

  14. data[idx++] = readData; // 填充缓存数组


  15. if(idx >= DATA_SIZE)

  16. {

  17. idx = 0; // 数组填满,从头再填


  18. // 通过缓存数组获取脉冲信号的波峰、波谷值,并计算中间值作为判定参考阈值

  19. max = Get_Array_Max(data, DATA_SIZE);

  20. min = Get_Array_Min(data, DATA_SIZE);

  21. mid = (max + min) / 2;

  22. filter = (max - min) / 2;

  23. }


  24. PRE_PULSE = PULSE; // 保存当前脉冲状态

  25. PULSE = (readData > mid) ? TRUE : FALSE; // 采样值大于中间值为有效脉冲


  26. if(PRE_PULSE == FALSE && PULSE == TRUE) // 寻找到“信号上升到振幅中间位置”的特征点,检测到一次有效脉搏

  27. {

  28. pulseCount++;

  29. pulseCount %= 2;


  30. if(pulseCount == 1) // 两次脉搏的第一次

  31. {

  32. firstTimeCount = timeCount; // 记录第一次脉搏时间

  33. }

  34. if(pulseCount == 0) // 两次脉搏的第二次

  35. {

  36. secondTimeCount = timeCount; // 记录第二次脉搏时间

  37. timeCount = 0;


  38. if((secondTimeCount > firstTimeCount))

  39. {

  40. IBI = (secondTimeCount - firstTimeCount) * SAMPLE_PERIOD; // 计算相邻两次脉搏的时间,得到 IBI

  41. BPM = 60000 / IBI; // 通过 IBI 得到心率值 BPM

  42. if(BPM > 200) //限制BPM最高显示值

  43. BPM = 200;

  44. if(BPM < 30) //限制BPM最低显示值

  45. BPM = 30;

  46. }

  47. }

  48. // printf("B%d\r\n", BPM);

  49. printf("SIG = %d IBI = %d, BMP = %d\r\n\r\n", readData, IBI, BPM);

  50. }

  51. SIG = readData;

  52. // printf("S%d\r\n", SIG); // 上位机S数据发送

  53. timeCount++; // 时间计数累加

  54. delay_ms(SAMPLE_PERIOD); // 延时再进行下一周期采样

  55. if(i++ >= 50)

  56. {

  57. LED = !LED;

  58. i = 0;

  59. }

  60. }

  61. }

将传感器正面轻按在食指上,单片机在每检测到一个脉搏时打印心率值 BPM 和相邻两次脉搏的时间间隔 IBI,实测结果还算稳定。

注意事项:

  • 避免手指触碰传感器背面

  • 传感器与手指之间不要施加过大压力,否则会阻碍血液流动而读不到脉搏信号

  • 传感器与手指之间的接触要保持稳定,按压力度的轻微变化都会影响电压值

要获取到稳定的数据,可以胶布缠一下:

另外这种传感器还可以夹在耳垂下面:

总结

与许多可穿戴设备的心率传感器相比, PulseSensor 还存在很大差距,而自己写程序也仅仅是达到「勉强可用」的程度,输出数据偶尔还是会有大波动。代码也还有许多可改进的地方(比如将 20ms 的数据采样处理用定时器中断实现)。传感器采集到数据只是前提,对数据的处理才是一切应用的核心,不断地调整参数、改良算法也是整个过程中最有趣的部分。

电子电路开发学习 单片机点灯小能手,电子行业从业者。开发板评测、嵌入式开源项目分享、学习笔记记录。可能不会经常更新,但每一篇都是精心编写。
评论
  • Matter加持:新世代串流装置如何改变智能家居体验?随着现在智能家庭快速成长,串流装置(Streaming Device,以下简称Streaming Device)除了提供更卓越的影音体验,越来越多厂商开始推出支持Matter标准的串流产品,使其能作为智能家庭中枢,连结多种智能家电。消费者可以透过Matter的功能执行多样化功能,例如:开关灯、控制窗帘、对讲机开门,以及操作所有支持Matter的智能家电。此外,再搭配语音遥控器与语音助理,打造出一个更加智能、便捷的居家生活。支持Matter协议
    百佳泰测试实验室 2025-01-03 10:29 143浏览
  • 影像质量应用于多个不同领域,无论是在娱乐、医疗或工业应用中,高质量的影像都是决策的关键基础。清晰的影像不仅能提升观看体验,还能保证关键细节的准确传达,例如:在医学影像中,它对诊断结果有着直接的影响!不仅如此,影像质量还影响了:▶ 压缩技术▶ 存储需求▶ 传输效率随着技术进步,影像质量的标准不断提高,对于研究与开发领域,理解并提升影像质量已成为不可忽视的重要课题。在图像处理的过程中,硬件与软件除了各自扮演着不可或缺的基础角色,有效地协作能够确保图像处理过程既高效又具有优异的质量。软硬件各扮演了什么
    百佳泰测试实验室 2025-01-03 10:39 137浏览
  • 【工程师故事】+半年的经历依然忧伤,带着焦虑和绝望  对于一个企业来说,赚钱才是第一位的,对于一个人来说,赚钱也是第一位的。因为企业要活下去,因为个人也要活下去。企业打不了倒闭。个人还是要吃饭的。企业倒闭了,打不了从头再来。个人失业了,面对的不仅是房贷车贷和教育,还有找工作的焦虑。企业说,一个公司倒闭了,说明不了什么,这是正常的一个现象。个人说,一个中年男人失业了,面对的压力太大了,焦虑会摧毁你的一切。企业说,是个公司倒闭了,也不是什么大的问题,只不过是这些公司经营有问题吧。
    curton 2025-01-02 23:08 289浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 155浏览
  • 本文继续介绍Linux系统查看硬件配置及常用调试命令,方便开发者快速了解开发板硬件信息及进行相关调试。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。查看系统版本信息查看操作系统版本信息root@ido:/# cat /etc/*releaseDISTRIB_ID=UbuntuDISTRIB_RELEASE=20.04DISTRIB_CODENAME=focalDIS
    Industio_触觉智能 2025-01-03 11:37 137浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 119浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 164浏览
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 159浏览
  • 在测试XTS时会遇到修改产品属性、SElinux权限、等一些内容,修改源码再编译很费时。今天为大家介绍一个便捷的方法,让OpenHarmony通过挂载镜像来修改镜像内容!触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系统,适合鸿蒙开发入门学习。挂载镜像首先,将要修改内容的镜像传入虚拟机当中,并创建一个要挂载镜像的文件夹,如下图:之后通过挂载命令将system.img镜像挂载到sys
    Industio_触觉智能 2025-01-03 11:39 113浏览
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 173浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦