一、系统概述与核心功能

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=18Pii=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=18Pii=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)上传长期数据,生成趋势报告
  • 多人账户:支持多用户档案管理,追踪个人平衡能力变化
  • 跌倒风险评估:基于摇摆幅度/速度预测跌倒风险,提前预警
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐