news 2026/9/4 7:56:49

基于STM32的嵌入式健康监测终端:体温、心率与跌倒检测实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32的嵌入式健康监测终端:体温、心率与跌倒检测实战

简介:面向嵌入式开发与健康监测场景的STM32完整工程,基于STM32微控制器实时采集体温、心率,并利用加速度计/陀螺仪(MPU6050)实现老人跌倒检测,数据可通过串口、蓝牙或Wi-Fi上报,适用于居家看护、养老院监护、远程医疗预警等场景。压缩包共230个文件,约29.37MB,主要包含C/H源码、Keil工程配置、编译生成的AXF/HEX固件,以及传感器驱动、LCD显示和定时器相关代码,可帮助学习者快速理解多传感器融合与嵌入式数据上报流程。已有300人学习下载。资源内除了完整工程代码,还提供心率PPG/ECG处理思路、跌倒判断逻辑、通信协议设计等关键模块,适合作为课程设计、毕业设计或产品原型参考,便于在此基础上进行二次开发和功能扩展。

1. 从一颗传感器到一整套看护方案:这个项目到底在做什么

先说结论:这不是一个"实验室里跑通Demo"就完事的项目,而是一套可以部署到养老院、独居老人家庭或康复病房的嵌入式健康监测终端。核心功能三件事——实时测量体温、采集心率数据、识别老人跌倒动作,全部跑在一块STM32上,不上云也能本地独立工作。

我为什么强调"本地独立工作"?因为市面上很多所谓健康监测设备,一旦断网就成了板砖,传感器数据全走云端处理。而STM32方案的优势恰恰在于:所有信号采集、滤波、特征判断都在MCU内部完成,只有最终报警信息才需要对外传输。这对于网络条件不稳定的养老场景,反而更可靠。

整个系统的信号流大致是这样:体温传感器和心率传感器通过I2C或单总线把原始数据送进STM32,六轴加速度计(跌倒检测的核心器件)通过SPI或I2C持续输出三轴加速度和角速度。STM32内部跑三个并行任务——温度采集任务、心率滤波任务、跌倒识别算法——最后把状态汇总到屏幕和蜂鸣器。

这个项目适合谁来搞?

  • 正在学STM32但想做点"真正有社会价值"的项目的学生
  • 想给家里老人做一套低成本的监测设备、又不想买市面成品的人
  • 嵌入式开发者想练手传感器融合和状态机设计

如果你只会点灯、点数码管,这个项目正好把你往前推一大步:它涉及I2C、SPI、定时器中断、串口通信、ADC采集、低功耗设计——STM32的大部分核心外设,你都能碰到。

2. 传感器选型逻辑:为什么是这三颗芯片,而不是别的

选传感器是整条开发链路里最容易被新手低估的一环。很多人上来就买模块,看见引脚能接就焊,结果数据的噪声大得没法看,又找不到问题出在哪。我直接把常用方案摆个表格,说说我踩过的取舍。

功能模块可用传感器接口方式我的选择选择理由
体温测量DS18B20、MLX90614、NTC热敏电阻单总线 / I2C / ADCMLX90614非接触测量,更卫生,适合老人日常佩戴
心率采集MAX30102、PulseSensor、BH1790GLCI2C / ADCMAX30102集成红光+红外双LED,能算血氧,前期扩展空间大
跌倒检测MPU6050、MPU9250、LSM6DS3I2C / SPIMPU6050资料最多,六轴数据足够,价格便宜

先说体温。DS18B20是很多教程的首选,便宜、单总线、不用校准。但它有个致命问题——属于接触式测量,放在身上会被环境温度慢慢"掰弯",测出来的数据其实是"皮肤加热了传感器"的结果。MLX90614是红外测温,对准手腕动脉区就能读,不接触皮肤,误差在±0.2°C左右,而且它的SMBus协议和I2C几乎兼容,STM32标准库直接能驱动。

心率方面我不推荐模拟输出的PulseSensor。因为它输出的是模拟电压,要用ADC采集,而ADC的噪声很容易干扰心率信号的峰值识别。MAX30102直接输出数字信号,内部自带滤波逻辑,可配置采样率,I2C接口读取寄存器值就行。而且它带绿光LED,绿光对血液吸收率比红光高,穿透浅表层血管的能力更适合手腕佩戴式测量。

重点说MPU6050。这颗六轴芯片(三轴加速度+三轴陀螺仪)简直是为跌倒检测量身定做的。加速度计能感知重力方向变化,陀螺仪能感知身体旋转角速度。摔倒的过程在物理意义上非常明确:先是自由落体般失重,加速度数值骤降,然后猛烈撞击地面,加速度瞬间飙升,最后人躺倒在地,身体姿态从竖直变为水平。

我还考虑过MPU9250,多了一个磁力计,看起来更高级。但对跌倒检测来说,磁力计除了增加代码量毫无用处——检测靠的是加速度和角速度,不是方向和航向。多花钱买用不上的功能,不如把省下的钱花在传感器的滤波电路上。

3. 硬件电路与PCB设计避坑实录

传感器的模数转换、信号调理这些电路如果没弄好,后端代码怎么写都救不回来。我直接说我踩过最疼的两个坑。

3.1 MLX90614的供电纹波问题

第一次焊完板子,MLX90614读出来的温度在33°C到41°C之间乱跳,以为是I2C时序问题,折腾了两天驱动代码。最后用示波器一看,电源线上有200mV的纹波,导致MLX90614内部ADC的参考电压也跟着抖。

解决办法很简单:在MLX90614的VCC和GND之间加一个10μF的钽电容和一个0.1μF的陶瓷电容并联。如果还想更稳,就把传感器单独用一颗LDO(我用的AMS1117-3.3)供电,别跟电机、蜂鸣器共用电源平面。实测共用电源时数据波动±0.5°C,独立供电后稳定在±0.1°C。

3.2 MPU6050的中断引脚与时钟同步

MPU6050有一个INT引脚,数据准备好后会拉高通知MCU读取。很多教程直接把这个引脚悬空,主循环轮询读取,虽然功能能实现,但时序上容易读错。因为轮询读取时,可能刚好读到新旧数据各一半的状态,表现为数据跳变。

我建议把INT引脚接到STM32的外部中断引脚(比如PA1),配置为上升沿触发。这样一来,每次数据更新时MPU6050会主动"敲门",MCU在中断里读取,保证读到的是一帧完整的数据。这个做法需要把I2C配置为主模式,速率设置在400kHz以下,否则MPU6050容易通信超时。

还有一个细节:MPU6050的I2C地址是0x68,但如果AD0引脚接高电平就变成0x69。很多人在这翻车,明明代码没错,就是找不到设备。直接把AD0接地或者悬空,固定用0x68地址。

3.3 蜂鸣器和继电器的干扰隔离

这个项目的上电自检逻辑里,蜂鸣器会响一声。第一次整体调试时,蜂鸣器一响,屏幕上的心率数值直接跳空一拍。查了半天,原来是蜂鸣器是感性负载,开关瞬间产生反向电动势,顺着电源线和地线干扰了MAX30102的I2C通信。

解决手段是两层:蜂鸣器并联一个1N4148二极管(续流二极管),方向反接,吸收反向电动势;电源入口加一个磁珠和10μF电容构成的π形滤波器。从那以后,蜂鸣器再怎么响,传感器数据都纹丝不动。这个经验直接套用到你的项目里,能省一个礼拜的调试时间。

4. 定时器分时复用:一个定时器干三件事的调度策略

STM32的定时器资源看着不少,但真做项目时就发现不够用:温度采集要周期刷新、心率传感器要定时读取和滤波、跌倒检测需要高频(100Hz)读取加速度数据、显示屏要刷新、蜂鸣器要控制。如果每件事都占一个定时器,F103C8T6这种主流芯片立马就不够用了。

我的做法是一个TIM2搞定所有周期任务,用"时基+任务标记"的方式做时间片轮转调度。这个思路在裸机开发里非常实用,不依赖RTOS,代码量反而更小。

具体操作:TIM2配置为1ms中断(72MHz主频下,预分频72-1,自动重载1000-1),每进一次中断执行tick++。然后在主循环里判断全局的tick变量:

extern volatile uint32_t tick; #define TEMP_PERIOD 1000 // 温度采集周期:1秒 #define HEART_PERIOD 50 // 心率传感器读取:20Hz #define FALL_PERIOD 5 // 跌倒检测:200Hz读加速度 uint8_t temp_flag = 0; uint8_t heart_flag = 0; uint8_t fall_flag = 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { tick++; if (tick % TEMP_PERIOD == 0) temp_flag = 1; if (tick % HEART_PERIOD == 0) heart_flag = 1; if (tick % FALL_PERIOD == 0) fall_flag = 1; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } int main(void) { while (1) { if (temp_flag) { temp_flag = 0; read_temperature_task(); } if (heart_flag) { heart_flag = 0; read_heart_rate_task(); } if (fall_flag) { fall_flag = 0; read_fall_task(); } // 其余时间跑显示刷新和按键扫描 } }

这里有个关键点:主循环的任务必须短平快,不能阻塞。例如读取温度时I2C通信等待时间较长,那就分两次读——先发出读取命令,下次轮询时再取结果。如果主循环卡在某个传感器的等待循环里几百毫秒,其他任务的定时就全乱套了。

我在实际测试中发现,用这个分时复用调度后,跌倒检测的采样率能稳定在200Hz,心率传感器读取稳定在20Hz,温度刷新1秒一次,三件事互不干扰,CPU占用率还不到30%。

5. 跌倒检测核心算法:差分阈值+姿态判定的实战经验

跌倒检测是整个项目算法含量最高的部分,也是最容易"看起来能做、实际根本不准"的部分。光看加速度的绝对值是远远不够的——老人弯腰捡东西、快速坐下、被人搀扶时的加速度变化,跟跌倒看起来非常像。

我的做法是两段式判定,先看运动强度,再看姿态变化。具体拆成三个判据:

**判据一:合加速度突变。**用公式A = sqrt(Ax² + Ay² + Az²)算出合加速度。正常情况下合加速度接近1g(重力加速度),跌倒时先失重(A远小于1g),再撞击(A远大于1g)。判定条件设定为:先检测到A小于0.4g持续至少50ms,随后1秒内A超过2.5g的峰值。这个阈值是我拿20组模拟数据调出来的,不同身高体重的人差异不大,比较普适。

**判据二:姿态角翻转。**跌倒后人体大概率从竖直变成水平。用加速度计分量计算倾斜角,pitch = atan2(Ay, sqrt(Ax² + Az²)),当角度从竖直(接近90°)快速变化到水平(小于30°)时,判定为一次姿态翻转。这里有个细节——必须用低通滤波后的角度。MPU6050原始数据小抖动很大,直接算角度会出现突然翻转的假象。我用滑动平均窗口(窗口长度16,约80ms)把角度曲线磨平后再判断。

**判据三:静止确认。**跌倒在地后,如果只是瞬间的冲击,人可能马上就坐起来了,这种情况不需要报警。所以算法最后一步是"倒地后3秒内,合加速度一直稳定在1g附近(排除继续剧烈运动)",才确认是"跌倒后未起身"状态。

三个判据按顺序执行,形成一个简单的状态机:正常 → 疑似失重 → 撞击确认 → 姿态翻转 → 静止躺倒 → 触发报警。任意一步超时未满足,状态机自动复位回"正常"。

这套算法实测下来,对"走路滑倒""从椅子跌坐""床上滚落"三种场景都能正确识别,误报率大约百分之几,主要是快速弯腰捡东西后没立刻直起身的情况。这个可以通过提高姿态翻转的阈值角度(从30°降到20°)来规避,但也可能漏掉一些"跌坐后身体仍然前倾"的场景——阈值要按实际使用环境调整,没有一个放之四海而皆准的参数

6. 心率数据为什么不可信:信号质量的判断与滤波处理

如果你直接把MAX30102的原始数据画出来,那基本是一条疯狂抖动的曲线。要从中算出准确的心率,得做三件事:滑动平均滤波、峰值检测、还有最关键的信噪比判断。

MAX30102的原始数据是红外光透过毛细血管后的吸收量,心脏泵血时血量变化会在波形上形成一个小峰。这个峰非常微弱,很容易被手指移动、环境光干扰盖掉。我用的处理流程是这样的:

首先做滑动平均滤波。窗口长度取多少很关键——窗口太短滤不掉高频噪声,窗口太长会把心率信号的细节也磨没。按照心率范围60-150次/分,主频大约1-2.5Hz,采样率如果是20Hz,那一个波形周期大约有8-20个采样点。滑动平均窗口取5,能在保留波形峰谷特征的前提下滤掉大部分高频抖动。

然后是峰值检测。我用的方法是"求导+阈值":计算相邻采样点的差值,当差值的符号从正变负、且当前值超过一个动态阈值时,认为是一个波峰。动态阈值怎么定?我是每3秒更新一次,取这3秒内振幅最大值的60%作为这轮检测的阈值。这样即便手轻微晃动导致振幅变化,峰谷检测都能自适应调整。

但还有一个更大的坑——很多情况下,波形根本没有规律。比如老人手凉、末梢血液循环差的时候,MAX30102的红外波形几乎是平的,随机噪声都比你血管搏动的信号大。如果这时还硬算心率,算出来的数完全是瞎猜的。所以一定要加一个信号质量判断模块:

uint8_t check_signal_quality(uint16_t *buffer, uint16_t len) { uint16_t max_val = 0, min_val = 0xFFFF; for (uint16_t i = 0; i < len; i++) { if (buffer[i] > max_val) max_val = buffer[i]; if (buffer[i] < min_val) min_val = buffer[i]; } uint16_t amplitude = max_val - min_val; // 振幅低于300个ADC码值,认为信号不可信 if (amplitude < 300) return 0; return 1; }

这个振幅阈值300是我调出来的经验值——正常佩戴时MAX30102的红外ADC数值在几万范围内,红光通道波形峰谷差在500-1500之间。如果你发现连300都过不了,大概率是佩戴太松或手指冰凉,此时不再计算心率,直接在屏幕上提示"请重新佩戴",而不是显示一个荒谬的数字。宁可不显示,也不显示假数据——这一点在医疗健康类项目里比什么都重要。

7. 一个让我排查了两天的Bug:I2C通信被中断抢占

这个项目的整体调试还算顺利,唯一让我头疼了两天的问题是:心率数据和温度数据偶尔会出现"瞬间跳变",甚至直接读到0xFF这种错误字节。因为两个传感器的数据我都放在中断里读取,理论上不该出这种事,但复现时发现,只要蜂鸣器报警(跌倒状态触发),I2C读取就立刻乱套。

排查过程是这样的:先怀疑是I2C时序被拉长导致超时,于是把I2C时钟从400kHz降到100kHz——问题依旧。然后怀疑是外部中断频繁触发导致I2C通信被打断,于是把外部中断优先级降到了最低——依然乱。最后才发现,问题出在MPU6050的INT引脚接线上:这个引脚和蜂鸣器控制引脚在PCB布局上挨得太近,蜂鸣器的2000Hz方波信号通过PCB走线间的寄生电容耦合到了INT线上,产生了大量假中断。

解决起来很直接:把MPU6050的INT引脚在软件上配置为内部上拉,同时把控制蜂鸣器的GPIO从默认的推挽输出改成开漏输出(外加外部上拉电阻)。改了之后,假中断完全消失,I2C读取错误率直接降到0。

这个教训的通用价值是:多传感器系统的"传感器间干扰",不一定来自电源,也可能来自GPIO之间的电磁耦合。调试时遇到诡异现象,先怀疑物理层,再怀疑协议层和代码层。顺序反过来,很可能白白浪费几天时间。

8. 数据可视化与后续扩展:屏幕显示、低功耗和无线传输的思路

系统做出来之后,显示层我用了0.96寸OLED(SSD1306),I2C接口,一页显示体温和心率,翻页显示姿态状态。这块屏幕在STM32平台上有现成的库,移植成本极低。但如果你想做更复杂的界面,比如波形显示,OLED就不够用了,可以考虑换TFT-LCD或LVGL加持的屏幕。

功耗方面,整机在连续工作模式下电流大约80mA,其中屏幕占了20mA,传感器占了30mA,MCU自身30mA。如果做成便携设备(比如老人随身佩戴),这个功耗是不合格的。低功耗优化的基本思路是:让传感器间歇工作,比如每5分钟测一次体温和心率,每次测10秒后进入待机;摔倒检测模块保持高频工作(毕竟这个不能停);MCU用停机模式,靠RTC定时唤醒。用这个方案,一块1000mAh的锂电池可以撑一周左右。

无线传输方面,如果要把数据传给手机或后台,我建议用ESP8266或ESP32做WiFi模块,STM32通过串口把传感器数据发给它,走MQTT协议上云。这里有个要注意的问题:STM32和ESP8266串口通信时,波特率不要设太高,115200在长距离传输下容易丢字节,9600太慢传不了高频率的数据波形,57600是我实测比较稳定的折中值。

这套系统如果继续往下做,可以加GPS定位(跌倒时带上位置信息)、加ECG心电模块(做更全面的心脏健康评估)、加语音播报(跌倒时自动呼叫老人或家属)。从项目完成度来说,先把手上的三个模块做扎实、数据做稳定,比贪多求全更有价值——这也是我个人做嵌入式项目最深的体会。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 1:40:23

Excel多条件筛选全攻略:从基础操作到函数公式与自动化实践

1. 先搞清楚“多条件筛选”到底要解决什么问题很多人一听到“Excel多条件筛选”&#xff0c;第一反应就是去点那个漏斗图标&#xff0c;或者去学一堆复杂的函数。但实际工作中&#xff0c;真正卡住你的往往不是“会不会用”&#xff0c;而是“用哪个”和“怎么用才稳”。筛选数…

作者头像 李华
网站建设 2026/9/4 4:38:57

向量数据库与RAG实战:从Embedding到AI知识库的完整链路

很多人第一次接触“AI 知识库”这个概念时&#xff0c;都会有一个困惑&#xff1a;我明明已经把文档喂给大模型了&#xff0c;为什么它还是答不出来&#xff1f;我自己在最初做客服问答机器人时&#xff0c;也踩过同样的坑。把 PDF、Word、网页内容一股脑丢给大模型&#xff0c…

作者头像 李华
网站建设 2026/9/4 4:36:27

动态压枪技术原理与Python模拟实现:从游戏辅助到自动化测试

1. 这篇文章真正要解决的问题如果你是一名FPS游戏爱好者&#xff0c;或者正在寻找提升游戏内射击稳定性的方法&#xff0c;那么“动态压枪”这个词你一定不陌生。尤其是在《无畏契约》、《CS:GO》、《APEX英雄》这类对枪法要求极高的游戏中&#xff0c;后坐力控制是区分高手与普…

作者头像 李华
网站建设 2026/9/4 0:58:07

5个提升开发效率的Cursor Grok Bot实战指南:从代码审查到文档摘要

如果你在用 Cursor&#xff0c;并且已经试过基础的代码生成、代码解释&#xff0c;那接下来最该关注的可能不是“还能做什么”&#xff0c;而是“怎么让它更省时间”。Grok Bot 这个功能&#xff0c;很多人只是打开用一下&#xff0c;感觉和普通对话差不多就关掉了&#xff0c;…

作者头像 李华
网站建设 2026/9/3 20:41:33

msado15.dll 32位/64位版本解析与缺失修复指南

简介&#xff1a;msado15.dll 是微软 ADO 数据访问接口的核心动态链接库&#xff0c;面向使用 Windows 数据库开发的技术人员&#xff0c;集中收录了三十二位与六十四位不同版本&#xff0c;覆盖本地与远程数据库访问场景&#xff0c;可解决因架构不匹配、文件缺失或版本冲突导…

作者头像 李华
网站建设 2026/9/3 1:40:14

深度学习花书中文PDF与源码实战指南:从理论到项目落地

简介&#xff1a;这是一份《深度学习》&#xff08;俗称“花书”&#xff09;中文PDF资源的配套项目源码&#xff0c;面向深度学习初学者以及需要系统建立AI知识体系的读者。压缩包共收录3个文件&#xff0c;核心为index.html页面文件&#xff0c;另含.inscode配置和.gitignore…

作者头像 李华