ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

智能运动手表固件开发:IMU/PPG采集、计步心率算法与BLE低功耗同步

智能运动手表固件开发:IMU/PPG采集、计步心率算法与BLE低功耗同步 简介围绕可穿戴设备与智能运动手表展开的毕业设计文档面向电子、嵌入式、物联网等专业学生及电子设计竞赛参赛者帮助梳理从需求分析、方案论证到软硬件实现的完整思路。文档共1个docx文件压缩包约749KB以赛题报告形式呈现包含系统功能分析、供电与主控模块选型、传感器与显示方案比较、蓝牙通信及安卓端数据处理等章节。已有143人学习下载适合作为毕业设计选题参考与竞赛作品复盘材料。读者可从中了解ATmega644PA主控、PCF8563时钟、MPU6050计步、BMP180气压温度采集、OLED显示及蓝牙2.1通信的搭配方式也能看到安卓APP对步数、卡路里、运动距离和睡眠数据的二次分析方法。文档还给出电源管理、模块框图和开发环境选择依据便于整理自己的硬件电路与软件架构形成可落地的设计框架。1. 一块表要塞进 IMU、PPG、BLE 和 200mAh 电池难在哪一块 40mm 的运动手表要在表壳里塞下 IMU、PPG 光学心率、GPS、BLE 和一块 200mAh 上下的电池留给固件的功耗预算往往只有几毫安。很多可穿戴设备方向的毕业设计卡在同一个位置传感器能读出数、屏幕能刷新、蓝牙能连上但连跑 30 分钟就掉电步数比手机多记 20%跑步时心率曲线抖成锯齿。智能运动手表和普通手环的分界线在于它要在本地完成滤波、计步、心率估计、卡路里换算再通过 BLE 把结构化结果同步给手机端。这意味着 MCU 选型、采样率配对、算法阈值、任务调度和功耗档位是互相咬死的改一个参数就会牵动另一端。这篇按运动手表真实链路拆开讲硬件怎么选、采样率怎么配、计步和心率算法怎么写、FreeRTOS 怎么分任务省电、BLE 怎么拆服务和缓存数据、最后用什么数据证明精度达标。适合做可穿戴设备毕业设计的同学也适合第一次接手手表固件或算法开发的工程师。2. 智能运动手表硬件选型MCU、PPG 与六轴 IMU 怎么定选型顺序建议是 MCU 先定再定传感器最后配电源链路。原因是本地跑不跑滤波和峰值检测决定 RAM 和算力水位算力水位又决定能不能把 BLE 协议栈和算法塞进同一颗芯片。2.1 主控 MCU 的三条路线与功耗账三条常见路线取舍点不在主频而在睡眠电流和有没有 FPU。路线内核特征运行电流适合场景主要风险低功耗 Cortex-M024MHz无 FPU3mA 档只做计步 BLE 透传浮点滤波吃力靠定点化补Cortex-M4F64~120MHz带 FPU8~15mA本地心率算法 传感器融合睡眠电流必须做到 2μA 以下双核 M4 M0应用核 传感核分核近常开24 小时连续 PPG 监测BOM 和调试复杂度上升多数毕业设计落在第二条路线最划算M4F 能把心率滤波和加速度计融合放进本地手机端只需接收结果。如果只是做计步和消息提醒M0 也够但要接受算法全部定点化调参会更痛苦。提示评估 MCU 时先看 STOP 模式电流和唤醒时间而不是只看主频。手表 90% 时间在睡眠睡眠电流高 1μA续航就差小半天。2.2 PPG 与 IMU 的采样率配对和 I2C 配置PPG 和 IMU 必须共用同一条数据时间轴否则心率曲线和步频曲线对不上。常见做法是 IMU 走 50Hz、PPG 走 25Hz都用同一个 1ms tick 打时间戳。// IMU 与 PPG 挂在同一条 I2C 上地址分开 #define IMU_ADDR 0x68 // 六轴 IMU #define PPG_ADDR 0x57 // PPG 前端 // 1. IMU 配到 50Hz ODR±4g 量程带宽压到 20Hz i2c_write_reg(IMU_ADDR, 0x10, 0x28); // ODR 50Hz i2c_write_reg(IMU_ADDR, 0x11, 0x08); // 满量程 ±4g i2c_write_reg(IMU_ADDR, 0x12, 0x04); // 低通带宽约 20Hz滤掉腕部高频振动 // 2. PPG 前端配到 25HzLED 电流 8mA 档4 路 LED 时分复用 i2c_write_reg(PPG_ADDR, 0x0A, 0x02); // 采样率 25Hz i2c_write_reg(PPG_ADDR, 0x0C, 0x1F); // LED1 驱动电流 i2c_write_reg(PPG_ADDR, 0x0D, 0x1F); // LED2 驱动电流 i2c_write_reg(PPG_ADDR, 0x09, 0x03); // 4 路 LED 轮询模式抗运动伪影这段配置的逻辑是IMU 带宽压到 20Hz 是为了让计步的峰值检测先拿到干净波形把高频抖动交给硬件滤掉PPG 用 4 路 LED 轮询而不是单路是为了在跑步时不同 LED 的穿透深度能互相补偿减少运动伪影导致的误检。参数改动方向很明确量程从 ±4g 调到 ±8g 能适应更剧烈的运动代价是分辨率减半LED 电流从 0x1F 降到 0x11 能省电但弱灌注人群的信号会更差。失败时先看两个地方如果 PPG 波形一直是平线检查 LED 电流和佩戴检测引脚如果 IMU 数据有明显毛刺把带宽寄存器再压到 10Hz 试试。2.3 电源链路与充电管理的最小设计电源链路的最小集合是电池、充电 IC、DCDC 和负载开关。手表和手机最大的不同是待机功耗要压到微安级所以外设尽量用负载开关断电而不是靠软件写寄存器关。常见做法是 PPG 和 GPS 各挂一个负载开关心率任务结束就切掉供电DCDC 选静态电流低的型号关掉轻载下的 PFM 抖动。充电 IC 要关注的是充电电流能不能配到 100mA 以下以及有没有 NTC 温度检测脚毕业设计里经常忽略这个实测充电时表壳温度能到 45℃ 以上。3. 运动手表数据采集与算法落地计步、心率、卡路里怎么算硬件链路通了之后真正决定手表好不好用的是算法层。计步和心率是两个独立问题但都必须处理运动伪影所以放在一起讲。3.1 用加速度计做计步的最小可跑实现计步的本质是在三轴合成幅值上做峰值检测难点在阈值和去抖。下面这段内核在 50Hz ODR 下可跑。#include math.h #include stdint.h #define ACC_BUF_LEN 50 // 1 秒滑动窗口ODR 50Hz #define STEP_TH 0.35f // 幅值阈值单位 g #define STEP_INTERVAL 250 // 最小步间隔单位 ms对应 240 步/分 static float s_buf[ACC_BUF_LEN]; static uint8_t s_idx 0; static uint32_t s_last_step_ms 0; static uint32_t s_steps 0; // 每个采样点回调一次ax/ay/az 单位 gts_ms 为毫秒时间戳 void step_feed(float ax, float ay, float az, uint32_t ts_ms) { float mag sqrtf(ax*ax ay*ay az*az); // 1. 三轴合成幅值 mag - 1.0f; // 2. 去掉重力直流分量 s_buf[s_idx] mag; uint8_t i_cur s_idx; uint8_t i_prev (s_idx ACC_BUF_LEN - 1) % ACC_BUF_LEN; uint8_t i_p2 (s_idx ACC_BUF_LEN - 2) % ACC_BUF_LEN; s_idx (s_idx 1) % ACC_BUF_LEN; // 3. 当前点必须是窗口内的局部峰值且超过阈值 if (s_buf[i_cur] STEP_TH s_buf[i_cur] s_buf[i_prev] s_buf[i_cur] s_buf[(i_cur 1) % ACC_BUF_LEN]) { // 4. 步间隔去抖抖掉腕部佩戴松动造成的连续振荡 if (ts_ms - s_last_step_ms STEP_INTERVAL) { s_steps; s_last_step_ms ts_ms; } } }逻辑说明第一步和第二步把三轴合成幅值归一化重力分量约等于 1g减掉后只剩运动加速度。第三步是峰值判据要求当前点同时大于左右相邻点并超过阈值。第四步是去抖两次有效步之间必须间隔 250ms 以上否则同一次摆臂可能被计成两步。参数说明STEP_TH从 0.35g 调到 0.2g 会变得更灵敏适合慢走但坐姿下手抖可能被误计调到 0.5g 则跑步正常但慢走漏计。STEP_INTERVAL对应步频上限250ms 约等于每分钟 240 步正常跑步不会超。常见误用是直接把阈值写成固定常数遇到不同佩戴松紧就崩。改进方向是用滑动窗口的均值加标准差做自适应阈值代价是增加一点内存。3.2 PPG 心率信号的带通滤波与峰值提取PPG 原始信号里混着呼吸、运动伪影和直流漂移直接找峰值会得到离谱结果。下面是离线验证用的最小实现参数可以直接搬到固件里做定点化。import numpy as np from scipy.signal import butter, filtfilt, find_peaks FS 25.0 # PPG 采样率单位 Hz # 0.5~4Hz 带通对应 30~240 BPM 的心率区间 b, a butter(3, [0.5 / (FS / 2), 4.0 / (FS / 2)], btypeband) def hr_from_ppg(ppg): x ppg - np.mean(ppg) # 1. 去直流漂移 x filtfilt(b, a, x) # 2. 零相位带通避免峰值时间错位 th 0.5 * np.std(x) # 3. 自适应阈值抑制运动伪影误检 peaks, _ find_peaks(x, heightth, distanceint(0.25 * FS)) # 4. 峰间隔下限 if len(peaks) 2: return 0 rr np.diff(peaks) / FS # 5. 相邻峰间隔单位秒 rr rr[(rr 0.25) (rr 2.0)] # 6. 剔除生理上不可能的 RR if len(rr) 0: return 0 return int(round(60.0 / np.median(rr))) # 7. 中位数抗单点野值逻辑说明前两步把信号搬到干净的频带第三步的自适应阈值比固定阈值稳得多因为运动时信号幅度整体会抬高第四步的distance限制相邻峰最小间隔防止把一次搏动的双峰误判成两次心跳第七步用中位数而不是均值是因为单点误检对均值影响很大对中位数几乎不影响。参数说明带通下限从 0.5Hz 调到 0.7Hz 能更好滤掉呼吸伪影但心率低于 42 BPM 的极端情况可能被削掉上限从 4Hz 调到 3Hz 适合日常但高强度间歇跑心率接近 190 BPM 时会截断。失败时看两个现象心率曲线在跑步启动瞬间出现 30 BPM 的跳变多半是运动伪影误检加一层加速度计相关性判断可以缓解心率一直偏低且和步频接近说明带通上限设得太低把运动伪影当成信号了。3.3 卡路里与运动强度换算的参数表卡路里换算常用 MET 公式kcal MET × 体重(kg) × 时长(h)。关键是按运动类型选对 MET 值。运动类型MET 值典型场景备注静坐1.0办公、阅读基线下限慢走 4km/h3.0通勤计步 时长即可估快走 6km/h4.3健走需要速度估计慢跑 8km/h8.3有氧需要 GPS 或步频跑步 10km/h9.8训练心率辅助更准骑行 20km/h8.0户外计步不适用换算前要先做运动分类静止、走、跑、骑行。常见做法是用步频阈值切走路和跑步步频 150 步/分上下是分界再加心率区间做二次确认。这比单看速度稳因为走和跑的加速度波形差异比速度差异更明显。4. 低功耗固件与 BLE 数据同步的工程实现算法跑通之后续航和同步是第二道坎。手表固件的核心矛盾是算法要高频采样但电池只够低频唤醒。4.1 FreeRTOS 任务划分与 tickless 低功耗配置按数据流分三个任务最省心传感器采集、算法处理、BLE 上报。// 传感器任务10ms 周期读 IMU/PPG 并打时间戳 // 算法任务1000ms 周期算心率、步数增量、卡路里 // BLE 任务事件驱动攒够一批再打包上报 void app_tasks_init(void) { xTaskCreate(sensor_task, sensor, 256, NULL, 4, NULL); // 优先级 4 xTaskCreate(hr_task, hr, 256, NULL, 3, NULL); // 优先级 3 xTaskCreate(ble_task, ble, 512, NULL, 2, NULL); // 优先级 2 } // 空闲钩子里下电外设并进 STOP 模式 void vApplicationIdleHook(void) { imu_set_low_power(); // 1. IMU 降到 12.5Hz 低功耗档 ppg_shutdown_if_idle(); // 2. 无佩戴时直接关 PPG __WFI(); // 3. 等中断唤醒进 STOP }逻辑说明优先级 4 3 2 保证采样不被算法和 BLE 抢走采样丢点会导致计步波形断裂。空闲钩子做三件事降 IMU 采样率、按佩戴检测关 PPG、进 STOP 模式。这样平均电流能压到 1mA 上下200mAh 电池能撑 8 天左右。参数说明__WFI()之前必须确认所有 pending 的中断都已处理否则唤醒后会立刻再次进 STOP表现为时间戳跳变BLE 任务的栈给 512 是因为协议栈本身占用较大给 256 会在上报批量数据时溢出。失败时先看时间戳如果采样间隔经常大于配置的 10ms说明传感器任务被抢占需要提高优先级或降低算法任务负载如果 STOP 模式电流比预期高一个量级检查有没有外设时钟没关。4.2 BLE GATT 服务与特征值如何拆分BLE 服务拆分直接决定手机端解析逻辑。优先复用标准服务自定义服务只放原始数据。服务 UUID特征值属性内容0x180D 心率0x2A37Notify心率测量值0x1814 跑步速度与步频0x2A53Notify累计步数、瞬时步频0x180F 电池0x2A19Read Notify电量百分比自定义 0xFE010xFE02Notify三轴 PPG 原始块自定义 0xFE010xFE03Write采样率、上报周期配置拆分的逻辑是标准服务让第三方 App 零成本接入自定义服务留给自家 App 做调试和算法迭代。原始数据块放自定义服务是因为标准服务的心率值已经做过算法无法回溯。参数说明上报周期从 1s 调到 5s 能省电但实时心率曲线会变卡MTU 协商到 247 字节后一包能塞下 4 组三轴 PPG 数据减少连接事件数。4.3 数据缓存与断连重传的实现手表和手机断连是常态缓存策略决定了数据会不会丢。简单办法是做一个 4096 字节的环形缓冲区未确认的数据不出队。常见的坑是把 BLE 的 Notify 当成可靠传输。正确做法是在应用层加一个序列号手机端收到后回 ACK手表端收到 ACK 才推进读指针。断连期间数据继续写缓冲区重连后按序列号续传。容量估算4096 字节按每包 20 字节算能存约 200 条按 5s 一条够撑 16 分钟断连。要撑更久就把缓冲区加大代价是 RAM。5. 答辩前用回放数据核对手表精度答辩演示最怕现场翻车所以精度必须提前用回放数据验证。回放的思路是把采集到的原始数据存成 CSV用同样的算法离线跑一遍对比手算的真值。5.1 用离线回放脚本验证计步与心率# 采集阶段固件把原始 IMU 和 PPG 按行打出格式 time_ms,ax,ay,az,ppg # 回放阶段读 CSV跑同一套算法输出对比 python replay_check.py --csv run_0512.csv --label 走跑交替_10min回放脚本要做的三件事用与固件完全相同的阈值和参数跑一遍把结果和参考设备手机或胸带对齐时间轴逐秒对比误差。这样能排除固件里时间戳或采样率的低级错误。5.2 关键指标表与可接受容差指标测试方法可接受容差常见失分点计步误差走跑各 5 分钟对手机±5% 以内慢走漏计、手抖多计心率误差对比胸带±5 BPM 以内跑步启动瞬间跳变卡路里误差对比代谢仪或间接估算±15% 以内运动分类错误续航满电静置 每天 1 小时运动5 天以上睡眠电流超标重连成功率主动断连 20 次100%缓冲区溢出5.3 答辩现场演示的一个技巧现场演示不要依赖实时采集容易受光线和佩戴影响。更稳的做法是预置三组回放数据一组正常走路、一组跑步、一组断连重连演示时直接播放。播放的同时手算一组步数做对照能直观展示算法一致性。注意回放演示的脚本要提前跑通并缓存结果答辩机器上现装依赖很容易出问题。如果要做成实时演示记得把 PPG 的 LED 电流临时调高一档现场灯光和佩戴松紧对信号的影响比实验室大得多。演示前用湿巾擦一下手表背面的光学窗口汗渍和指纹会显著降低信号质量。本文还有配套的精品资源点击获取
返回列表