基于毫米波雷达与STM32的睡眠监测系统设计与实现 1. 项目整体设计思路为什么是毫米波雷达加STM32睡眠监测这个方向我一直觉得是嵌入式项目里很有“嚼头”的一类。它不光是采集数据那么简单牵扯到传感器选型、信号处理、低功耗设计、数据可视化甚至还有一点医疗健康的背景知识。这个项目选了毫米波雷达作为感知前端用STM32做主控这个组合本身就很有说法。先聊聊为什么不用摄像头、不用手环。摄像头方案最大的问题是隐私卧室这种场景用户对镜头天然有抵触心理而且摄像头对光线敏感晚上关灯之后画质断崖式下跌算法再强也扛不住。手环、手表这类穿戴设备虽然能测心率、翻身但用户得记得戴戴着睡觉还有个舒适度问题充电也麻烦监测数据断断续续。毫米波雷达的好处在于非接触、无感、全天候工作你睡你的它测它的既不用接触皮肤也没有视觉侵入感用户完全没有佩戴负担。这一点在产品逻辑上是降维打击所以近几年的智能睡眠监测产品从消费级到医疗级大量转向毫米波方案。那为什么主控选STM32而不是树莓派或者直接上Linux方案核心在于产品化的思维。STM32资源够用、功耗可控、成本低廉更重要的是生态成熟从CubeMX生成工程到调试工具链都极其完善做量产方案稳定性有保障。像树莓派虽然算力强但启动慢、功耗高、体积大而且量产供应链不稳定断电还容易掉SD卡数据这在睡眠监测这种7×24小时运行场景里都是硬伤。毫米波雷达本身在前端做了大量预处理输出的数据量并不大STM32完全吃得消。所以这个方案的本质是——用合适的芯片干合适的活不堆算力只解决问题。项目的完整链条是这样的毫米波雷达模块通过SPI或串口把原始I/Q数据或点云数据送给STM32STM32跑信号处理算法从回波中提取呼吸、心跳、体动等生理特征然后通过状态机判断睡眠阶段最后把结果存到Flash或通过WiFi模块上传到云端、手机App。整体拆下来就是三个子系统的协同感知层、处理层、应用层。接下来我把每一层的设计和实现讲透。2. 硬件选型与电路设计几个关键决策背后的理由2.1 雷达模块怎么选24GHz还是60GHz不单是频率问题毫米波雷达的工作频率直接影响探测精度、模块尺寸和成本。市面上的方案基本上两大阵营24GHz和60GHz。频率越高波长越短理论上多普勒灵敏度对微小动作的感知能力就越强60GHz方案在呼吸心跳检测上普遍表现更好因为呼吸引起的胸腔起伏幅度只有几毫米到十几毫米60GHz约5mm的波长对这个级别的微动更敏感。但60GHz模块价格普遍比24GHz高而且国内一些方案对60GHz的法规认证要求也更复杂。24GHz的优势是技术成熟、成本低、出货量大做睡眠存在性检测、体动检测是绰绰有余的做精细的呼吸心跳提取就看具体模块的算法底子了。我自己做这类项目更倾向于选自带生命体征算法的雷达模块比如英飞凌的BGT60系列或者一些国产高精度模组。原因很现实呼吸心跳检测的算法难度在于信号极其微弱很容易被环境杂波淹没从零开始写一整套雷达信号处理链路的工程量非常大而且调参周期很长。模块厂商如果已经出了SDK或者算法库直接在STM32上调用接口拿结果省下的时间可以花在系统集成和睡眠阶段判断算法上。当然如果是为了学习雷达信号处理核心原理也可以选只输出I/Q原始数据的模块自己写FFT和滤波器这个在下一节展开。2.2 STM32选型从F103到F407的思路STM32家族庞大这个项目用哪款我的建议是如果雷达模块带算法库外设资源要求不高用STM32F103C8T6这种经典款就够跑。但如果你打算自己做信号处理在MCU上跑FFT、滑动平均滤波、峰值检测那就建议上带FPU的型号比如STM32F407VET6或者STM32F411系列。为什么FPU重要因为FFT运算里有大量浮点乘加没有硬件浮点单元的F103只能靠软件模拟算一个256点的FFT要几毫秒功耗和延迟都上去了而M4内核的FPU让浮点运算变成单指令周期256点FFT轻松跑到微秒级。F407主频168MHz带DSP指令集做实时雷达信号处理完全够用还有足够的RAM和Flash存放数据缓冲区。F103和F407的区别还有一个隐藏点F407有两路CAN和更多串口外加DCMI摄像头接口如果你后续想在这个系统上扩展功能比如加个摄像头做联动验证F407的余量就更足了。不过从本项目“先跑通、再优化”的角度F103C8T6也能搭出原型后面如果需要升级CubeMX里换个MCU型号代码基本无缝迁移这正是用HAL库开发的红利。2.3 电路设计要点电源和接口是两个大坑睡眠监测设备是长期通电设备电源设计必须重视纹波。雷达模块对电源噪声敏感如果LDO选得不好输出纹波直接进入射频前端会让回波信号底噪抬高呼吸特征被淹在噪声里。我建议雷达模块单独用一颗低噪声LDO供电比如AMS1117的纹波性能不够换成LDO型号如LP5907或者RT9013PSRR高、噪声低。STM32主控部分可以用另一路供电避免数字电路开关噪声耦合到模拟/射频前端。接口方面雷达模块与STM32之间的通信优先选SPI速率高、时序可控。串口也行但要注意电平匹配很多雷达模块是3.3V或者1.8V电平STM32用3.3V如果模块输出1.8V需要加电平转换否则长期运行有IO口损坏的风险。另外天线布局很关键雷达模块的天线面要朝向床的方向而且尽量远离金属件、电机等强反射源金属床头、手机充电器都会产生固定杂波反射干扰呼吸信号提取。这些细节在原型阶段不觉得到了实际铺床测试时全是坑。3. 核心算法与数据处理从雷达回波到呼吸心跳3.1 雷达原始数据里到底有什么要理解算法先得明白毫米波雷达在睡眠场景下探测到了什么。雷达发射FMCW信号遇到人体胸腔表面反射回来接收端混频后得到中频信号。胸腔随呼吸和心跳周期性起伏这个起伏会改变信号传播路径长度反映到中频信号上就是相位的变化。所以核心思路是提取中频信号特定距离门上的相位信息该相位随时间变化的波形就包含呼吸和心跳信号。相位变化本质上反映了胸腔表面的微动位移。呼吸运动让胸腔位移大约4到12毫米心跳引起的位移只有0.2到0.5毫米两者幅度差了至少一个数量级这就是为什么呼吸信号容易提取心跳信号要费一番功夫的原因。好在频率上两者有区分度呼吸在0.1到0.5Hz之间也就是每分钟6到30次心跳在0.8到2Hz之间每分钟48到120次只要采样够快、FFT够长就有希望在频域把两者分开。如果用的是输出点云或者带算法库的雷达模块情况就简单很多。模块会返回目标的距离、速度和角度信息甚至直接给出呼吸率、心率、体动等级。那你的工作重点就从信号处理变成了数据处理如何处理这些结果并判断睡眠阶段。3.2 在STM32上做FFT的关键细节如果走自研算法路线有一块必须做扎实FFT的实现。STM32上做FFT有几种选择。第一是调用CMSIS-DSP库的arm_cfft_f32这是ARM官方优化的库使用Cortex-M4的FPU和DSP指令效率远高于自己写的实现。第二是用ST官方的DSP库来做实数FFTarm_rfft_fast_f32更贴近实际物理信号因为ADC或雷达输出的实数序列不需要做复数FFT的冗余运算。FFT参数的选择直接决定频率分辨率。睡眠监测要有足够的频率分辨率去区分呼吸和心跳分辨率的计算公式是频率分辨率等于采样率除以FFT点数。假设采样率是50Hz已经能覆盖最高心跳的三次谐波FFT点数取1024频率分辨率就是50/1024约等于0.049Hz换算成每分钟是2.9次这个分辨率足以区分呼吸和心跳的峰值。如果采样率降到20HzFFT点数还是1024分辨率只有1.17次每分钟呼吸和心跳之间虽然还分得开但峰的定位就不够精细了。所以工程上我会选50Hz采样加1024点FFT计算量在F407上毫无压力F103也能承受。FFT之前还要注意一个被很多人忽略的细节加窗。直接对截断后的信号做FFT频谱会发生频谱泄漏本来一个单一频率的呼吸峰会扩散到旁边的一堆频点主峰能量被稀释。加汉宁窗或汉明窗能显著抑制旁瓣泄漏代价是主瓣变宽一点但在睡眠监测这种信号非常窄带的场景里这个代价完全值得。3.3 呼吸率和心率的提取步骤信号处理链路我建议这么做先把雷达输出的I/Q数据做反正切解调得到相位相位信号里有低频漂移和直流偏置需要做带通滤波。呼吸通道用0.1到0.5Hz的带通心跳通道用0.8到2Hz的带通。滤波器可以用IIR的巴特沃斯在STM32上用二阶节级联的方式实现数值稳定性比直接高节数形式好很多。滤波完成之后就到了峰值检测环节。呼吸波形是比较干净的正弦状信号峰值检测很容易计算连续峰值之间的间隔时间换算成次数每分钟即可也可以在FFT结果里找最大值对应的频率点两者可以交叉验证。心跳信号经过带通滤波后依然有毛刺直接用峰值计数容易误判我建议在时域上先做平滑比如滑动窗口平均平滑窗口大约0.2秒然后再做峰值检测这样心率估计值的抖动会小很多。这里有个容易混淆的细节呼吸和心跳虽然频带不同但呼吸的高次谐波可能落在心跳频带内如果用户呼吸急促比如每分钟24次那呼吸的二次谐波是48次每分钟0.8Hz已经在心跳频带边缘了。所以不能只靠频率盲分还要看信号幅度。呼吸谐波幅度通常远小于基波但依然可能干扰峰值检测。工程上一种简单策略是对心跳频带内的信号做幅度阈值判断幅度太小的峰认为是噪声或呼吸谐波直接丢弃。另一种策略是自适应滤波用呼吸基频的整数倍构建参考信号从心跳通道中消掉这属于进阶玩法原型阶段用阈值就足够了。3.4 体动检测和存在性检测怎么处理除了呼吸和心跳睡眠监测里还需要两个关键指标体动和人在不在床上。体动检测在时域上相对直接因为翻身、挥臂这些动作产生的回波幅度远大于呼吸微动可以将原始中频信号的能量或者相位差分能量作为指标。计算一个滑动窗口内相位差分绝对值的累加窗口通常取2到5秒。如果这个值超过预设阈值就认为检测到一次体动。阈值怎么定先采几段安静呼吸的数据作为基线取基线能量的2到3倍作为阈值并且留一个滞后区间避免频繁抖动。存在性检测也很重要有时候人会半夜坐起来或者滑下床雷达探测到却没有体动触发。一个较稳的做法是检测呼吸能量本身。呼吸信号的能量在目标存在时持续存在且稳定一旦人不在床上呼吸能量会掉到接近底噪水平。所以实时统计呼吸频带内信号能量如果能量连续10秒低于阈值就判定为离床这样做既简单又可靠。4. 睡眠阶段判断策略把生理特征翻译成睡眠结构4.1 睡眠分期的基本原理呼吸心跳体动都拿到了接下来就是最有产品价值的部分判断睡眠阶段。医学上睡眠分为清醒期(Wake)、快速眼动期(REM)、浅睡期(Light)和深睡期(Deep)标准方法是多导睡眠监测(PSG)配合脑电等信号。但消费级产品没有脑电只能靠间接特征做估算模型这当然做不到医疗级精准但作为日常健康趋势参考已经足够。基于雷达特征的判断逻辑可以这样组织深睡期心跳最低且稳定呼吸规则而平缓几乎没有体动浅睡期心跳略升呼吸有一定的波动体动偶尔出现REM期心跳和呼吸变得不规则类似于浅睡但体动被肌肉抑制反而很少清醒期心跳高呼吸不规则体动频繁。这个特征集合恰恰是雷达能测到的所以映射关系建立起来之后就可以设计状态机或者打分模型。4.2 状态机还是打分模型原型阶段我更推荐状态机逻辑清晰、调试方便。定一个1分钟的时间片统计每个时间片内的心率均值、呼吸率均值、呼吸节律变异性、体动次数然后按优先级和阈值做状态转移。比如体动次数超过3次且心率高于清醒阈值判为清醒体动少、呼吸率落在深睡区间且心率很低判为深睡其余情况判为浅睡或者REM。REM和浅睡在雷达特征上很难区分在没有额外信号源的情况下可以在状态机里把两者合并为“浅睡/REM”产品端显示时统一处理。状态机的一个风险是边界条件的判定容易震荡。比如心率刚好在阈值附近波动可能导致状态在浅睡和深睡之间反复横跳。解决办法是加入滞回区进入深睡需要心率低于55但退出深睡需要心率升到60以上才触发这一上一下的间隔避免了临界抖动。另外短时间内的状态突变可以先缓存连续三个时间片的判定一致才真正更新状态这样更稳。随着数据积累还能升级为打分模型对心率变异性、呼吸节律变异性、体动频率等特征分别打分加权求和后映射到睡眠质量评分。这个模型的可解释性好后续还能接入用户年龄、作息历史做个性化校准。我在实际项目中是先跑状态机收集了一两个星期的数据再拿这批数据去标定打分模型的权重效果比直接拍脑袋定权重好很多这也是一个数据驱动迭代产品的思路。4.3 数据存储和上报设计的取舍睡眠监测数据要连续记录存储方案有两种思路。一是STM32外接Flash把每分钟的统计结果追加写入优点是设备本地保存、不依赖网络用户早上醒来在设备上就能看昨夜摘要缺点是如果用户想查看历史趋势还得配合App同步。二是模块化设计STM32通过串口或SPI把数据转发给WiFi模块如ESP8266或ESP32实时推送到MQTT服务器再由App拉取。从系统可靠性的角度我建议先本地存再异步上报。睡眠数据是连续整晚的流式数据任何网络闪断都可能导致云端数据不完整本地Flash相当于一级缓冲。Flash的写入注意磨损均衡睡眠数据每分钟一条记录一晚大约8小时就是480条每条可以压缩到紧凑的结构体比如时间戳、心率、呼吸率、体动次数、睡眠阶段各1字节一条记录撑死16字节一晚的存储量不到8KB一片4MB的SPI Flash可以存一年多磨损均衡压力很小。为了保险每条记录前加两个字节的CRC校验防止Flash位翻转导致数据出错。5. 实操过程中的三个高频问题及排查方案5.1 信号质量差呼吸波形时有时无这个问题我遇到得最频繁。现象是串口打印出来的呼吸率数值跳变特别大甚至长时间没有数值但人明明就躺在床上。排查思路是一层层做排除。先把雷达模块天线面和朝向调整好确保主波束指向人的躯干位置而不是头部或腿部。然后是环境干扰排查把房间里可能产生射频干扰的设备暂时关掉比如无线充电座、WiFi路由器看波形是否改善。如果还不行就用示波器或者直接看雷达输出的I/Q信号检查是否有异常的直流偏置或者饱和现象排除硬件连接问题。电源因素是另外一个隐蔽的原因。我用电池供电时信号正常换成插座电源后就出现周期性波动后来发现是USB电源适配器的开关噪声耦合进了雷达前端最终在供电链路里加了一级LC滤波才解决。所以如果你发现信号波形在某个时间段规律性变差优先怀疑电源而非算法。5.2 心率数值偏慢或间歇性丢失心率检测的难点在于信号微弱被呼吸谐波或者环境杂波干扰。如果检测到的心率总是呼吸率的两倍左右大概率是呼吸谐波被误检成了心跳峰。对策是检查带通滤波器的阶数和截止频率。8到12Hz的频带覆盖范围如果太宽谐波就容易混进来。可以把心跳带通滤波器的上限收窄同时加一个幅度阈值要求峰值的幅度达到呼吸峰幅度的10%以上才认为是有效心跳峰。心率间歇性丢失的原因通常有两个。一是用户睡着了翻身之后胸腔相对雷达的朝向改变雷达回波的幅度变弱信号落入噪声基底。解决办法是算法上增加目标角度跟踪或者通过多个距离门的能量做空间多样性哪个距离门能量大就用哪个距离门的信号。二是滑动窗口设置太短心跳信号在频域的能量还没积累起来就被截断了。把FFT窗口从1024点加长到2048点保持50Hz采样时间窗就是约41秒周期性的心跳峰会更加锐利检测稳定性明显提升。5.3 上位机显示延迟和掉线问题睡眠监测系统的上位机显示延迟多半不是MCU处理慢而是数据上报链路的问题。如果走MQTT排查几个典型环节确认WiFi模块和路由器之间的距离信号强度太低会造成重传风暴确认MQTT的QoS级别睡眠监测数据可以接受偶尔丢失QoS 0就够了使用QoS 1会导致消息确认机制在网络抖动时大量堆积确认上位机是否以数据帧方式解析如果客户端收到数据后还要做JSON解析、数据库写入、界面刷新这一整套链路那延迟就不仅受网络影响页面上显示一个红点或一个波形客户端内部的耗时可能比传输耗时更长。建议工程配置是STM32端每30秒组装一条JSON消息通过串口发给ESP32ESP32以原子方式发布到MQTT上位机订阅到消息后直接解析并写入时序数据库前端只展示最近一分钟的波形和当前睡眠状态不做历史数据的全量拉取。这样整个链路从传感器到界面端到端延迟可以控制在一秒以内视觉体验很顺滑。6. 项目扩展方向与个人实操感受这个项目能做深的地方太多了。沿着算法方向扩展可以在现有呼吸心跳数据上做呼吸暂停事件检测这需要检测呼吸波形中的暂停段——呼吸幅度持续低于基线20%并超过10秒如果有周期性反复则可以给出疑似事件提醒。这在医疗级睡眠监测里是很有价值的预警信息。沿着产品方向扩展可以加一个温湿度传感器和人体存在检测联动环境不舒适时自动通过App提醒调节空调把单一监测设备变成一个睡眠环境管理中枢。我在整个项目开发过程中最深的一点体会是毫米波雷达的前端处理能力决定了系统上限。如果你只是把雷达模块当传感器用、拿原始数据之后在MCU里硬抠呼吸心跳信号那门槛确实高但一旦成功你会对信号链路有非常深刻的理解。而如果你选择带算法的雷达模块那你的精力就更应该放在睡眠阶段判断和产品体验打磨上这两者没有优劣之分取决于你的项目目标。还有一个小细节想分享调试睡眠监测算法时最好的测试环境其实是自己家而不是实验室。实验室的电磁环境和床垫材质跟实际住宅差别很大我早期在实验室调好的参数拿到卧室里跑误报率明显上升。后来干脆把自己的床变成了试验台连续采集了三百多个晚上的数据做迭代信号质量阈值、体动检测灵敏度这些参数都是拿真实睡眠数据打磨出来的。做嵌入式产品硬件选型和代码实现固然重要但真正让产品可用的永远是跑在真实场景里那段时间的迭代与修正。这套系统从硬件搭建到算法调通大致需要三到四周的业余时间。如果你手上正好有开发板建议从CubeMX生成一个能读取雷达串口数据的工程开始先打日志观察数据再慢慢加算法你会发现睡眠监测其实并没有想象中那么玄乎它就是一环扣一环的信号处理和状态推理每一层搞清楚整个系统自然就跑起来了。