公司动态
基于STM32的人体健康监测系统:温度、心率与摔倒检测设计
简介一套面向老年人健康监护的STM32嵌入式系统完整工程包围绕体温、心率与老人摔倒检测三大核心功能展开。系统基于STM32F103微控制器通过温度传感器获取体温利用PPG/ECG方式采集心率并借助加速度计和陀螺仪完成跌倒识别检测异常后经串口、蓝牙或Wi-Fi上报适用于居家看护、养老院监护与远程医疗预警等场景。整个压缩包共230个文件约29.37MB含50个H头文件、47个C源文件以及Keil工程配置、AXF/HEX烧录文件、MAP映射、HTML说明文档和JPG原理图等代码结构清晰便于直接打开工程阅读、编译或烧录验证。目前已吸引300人学习使用适合嵌入式学习者系统练习STM32外设驱动、传感器数据融合与通信上报设计也可作为健康监测类课程设计或毕业设计的基础模板。1. 项目整体设计思路为什么选STM32系统怎么搭这两年接了不少嵌入式相关的私活其中健康监测类的需求一直很稳定尤其是给家里老人做便携式监测设备这个方向。这个项目基于STM32的人体健康监测温度、心率、老人摔倒检测本质上是一个典型的多传感器数据采集 轻量级信号处理 异常告警系统非常能锻炼一个人从选型到驱动再到算法的完整能力。如果你正准备做课设、毕设或者想拿一个完整的嵌入式项目去面试这套方案都很值得完整走一遍。先说为什么主控一定要选STM32而不是Arduino或者ESP32。Arduino上手快但遇到多路传感器同时工作、还要跑一点滤波算法和摔倒判定的场景它的实时性和资源分配就不太够用了。ESP32的优势在网络但这个项目如果不需要微信推送、APP远程查看纯本地监测的话ESP32反而显得冗余而且它的ADC精度和定时器资源在某些封装上并不算富裕。相比之下STM32F103系列有72MHz主频、3个USART、2个I2C、多个定时器、12位ADC外设接口丰富、价格便宜、资料极多正好能把这三种传感器的数据流同时管起来。我用的具体型号是STM32F103C8T664脚LQFP封装板子面积小适合做便携原型而且淘宝十来块钱一片烧了也不心疼。系统的整体架构其实不复杂核心就三条数据链路体温MLX90614红外测温模组通过I2C总线读温度数据非接触式适合老人床头或手环场景心率MAX30102血氧心率传感器通过I2C读PPG波形数据软件滤波后算心率值摔倒检测MPU6050六轴传感器通过I2C读加速度和角速度融合后做姿态解算再跑摔倒判定算法。三路传感器全挂在I2C总线上这就需要处理地址冲突和速率匹配的问题。MLX90614的I2C地址是0x5AMAX30102是0x57MPU6050是0x68AD0接地时三个地址互不冲突可以直接挂同一条总线。不过要注意I2C速率不要拉太高MLX90614比较挑时序标准模式100kHz最稳MAX30102和MPU6050也都能兼容。另外建议给3.3V主电源加一颗100uF的钽电容因为MAX30102点亮的瞬间电流会冲到几十毫安MPU6050上电也有一个不稳定窗口电容能避免电压跌落导致I2C通信偶发失败。如果只是把三路数据读出来然后串口打印那这项目也就值一个入门课的份量。真正有价值的是后面的算法部分尤其是摔倒检测的判断逻辑以及心率信号的预处理思路。下面我按传感器逐个拆开讲。2. 体温监测从传感器选型到标定细节体温检测这块市面上常见的方案有两类一类是DS18B20这种接触式数字温度计另一类是非接触式的红外测温方案。这个项目面向的是老人很多老人不会配合把传感器紧紧夹在腋下或含在嘴里所以我直接选了MLX90614红外测温模组隔空一照就能读到体表温度体验好得多。如果你打算做手腕佩戴式的设备红外方案几乎是唯一选择因为手腕皮肤温度和核心体温之间有固定差值可以通过标定补偿。2.1 红外测温方案与硬件接法MLX90614的这个红外模组拆开来看其实有两个核心部件一个红外热电堆传感器负责捕捉目标物体发出的红外辐射一个信号处理芯片负责把辐射量换算成温度值。它通过SMBus接口输出温度数据这个SMBus本质上就是I2C的变种时序上略有差异所以读它的时候建议用软件模拟I2C而不是硬件I2C这样时序更容易控制。我踩过坑STM32的硬件I2C在快速模式下兼容性一般和MLX90614通信时偶尔会出现ACK异常换成软件模拟之后一次都没出过问题。硬件接法很简单一共四根线VCC接3.3VGND接地SCL和SDA分别接STM32的两个普通GPIO口我这里用的是PB6、PB7但换成任意两个IO都行只要软件模拟时把引脚定义改掉。需要注意的是MLX90614的引脚顺序不同厂家模组可能不一样焊接前先看模组上的丝印别把SDA和SCL接反了接反不会烧但会通信超时排查起来很费时间。2.2 数据读取与发射率补偿读温度的步骤是这样的先向MLX90614的地址0x5A发送一个读命令读它的RAM地址0x07这个地址存的是物体温度原始数据。返回的数据是两字节的16位数据低位在前高位在后。关键计算来了把这16位数据乘以0.02得到的就是开尔文温度再减去273.15才转成摄氏度。举个例子如果读到的原始值是15202那温度就是15202 × 0.02 - 273.15 30.89摄氏度。这里最容易犯的错是忘了减273.15我见过不少人在这一步把体温算成了300多度。还有发射率问题。红外测温测到的其实是物体表面辐射的温度而辐射能力和物体表面的发射率直接相关。MLX90614出厂默认发射率是0.95这是为人体皮肤设定的经验值如果你用它去测一杯水或者一块金属板读数偏差会很大。测人体时0.95基本够用如果想更准可以写寄存器0x04修改发射率系数。此外环境温度MLX90614内部也有一个环境温度传感器读RAM地址0x06即可会对测量结果产生间接影响模组内部的自动补偿算法已经处理了一部分但环境温度剧烈变化时比如从室外拿进室内还是建议等一两分钟让模组热平衡后再读数。补充一个用ADC测温的备选方案如果你手头没有MLX90614用NTC热敏电阻 STM32内置ADC也能实现接触式测温原理是NTC阻值随温度变化通过电阻分压电路把阻值变化转化为电压变化再用ADC采样最后查表或套用Steinhart-Hart方程算温度。这个方案精度不错成本极低但缺点是响应慢而且必须是接触式老人体验差一些。3. 心率检测PPG信号的采样与滤波心率检测是这个项目里算法含量最高的部分也是最容易出幺蛾子的地方。我用的是MAX30102这种传感器是基于光电容积脉搏波描记法PPG原理工作的它内部集成了一组红光LED、一组红外光LED和一个光敏二极管。LED发出的光穿透皮肤组织一部分被血液吸收反射回来的光强度随血管搏动而周期性变化光敏二极管把这个变化转换成电信号再经内部ADC变成数字信号输出。简单说测的就是血液流过手指时对光的吸收率变化。3.1 MAX30102的驱动与配置要点MAX30102的配置入口在0x09寄存器这是模式配置寄存器写入0x03表示让红光和红外光同时工作SpO2模式如果只需要心率其实只开红外光就行写入0x02即可。这个模式下内部LED驱动电流默认是6.4mA我实测下来对肤色较深的人这个电流偏低波形幅度太小建议把0x0C寄存器里的LED电流调到10mA以上。还有一个重要配置是采样率0x0A寄存器控制采样率我推荐设为200Hz或400Hz这对应每秒200到400个采样点。心率信号本身只有1~3Hz200Hz的采样率已经足够再高反而数据量太大给后续滤波和显示都增加负担。驱动里最坑的一点是MAX30102有个FIFO数据缓冲区内部有32个采样点你必须一个个读出来而且读的时候要先读0x04寄存器拿到FIFO里的数据量然后从0x07寄存器连续读取。如果读得太快或太慢会出现数据错位或重复。我的做法是每次中断触发后先把FIFO里的数据全部读空存到本地数组里再统一处理而不是读一个处理一个。3.2 心率算法从原始波形到BPMPPG原始波形有个特点它叠加了一个很缓慢的基线漂移主要是呼吸引起的组织容积变化同时又有高频噪声环境光、工频干扰都会混进去。所以你直接拿原始波形找峰值出来的心率值会跳得离谱。我用的处理链条是先做滑动平均滤波消除高频毛刺窗口大小设5个采样点再做一个高通滤波去掉基线漂移一阶高通截止频率0.5Hz左右最后对滤波后的波形做峰值检测。峰值检测不能只找一个最大值必须设置一个动态阈值记录当前时刻之前2秒内的信号最大值和最小值阈值取两者的中值只有当信号从低于阈值穿越到高于阈值时才计一次心跳。同时还要加一个不应期保护检测到一次心跳后接下来的250毫秒内不再检测新的峰值否则双峰波形会被误判成两次心跳。心率值的计算不要用单次间隔太容易跳变。我的做法是缓存最近10个R-R间隔也就是相邻两次心跳的时间差取平均值后换算成每分钟心跳数。换算公式是BPM 60 / 平均间隔秒。比如平均间隔0.8秒心率就是75次/分钟。实测下来这种滑动平均处理后的心率在静息状态下波动能控制在正负2以内对于健康监测场景完全够用。这里必须提醒一句手指按压传感器的力度会直接影响波形质量。压得太紧血管被压扁血流信号反而减弱压得太松光泄漏严重环境光干扰增大。用透射式传感器手指夹住比反射式手指贴在表面更稳定MAX30102两者都支持但反射式对佩戴松紧度极其敏感这也是很多手环心率不准的根本原因。做原型验证时建议用指夹式结构后面再考虑集成到手环里通过结构设计固定压力。4. 摔倒检测六轴数据的姿态解算与判定逻辑摔倒检测是整个项目里最有技术含金量的部分因为它本质上是一个实时姿态识别 冲击判定的问题。传感器用MPU6050内置三轴加速度计和三轴陀螺仪。加速度计测的是物体受到的加速度包括重力陀螺仪测的是角速度。静止时加速度计的合成模长应该接近1g约9.8m/s²如果检测到短暂的远大于1g的加速度突变通常意味着发生了剧烈运动。4.1 MPU6050的数据融合读取MPU6050原始数据不难先读取加速度计和陀螺仪的原始寄存器值然后除以对应的灵敏度系数就能得到物理单位——加速度单位是g角速度单位是度/秒。但有个关键问题单独使用加速度计无法区分直线加速运动和倾斜单独使用陀螺仪又会产生积分漂移长时间使用角度会慢慢偏掉。所以必须做数据融合这里我用的是互补滤波算法原理很简单加速度计长期准确但短期噪声大陀螺仪短期准确但长期漂移那就取长补短——高频部分信陀螺仪低频部分信加速度计融合公式如下pitch 0.98 * (pitch gyro_y * dt) 0.02 * acc_pitch roll 0.98 * (roll gyro_x * dt) 0.02 * acc_roll这个公式里的0.98和0.02是权重系数dt是采样周期秒。如果你不想自己写融合可以直接用MPU6050内部的DMP数字运动处理器它会自己输出四元数或欧拉角省去不少功夫。但DMP的库文件比较大而且我对DMP的输出时机有疑虑——DMP的输出频率是固定的不一定匹配你的检测逻辑所以我更推荐自己写互补滤波代码量不大而且后续调参你心里有底。4.2 摔倒判定算法设计摔倒的物理过程一般分三阶段先是人体从直立状态快速倾斜或移动然后身体某一部位与地面碰撞产生一个瞬间的高冲击加速度最后人体倒地不动或以较低速度平移。我的判定算法基于这三个阶段的特征设计了三个条件必须同时满足才判为摔倒单条件触发不算冲击条件三轴加速度合成模长sqrt(ax² ay² az²)超过2.8g。正常走路时最大也就1.5g左右跑步抬脚时可能冲到2g但超过2.8g的冲击基本只在摔倒或剧烈撞击时出现。这个阈值可以根据使用对象的运动强度微调老人可以下调到2.5g。姿态条件摔倒后人体大概率是平躺或侧卧的此时pitch角或roll角的绝对值会超过50度。这个条件用来排除人蹲下捡东西这类加速度冲击不大但姿态变化明显的场景。静止条件摔倒后的2秒内加速度计的波动幅度很小合成模长稳定在0.8g到1.2g之间。这个条件用来排除人猛然站起来或拍球这类冲击大但不会静止的动作。这三个条件都满足后还要加一个5秒的延迟确认机制5秒内如果被监测者自己站了起来姿态恢复直立、加速度波动变大就取消告警如果5秒后仍然躺着不动才真正触发声光报警和串口告警。这个机制非常关键能大幅减少误报因为很多人摔倒后会本能地爬起来这种情况不需要紧急告警避免打扰家属。有一个反直觉的细节要特别说明慢速摔倒往往比快速摔倒更危险。老人骨质疏松很多时候是坐着滑到地上或者倚着墙慢慢倒下这种摔倒的加速度冲击远低于2.8g上面的判定条件根本不会触发。针对这种情况我额外加了一个低速倾倒检测分支如果姿态角在2秒内从接近0度缓慢变化到超过60度同时伴随一个0.4m/s²以上的水平加速度也视为疑似摔倒。这个分支会有一定误报率比如老人弯腰捡东西再站起来就可能触发所以这个分支的告警延时要更长15秒。这个思路是我在医院场景里和一位做康复技术的朋友聊天时受到的启发后来自己也觉得加上之后实用性提升很大。5. 整机联调、常见问题与避坑实录最后这部分我不按教程式的分步讲了直接整理成联调过程中最常遇到的几个问题。这些都是实际踩过的坑网上的教程很少会提到遇到的话排查思路会有帮助。5.1 常见问题速查现象可能原因排查与解决I2C总线上三个传感器只有一个能通信地址冲突或总线被拉死逐个检查地址是否相同SDA/SCL上拉电阻是否焊接建议4.7kΩ到3.3VMLX90614测出300多度忘减273.15的开尔文转换乘0.02后必须减273.15才是摄氏度MAX30102读出的波形是平的LED电流太小或传感器没贴紧皮肤调大0x0C寄存器的LED电流重新调整佩戴方式心率值偶尔翻倍或减半峰值检测阈值不合适采用滑动窗口动态阈值检查不应期设置是否合理MPU6050的pitch角会慢慢漂移陀螺仪零漂未做校准上电静止时采样200个角速度数据取平均作为零偏实时减去摔倒检测频繁误报阈值太低或没有考虑运动场景适当提高冲击阈值增加连续运动时暂停检测的逻辑接了三个传感器后系统偶发卡死中断优先级不当或I2C死锁给I2C通信加超时保护把传感器读取放在同一优先级避免嵌套中断程序烧录后OLED无显示引脚冲突或初始化顺序问题检查是否用了同一个GPIO做多种功能外设初始化顺序尽量统一5.2 个人调试心得联调顺序很有讲究。我的习惯是逐模块调通再合系统先把MLX90614读出来串口打印温度稳定运行半小时确认无偶发超时然后再加MAX30102把波形打印出来肉眼确认PPG信号周期正常最后才加MPU6050和摔倒判定算法。一次把三个传感器全接上然后查问题那基本是地狱模式串口里全是垃圾数据根本分不清是谁的问题。串口打印的调试信息也要讲究策略。开发阶段建议把原始数据、滤波后数据、判定结果全打印出来加上时间戳到了验证阶段就只打印关键状态比如温度、心率、姿态角、判定标志位。我一般用115200波特率数据量够而且这个波特率下串口发送的时序比较稳定不会因为中断打扰I2C通信。最后说一个经验之谈这个项目的算法参数不要一次调到位先用一个假人或者自己模拟动作做标定记录下不同动作的波形特征再根据这些数据调阈值。我试过直接在老人身上调参数一方面老人配合时间有限另一方面数据参考价值有限因为老人的动作幅度和年轻人差别很大。用自己先模拟比如故意缓慢躺倒、快速扑倒、蹲下捡东西、原地蹦跳每种动作记录20组数据再去设定阈值这样才能保证判定逻辑真的干得过各种日常动作的干扰。这个项目做完之后扩展空间其实挺大的加个GSM模块或WiFi模块就能把数据推送到手机加个GPS模块就能在摔倒告警时带上定位信息加个OLED屏幕就能让老人自己看到实时体温心率和姿态状态。每一步扩展都不算难但都需要处理好传感器数据这一层的稳定性这块地基打牢了上面的应用才好盖。本文还有配套的精品资源点击获取