ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于MSP430的老人跌倒检测手环:GSM报警与GPS北斗定位设计

基于MSP430的老人跌倒检测手环:GSM报警与GPS北斗定位设计

上个月接了这个老人跌倒手环的活,我一开始觉得这事不难:传感器、定位模块、GSM模块,全是现成货,拼一块板、写几行代码,能发短信就行。真正把样机戴在假人手腕上测了两周,我才发现标题里那串词——MSP430单片机手环、老人跌倒、GSM短信、GPS北斗定位、地图设计——没有一个字是白给的。跌倒判断的误报率、GSM发射瞬间的电源崩塌、GPS室内无信号、还有经纬度怎么变成家属能看懂的地图链接,每一样都在实际调试里卡过我。

这篇就把这个项目的完整设计过程和踩坑记录写出来,适合正在做类似可穿戴报警项目、或者想用 MSP430 做低功耗物联网设备的朋友,可以直接照着搭,也能提前绕开我走过的弯路。

1. 整体链路拆解:从传感器采集到家属收到地图短信需要经过哪些环节

这套手环本质上是一条数据链路:运动传感器不停采集姿态数据,单片机实时判断有没有发生跌倒;一旦判定跌倒,立刻唤醒定位模块读取坐标,再通过GSM模块向外发送一条包含经纬度和地图链接的短信,家属手机上点一下链接就能看到老人当前位置。

1.1 系统组成与模块选型

整个系统的核心器件我列在下面,这不是随手的配置,每一项都是根据功耗、接口、协议和采购便利性反复比过的。

功能模块选型思路接口备注
主控MSP430F1493路UART、I2C、多路GPIO低功耗是选它的首要原因,后面专门讲
运动传感器六轴姿态传感器(MPU6050这类)I2C测加速度和角速度,跌倒检测的主力
定位模块GPS/北斗双模定位模块UART输出NMEA协议数据,冷启动需要时间
通信模块四频GSM/GPRS模块UART发短信,峰值电流大,供电要专门处理
电池单节18650锂电池—容量大、驱动GSM发射够力、可充电
充电管理单节锂电池充电管理IC—充电电流可调,带过充保护
电源稳压3.3V LDO稳压器—给MSP430和传感器供电,稳定优先

为什么主控选 MSP430 而不是 STM32,或者干脆用 Arduino?理由很简单:这是一个要戴在手腕上的电池设备,续航是体验的底线。MSP430系列的超低功耗模式确实做得极致,LPM3模式下主控自身电流是微安级别,配合中断唤醒,可以在“休眠-采集-判断-再休眠”的节奏里把平均电流压得很低。STM32当然性能更强,但对手环这种单一任务场景,属于杀鸡用牛刀,功耗还压不下来;Arduino开发快,但板载硬件本来就不是为超低功耗场景设计的,待机就有十几毫安到几十毫安,戴在手上基本一天一充。MSP430的另外两个优势是内部时钟够用、IO口耐折腾,跌打损伤中不至于因为一个电平问题烧掉整块板。

1.2 串口资源分配与接口接线

这个项目里最容易在初期翻车的是串口分配。MSP430F149带两个硬件UART,但系统里GSM模块和GPS模块各占一个UART,调试又需要一个串口,这就出现了资源紧张。我的做法是:UART1给GSM模块常驻,UART0给GPS模块,调试输出用LED指示灯加一个复用拨码开关:平时UART0接GPS,调试模式下把GPS的TX断开,让UART0接USB转串口板。

实际测试时我在板子上留了一组杜邦线插针,方便切换。如果你手里的主控型号没有两个UART,也可以用一个UART做软串口模拟,但GSM模块AT指令通信对时序比较敏感,软件模拟串口容易丢字节,建议硬件UART优先给GSM,GPS用软件串口,这个优先级不要弄反。原因后面会说:GPS数据是连续不间断往外吐的,NMEA语句靠帧头区分,就算中间丢一两个字符,下一帧还是能重新对齐;而GSM的AT指令是一问一答的交互式协议,时序错乱整个指令就会失效。

信号链路整体是这样走的:传感器数据通过I2C进MSP430,判断逻辑在主控内完成;GPS模块开机后UART连续输出定位数据,主控按帧解析;GSM模块收到主控发来的短信指令后执行AT指令。三个模块之间没有复杂的互相握手,主控是唯一的调度中心。

2. 跌倒检测算法:合加速度阈值只是开始,姿态角二次确认才靠谱

跌倒检测是整个项目的灵魂。把阈值设低了,老人弯腰捡个东西就触发报警,家属一天收十条短信,最后直接把手环关了;设高了,真摔了又没反应。这一章我把自己的三段式算法完整展开,可以直接抄。

2.1 第一步:合加速度冲击检测

MPU6050输出的三个轴加速度数据不能用单轴直接判断,因为手环佩戴方向会变,抬手、甩手、翻身都会让某个轴的读数大幅变化。我取的组合值是三轴合加速度,即向量模长:

acc_mag = sqrt(ax*ax + ay*ay + az*az)

静止状态下,合加速度约等于 1g(9.8m/s²),无论手环怎么翻转,这个值都稳定在1附近。跌倒的时候身体先加速下落再撞击地面,合加速度会在短时间内冲破2.5g甚至3g。我实际测试过,把佩戴手环的假人从1米高摔到海绵垫上,峰值能到3.5g左右;而正常走路、摆臂、上下楼梯,合加速度峰值基本在1.8g以内。所以第一道门槛设成 2.5g,持续至少100ms 才认为是一次冲击事件。

这里有个关键细节:只看峰值不够,还要看持续时间。有人猛地抬手、拍桌子,瞬时峰值也可能超过2.5g,但持续时间只有几十毫秒。真正的跌倒是自由落体加撞击,撞击持续时间通常在100到200ms以上。所以代码里要做窗口统计,不能一超过阈值就立刻触发,而是要在时间窗内连续采样判断。

2.2 第二步:姿态角二次确认

冲击事件只能说明“身体剧烈运动了一下”,不能说明“人倒地了”。跌倒之后人的姿态会从直立变成平躺或侧躺,而这个姿态在跌倒后通常会维持一段时间。所以第二步是把冲击事件后一段时间的姿态角拉出来确认。

用加速度计的静态倾角计算公式:

angle = acos(az / acc_mag) * 180 / PI

意思是重力方向与手环Z轴的夹角。手环佩戴在手腕上,正常站立时手腕自然下垂或微曲,这个夹角有一个相对固定的基准值;跌倒平躺后,手臂和重力方向的关系会明显改变,夹角会偏向 0° 或 180° 附近。注意,不同老人佩戴手环的松紧、手臂自然姿态差异很大,所以设备开机后要做一个“静止3秒标定”,把这支手臂的基准夹角记下来,后续判断都基于相对角度变化,而不是绝对值。这个标定步骤在量产或给老人戴上前必须做,否则误报率会直线上升。

算法上没有那么玄乎,不需要上机器学习。我用的是:合加速度超阈值后,开始记录接下来2秒内的姿态角,如果相对基准角变化超过45°,并且这个姿态保持超过5秒,就视为高度疑似跌倒。两个条件同时满足,才进入报警流程。

2.3 第三步:连续触发与防抖延时

即使有两步确认,还是可能误报。我遇到最多的误报场景是老人坐椅子时猛起猛坐,合加速度也能短暂飙到2.5g以上,姿态角也有变化,如果立刻报警就太烦人了。解决方案是加入“连续3次确认”机制,每次确认间隔200ms,三次都满足才触发。这样短促的起坐动作、弯腰捡东西(状态很快恢复直立)基本都会被过滤掉。

触发报警后还有一个300ms到500ms的延时再真正发短信,这个延时不是为了等什么数据,而是给老人一个“虚惊一场”的人工取消机会。现实一点的解释是:如果老人只是摔倒后迅速自己爬了起来,姿态已经恢复正常,系统自动取消报警,不打扰家属。这个功能看起来小,但对家属体验的改善比任何算法优化都大。样机测试时我甚至把取消窗口放到10秒,配合手环上的按键,长按3秒取消报警,实际使用中确实收到了不少好评。

有人可能问,既然这么复杂,为什么不直接把陀螺仪数据一起融合算欧拉角?我的回答是:MPU6050的陀螺仪有温漂和零偏,融合姿态角需要卡尔曼或互补滤波,运算量上来了,代码复杂度也上来了。对可穿戴设备来说,加速度计数据在跌倒冲击这种剧烈场景下已经能提供足够信息量,用最简单的数学表达把可靠率做到95%以上,比追求理论上99%却让工程师在调试中累死更重要。

3. GPS/北斗解析与GSM短信:串口上流动的每一帧数据都要读懂

跌倒判定一旦通过,系统要做的事情就变成了“报告位置”。这一章里我会说清楚定位模块输出的数据怎么解析,以及怎么通过AT指令把它变成一条短信发出去。

3.1 NMEA帧格式里最容易搞错的GNGGA/GNRMC

GPS/北斗双模定位模块上电后,UART会以默认波特率(通常是9600)持续输出NMEA 0183协议语句。这里有个绝大多数新手会踩的坑:双模模块输出的句头是$GNGGA和$GNRMC,不是传统GPS单模模块的$GPGGA和$GPRMC。GN代表GPS与北斗双系统的定位结果,GP只代表GPS系统。如果你写解析程序时光盯着GP开头的语句找,会发现串口数据明明在动,却怎么也解析不出坐标。

我实际关心的只有两条语句,其中$GNRMC最关键,包含了经纬度、速度、UTC时间和定位状态。一帧示例:

$GNRMC,083559.00,A,3129.41528,N,12020.51305,E,0.0,0.0,...

字段含义分别是:UTC时间(时分秒)、定位状态(A=有效,V=无效)、纬度、北纬/南纬、经度、东经/西经、速度、航向。代码解析时要做字符分割,取到经纬度字段后先判定位状态,状态不是A的视频说明还没定位成功,这时候的坐标不能用。

3.2 度分坐标转十进制度的计算示例

NMEA协议里的经纬度格式是“度分”格式,不是我们在地图链接里常用的十进制度。比如3129.41528 N,意思是31度29.41528分。要转成十进制,公式是:

度 = 取整数部分,即31 分 = 29.41528 十进制度 = 31 + 29.41528 / 60 = 31.4902547

我写的转换函数是这样:

double convert_nmea_to_decimal(double raw) { int degree = (int)(raw / 100); double minute = raw - degree * 100; return degree + minute / 60.0; }

这个函数在调试时陪我跑了无数遍,建议先用电脑串口助手抓一段真实定位数据,先从终端里确认格式,再写解析函数,不要直接把网上找的代码贴进去,因为不同模块的NMEA字段细节可能有细微差异,比如有的没有速度字段,有的带校验和不带校验位,先把原始文本打印出来比什么都靠谱。

3.3 GSM短信发送:AT指令流程与失败重试

坐标解析出来之后,主控通过另一个UART向GSM模块发送AT指令。发送短信的指令序列非常简单,真正考验人的是各种失败场景。基础流程:

AT+CMGF=1 // 短信设为文本模式 AT+CMGS="13xxxxxxxxx" // 设置接收号码 > FALL ALERT! ... // 短信内容 0x1A // 发送,十六进制1A相当于Ctrl+Z

注意文本模式下短信内容一般建议用英文或数字,中文短信需要切到PDU模式并做Unicode编码,代码复杂度会高很多。我的方案是短信内容用英文加链接,避免中文在GSM模块内部的编码坑,家属收到后照样一眼看懂。

发送失败的三个高频原因:SIM卡没插好或没欠费,模块返回CMS错误;信号差,返回ERROR或发送超时;GSM模块启动初始化未完成就发了指令。我的处理是:每次开机先发AT+CPIN?查询SIM卡状态,返回READY才进主流程;发短信前发AT+CSQ查信号强度,返回数字低于10就默认网络不可用,先尝试三次,每次间隔5秒;短信发送后收到OK才置为成功,收到ERROR就等待10秒重发整个指令序列,最多5轮。这套流程在测试里把短信发送成功率从最初的70%提到了99%。

4. 报警短信里的坐标与地图链接设计:让家属点一下就能看到位置

定位数据进了短信,其实还有很多产品层面的细节。如果短信里只发“东经120.206866 北纬31.490254”这种裸坐标,绝大多数家属根本看不懂,也不会去搜索,报警短信等于白发。所以我在设计短信内容时加入了地图链接。

4.1 短信内容模板与链接生成

实际发送的短信模板长这样:

FALL ALERT! Time: 2024-05-20 14:30:25 Pos: 31.490254,120.206866 Map: https://xxx.map/location?lat=31.490254&lng=120.206866

地图链接的生成逻辑不复杂:主控把经纬度拼到固定前缀的URL参数里就行。不同地图服务商的链接格式有区别,但核心参数都是lat和lng。这里给一个通用建议:不要直接拼长链接,因为短信有长度限制,英文短信单条上限约160字符,链接太长会被拆成两条甚至三条,体验很差。我使用的是短链服务,把URL压缩到30个字符以内,这样短信总长度控制在120字符左右,稳定一条发完。

4.2 经纬度精度与最后1米问题

手持定位模块的典型精度是3到10米,即使在开阔地,定位点也可能偏离真实位置几个车位。所以短信里再怎么写精确到6位小数,也代表不了精确到1厘米。经纬度保留6位小数已经给出了约0.1米的精度,这对地图显示来说绰绰有余,更多小数位只是增加短信长度,没有实际意义。

这里分享一个实用技巧:我在地图链接生成前做了坐标抖动过滤。定位模块静止时,由于卫星信号和环境影响,经纬度会在几米范围内漂移。如果不加处理,理论上同一位置的报警链接会显示在不同坐标上。解决方法是拿最近两次有效定位做比较,如果距离小于10米就沿用上一次坐标,防止短时间内的随机漂移把位置画偏。这个逻辑用经纬度平面距离公式算就行,不需要调用高精度定位服务,几行代码就能实现。

4.3 没有网络环境下基站定位兜底

GPS/北斗模块在室内、地下通道、电梯里经常拿不到有效定位。作为报警设备,不能因为没卫星信号就不发报警,所以必须设计兜底方案:GSM模块本身可以通过基站信息获取粗定位。主控在GPS模块连续30秒无有效定位时,切换策略,发送AT指令让GSM模块返回当前注册基站的三网信息或经纬度粗定位结果。

基站定位精度大概是百米到公里级,比卫星定位差很多,但至少能告诉家属“老人大概在这个城市这个片区”,比完全没位置强得多。我做的短信在兜底定位时会额外加一个“ACCURACY: LOW”字段,提醒家属位置可能不准,不要按着精准坐标找。这种诚实的设计,反而比假装精确更能赢得家属信任。

5. 低功耗设计:把待机电流从几十毫安压到2毫安以内

手环项目的成败有一半在续航上。第一版样机我犯了几乎所有新手都会犯的错:所有模块上电常供,主控跑在一个8MHz主频的死循环里。结果整个板待机电流轻松超过30mA,一块2000mAh的电池撑不到两天,戴在假人手腕上都能感觉到发热。后面做了一轮彻底的功耗改造,才真正敢说能戴一个月。

5.1 把GPS和GSM的电源切掉,待机电流降一个数量级

改造的核心思路只有一个:平时不该供电的模块全部断电。GPS模块工作电流约20到30mA,GSM模块待机也不低,登录网络时候更是有几百毫安的瞬时电流。这两个模块如果常驻功耗,任何低功耗主控都救不回来。

我加了两路MOS管开关做电源控制,单片机的两个GPIO分别控制GPS模块和GSM模块的电源端。平时两路都是关闭状态,只有跌倒判定触发后,主控才先给GPS模块上电,等定位数据有效后再给GSM模块上电发短信。短信发完,再按顺序断电,回到休眠状态。

这个开关不能随便用三极管就能通杀,选择MOS管时要注意导通电阻不要太大,否则模块满载工作时压降会触发欠压复位。我选的是低内阻N沟道MOS管,GSM模块工作电流峰值接近2A,开关的连续电流能力余量要留到3A以上,不然一发射就开始发热抖动。

5.2 电池容量、真实续航与充电策略

改造后的待机电流分配是这样的:MSP430进入LPM3休眠模式,电流约1微安级别;六轴传感器放在低功耗测量模式,约几十微安;两个无线模块断电后电流为零。主控每隔500ms醒来一次,读一次加速度传感器,判断是否需要进入高采样模式,平时绝大多数时间都在休眠。综合下来,整机待机电流可以压到1.5mA以内。

用一块1000mAh的18650锂电池来做简单估算:

可用容量按80%计算:800mAh 待机时间 = 800mAh / 1.5mA = 533小时 ≈ 22天

考虑到电池自放电、LDO静态电流、传感器唤醒采样开销,实际使用我测下来大概20天左右。如果手环触发一次报警,整个过程GPS上电约40秒、GSM上电约20秒,额外消耗约30mAh,对比1000mAh总容量,可以忽略不计,基本不影响整体续航。充电部分用的单节锂电池充电管理IC,恒流恒压模式,充电电流设在500mA,既不伤电池也能在两三个小时内充满。

6. 实测阶段踩过的坑:电源、天线、模拟跌倒、串口电平

最后这部分是项目里最值钱的经验,全部来自真实测试,比看一百篇原理分析都有用。

6.1 模拟跌倒测试的正确打开方式

我最初测试用的方法是把手环绑在鼠标垫假人上往沙发上扔,结果数据波动大且不稳定,误报率极高。后来改进成:找一个1.5米高的桌沿,让假人侧倒到海绵垫上,并且保证自由落体方向与手环佩戴轴垂直,这才模拟出了接近真实跌倒的冲击波形。这提醒我们:用假人模拟跌倒必须控制跌落高度、落点软硬、撞击角度三个变量,否则测试数据无法复现,调出来的阈值也是碰运气。

测试数据里最典型的是:正常走路合加速度峰值约1.6g,上下楼梯约1.9g,快速坐起约2.2g,而假人跌倒峰值稳定在3.5g左右。如果某次测试的峰值低于2.8g,我就认为那次跌倒模拟不合格,重新调整跌落姿势再测。阈值设置要有余量,但又不能离正常运动峰值太近,2.5g是我测试后确定的甜点值。

6.2 GSM发射瞬间导致单片机重启

第一次把样机装进壳子里做整机联调时,一触发报警就白屏重启,疑似原地无限复位。查了很久,最后用示波器抓GSM模块发射瞬间的电源轨,发现电压跌落超过600mV,直接把LDO输出拉到单片机复位电压以下。

问题根源是GSM模块发射时瞬态电流可达2A,而我的供电线又细又长,电池内阻加线路电阻扛不住这个冲击。解决方法是:在GSM模块电源引脚旁并联两个470uF的低ESR法拉电容和一个小容量的MLCC,同时把给GSM模块供电的导线换成短而粗的线径。这个改动之后,GSM发射瞬间的电压跌落到150mV以内,整机稳定通过测试。教训是:无线模块的电源设计永远不要只看平均电流,要看的是峰值电流和冲击响应。

6.3 室内定位失败与冷启动时间

GPS/北斗模块在室内的表现堪忧,几乎拿不到有效定位。第一次整机测试,我在写字楼里触发报警,等了40秒,定位状态始终是V。后来把测试搬到了窗边,才在35秒左右拿到第一个有效点。冷启动时间受电离层、卫星几何分布和视野遮挡影响很大,30到60秒是正常范围。

这给产品的启示是:手环跌倒报警时,如果GPS长时间没有定位,不能干等。我的做法是到了30秒就启用GSM基站定位兜底,先发出一条低精度位置短信,同时保留GPS继续搜索;如果随后GPS定位成功,再补发一条精确位置的短信。两条短信虽然有些重复,但家属能前后对比,反而是个负责任的设计。

6.4 手腕佩戴角度的标定问题

姿态角二次确认算法里,基准角的标定是整个系统中唯一需要人工介入的步骤。刚开始我把基准角写死成固定值,结果每个测试者的手腕静止姿态都不同,有的人自然下垂,有的人微屈,还有人习惯性地握拳,同一个阈值下误报率差异巨大。改成开机3秒静止标定后,误报率立刻降了下来。

标定逻辑很简单:设备上电后显示LED慢闪3秒,提示老人保持静止,主控取100组加速度数据的平均值作为基准。这个步骤虽然简单,但必须写进用户说明书里,并且最好在配网或初始设置时做个引导,否则老人不知道要静止,标定出来的基准就是错的。

调试佩带角度时还有一个重要提示:六轴传感器最好设计在固定的PCB位置,不要用飞线接出来让它在壳体内乱晃,否则传感器坐标系整机会变。量产时传感器应该做在固定安装位的板载焊盘上,手环外壳内部也最好设计定位槽,保证每一次装配的传感器方向一致。否则即使做了开机标定,加速度坐标轴方向不一致也会让算法结果飘掉。

这套系统做完之后,我再也不觉得“单片机手环”是个简单项目了,它把低功耗设计、传感器算法、串口协议解析、移动通信、定位导航、产品交互全部串在了一条链上。如果下一步还有扩展计划,我建议往两个方向走:一是把跌倒判定的姿态判断进一步优化,加入心电图或心率异常作为辅助确认条件,减少老人心脏病发作伴随摔倒的漏报;二是加一个蓝牙低功耗模块,平时把数据同步到家里的网关,老人不出门也能有人盯着状态。做这种东西,真正值钱的地方不在模块有多新,而在于把每个环节都调得比其他方案更稳一些。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进