基于STM32的便携式多功能人体平衡检测仪设计
基于STM32的便携式人体平衡检测仪以"足底压力采集-重心轨迹计算-平衡能力评估-康复训练指引"为核心,通过压力传感器阵列测量人体站立时足底压力分布,结合MPU6050九轴运动传感器追踪身体姿态,实时计算重心投影轨迹(COP, Center of Pressure),评估前庭系统、本体感觉系统的平衡能力,适用于眩晕门诊筛查、运动员平衡训练、老年人跌倒风险评估、康复训练监测等场景。设备小巧便携(≤A
一、系统概述与核心功能
1. 系统定位
基于STM32的便携式人体平衡检测仪以"足底压力采集-重心轨迹计算-平衡能力评估-康复训练指引"为核心,通过压力传感器阵列测量人体站立时足底压力分布,结合MPU6050九轴运动传感器追踪身体姿态,实时计算重心投影轨迹(COP, Center of Pressure),评估前庭系统、本体感觉系统的平衡能力,适用于眩晕门诊筛查、运动员平衡训练、老年人跌倒风险评估、康复训练监测等场景。设备小巧便携(≤A4纸大小),支持蓝牙上传数据与手机APP联动。
2. 核心功能模块
| 模块 | 功能描述 |
|---|---|
| 压力采集 | 8-12路高精度压力传感器(应变片/薄膜压阻式),采集足底压力分布,精度±0.5%FS |
| 姿态感知 | MPU6050九轴传感器(3轴加速度+3轴角速度+3轴磁力计),融合算法计算身体倾角与角速度 |
| 重心计算 | 实时计算COP轨迹坐标、重心摇摆幅度(RMS)、轨迹总长度、包络面积、平均摇摆速度等量化指标 |
| 运动模式 | 静态模式:睁眼/闭眼站立、Romberg测试;动态模式:单脚站立计时、前后/左右摇摆挑战 |
| 评估体系 | 基于年龄/性别建立常模数据库,输出平衡能力评分(0-100分)与异常部位提示 |
| 数据交互 | 蓝牙BLE(CC2541/DA14531)上传数据至手机APP;Micro USB导出CSV报告 |
| 康复训练 | 内置训练游戏(如"保持小球在屏幕中心"),实时反馈训练效果 |
| 低功耗设计 | 待机电流<50μA,锂电池供电续航≥24小时,无操作后10分钟自动休眠 |
二、硬件设计方案
1. 核心硬件选型
| 模块 | 型号 | 关键参数 | 接口方式 |
|---|---|---|---|
| 主控MCU | STM32L432KC | 80MHz Cortex-M4,超低功耗(80μA/MHz运行),256KB Flash,支持USB DFU升级 | 核心控制器 |
| 压力传感器 | FlexiForce A401(×8) | 量程0-25kg/片,精度±3%,柔性薄膜压阻式,厚度0.2mm,尺寸9×14mm | 模拟信号→AD7795(24位ADC) |
| ADC模块 | AD7795 | 24位Σ-Δ ADC,6路差分输入,内置PGA(×1~×128),SPI接口,温漂<1ppm/℃ | SPI1(PA5=SCK,PA6=MISO,PA7=MOSI,PA4=CS) |
| 姿态传感器 | MPU6050(九轴) | 3轴加速度(±16g),3轴角速度(±2000°/s),3轴磁力计(±4800μT),I2C接口 | I2C1(PB6=SCL,PB7=SDA) |
| 蓝牙模块 | DA14531(BLE 5.1) | 超低功耗,传输距离30m,支持OTA升级,与手机APP通信 | UART1(PA9=TX,PA10=RX) |
| 显示模块 | OLED 12864(I2C) | 0.96寸,128×64像素,自发光,视角>160°,低功耗(<10mA) | I2C1(复用总线,地址0x3C) |
| 交互模块 | 触摸按键×4+振动马达 | 触摸按键(CAP1188低功耗IC),振动马达(训练反馈/异常报警) | I2C2(PC0-PC3)+ GPIO(PC4) |
| 电源模块 | 2000mAh锂电池+TP4056 | 3.7V/2000mAh,TP4056充电管理(5V/1A),XC6206稳压至3.3V,带低电量检测 | 双电源自动切换 |
| 测力平台 | 铝合金踏板(300×200mm) | 厚度3mm,底部安装8个压力传感器(前掌4个+后跟4个),防滑纹理表面 | 机械结构 |
2. 测力平台结构与压力传感器布局
测力平台采用矩形铝合金踏板(300×200×3mm),下方均匀分布8个FlexiForce A401压力传感器,布局如下:
┌───────── 300mm ──────────┐
│ │
┌────┬────┐ ┌────┬────┐
│ P1 │ P2 │ │ P5 │ P6 │ ← 前掌区
└────┴────┘ └────┴────┘
│ │
│ ▲ 重心投影 │
│ │
┌────┬────┐ ┌────┬────┐
│ P3 │ P4 │ │ P7 │ P8 │ ← 后跟区
└────┴────┘ └────┴────┘
│ │
└───────── 200mm ──────────┘
P1-P4: 左脚区域 P5-P8: 右脚区域
重心计算原理(基于8传感器坐标):
XCOP=∑i=18(Pi×xi)∑i=18PiX_{COP} = \frac{\sum_{i=1}^{8} (P_i \times x_i)}{\sum_{i=1}^{8} P_i}XCOP=∑i=18Pi∑i=18(Pi×xi)
YCOP=∑i=18(Pi×yi)∑i=18PiY_{COP} = \frac{\sum_{i=1}^{8} (P_i \times y_i)}{\sum_{i=1}^{8} P_i}YCOP=∑i=18Pi∑i=18(Pi×yi)
其中 PiP_iPi 为第i个传感器的压力值,(xi,yi)(x_i, y_i)(xi,yi) 为传感器坐标。
3. 硬件电路设计要点
3.1 压力采集电路
FlexiForce A401(压阻式)
├── 串联电阻Rref(10kΩ,构建分压电路)
├── 输出电压 Vout = Vin × Rflex / (Rflex + Rref)
└── 运放INA333(仪表放大器,增益×100)→ AD7795差分输入
AD7795配置:
- 通道0-3:左脚4路传感器
- 通道4-7:右脚4路传感器
- 采样率:16.7Hz(符合平衡仪标准)
- 分辨率:24位(约0.00006%FS)
3.2 姿态传感器电路
MPU6050引脚连接:
- VCC → 3.3V
- GND → GND
- SCL → PB6(I2C1)
- SDA → PB7(I2C1)
- INT → PA0(数据就绪中断)
- AD0 → GND(I2C地址0x68)
3.3 抗干扰设计
- 电源隔离:压力采集电路与数字电路通过磁珠(600Ω@100MHz)隔离
- 信号屏蔽:模拟信号走线包地,AD7795电源引脚并联10μF+0.1μF去耦电容
- 接地策略:模拟地(AGND)与数字地(DGND)单点连接(通过0Ω电阻)
- 电磁屏蔽:测力平台底板贴铜箔屏蔽层,接地处理
三、软件设计与核心代码
1. 系统架构(FreeRTOS多任务调度)
采用FreeRTOS实时操作系统,划分7个核心任务(优先级从高到低):
| 任务 | 优先级 | 功能 |
|---|---|---|
| 压力采集任务 | 5 | 通过SPI读取AD7795的8路压力数据,滑动平均滤波 |
| 姿态感知任务 | 4 | 读取MPU6050的加速度/角速度/磁力计数据,互补滤波融合姿态 |
| 重心计算任务 | 3 | 计算COP坐标、RMS摇摆幅度、轨迹总长、包络面积等量化指标 |
| 平衡评估任务 | 3 | 基于年龄/性别常模数据库,输出平衡能力评分与异常提示 |
| 显示更新任务 | 2 | 实时显示COP轨迹图、评分、剩余时间、训练进度 |
| 蓝牙传输任务 | 2 | 打包数据通过BLE上传至手机APP |
| 低功耗管理任务 | 1 | 检测无操作超时,控制休眠/唤醒 |
2. 核心代码实现
2.1 AD7795驱动(SPI通信,24位ADC)
#include "ad7795.h"
#include "spi.h"
// AD7795寄存器地址
#define AD7795_COMMS 0x00 // 通信寄存器
#define AD7795_MODE 0x01 // 模式寄存器
#define AD7795_CONFIG 0x02 // 配置寄存器
#define AD7795_DATA 0x03 // 数据寄存器
#define AD7795_ID 0x04 // ID寄存器
#define AD7795_IO 0x05 // I/O端口寄存器
#define AD7795_OFFSET 0x06 // 偏移校准寄存器
#define AD7795_FULL_SCALE 0x07 // 满量程校准寄存器
// 写AD7795寄存器
void AD7795_WriteReg(uint8_t reg, uint8_t *data, uint8_t len) {
uint8_t cmd = 0x00; // 写操作
cmd |= (reg & 0x07); // 寄存器地址
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS=0
SPI1_WriteByte(cmd);
for (uint8_t i = 0; i < len; i++) {
SPI1_WriteByte(data[i]);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS=1
}
// 读AD7795寄存器
void AD7795_ReadReg(uint8_t reg, uint8_t *data, uint8_t len) {
uint8_t cmd = 0x40; // 读操作
cmd |= (reg & 0x07);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
SPI1_WriteByte(cmd);
for (uint8_t i = 0; i < len; i++) {
data[i] = SPI1_ReadByte();
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}
// 初始化AD7795
uint8_t AD7795_Init(void) {
uint8_t id[1];
AD7795_ReadReg(AD7795_ID, id, 1);
if ((id[0] & 0x0F) != 0x0B) return 1; // ID验证失败
uint8_t config[2];
// 配置寄存器:增益×128,单极性,禁用缓冲
config[0] = 0x10; // 增益=128,单极性
config[1] = 0x00; // 通道0,禁用缓冲
AD7795_WriteReg(AD7795_CONFIG, config, 2);
uint8_t mode[2];
// 模式寄存器:连续转换,16.7Hz采样率
mode[0] = 0x00;
mode[1] = 0x0F; // 16.7Hz
AD7795_WriteReg(AD7795_MODE, mode, 2);
return 0;
}
// 读取单通道数据(24位)
int32_t AD7795_ReadChannel(uint8_t ch) {
uint8_t config[2];
uint8_t data[3];
// 切换通道
AD7795_ReadReg(AD7795_CONFIG, config, 2);
config[1] = (config[1] & 0xF8) | (ch & 0x07); // 设置通道
AD7795_WriteReg(AD7795_CONFIG, config, 2);
// 等待转换完成
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET); // RDY=0
// 读取数据
AD7795_ReadReg(AD7795_DATA, data, 3);
int32_t result = ((int32_t)data[0] << 16) |
((int32_t)data[1] << 8) |
data[2];
return result;
}
// 读取全部8路传感器数据
void AD7795_ReadAllChannels(int32_t *pressure_data) {
for (uint8_t ch = 0; ch < 8; ch++) {
pressure_data[ch] = AD7795_ReadChannel(ch);
// 转换为重量(kg),需根据实际标定系数调整
pressure_data[ch] = AD7795_ToWeight(pressure_data[ch]);
}
}
2.2 重心计算算法(COP计算)
#include "cop_calculator.h"
// 传感器坐标(单位:mm,以踏板中心为原点)
typedef struct {
float x; // X坐标(-150~+150mm)
float y; // Y坐标(-100~+100mm)
} SensorPos_t;
static const SensorPos_t sensor_pos[8] = {
{-75.0f, 50.0f}, // P1: 左前
{ 75.0f, 50.0f}, // P2: 右前
{-75.0f, -50.0f}, // P3: 左后
{ 75.0f, -50.0f}, // P4: 右后
{-75.0f, 50.0f}, // P5: 左前外
{ 75.0f, 50.0f}, // P6: 右前外
{-75.0f, -50.0f}, // P7: 左后外
{ 75.0f, -50.0f}, // P8: 右后外
};
// 压力数据(单位:kg)
float pressure[8] = {0};
// 重心计算结果结构体
typedef struct {
float cop_x; // COP X坐标(mm)
float cop_y; // COP Y坐标(mm)
float total_force; // 总压力(kg)
float rms_x; // X方向RMS摇摆幅度
float rms_y; // Y方向RMS摇摆幅度
float path_length; // 轨迹总长度
float envelope_area; // 包络面积
float sway_velocity; // 平均摇摆速度
} COP_Result_t;
COP_Result_t cop_result;
// 环形缓冲区(存储最近100个COP点)
#define COP_BUF_SIZE 100
float cop_x_buf[COP_BUF_SIZE];
float cop_y_buf[COP_BUF_SIZE];
uint8_t cop_idx = 0;
uint8_t cop_count = 0;
// 计算COP坐标
void COP_Calculate(int32_t *raw_data) {
float sum_x = 0, sum_y = 0;
float total = 0;
// 转换为kg并累加
for (uint8_t i = 0; i < 8; i++) {
pressure[i] = (float)raw_data[i] * CALIB_K[i]; // 标定系数
sum_x += pressure[i] * sensor_pos[i].x;
sum_y += pressure[i] * sensor_pos[i].y;
total += pressure[i];
}
if (total < 1.0f) { // 无人站立
cop_result.cop_x = 0;
cop_result.cop_y = 0;
cop_result.total_force = 0;
return;
}
// 计算COP
cop_result.cop_x = sum_x / total;
cop_result.cop_y = sum_y / total;
cop_result.total_force = total;
// 存入缓冲区
cop_x_buf[cop_idx] = cop_result.cop_x;
cop_y_buf[cop_idx] = cop_result.cop_y;
cop_idx = (cop_idx + 1) % COP_BUF_SIZE;
if (cop_count < COP_BUF_SIZE) cop_count++;
}
// 计算RMS摇摆幅度
void COP_CalcRMS(void) {
if (cop_count < 10) return;
float mean_x = 0, mean_y = 0;
for (uint8_t i = 0; i < cop_count; i++) {
mean_x += cop_x_buf[i];
mean_y += cop_y_buf[i];
}
mean_x /= cop_count;
mean_y /= cop_count;
float var_x = 0, var_y = 0;
for (uint8_t i = 0; i < cop_count; i++) {
float dx = cop_x_buf[i] - mean_x;
float dy = cop_y_buf[i] - mean_y;
var_x += dx * dx;
var_y += dy * dy;
}
cop_result.rms_x = sqrtf(var_x / cop_count);
cop_result.rms_y = sqrtf(var_y / cop_count);
}
// 计算轨迹总长度
void COP_CalcPathLength(void) {
if (cop_count < 2) return;
float path = 0;
for (uint8_t i = 1; i < cop_count; i++) {
float dx = cop_x_buf[i] - cop_x_buf[i-1];
float dy = cop_y_buf[i] - cop_y_buf[i-1];
path += sqrtf(dx*dx + dy*dy);
}
cop_result.path_length = path;
}
// 计算包络面积(凸包面积,简化版)
void COP_CalcEnvelopeArea(void) {
if (cop_count < 3) return;
// 找到边界点(X/Y最大最小值)
float min_x = cop_x_buf[0], max_x = cop_x_buf[0];
float min_y = cop_y_buf[0], max_y = cop_y_buf[0];
for (uint8_t i = 1; i < cop_count; i++) {
if (cop_x_buf[i] < min_x) min_x = cop_x_buf[i];
if (cop_x_buf[i] > max_x) max_x = cop_x_buf[i];
if (cop_y_buf[i] < min_y) min_y = cop_y_buf[i];
if (cop_y_buf[i] > max_y) max_y = cop_y_buf[i];
}
// 简化:用矩形面积近似
cop_result.envelope_area = (max_x - min_x) * (max_y - min_y);
}
2.3 MPU6050姿态感知(互补滤波)
#include "mpu6050.h"
#include "i2c.h"
// MPU6050初始化
uint8_t MPU6050_Init(void) {
uint8_t who_am_i;
HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, 0x75, 1, &who_am_i, 1, 100);
if (who_am_i != 0x68) return 1;
// 唤醒MPU6050
uint8_t data = 0x00;
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, 0x6B, 1, &data, 1, 100);
// 配置加速度量程±2g,角速度量程±2000°/s
data = 0x00; // ACCEL_CONFIG: ±2g
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, 0x1C, 1, &data, 1, 100);
data = 0x18; // GYRO_CONFIG: ±2000°/s
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, 0x1B, 1, &data, 1, 100);
return 0;
}
// 读取加速度和角速度
void MPU6050_ReadRaw(int16_t *ax, int16_t *ay, int16_t *az,
int16_t *gx, int16_t *gy, int16_t *gz) {
uint8_t buf[14];
HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, 0x3B, 1, buf, 14, 100);
*ax = (buf[0]<<8) | buf[1];
*ay = (buf[2]<<8) | buf[3];
*az = (buf[4]<<8) | buf[5];
*gx = (buf[8]<<8) | buf[9];
*gy = (buf[10]<<8) | buf[11];
*gz = (buf[12]<<8) | buf[13];
}
// 互补滤波计算倾角
typedef struct {
float pitch; // 俯仰角(前后方向)
float roll; // 横滚角(左右方向)
float yaw; // 偏航角
} EulerAngle_t;
EulerAngle_t euler;
void ComplementaryFilter(int16_t ax, int16_t ay, int16_t az,
int16_t gx, int16_t gy, int16_t gz,
float dt) {
// 加速度计计算的倾角(弧度)
float accel_pitch = atan2f((float)-ax, sqrtf((float)ay*ay + (float)az*az));
float accel_roll = atan2f((float)ay, (float)az);
// 角速度转换为弧度/秒
float gyro_pitch = (float)gx * 2000.0f / 32768.0f * M_PI / 180.0f;
float gyro_roll = (float)gy * 2000.0f / 32768.0f * M_PI / 180.0f;
float gyro_yaw = (float)gz * 2000.0f / 32768.0f * M_PI / 180.0f;
// 互补滤波(加速度权重0.02,角速度权重0.98)
euler.pitch = 0.98f * (euler.pitch + gyro_pitch * dt) + 0.02f * accel_pitch;
euler.roll = 0.98f * (euler.roll + gyro_roll * dt) + 0.02f * accel_roll;
euler.yaw += gyro_yaw * dt;
// 弧度转度数
euler.pitch *= 180.0f / M_PI;
euler.roll *= 180.0f / M_PI;
euler.yaw *= 180.0f / M_PI;
}
2.4 平衡能力评估算法
#include "balance_assessment.h"
// 年龄组常模数据(简化版,单位:mm)
typedef struct {
uint8_t age_group; // 年龄段(1=20-29, 2=30-39, 3=40-49, 4=50-59, 5=60+)
float rms_x_normal; // 睁眼X方向正常值
float rms_y_normal; // 睁眼Y方向正常值
float rms_x_closed; // 闭眼X方向正常值
float rms_y_closed; // 闭眼Y方向正常值
float path_normal; // 睁眼轨迹长度正常值
float area_normal; // 睁眼包络面积正常值
} NormData_t;
static const NormData_t norm_table[5] = {
// 年龄组, RMS_X(睁), RMS_Y(睁), RMS_X(闭), RMS_Y(闭), Path, Area
{1, 3.5f, 2.8f, 4.2f, 3.6f, 80.0f, 150.0f}, // 20-29岁
{2, 4.0f, 3.2f, 4.8f, 4.1f, 95.0f, 180.0f}, // 30-39岁
{3, 4.8f, 3.8f, 5.8f, 4.9f, 112.0f, 220.0f}, // 40-49岁
{4, 5.8f, 4.5f, 7.2f, 6.0f, 135.0f, 280.0f}, // 50-59岁
{5, 7.5f, 6.0f, 9.5f, 8.0f, 170.0f, 380.0f}, // 60岁+
};
// 评估得分(0-100分)
typedef struct {
float score; // 总分
uint8_t level; // 等级(1=优秀,2=良好,3=一般,4=较差,5=很差)
uint8_t anomaly_part; // 异常部位(0=无,1=前庭系统,2=本体感觉)
char report[128]; // 评估报告文本
} AssessmentResult_t;
AssessmentResult_t assessment;
// 获取年龄段索引
uint8_t GetAgeGroup(uint8_t age) {
if (age <= 29) return 0;
if (age <= 39) return 1;
if (age <= 49) return 2;
if (age <= 59) return 3;
return 4;
}
// 睁眼/闭眼模式评估
void Balance_Assess(uint8_t age, uint8_t gender, uint8_t eye_status) {
uint8_t group = GetAgeGroup(age);
const NormData_t *norm = &norm_table[group];
// 计算偏离率
float dev_x, dev_y;
if (eye_status == 0) { // 睁眼
dev_x = cop_result.rms_x / norm->rms_x_normal;
dev_y = cop_result.rms_y / norm->rms_y_normal;
} else { // 闭眼
dev_x = cop_result.rms_x / norm->rms_x_closed;
dev_y = cop_result.rms_y / norm->rms_y_closed;
}
float dev_path = cop_result.path_length / norm->path_normal;
float dev_area = cop_result.envelope_area / norm->area_normal;
// 综合偏离率(加权平均)
float total_dev = 0.3f*dev_x + 0.3f*dev_y + 0.2f*dev_path + 0.2f*dev_area;
// 计算得分(偏离率越低得分越高)
assessment.score = 100.0f * expf(-total_dev * 0.8f);
if (assessment.score > 100.0f) assessment.score = 100.0f;
// 确定等级
if (assessment.score >= 90) assessment.level = 1; // 优秀
else if (assessment.score >= 75) assessment.level = 2; // 良好
else if (assessment.score >= 60) assessment.level = 3; // 一般
else if (assessment.score >= 40) assessment.level = 4; // 较差
else assessment.level = 5; // 很差
// 判断异常部位
if (eye_status == 1 && dev_x > 1.5f && dev_y > 1.5f) {
assessment.anomaly_part = 1; // 前庭系统异常(闭眼时摇摆显著增加)
} else if (dev_x > 1.5f || dev_y > 1.5f) {
assessment.anomaly_part = 2; // 本体感觉异常
} else {
assessment.anomaly_part = 0; // 正常
}
// 生成报告
const char *level_str[] = {"优秀", "良好", "一般", "较差", "很差"};
snprintf(assessment.report, sizeof(assessment.report),
"评估得分: %.1f分\n等级: %s\n"
"X摇摆: %.1fmm, Y摇摆: %.1fmm\n轨迹长: %.1fmm",
assessment.score, level_str[assessment.level-1],
cop_result.rms_x, cop_result.rms_y, cop_result.path_length);
}
2.5 主程序框架(FreeRTOS任务调度)
#include "stm32l4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
// 全局变量
int32_t pressure_raw[8];
QueueHandle_t pressure_queue;
QueueHandle_t cop_queue;
// 压力采集任务
void Pressure_Task(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(60); // ~16.7Hz采样
while (1) {
AD7795_ReadAllChannels(pressure_raw);
COP_Calculate(pressure_raw);
COP_CalcRMS();
COP_CalcPathLength();
COP_CalcEnvelopeArea();
xQueueSend(cop_queue, &cop_result, 0);
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
// 姿态感知任务
void IMU_Task(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = pdMS_TO_TICKS(10); // 100Hz采样
uint32_t last_time = 0;
while (1) {
int16_t ax, ay, az, gx, gy, gz;
MPU6050_ReadRaw(&ax, &ay, &az, &gx, &gy, &gz);
uint32_t current_time = HAL_GetTick();
float dt = (current_time - last_time) / 1000.0f;
last_time = current_time;
ComplementaryFilter(ax, ay, az, gx, gy, gz, dt);
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 80MHz
// 初始化外设
MX_SPI1_Init(); // AD7795
MX_I2C1_Init(); // MPU6050/OLED
MX_USART1_UART_Init(); // 蓝牙BLE
MX_GPIO_Init(); // 触摸按键/振动马达
// 初始化模块
AD7795_Init();
MPU6050_Init();
OLED_Init();
BLE_Init();
// 创建队列和任务
cop_queue = xQueueCreate(10, sizeof(COP_Result_t));
xTaskCreate(Pressure_Task, "Pressure", 256, NULL, 5, NULL);
xTaskCreate(IMU_Task, "IMU", 256, NULL, 4, NULL);
xTaskCreate(COP_Display_Task, "Display", 512, NULL, 3, NULL);
xTaskCreate(BLE_Transfer_Task, "BLE", 256, NULL, 2, NULL);
xTaskCreate(Assessment_Task, "Assess", 256, NULL, 3, NULL);
xTaskCreate(LowPower_Task, "LowPower", 128, NULL, 1, NULL);
vTaskStartScheduler();
while (1);
}
四、关键技术与优化
1. 重心计算精度优化
- 传感器标定:每个传感器独立标定,建立压力-电压转换系数矩阵 KiK_iKi
- 滑动平均滤波:8点滑动平均消除高频噪声
- 异常值剔除:3σ原则排除离群压力值
- 非线性补偿:FlexiForce传感器非线性校正(二次多项式拟合)
2. 姿态融合算法优化
- 自适应互补滤波:根据运动强度动态调整加速度/角速度权重
- 磁力计补偿:加入HMC5883L磁力计,消除Z轴漂移
- 零偏校准:上电时静置5秒,自动校准陀螺仪零偏
3. 低功耗设计
- 动态采样率:无人站立时降低采样率至5Hz,有人时升至16.7Hz
- 模块分时供电:AD7795/MPU6050/蓝牙模块在不使用时间段电
- STOP模式:无操作10分钟后进入STOP模式(电流<10μA),触摸按键中断唤醒
参考代码 便携式多功能动人体平衡检测仪 www.youwenfan.com/contentcst/133255.html
五、系统调试与扩展
1. 调试步骤
| 阶段 | 操作 | 工具 |
|---|---|---|
| 硬件调试 | 测量各传感器供电电压,验证SPI/I2C通信 | 万用表、示波器 |
| 压力标定 | 用标准砝码(1kg/5kg/10kg)标定各传感器 | 标准砝码、串口打印 |
| COP验证 | 在踏板不同位置站立,验证COP坐标准确性 | 卷尺测量实际位置 |
| 姿态测试 | 倾斜踏板验证倾角计算精度 | 倾角仪、串口打印 |
| 功耗测试 | 测量运行/待机/STOP模式电流 | 万用表(串联测量) |
2. 扩展功能
- 动态平衡测试:添加足部压力薄膜传感器阵列,测量行走时足底压力分布
- 康复训练游戏:开发"平衡走钢丝""重心跟踪球"等训练游戏,提升趣味性
- 云数据分析:通过Wi-Fi(ESP8266)上传长期数据,生成趋势报告
- 多人账户:支持多用户档案管理,追踪个人平衡能力变化
- 跌倒风险评估:基于摇摆幅度/速度预测跌倒风险,提前预警
更多推荐



所有评论(0)