资讯动态

基于STM32与ADXL345的穿戴式运动识别系统设计

发布时间:2026/10/28 3:15:39 来源:尧图企业网站定制
简介面向可穿戴健康监测方向的学生与工程实践者这份文档围绕手腕佩戴场景给出了一套融合三轴加速度传感器与生物传感器的运动及心率监测系统设计方案可用于课程设计、毕业课题或小型硬件项目参考。压缩包内为1个docx文件约283KB集中呈现系统总体架构与各模块电路设计。内容从亚健康与运动强度失当的现实问题切入依次展开硬件总体方案、基于ADXL345的加速度采集与IIC接口设计、基于MAX30100的光体积容积法心率与血氧检测、LM1117-3.3稳压电源以及SD卡数据存储电路并说明借助MATLAB对加速度信号分析以识别散步、跑步、跳绳、羽毛球、骑单车等运动。读者可据此理清传感器选型依据、接口连接与低功耗设计思路获得从数据采集到运动识别、生命体征监测的完整实现脉络。目前已有187人学习。1. 手腕上那点加速度到底能识别出什么运动去年帮一个做硬件的朋友看一块腕带板子他抱怨说加速度计数据“全是毛刺根本没法用”。我让他把原始三轴数据导出来画了个图散步和跑步的波形一眼就能分开跳绳的竖直方向周期性尖峰更是明显得不行。问题从来不在传感器而在于没人把原始信号到运动标签这条链路走通。这份《穿戴式运动及心率监测系统的研究》文档做的就是这件事用一颗 ADXL345 三轴加速度传感器加一颗 MAX30100 生物传感器挂在手腕上识别散步、跑步、跳绳、羽毛球、骑单车五类运动同时采集心率把运动强度和生命体征绑在一起看。适合正在做可穿戴设备原型、运动识别算法入门、或者想把 STM32 采集链路跑通的开发者。它不是一个成品固件包而是一套从硬件选型、数据采集、预处理、特征提取到阈值分类器的完整设计思路照着能把一条最小可用的腕带监测链路搭起来。2. 硬件架构拆解五颗芯片如何拼出一条腕带链路整套硬件的核心诉求很朴素能戴在手腕上、体积小、功耗低、数据能存下来。围绕这个诉求系统拆成五个模块——STM32F103 主控加最小系统、ADXL345 加速度采集、MAX30100 心率血氧采集、蓝牙传输、SD 卡存储。下面按信号流向把每个模块的选型理由和接线要点讲清楚这部分直接决定后面软件能不能跑通。2.1 主控与传感器选型为什么是 STM32F103 ADXL345 MAX30100主控选 STM32F103 是这类腕带项目的常见做法Cortex-M3 内核、片上资源够用、3.3V 单电源、开发资料多跑传感器采集和 SD 卡写入这种任务绰绰有余。真正需要斟酌的是两颗传感器。ADXL345 的关键参数值得单独列一下因为后面配置寄存器时全靠这些参数数值对项目的意义分辨率13 位手腕细微抖动也能分辨利于特征提取量程±16g跳绳、羽毛球挥拍这类冲击不会削顶输出格式16 位二进制补码软件解析时要注意符号扩展通信接口SPI3/4 线或 I2C本项目选 I2C省 IO器件地址0x1D7 位不含最低位写 0x3A、读 0x3B选它的核心理由是低功耗模式加小封装腕带设备对这两点极其敏感。MAX30100 则是集成脉搏血氧和心率检测的模块内部两颗 LED 加一个光电探测器用光体积容积法PPG测脉搏3.3V 供电待机电流可忽略戴在手腕、手指、耳垂这些较薄的部位都能工作。两颗传感器都用 I2C这意味着总线要复用接线时得留意地址冲突。2.2 I2C 总线接线与地址配置三根线背后的坑ADXL345 和 STM32 之间实际只需要三根线SDA、SCL、3D INT。SDA/SCL 走 I2CINT 是中断输出线主控靠它触发数据就绪读取避免轮询浪费功耗。ADXL345 的 ADDR 地址线接高电平器件地址定为 0x1D所以写操作是 0x3A、读操作是 0x3B。这个细节在文档里写得很明确但实际接线时最容易翻车——如果 ADDR 悬空或者接低地址就变成 0x53代码里写死的 0x3A 会一直 NACK。初始化 ADXL345 的代码大致长这样用 HAL 库的 I2C 接口/* ADXL345 初始化进入测量模式设置量程与数据速率 */ #define ADXL345_ADDR_W 0x3A #define ADXL345_ADDR_R 0x3B #define ADXL345_POWER_CTL 0x2D #define ADXL345_DATA_FORMAT 0x31 #define ADXL345_BW_RATE 0x2C void adxl345_init(I2C_HandleTypeDef *hi2c) { uint8_t val; /* 0x08 Measure 位退出待机进入测量模式 */ val 0x08; HAL_I2C_Mem_Write(hi2c, ADXL345_ADDR_W, ADXL345_POWER_CTL, I2C_MEMADD_SIZE_8BIT, val, 1, 100); /* 0x0B 全分辨率 ±16g 量程 */ val 0x0B; HAL_I2C_Mem_Write(hi2c, ADXL345_ADDR_W, ADXL345_DATA_FORMAT, I2C_MEMADD_SIZE_8BIT, val, 1, 100); /* 0x0A 100Hz 输出数据速率够覆盖跳绳的高频分量 */ val 0x0A; HAL_I2C_Mem_Write(hi2c, ADXL345_ADDR_W, ADXL345_BW_RATE, I2C_MEMADD_SIZE_8BIT, val, 1, 100); }这段代码做了三件事POWER_CTL 写 0x08 让芯片从待机进入测量DATA_FORMAT 写 0x0B 打开全分辨率并设 ±16gBW_RATE 写 0x0A 把输出速率定在 100Hz。100Hz 这个值不是随便定的——跳绳时手腕竖直方向动作频率能到 3~5Hz按奈奎斯特定理采样率至少要是信号频率的两倍以上100Hz 留足了余量同时又不至于让 SD 卡写入压力过大。参数要改的话量程调小比如 ±4g能提高小动作的分辨率但跳绳冲击可能削顶我一般宁可留大量程靠软件归一化。2.3 电源与存储LM1117-3.3 和 SD 卡的取舍电源部分设计得很克制5V 锂电池供电LM1117-3.3 稳压到 3.3V主控、两颗传感器、蓝牙模块全部吃 3.3V不需要其他电压轨。这种“单一电压域”的设计对腕带设备是加分项少一路电源就少一处噪声源和故障点。LM1117 是线性稳压效率不如开关电源但输出纹波小对 MAX30100 这种测微弱光电流的传感器更友好——PPG 信号本身就很弱电源噪声一大心率读数就开始飘。SD 卡存储电路的作用是把一段时间内的加速度和心率数据落盘供后续上传到计算机训练分类器。这里有个设计取舍值得说实时识别和离线训练是两条路。文档里的方案是采集存卡、上传 MATLAB 分析也就是说运动识别算法先在 PC 上跑通再考虑往 MCU 上搬。对原型阶段这是对的MATLAB 调阈值、看波形比在单片机上 printf 调试快得多。等分类器稳定了再把阈值判断逻辑固化成 C 代码烧进去。3. 软件与算法链路从原始三轴数据到运动标签硬件只负责把信号采回来真正决定识别率的是软件链路。文档把算法拆成三大部分原始数据预处理、特征参数提取、分类器设计。这条链路每一步都有明确的工程目的不是学术凑数下面逐段拆。3.1 数据预处理三步滤波、归一化、加窗滤波解决的是手腕抖动带来的毛刺。设备戴在手腕上肌肉抖动、设备机械振动都会污染加速度和脉搏信号。方案用卡尔曼滤波核心思想是用前一时刻的估计值加当前时刻的观测值递推算出当前最优估计准则是最小均方根误差。选卡尔曼而不是简单的滑动平均是因为它对动态信号跟随性好跑步时加速度快速变化滑动平均会把有用分量也抹平。归一化解决的是个体差异。不同人做同一个动作幅度和速度都不一样同一个人不同次做加速度值也不确定。把原始数据统一映射到 [-1, 1] 区间分类器才不会因为某个人动作大就误判。这一步是让“训练出来的分类器适用于所有个体”的关键不做归一化阈值就得按人调产品化无从谈起。加窗解决的是数据太长没法直接提特征的问题。连续采集的信号在时间域上切成等长窗口本项目采用滑动窗口分割相邻窗口重叠率 50%。为什么是 50%文档引用的结论是 50% 重叠识别效果最好——重叠太少会漏掉窗口边界处的动作重叠太多计算量翻倍收益递减。另外心率信号要做同样的加窗保证加速度和脉搏在时间上对齐后面才能做数据融合。用 Python 把这三步串起来大概是这样方便在 PC 上先验证import numpy as np from scipy.signal import butter, filtfilt def preprocess(acc_xyz, fs100, win_sec2.0, overlap0.5): # 1) 归一化按三轴合加速度的峰值缩放到 [-1, 1] norm np.max(np.linalg.norm(acc_xyz, axis1)) acc_norm acc_xyz / (norm 1e-6) # 2) 低通滤波去手腕高频抖动截止 20Hz b, a butter(4, 20 / (fs / 2), btypelow) acc_filt filtfilt(b, a, acc_norm, axis0) # 3) 滑动窗口50% 重叠 win int(win_sec * fs) step int(win * (1 - overlap)) windows [acc_filt[i:i win] for i in range(0, len(acc_filt) - win 1, step)] return acc_filt, windows这里用 Butterworth 低通替代卡尔曼做演示是因为 PC 端离线处理用 filtfilt 零相位滤波更直观上 MCU 时再换回卡尔曼递推两者目的都是去毛刺。win_sec2.0表示每个窗口 2 秒overlap0.5对应文档的 50% 重叠fs100和硬件采样率对齐。窗口长度是个要调的参数太短特征不稳太长动作混叠2 秒对散步跑步这类周期性动作是常见起点。3.2 特征参数提取四个特征怎么对应五种运动文档的特征设计很务实没有堆上百种特征而是先分析动作差异再定特征。散步和跑步的区别在水平速度和手臂摆动频率骑单车水平速度更快但频率几乎不变匀速骑行跳绳水平速度接近 0、频率快且稳定羽毛球有短暂静止速度为 0加挥拍高频。基于这个分析提取四个特征滑动窗口平均速度、最大速度、最小速度、最大频率。对应公式就是均值、最大值、最小值、最大频率四个统计量。这套特征的精妙之处在于四个数就能把五种运动在特征空间里分开平均速度分散步/跑步/骑车三档频率分跳绳/羽毛球极值辅助区分羽毛球挥拍和静止段。特征少意味着 MCU 上计算量小实时性好这是嵌入式算法该有的克制。3.3 多层阈值分类器不用神经网络的轻量方案分类器用的是基于多层阈值的方法本质是一棵手写的决策树。文档给的阈值逻辑是先看水平方向平均速度小于 7 判散步7 到 15 判跑步大于 15 判骑车再用竖直方向运动频率区分羽毛球和跳绳。判断出运动状态后结合心率数据评估身体状态。/* 多层阈值分类输入窗口特征输出运动类型枚举 */ typedef enum { WALK, RUN, BIKE, BADMINTON, ROPE, UNKNOWN } motion_t; motion_t classify(float mean_v, float max_freq) { if (mean_v 7.0f) { return WALK; } else if (mean_v 15.0f) { return RUN; } else { /* 高速段再按频率细分骑车频率低且稳跳绳频率高 */ if (max_freq 2.5f) { return ROPE; } return BIKE; } }这段是简化示意实际还要加羽毛球分支靠极值速度接近 0 加高频挥拍判断。阈值 7 和 15 是文档给的单位取决于归一化后的速度定义换传感器量程或归一化方式后必须重新标定不能照抄。这也是阈值分类器的软肋可解释性强、算力需求低但阈值对个体和佩戴松紧敏感产品化时通常要加一层自适应校准。不过对原型和课程项目这套方案足够跑通闭环而且每一步都能在 MATLAB 里画图验证调试体验比黑盒模型好太多。4. 避坑与排查腕带原型最容易翻车的五个地方这套链路我在类似项目里踩过的坑不少挑五个最典型的按“现象 → 原因 → 解决”记下来照着排查能省不少时间。现象一I2C 读 ADXL345 一直返回 0xFF 或 NACK。原因多半是器件地址不对——ADDR 引脚悬空或接低时地址变成 0x53代码里写死 0x3A 自然读不到。解决先用 I2C 扫描工具扫一遍总线确认实际地址再检查 ADDR 是否可靠接高电平。现象二心率读数剧烈跳动静坐时也在 40 到 120 之间乱蹦。原因是 PPG 信号弱电源纹波或佩戴松动都会污染。解决先确认 LM1117 输出端滤波电容到位再检查 MAX30100 是否贴合皮肤、有没有漏光软件上加中值滤波或滑动平均压一下野值。现象三跳绳识别成跑步或者羽毛球识别成散步。原因是阈值没按实际数据标定或者归一化把幅度信息抹掉了。解决把每个动作的窗口特征导出来画散点图看特征空间里到底有没有重叠重叠严重就加特征比如竖直方向方差别硬调阈值。现象四SD 卡写入偶尔丢数据。原因是 100Hz 采样下写入频率高SD 卡单次写延迟抖动大缓冲区不够就溢出。解决采集和写入解耦用环形缓冲区加 DMA写入放低优先级任务或者先降采样率到 50Hz 验证链路再逐步提上去。现象五换个人戴识别率断崖式下跌。原因是归一化只按峰值缩放没考虑佩戴位置和松紧差异。解决加一段静止校准开机时让人静站几秒用重力分量估计佩戴姿态把三轴旋转到统一坐标系再归一化。提示这五个坑里前两个是硬件接线问题后三个是算法标定问题。排查顺序建议先硬件后算法I2C 都读不通就别急着调阈值。5. 二次开发把 MATLAB 原型搬上 MCU 的改造路径这套设计最值得二次开发的地方是它天然分成“离线验证”和“在线识别”两段。文档的方案是采集存 SD 卡、上传 MATLAB 分析这对原型阶段完全够用但真要做成能实时出结果的腕带就得把算法往 MCU 上搬。我一般按下面这条路径改风险最低。第一步先在 PC 上把整条链路跑成脚本读 SD 卡导出的 CSV做预处理、提特征、跑阈值分类和人工标注对比算准确率。这一步的目的是确认特征和阈值在你的数据上成立别急着写 C。第二步把预处理和特征提取逐函数翻译成 C在 MCU 上用固定样本回放验证输出和 Python 结果逐窗口比对误差超过 1% 就查浮点精度或滤波系数。第三步把分类器固化成状态机加上心率融合逻辑——文档里“不同运动对应不同心率范围输出预警”这部分落地时建议做成可配置的阈值表别写死在代码里。改造项原型做法产品化建议数据通路存 SD 卡后离线分析环形缓冲 实时窗口计算滤波MATLAB 离线卡尔曼MCU 上定点化递推注意溢出阈值文档固定值 7/15开机静止校准 可配置表心率融合人工对照运动强度分级 越界告警有个具体技巧值得单独说阈值分类器最怕换人换佩戴就失准我习惯在固件里留一个“标定模式”长按按键进入让人按提示做 10 秒散步、10 秒跑步固件自动记录这两个动作的平均速度分布把散步/跑步的分界阈值定在两者中点。这样每台设备出厂前或换用户时跑一遍比全局固定阈值稳得多。代价是多一个标定流程但换来的是识别率不随人崩这笔账划算。还有个验证习惯每次改完特征或阈值我都会把同一段原始数据分别喂给旧版和新版算法把两次输出的运动标签按时间轴画出来对比看差异集中在哪些窗口。差异集中在动作切换的边界窗口是正常的因为窗口重叠导致边界模糊差异散落在动作中段就说明特征或阈值有问题。这个对比图比单看准确率有用得多准确率涨了两个点但边界全乱这种“提升”不要也罢。从那以后我每次把离线算法往 MCU 上搬都强制先做一遍逐窗口数值比对Python 和 C 的输出对不上就绝不往下走血泪经验是曾经跳过这步结果浮点转定点时一个系数精度不够跑步全被判成骑车查了两天才定位到。希望这套拆解能帮你把腕带这条链路顺利跑起来。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑