news 2026/10/21 18:47:11

基于STM32单片机的手环跌倒检测系统:GSM短信报警与GPS北斗定位

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32单片机的手环跌倒检测系统:GSM短信报警与GPS北斗定位

爸妈年纪大了,最怕的就是一个人在家时磕着碰着没人知道。前阵子我做养老相关的硬件方案验证,最常被问到的一个问题是:能不能做一个老人随身带的小手环,万一摔倒了能自动通知家人,家人手机上还能看到位置。这个需求听起来复杂,但拆开技术点,就是标题里那几样东西的叠加——基于STM32单片机的手环跌倒检测系统,带GSM短信报警、GPS北斗定位、APP地图显示,再加一个按键控制的高亮灯手电筒。整套方案并不神秘:STM32做主控,加速度传感器判断跌倒动作,GSM模块把报警短信发出去,GPS/北斗模块把坐标塞进短信里,手机端APP把坐标落到地图上,按键和LED负责手电筒照明。不管你是做毕业设计、课程设计,还是想给家里老人做一款守护设备原型,这条链路都有参考价值。下面我从选型到代码,从短信指令到坐标纠偏,按实际能落地的思路完整讲一遍。

1. 功能拆解与硬件选型:先别急着焊板子

1.1 从标题里拆出真正的功能模块

标题虽然很长,但信息密度很高。我把它拆成四个核心任务:

功能实现模块核心要点
跌倒检测三轴/六轴加速度传感器采集加速度,识别摔倒冲击与姿态变化
短信报警GSM模块AT指令控制发送报警短信
定位GPS/北斗双模定位模块NMEA协议解析经纬度、时间、定位状态
地图显示手机APP接收位置并在地图SDK上展示
手电筒高亮LED + 按键 + MOS驱动按键控制大功率LED通断、亮度

看清楚模块清单后再谈选型,否则很容易买一堆用不上的器件。实际项目中,手电筒看起来很简单,但它的硬件驱动和按键逻辑,反而和跌倒检测一样需要仔细设计。

1.2 主控为什么选STM32

这套系统的外设需求不算复杂:一路I2C读加速度传感器,两路UART分别接GSM模块和定位模块,几个GPIO接按键和LED,最多再来一个UART接调试串口。随便找一颗满足这些外设的单片机都能做,但STM32F103C8T6几乎成了这类项目的默认答案。

原因很现实:一是便宜,十几块钱的芯片足够跑完整套逻辑;二是资料多,无论是寄存器操作还是HAL库,踩坑经验在论坛里一抓一大把;三是功耗和性能平衡,72MHz的主频做合加速度计算、状态判读绰绰有余,20KB RAM在合理裁剪代码时也够用。

有人会问,用Arduino不是更快?但GSM模块和定位模块的串口通信、超时处理、缓冲区管理这些逻辑,Arduino的跑马灯式开发风格容易把流程写乱,而且最终要落到低功耗、看门狗这些可靠设计时,STM32的开发自由度明显更高。

1.3 传感器、通讯模块与供电的选型逻辑

加速度传感器我建议使用MPU6050,虽然是六轴(加速度计+陀螺仪),但跌倒检测用加速度计就够,陀螺仪的角速度数据可以作为辅助判据,对识别姿态变化很有帮助。如果追求省电和简单,ADXL345单加速度计也完全可以。MPU6050走I2C,一根SDA一根SCL就能搞定,接线干净。

GSM模块要选串口电平兼容的型号。我常用的模块支持2.8V左右的高电平串口,和STM32的3.3V电平可以直接对接,不需要电平转换芯片。GSM模块整机供电是另一个大坑,发射时峰值电流可能飙到2A,绝对不能从单片机稳压输出上硬拖,后面我会专门讲供电。

定位模块建议选支持GPS和北斗双模的型号,单独GPS模块在楼宇密集区的定位速度和稳定性差不少。手里有一块串口输出NMEA语句的双模模块,9600波特率,冷启动大概几十秒,室外空旷环境表现很稳。

供电部分则是一节3.7V锂电池加充放电管理,再用一颗低压差稳压芯片给STM32和传感器供3.3V。GSM模块可以从电池端取电,因为它的工作电压范围通常是3.4V到4.4V,刚好贴合锂电池电压区间。

2. 跌倒识别这一关:加速度数据如何变成报警信号

2.1 先从物理量说起

很多人第一次接触跌倒检测,觉得只要“加速度突然变大”就触发报警。但真正落地时你会发现,这个判断太粗糙了。要理解跌倒识别的算法,得先明白加速度传感器到底在测什么。

静止站立时,三个轴的合成加速度约为1g(约9.8米每平方秒)。注意,如果把传感器固定在手上或者腰间,静止时重力加速度会分摊到多个轴上,不一定全部落在Z轴。跌倒过程大致是:身体先失重(人往下掉),合加速度短暂下降;随后撞到地面,出现明显的冲击峰值,可能超过2.5g甚至3g;最后人躺平,姿态和站立时完全不同,而且短时间内不再大幅运动。

所以,跌倒识别不是看单一数值,而是看整个时间窗口内的“合加速度曲线 + 姿态变化 + 后续静止状态”。这三个特征缺一不可,少了任何一个,误报率都会高到无法使用。

2.2 跌倒的三段式特征

我把常用判断特征整理成表,这组参数可以作为初版算法的基础:

阶段特征参考判断条件
失重阶段合加速度回落低于0.8g,持续若干毫秒
冲击阶段合加速度尖峰超过2.5g~3.0g
姿态阶段传感器姿态趋于水平某水平轴加速度接近1g
静止阶段短时间内运动幅度小几秒内合加速度稳定在1g附近

单看冲击峰值很容易把“拍桌子”、“猛捶沙发”也当成跌倒。加上姿态判断后,剧烈撞击但人没有躺下的动作会被过滤掉;再加上后静止判断,又可以把“跳跃后落地但马上恢复站立”这样的场景排除。

2.3 算法流程与防误报设计

实际操作中,我倾向用一段滑动窗口循环处理数据,而不是依赖中断。每50毫秒采集一次加速度,计算合加速度,然后用一个状态机来控制流程:

  1. 正常状态:持续采样,发现合加速度超过阈值,进入“疑似撞击”状态;
  2. 疑似撞击:等待几百毫秒,检查加速度是否回落,同时观察姿态是否改变;
  3. 疑似倒地:继续观察数秒,如果合加速度稳定在1g上下并且姿态接近水平,则判定跌倒;
  4. 报警确认:触发GSM短信,同时进入冷却时间,防止重复误报。

防误报还有一招很实用:在APP或者短信里留一条退路,例如报警短信发出后,允许老人通过按键取消报警,或者家人收到短信后先电话确认。这个设计在真实场景里非常重要,因为算法永远无法做到百分百准确。

2.4 代码级实现思路

下面是一个简化版的C语言思路,我用定时器中断保证采样周期稳定:

void ReadAccel(uint16_t *ax, uint16_t *ay, uint16_t *az); float vector_magnitude = sqrt(ax*ax + ay*ay + az*az) / ACCEL_SCALE; if (vector_magnitude > IMPACT_THRESHOLD) { impact_flag = 1; impact_time = HAL_GetTick(); } else if (impact_flag) { // 检查冲击后是否进入低运动状态 if (vector_magnitude < STATIC_THRESHOLD) { static_counter++; if (static_counter > STATIC_SAMPLES) { // 判定位姿是否接近水平 if (IsHorizontal()) { SendAlertSMS(); impact_flag = 0; EnableCooldown(COOLDOWN_5MIN); } } } }

代码本身不难,难的是确定那几个阈值。我个人的经验是,先把阈值放低(比如2.2g),然后带着板子做各种日常动作实测,记录误报情况,再逐步调高到误报和漏报都能接受的区间。没有捷径,就是反复试。

3. 报警链路打通:GSM短信与定位模块的串口协作

3.1 GSM模块的AT指令入门

GSM模块和STM32之间就是串口通信,核心是AT指令。模块上电后先要等它搜网,这个时间从几秒到几十秒不等。判断模块状态最常用的指令是:

AT

模块返回OK,说明串口通信正常。接着查询网络注册状态:

AT+CREG?

返回+CREG: 0,1表示已经注册上网络。发送短信的流程是先进入短信发送模式:

AT+CMGS=13800001234

然后输入短信内容,最后发一个十六进制的结束符0x1A(对应ASCII的CTRL+Z)。在STM32里用串口发送时,结束符要用char end = 0x1A;这种方式发送,不能用字符串直接拼。

还有个容易踩的细节:AT指令的换行符。很多模块要求以\r\n结尾,少发一个回车,指令就石沉大海。调试时先用一个串口工具,手动把整个流程跑通,再写进单片机程序。

3.2 GPS/北斗模块的NMEA数据怎么解析

定位模块上电后,会以9600波特率不断输出NMEA格式的文本行。常用的语句有$GNGGA(位置信息)和$GNRMC(推荐最小定位信息)。

看一条典型的GGA语句理解字段含义:

$GNGGA,093856.00,3101.23456,N,12123.45678,E,1,08,1.2,12.5,M,0.0,M,,*5F

字段从左到右依次是:UTC时间(上午9点38分56秒)、纬度(31度1.23456分,北纬)、经度(121度23.45678分,东经)、定位状态(1表示有效定位)、卫星数、水平精度因子、海拔高度等。

单片机解析时不要用sscanf图省事,因为NMEA语句的字段长度不固定,而且数据是异步到达的,容易遇到半行数据。正确做法是:以$开头到\r\n结束,攒一整行后再按逗号拆字段。解析出经纬度后,还要把“度分格式”换算成“度”格式:

float raw_degree = atof(buff); float degree = (int)(raw_degree / 100) + (raw_degree - (int)(raw_degree / 100) * 100) / 60;

3.3 短信内容设计与人机交互

报警短信的接收人是家人,内容必须让人一眼看懂并知道怎么处理。我的设计是:

[跌倒报警] 平台时间 14:23:10 位置:31.0173N,121.2334E 点此查看地图:http://xxxx?lat=31.0173&lon=121.2334

如果已经连到服务器,可以把坐标转换成地图链接,家人点开直接看位置。没条件部署服务器时,纯短信也能用,收信人把经纬度输入地图搜索框一样能定位,只是操作麻烦些。

短信长度也要考虑,一条普通短信最多约70个汉字或140个ASCII字符,链接和坐标要控制字数,太长会拆成多条,反而影响阅读。实际测试中,我最终把地址链接做了短链处理,保持短信只占一条。

3.4 定位失败与重复报警的兜底方案

定位模块不是任何时候都能定位成功,室内基本无信号,冷启动也可能搜不到星。此时强行把空坐标发出去,家人只会收到一条无意义短信。我在程序里的兜底逻辑是:

  • 跌倒判定成立后,立即尝试读取定位数据;
  • 如果1分钟内定位失败,先发送“老人跌倒但暂未定位到坐标”的短信;
  • 之后每过一段时间重试,一旦定位成功,再补发带坐标的位置短信;
  • 整个报警流程进入冷却期(例如5分钟),期间不再重复报警,避免GSM短信轰炸。

家人在场景里最需要的是“有人摔了”这个事实,坐标可以稍后补。这个优先级顺序比一开始就死等定位更实用。

4. 按键手电筒:小功能里藏着的硬件与软件细节

4.1 为什么LED不能直接接IO口

高亮灯手电筒听起来简单,但直接拿STM32的GPIO驱动大功率LED是行不通的。普通GPIO输出电流只有几毫安到二十毫安,而一颗1W白光LED需要350mA左右电流。真让GPIO硬扛,芯片发热不说,很快就烧引脚。

正确做法是用一颗N沟道MOS管做低边开关。MOS管的栅极接STM32的IO口,漏极接LED负极,LED正极通过限流电阻接电池正极,源极接地。IO输出高电平,MOS管导通,LED点亮;IO输出低电平,MOS管截止,LED熄灭。

如果电池是3.7V锂电池,LED的典型压降约3.0到3.3V,可以直接串一颗限流电阻凑到目标电流。以350mA为例,电阻值约(3.7 - 3.2) / 0.35 ≈ 1.4欧姆,选2欧姆左右、功率1W以上的电阻比较稳妥。要注意的是锂电池电压会从4.2V降到3.4V,电流会跟着变,所以这种限流方案精度一般。想要亮度恒定,可以换成恒流驱动芯片,但对一个手电筒功能来说,限流电阻加PWM调节完全够用。

4.2 按键消抖与单击/长按状态机

按键硬件上需要加一个上拉电阻,软件里最基础的是消抖。我的习惯是用定时器每10毫秒扫描一次按键电平,连续读到多次稳定电平后才认为按键状态有效,而不是用延时消抖。因为大电流LED开启瞬间电池电压会有波动,用阻塞延时容易把主流程卡住。

按键功能可以做成这样:

  • 单击:开关手电筒;
  • 长按:触发求助短信(可以和跌倒报警走同一条短信链路)。

为了区分单击和长按,我用一个简单状态机:记录按键按下的时间戳,松开时判断时间差。小于300毫秒是单击,大于1秒是长按。要注意的是,单击开关灯之后,还要等一个“按键释放确认”周期,防止按下后松开的瞬间又触发一次。

4.3 手电筒场景的功耗与使用体验

手电筒的耗电大头是LED,350mA的电流对一块几百毫安时的小锂电池来说撑不了多久。老人夜间起床使用,不需要全程满亮度。我建议加一个PWM亮度调节:短按第一下开灯是低亮度,第二下变高亮度,第三下关灯。这样既省电,又不至于半夜被强光刺眼。

另外,电池电压检测也建议做。当电压低于3.5V时,手电筒自动禁止高亮度模式,只允许低亮度照明。否则电池电压被拉低到GSM模块的最低工作电压以下,会导致整个系统不稳定,反而影响报警主功能。

5. APP地图显示:定位数据从短信到屏幕的落地之旅

5.1 数据链路怎么选

很多人看到标题里“APP地图设计”时,默认以为手环直接蓝牙连APP显示位置。但在GSM短信方案下,手环和手机之间并没有持续连接。实际做起来有三种路线:

方案数据流向适合场景
位置短信 + 地图链接手环 -> 家人手机短信,点链接看地图最简单,不用做APP
手环+蓝牙模块手环 -> 手机APP,近距离直接显示适合调试演示,距离受限
手环+服务器+APP手环通过GPRS/4G上传,APP从服务器拉取功能完整,适合长期产品

如果要以“APP地图设计”作为项目的重点,我建议做第三种,但考虑到GSM模块本身支持GPRS数据传输,可以让手环把定位数据通过TCP发送到服务器,APP再从服务器拉取。这样家人不在身边时,也能远程看到老人的实时位置和历史轨迹。

开发APP时不需要从零写地图引擎。接入国内主流地图开放平台的SDK,引入一个地图控件,添加标记点、轨迹线,几分钟就能搭出界面。前期调试时,甚至可以先做一个只读位置的小工具,验证数据链路通了再美化界面。

5.2 必须处理的坐标偏移问题

这是地图开发里最容易忽略的坑。定位模块输出的是WGS84坐标系(GPS卫星系统使用的国际标准坐标),而国内地图开放平台出于坐标加密要求,使用的通常是GCJ02坐标系(俗称“火星坐标系”)。直接把WGS84坐标传给地图SDK,你会发现位置整体偏移几百米,这是坐标系不匹配导致的。

解决办法是在服务器端或者APP端做一次坐标转换。网上有公开的转换算法,核心思路是把经纬度先换算成平面坐标,再做非线性纠偏。IOS直传这种方式看上去别人的代码能调通,但转换公式写错一个常数,地图上的位置就离谱到隔壁街道。我的建议是,先拿一个已知地点的GPS原始坐标和地图显示坐标对比,验证转换逻辑正确后再大批量处理数据。

5.3 APP端功能设计与刷新策略

APP的界面不需要复杂,核心要素就四块:

  • 地图视图:显示老人手环的最后已知位置;
  • 状态卡片:显示老人“正常/疑似跌倒”状态、最后定位时间;
  • 报警提示:收到跌倒报警时,地图自动定位到报警位置并弹出提醒;
  • 历史轨迹:按时间回放老人的活动轨迹,方便家人掌握日常活动习惯。

刷新策略上要克制。老人手环不是高频运动追踪器,位置上传频率设为每5分钟一次就够。实时性要求高的跌倒报警走的是短信,定位数据是辅助确认位置的,频繁刷新只会白白消耗流量和电池。

6. 实测排障记录:这些坑每一届做类似项目的人都会遇到

6.1 供电与串口通信:第一道坎

我刚开始把GSM模块和STM32用同一个3.3V稳压电源供电,结果只要模块一发射短信,单片机就复位。原因很简单:GSM发射瞬间电流太大,把电压瞬间拉低,STM32供电跌到复位阈值以下。解决方案是让GSM模块直接从电池取电,同时给它的电源输入端并联一个大容量的电解电容(比如470微法),吸收瞬态压降。单片机和其他传感器则通过稳压芯片供电,两路电只在电池端交汇,系统立刻稳定下来。

串口通信的坑也集中在前几次调试。模块波特率设成9600,单片机代码里却配成115200,出来的自然是乱码。还有公地问题,GSM模块、定位模块、STM32必须共地,否则UART电平参考点不一致,数据错乱是必然的。

6.2 定位测试与天线摆放

定位模块的实测结果和芯片参数是两回事。我把手环样机放在室内靠窗位置,冷启动定位用了接近两分钟;拿到楼顶空旷处,最快一次只要三十多秒。陶瓷天线对手环外壳的金属件特别敏感,手表表带、金属卡扣贴在天线附近,卫星数量明显下降。

建议所有项目成员拿到样机后,先固定一个标准测试位:室外空旷、手环腕带朝上、天线区域无遮挡。这样测出来的定位时间才有可比性,不会因为测试环境不一致得出互相矛盾的结论。

6.3 防误报参数的实际调参记录

跌倒检测的阈值调参过程最能体现“纸上谈兵”和“动手实测”的区别。我最初把冲击阈值设为2.5g,自动判定逻辑也写好了,结果实测时拍桌子、快速甩手、从椅子上跳起来都触发了报警,误报率高得没法用。把阈值调到3.0g,再加上“冲击后必须姿态接近水平”和“数秒内保持静止”两个条件,误报才明显减少。

有意思的是,后来在模拟老人缓慢倒地的场景时,发现冲击峰值只有2.2g左右。阈值定高了会漏报,定低了误报,最终我针对不同佩戴位置做了一组对照试验,同时引入了陀螺仪的角速度变化作为辅助判据才平衡下来。做完这轮实测,我深刻体会到一件事:算法参数一定要跟着真实使用场景调,不能只看数据手册。

实际的佩戴位置也很关键。手环如果戴在手腕上,跌倒时手臂的挥动可能导致加速度特征不明显;戴在腰部则更贴近人体重心变化。业内很多方案会选择推荐佩戴在腰腹部,但老人通常更习惯戴手腕,这会直接影响算法的判断逻辑。我在最终方案里做了一个简单的补偿:在APP里可以选择佩戴位置,不同位置对应不同阈值组。

做完整套系统,我最大的体会是,这类项目真正难的不是某一个单独模块,而是让传感器、通讯模块、电源管理和APP这一堆东西在有限体积、有限功耗和有限成本下稳定协同。给后来人的建议很实在:先分模块单独调试,每个模块用串口工具确认无问题后再整合;不要嫌麻烦,合体之后出了问题,排查成本成倍上升。后续想做产品级方案的话,可以考虑把GSM换成4G Cat.1模块,主控换成低功耗系列,再用实时操作系统管理多任务,整个系统的可靠性和续航都会再上一个台阶。

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

工业自动化控制系统工程实战:从传感器到上位机的链路与调试经验

说个真实感受&#xff1a;很多人一说“工业控制和自动化”&#xff0c;脑子里就只有PLC&#xff0c;觉得这就是个给设备写逻辑的活。但我这几年跑现场下来&#xff0c;越来越清楚这是个系统工程——从最底下的传感器、执行器&#xff0c;到中间的控制器&#xff0c;再到上位监控…

作者头像 李华
网站建设 2026/10/21 18:46:12

单调栈通解:一次遍历搞定四类左右大小问题

说实话&#xff0c;这类题我在不同场合见过很多次&#xff0c;形态从“找右边第一个比它大的数”到“找左边第一个比它小的数”&#xff0c;再到那些看起来更复杂的柱子、温度、接雨水问题&#xff0c;本质都绕不开同一个东西&#xff1a;单调栈。我第一次接触的时候也很头大&a…

作者头像 李华
网站建设 2026/10/21 18:45:31

Claude Code源码拆解:分层记忆架构与Agent知识持久化

断断续续啃了一个多月&#xff0c;这个系列终于走到了第五篇。前四篇里&#xff0c;我把Claude Code的对话调度、工具注册、权限校验和命令解析挨个拆了一遍&#xff0c;基本摸清了“一次请求从进来到出去”的主链路。但有个问题始终绕不过去——一个只能“聊完就忘”的Agent&a…

作者头像 李华
网站建设 2026/10/21 18:45:16

大学日本语期末考试题.doc批量解析:.doc格式处理与日文编码避坑指南

简介&#xff1a;这份大学日本语期末考试题doc文档&#xff0c;面向高校日语学习者及备考期末的在校学生&#xff0c;用于检验和巩固日语基础知识。试题系统覆盖汉字读音、平假名与汉字转换、片假名含义、语法填空、词汇综合运用及阅读理解等模块&#xff0c;例如「企業」读作「…

作者头像 李华
网站建设 2026/10/21 18:45:07

从零搭建轻量级代码评审工具:open-code-review 的设计与实现

1. 从零搭建代码评审工具&#xff1a;为什么我要造这个轮子代码评审这件事&#xff0c;做过团队协作开发的人都懂——它既是保证代码质量最有效的手段&#xff0c;也是最容易流于形式的环节。我待过几个不同规模的研发团队&#xff0c;从五六人的小作坊到几十人的中型团队&…

作者头像 李华
网站建设 2026/10/21 18:43:28

基于DDPG强化学习的四旋翼PD控制器参数自整定与Matlab仿真实践

四旋翼的PD控制器调参&#xff0c;说多了都是泪。比例项给大了抖&#xff0c;微分项给大了闹&#xff0c;人工凑参数往往要磨掉几个下午。最近我试了一个思路&#xff0c;把DDPG&#xff08;Deep Deterministic Policy Gradient&#xff09;强化学习算法和四旋翼的PD控制结合起…

作者头像 李华