基于STM32F103C8T6的智能健身姿态矫正系统设计与华为云实现

举报
DS小龙哥 发表于 2025/08/25 11:10:25 2025/08/25
【摘要】 本系统通过高精度传感器实时捕捉人体运动数据,利用边缘计算能力进行本地姿态分析,并通过触觉反馈实现无干扰的实时矫正。进一步地,通过接入华为云平台实现训练数据的云端存储与分析,为用户提供长期趋势追踪和科学建议,突破了传统健身设备的单一功能性局限。

 

项目开发背景

随着现代生活节奏加快和健康意识提升,健身已成为许多人日常生活的重要组成部分。然而,在自主训练过程中,由于缺乏专业指导,训练者往往难以确保动作的规范性,错误姿势的长期积累不仅会降低训练效果,还可能引发运动损伤。传统健身辅助设备通常依赖视觉或音频反馈,存在实时性不足、干扰性强等局限,难以满足高效训练的需求。

在此背景下,结合嵌入式技术与物联网的智能健身矫正系统应运而生。本系统通过高精度传感器实时捕捉人体运动数据,利用边缘计算能力进行本地姿态分析,并通过触觉反馈实现无干扰的实时矫正。进一步地,通过接入华为云平台实现训练数据的云端存储与分析,为用户提供长期趋势追踪和科学建议,突破了传统健身设备的单一功能性局限。

该系统以STM32F103C8T6为核心控制器,融合MPU6050和JY901多轴传感器的数据优势,既保证了基础姿态检测的实时性,又通过九轴传感器提升了复杂动作的解析精度。振动电机模块提供非侵入式的即时反馈机制,使训练者能够在不中断运动的前提下调整姿势。借助ESP8266模块的无线传输能力,系统将结构化数据上传至华为云平台,为后端分析提供数据基础。

最终通过QT开发的跨平台上位机软件,实现3D运动轨迹可视化、生成个性化训练报告及改进建议,形成从数据采集、实时干预到长期优化的完整闭环。该系统不仅适用于日常健身人群,也可为康复训练和专业运动员提供技术支撑,体现了智能硬件与云计算在健康领域的深度融合价值。

设计实现的功能

(1)实时采集人体运动姿态数据并进行姿态分析
(2)检测健身动作标准度并给出实时振动反馈
(3)训练数据及姿态分析结果上传至华为云平台
(4)QT上位机显示3D运动轨迹、生成训练报告和改进建议

项目硬件模块组成

(1)STM32F103C8T6最小系统核心板作为主控制器
(2)MPU6050六轴传感器采集姿态数据
(3)JY901九轴姿态传感器提供高精度运动数据
(4)振动电机模块提供实时触觉反馈
(5)ESP8266-01S Wi-Fi模块实现华为云平台通信
(6)洞洞板焊接传感器接口电路,杜邦线连接各模块

设计意义

基于STM32F103C8T6的智能健身姿态矫正系统设计旨在通过实时数据采集和分析,提升个人健身训练的准确性和安全性。该系统能够监控用户运动姿态,及时识别动作偏差,并通过振动反馈提供即时纠正,从而帮助用户避免因错误姿势导致的运动损伤,优化训练效果。

集成华为云平台使得训练数据得以远程存储和访问,支持长期趋势分析和个性化建议生成。用户或教练可以通过云平台查看历史记录,进行远程指导,促进健身计划的科学化和个性化调整。

QT上位机提供的3D运动轨迹可视化功能增强了用户体验,使用户能够直观了解自身动作模式,并结合生成的训练报告和改进建议,深化对训练效果的理解,推动持续改进。

硬件组成采用STM32F103C8T6作为核心控制器,结合MPU6050和JY901传感器实现高精度数据采集,确保了系统的可靠性和实时性。振动电机模块和ESP8266 Wi-Fi模块的集成,使得反馈和通信功能高效实现,整体设计注重实用性和成本效益,适用于家庭或健身房环境。

设计思路

系统设计以STM32F103C8T6最小系统核心板作为主控制器,负责协调各个模块的工作。该系统通过MPU6050六轴传感器和JY901九轴姿态传感器实时采集人体运动姿态数据,MPU6050提供基础的加速度和陀螺仪数据,而JY901则提供更高精度的姿态信息,包括磁力计数据,以增强姿态分析的准确性。传感器数据通过I2C或UART接口与STM32连接,STM32进行初步的数据滤波和融合处理,例如使用互补滤波或卡尔曼滤波算法来计算出稳定的姿态角度,如俯仰、横滚和偏航角。

姿态分析部分由STM32实现,通过预设的标准健身动作模型(如深蹲、俯卧撑等)进行实时比较。系统计算当前姿态与标准模型之间的偏差,当检测到动作不标准时,STM32会控制振动电机模块发出实时触觉反馈,提示用户进行调整。振动反馈的强度和模式可以根据偏差程度进行调节,确保用户能够及时感知并纠正动作。

数据上传功能通过ESP8266-01S Wi-Fi模块实现,STM32将处理后的姿态数据和分析结果打包成JSON格式,通过AT指令集控制ESP8266连接到华为云平台,使用MQTT或HTTP协议将数据上传。华为云平台用于存储历史训练数据、姿态分析结果,并支持后续的数据查询和管理,为上位机提供数据源。

QT上位机部分负责可视化显示,它从华为云平台获取数据,实时渲染3D运动轨迹,模拟用户的动作过程。同时,上位机生成训练报告,包括动作次数、标准度统计、改进建议等,基于云平台存储的数据进行趋势分析,帮助用户跟踪健身进度并优化训练计划。整个系统的硬件连接通过洞洞板焊接传感器接口电路,使用杜邦线灵活连接各模块,确保可靠性和易于调试。

框架图

+----------------+      +---------------------+      +-------------------+
| MPU6050        | ---> | STM32F103C8T6       | ---> | 振动电机模块      |
| (六轴传感器)   |      | (主控制器)          |      | (实时反馈)        |
+----------------+      +---------------------+      +-------------------+
      ↑                         |
+----------------+               |                         
| JY901          | --->          |                         
| (九轴传感器)   |               |                         
+----------------+               |                         
                                 V
                         +-------------------+
                         | ESP8266-01S       | ---> 华为云平台 ---> QT上位机
                         | (Wi-Fi模块)       |      (数据存储与分析)  (显示3D轨迹、报告)
                         +-------------------+

系统总体设计

本系统以STM32F103C8T6最小系统核心板作为主控制器,负责协调整个智能健身姿态矫正系统的运行。系统通过集成MPU6050六轴传感器和JY901九轴姿态传感器,实时采集人体运动过程中的加速度、角速度和其他姿态数据,确保数据采集的全面性和准确性。这些传感器通过洞洞板焊接的接口电路与主控制器连接,使用杜邦线进行模块间的信号传输,简化硬件布局并提高系统的可维护性。

采集到的姿态数据由STM32F103C8T6进行实时处理,包括数据滤波、融合和姿态解算,以分析用户的健身动作标准度。主控制器利用算法比较实际运动与标准动作模型,检测 deviations 并计算动作的准确度。这一过程确保系统能够快速响应,为后续的反馈和云上传提供基础。

系统通过振动电机模块提供实时触觉反馈,当检测到动作不标准时,STM32控制器会驱动振动电机发出振动信号,提示用户即时调整姿势。这种反馈机制设计为低延迟,以增强用户体验和训练效果,同时保持硬件简单可靠。

数据上传部分依靠ESP8266-01S Wi-Fi模块实现,STM32将处理后的训练数据及姿态分析结果通过串口通信发送至ESP8266模块,该模块连接到华为云平台,实现数据的稳定上传。华为云平台用于存储和分析历史数据,支持后续的数据查询和报告生成。

QT上位机软件负责接收云平台或直接来自系统的数据,显示用户的3D运动轨迹,可视化分析结果,并生成训练报告和改进建议。报告包括动作准确性统计和历史趋势,帮助用户跟踪进度和优化训练计划。整个系统设计注重实用性和可靠性,确保从数据采集到云平台集成的无缝衔接。

系统功能总结

功能模块 描述
实时姿态数据采集 使用MPU6050六轴传感器和JY901九轴传感器采集人体运动姿态数据
姿态分析 STM32F103C8T6主控制器实时处理数据,进行动作标准度分析
实时振动反馈 通过振动电机模块提供触觉反馈,当检测到动作不标准时振动提示
云平台数据上传 使用ESP8266-01S Wi-Fi模块将训练数据及分析结果上传至华为云平台
上位机显示与报告生成 QT上位机软件显示3D运动轨迹,并生成训练报告及改进建议

设计的各个功能模块描述

主控制器模块基于STM32F103C8T6最小系统核心板,负责整体系统的协调与控制,处理来自传感器的数据流,执行姿态分析算法,并管理各模块间的通信时序,确保实时性能和数据完整性。

姿态数据采集模块利用MPU6050六轴传感器和JY901九轴姿态传感器协同工作,MPU6050提供基本的加速度和陀螺仪数据,而JY901则补充更高精度的磁力计和融合算法,共同实现对人体运动姿态的实时采集,为后续分析提供原始数据输入。

姿态分析模块在STM32主控制器上运行定制算法,对采集到的传感器数据进行处理,包括滤波、融合和角度计算,以评估健身动作的标准度,检测 deviations 如角度偏差或节奏错误,并生成分析结果用于反馈和上传。

实时反馈模块通过振动电机模块实现,当姿态分析检测到动作不标准时,STM32控制振动电机发出触觉提示,为用户提供即时纠正信号,增强训练效果和用户体验。

云通信模块依托ESP8266-01S Wi-Fi模块,负责将训练数据和姿态分析结果通过无线网络传输至华为云平台,实现数据远程存储和后续处理,确保系统与云端的稳定连接和数据安全上传。

硬件接口模块采用洞洞板焊接传感器接口电路,并通过杜邦线连接各组件,包括STM32、传感器、Wi-Fi模块和振动电机,提供可靠的物理连接和信号传输,简化系统组装和维护。

上位机显示模块基于QT开发环境,接收来自云平台或直接的数据,实时显示3D运动轨迹可视化,并生成训练报告和改进建议,帮助用户回顾分析结果和优化健身计划。

上位机代码设计

// main.cpp
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <Qt3DCore/QEntity>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QPointLight>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QCylinderMesh>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void fetchDataFromCloud();
    void onDataReceived(QNetworkReply *reply);
    void update3DTrajectory(const QJsonArray &data);
    void generateReport();
    void saveReport();

private:
    QNetworkAccessManager *networkManager;
    Qt3DExtras::Qt3DWindow *view3D;
    QWidget *container;
    QVBoxLayout *layout;
    QPushButton *fetchButton;
    QPushButton *reportButton;
    QTextEdit *reportDisplay;
    QJsonArray currentData;

    void setupUI();
    void create3DScene();
    void addPointToScene(float x, float y, float z);
    QString generateImprovementSuggestions(const QJsonObject &data);
};

#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DRender/QMesh>
#include <Qt3DCore/QTransform>
#include <QHostAddress>
#include <QNetworkRequest>
#include <QUrl>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), networkManager(new QNetworkAccessManager(this))
{
    setupUI();
    create3DScene();
    connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onDataReceived);
}

MainWindow::~MainWindow()
{
}

void MainWindow::setupUI()
{
    setWindowTitle("智能健身姿态矫正系统上位机");
    setGeometry(1001001200800);

    container = new QWidget(this);
    setCentralWidget(container);
    layout = new QVBoxLayout(container);

    view3D = new Qt3DExtras::Qt3DWindow();
    QWidget *widget3D = QWidget::createWindowContainer(view3D);
    widget3D->setMinimumSize(800600);
    layout->addWidget(widget3D);

    fetchButton = new QPushButton("从华为云获取数据"this);
    layout->addWidget(fetchButton);
    connect(fetchButton, &QPushButton::clicked, this, &MainWindow::fetchDataFromCloud);

    reportButton = new QPushButton("生成训练报告"this);
    layout->addWidget(reportButton);
    connect(reportButton, &QPushButton::clicked, this, &MainWindow::generateReport);

    reportDisplay = new QTextEdit(this);
    reportDisplay->setPlaceholderText("训练报告将显示在这里...");
    layout->addWidget(reportDisplay);
}

void MainWindow::create3DScene()
{
    Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;

    // Camera
    Qt3DRender::QCamera *camera = view3D->camera();
    camera->lens()->setPerspectiveProjection(45.0f16.0f/9.0f0.1f1000.0f);
    camera->setPosition(QVector3D(0020.0f));
    camera->setViewCenter(QVector3D(000));

    // For camera controls
    Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
    camController->setLinearSpeed(50.0f);
    camController->setLookSpeed(180.0f);
    camController->setCamera(camera);

    // Light
    Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(rootEntity);
    Qt3DRender::QPointLight *pointLight = new Qt3DRender::QPointLight(lightEntity);
    pointLight->setColor("white");
    pointLight->setIntensity(1);
    lightEntity->addComponent(pointLight);
    Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform(lightEntity);
    lightTransform->setTranslation(camera->position());
    lightEntity->addComponent(lightTransform);

    view3D->setRootEntity(rootEntity);
}

void MainWindow::fetchDataFromCloud()
{
    QUrl url("https://huaweicloud.com/api/data")// Replace with actual Huawei Cloud API endpoint
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    // Add authentication headers if required, e.g., API key
    // request.setRawHeader("Authorization", "Bearer your_token_here");

    networkManager->get(request);
}

void MainWindow::onDataReceived(QNetworkReply *reply)
{
    if (reply->error() == QNetworkReply::NoError) {
        QByteArray response = reply->readAll();
        QJsonDocument jsonDoc = QJsonDocument::fromJson(response);
        if (jsonDoc.isArray()) {
            currentData = jsonDoc.array();
            update3DTrajectory(currentData);
        } else {
            QMessageBox::warning(this"错误""接收到的数据格式不正确");
        }
    } else {
        QMessageBox::critical(this"网络错误", reply->errorString());
    }
    reply->deleteLater();
}

void MainWindow::update3DTrajectory(const QJsonArray &data)
{
    Qt3DCore::QEntity *rootEntity = view3D->rootEntity();
    // Clear existing entities except camera and light
    QList<Qt3DCore::QEntity*> children = rootEntity->findChildren<Qt3DCore::QEntity*>();
    for (Qt3DCore::QEntity *child : children) {
        if (child != rootEntity && child->objectName() != "camera" && child->objectName() != "light") {
            delete child;
        }
    }

    // Add new points based on data
    for (int i = 0; i < data.size(); ++i) {
        QJsonObject point = data[i].toObject();
        float x = point["x"].toDouble();
        float y = point["y"].toDouble();
        float z = point["z"].toDouble();
        addPointToScene(x, y, z);
    }
}

void MainWindow::addPointToScene(float x, float y, float z)
{
    Qt3DCore::QEntity *rootEntity = view3D->rootEntity();
    Qt3DCore::QEntity *pointEntity = new Qt3DCore::QEntity(rootEntity);

    Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh();
    sphereMesh->setRadius(0.1f);

    Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial();
    material->setDiffuse(QColor(QRgb(0xff0000)));

    Qt3DCore::QTransform *transform = new Qt3DCore::QTransform();
    transform->setTranslation(QVector3D(x, y, z));

    pointEntity->addComponent(sphereMesh);
    pointEntity->addComponent(material);
    pointEntity->addComponent(transform);
}

void MainWindow::generateReport()
{
    if (currentData.isEmpty()) {
        QMessageBox::information(this"提示""没有数据可生成报告");
        return;
    }

    QString reportHtml = "<h1>健身训练报告</h1><ul>";
    for (int i = 0; i < currentData.size(); ++i) {
        QJsonObject dataPoint = currentData[i].toObject();
        reportHtml += "<li>时间: " + dataPoint["timestamp"].toString() + ", 姿态: (" +
                      QString::number(dataPoint["x"].toDouble()) + ", " +
                      QString::number(dataPoint["y"].toDouble()) + ", " +
                      QString::number(dataPoint["z"].toDouble()) + ")</li>";
        reportHtml += "<li>建议: " + generateImprovementSuggestions(dataPoint) + "</li>";
    }
    reportHtml += "</ul>";

    reportDisplay->setHtml(reportHtml);
}

QString MainWindow::generateImprovementSuggestions(const QJsonObject &data)
{
    // Simple example: compare with ideal values (假设理想值)
    float x = data["x"].toDouble();
    float y = data["y"].toDouble();
    float z = data["z"].toDouble();

    QString suggestions;
    if (x > 0.5) {
        suggestions += "减小X轴运动幅度; ";
    }
    if (y < -0.5) {
        suggestions += "增加Y轴运动; ";
    }
    if (z > 1.0) {
        suggestions += "注意Z轴稳定性; ";
    }
    return suggestions.isEmpty() ? "动作标准" : suggestions;
}

void MainWindow::saveReport()
{
    QString fileName = QFileDialog::getSaveFileName(this"保存报告""""HTML文件 (*.html)");
    if (!fileName.isEmpty()) {
        QFile file(fileName);
        if (file.open(QIODevice::WriteOnly)) {
            file.write(reportDisplay->toHtml().toUtf8());
            file.close();
        }
    }
}
# Project.pro
QT       += core gui network webenginewidgets
QT       += 3dcore 3drender 3dextras 3dinput

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = Project
TEMPLATE = app

SOURCES += main.cpp \
           mainwindow.cpp

HEADERS += mainwindow.h

此代码提供了一个基本的Qt上位机应用程序,用于从华为云获取数据、显示3D运动轨迹和生成训练报告。注意:华为云API端点、认证细节和数据格式需要根据实际配置调整。3D轨迹使用Qt3D显示,报告以HTML格式生成。改进建议基于简单规则,可根据实际需求扩展。

模块代码设计

#include "stm32f10x.h"

// 定义传感器地址
#define MPU6050_ADDR 0xD0 // MPU6050 I2C地址 (AD0低)
#define JY901_ADDR 0x50   // JY901 I2C地址 (假设默认)

// 定义引脚
#define MOTOR_GPIO_PORT GPIOA
#define MOTOR_GPIO_PIN GPIO_Pin_0

// 全局变量用于传感器数据
volatile float pitch_mpu = 0.0, roll_mpu = 0.0;
volatile float pitch_jy901 = 0.0, roll_jy901 = 0.0, yaw_jy901 = 0.0;
volatile int16_t accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z;

// 函数声明
void RCC_Configuration(void);
void GPIO_Configuration(void);
void I2C_Configuration(void);
void USART_Configuration(void);
void MPU6050_Init(void);
void JY901_Init(void);
void MPU6050_ReadData(int16_t *accel_x, int16_t *accel_y, int16_t *accel_z, int16_t *gyro_x, int16_t *gyro_y, int16_t *gyro_z);
void JY901_ReadAngles(float *pitch, float *roll, float *yaw);
void CalculateAngles_MPU6050(void);
void Motor_Control(uint8_t state);
void USART_SendString(USART_TypeDef *USARTx, volatile char *s);
void SysTick_Handler(void);
void Delay_ms(uint32_t nTime);

// SysTick计数器
volatile uint32_t TimingDelay;

int main(void) {
    // 系统初始化
    RCC_Configuration();
    GPIO_Configuration();
    I2C_Configuration();
    USART_Configuration();
    
    // 初始化SysTick用于延时
    if (SysTick_Config(SystemCoreClock / 1000)) {
        while (1);
    }
    
    // 传感器初始化
    MPU6050_Init();
    JY901_Init();
    
    // 主循环
    while (1) {
        // 读取MPU6050原始数据
        int16_t ax, ay, az, gx, gy, gz;
        MPU6050_ReadData(&ax, &ay, &az, &gx, &gy, &gz);
        accel_x = ax; accel_y = ay; accel_z = az;
        gyro_x = gx; gyro_y = gy; gyro_z = gz;
        
        // 计算MPU6050姿态角度
        CalculateAngles_MPU6050();
        
        // 读取JY901姿态角度
        float p, r, y;
        JY901_ReadAngles(&p, &r, &y);
        pitch_jy901 = p; roll_jy901 = r; yaw_jy901 = y;
        
        // 姿态分析示例:如果俯仰角超过阈值,触发振动
        if (pitch_mpu > 30.0 || pitch_mpu < -30.0) {
            Motor_Control(1); // 开启振动
        } else {
            Motor_Control(0); // 关闭振动
        }
        
        // 准备数据上传字符串
        char buffer[100];
        sprintf(buffer, "MPU:%.2f,%.2f|JY901:%.2f,%.2f,%.2f\r\n", pitch_mpu, roll_mpu, pitch_jy901, roll_jy901, yaw_jy901);
        USART_SendString(USART2, buffer);
        
        Delay_ms(100); // 延时100ms
    }
}

// 系统时钟配置
void RCC_Configuration(void) {
    // 使能GPIOA、GPIOB、USART2、I2C1时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_I2C1EN;
}

// GPIO配置
void GPIO_Configuration(void) {
    // 振动电机引脚PA0推挽输出
    GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
    GPIOA->CRL |= GPIO_CRL_MODE0_0;
    
    // I2C1引脚PB6(SCL)和PB7(SDA)开漏输出
    GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6 | GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
    GPIOB->CRL |= GPIO_CRL_MODE6_0 | GPIO_CRL_CNF6_1 | GPIO_CRL_MODE7_0 | GPIO_CRL_CNF7_1;
    
    // USART2引脚PA2(TX)推挽复用输出,PA3(RX)浮空输入
    GPIOA->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2 | GPIO_CRL_MODE3 | GPIO_CRL_CNF3);
    GPIOA->CRL |= GPIO_CRL_MODE2_0 | GPIO_CRL_CNF2_1;
    GPIOA->CRL |= GPIO_CRL_CNF3_0;
}

// I2C配置
void I2C_Configuration(void) {
    I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C
    I2C1->CR2 = 36// 设置时钟频率36MHz
    I2C1->CCR = 180// 设置CCR for 100kHz
    I2C1->TRISE = 37// 设置TRISE
    I2C1->CR1 |= I2C_CR1_PE; // 启用I2C
}

// USART配置
void USART_Configuration(void) {
    USART2->BRR = 0x1D4C// 设置波特率9600 @36MHz
    USART2->CR1 |= USART_CR1_TE | USART_CR1_UE; // 启用发送和USART
}

// MPU6050初始化
void MPU6050_Init(void) {
    // 唤醒MPU6050,设置陀螺仪和加速度计量程
    I2C1->CR1 |= I2C_CR1_START;
    while (!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = MPU6050_ADDR;
    while (!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2; // 清除ADDR位
    I2C1->DR = 0x6B// PWR_MGMT_1寄存器
    while (!(I2C1->SR1 & I2C_SR1_TXE));
    I2C1->DR = 0x00// 唤醒
    while (!(I2C1->SR1 & I2C_SR1_BTF));
    I2C1->CR1 |= I2C_CR1_STOP;
}

// JY901初始化(假设默认配置,无需额外初始化)
void JY901_Init(void) {
    // JY901通常默认输出角度,无需初始化命令
}

// 读取MPU6050数据
void MPU6050_ReadData(int16_t *accel_x, int16_t *accel_y, int16_t *accel_z, int16_t *gyro_x, int16_t *gyro_y, int16_t *gyro_z) {
    uint8_t data[14];
    I2C1->CR1 |= I2C_CR1_START;
    while (!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = MPU6050_ADDR;
    while (!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    I2C1->DR = 0x3B// 起始寄存器ACCEL_XOUT_H
    while (!(I2C1->SR1 & I2C_SR1_TXE));
    I2C1->CR1 |= I2C_CR1_START; // 重复起始条件
    while (!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = MPU6050_ADDR | 0x01// 读模式
    while (!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    for (int i = 0; i < 13; i++) {
        if (i == 12) I2C1->CR1 &= ~I2C_CR1_ACK;
        while (!(I2C1->SR1 & I2C_SR1_RXNE));
        data[i] = I2C1->DR;
    }
    I2C1->CR1 |= I2C_CR1_STOP;
    while (!(I2C1->SR1 & I2C_SR1_RXNE));
    data[13] = I2C1->DR;
    
    *accel_x = (int16_t)((data[0] << 8) | data[1]);
    *accel_y = (int16_t)((data[2] << 8) | data[3]);
    *accel_z = (int16_t)((data[4] << 8) | data[5]);
    *gyro_x = (int16_t)((data[8] << 8) | data[9]);
    *gyro_y = (int16_t)((data[10] << 8) | data[11]);
    *gyro_z = (int16_t)((data[12] << 8) | data[13]);
}

// 读取JY901角度数据
void JY901_ReadAngles(float *pitch, float *roll, float *yaw) {
    uint8_t data[6];
    I2C1->CR1 |= I2C_CR1_START;
    while (!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = JY901_ADDR;
    while (!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    I2C1->DR = 0x3D// 假设俯仰角寄存器地址(需查JY901手册确认)
    while (!(I2C1->SR1 & I2C_SR1_TXE));
    I2C1->CR1 |= I2C_CR1_START;
    while (!(I2C1->SR1 & I2C_SR1_SB));
    I2C1->DR = JY901_ADDR | 0x01;
    while (!(I2C1->SR1 & I2C_SR1_ADDR));
    (void)I2C1->SR2;
    for (int i = 0; i < 5; i++) {
        if (i == 4) I2C1->CR1 &= ~I2C_CR1_ACK;
        while (!(I2C1->SR1 & I2C_SR1_RXNE));
        data[i] = I2C1->DR;
    }
    I2C1->CR1 |= I2C_CR1_STOP;
    while (!(I2C1->SR1 & I2C_SR1_RXNE));
    data[5] = I2C1->DR;
    
    // 转换数据为角度(假设数据为16位有符号整数,单位0.1度)
    *pitch = (int16_t)((data[0] << 8) | data[1]) * 0.1;
    *roll = (int16_t)((data[2] << 8) | data[3]) * 0.1;
    *yaw = (int16_t)((data[4] << 8) | data[5]) * 0.1;
}

// 计算MPU6050姿态角度(简单互补滤波)
void CalculateAngles_MPU6050(void) {
    static float angle_pitch = 0.0, angle_roll = 0.0;
    static uint32_t previous_time = 0;
    uint32_t current_time = SysTick->VAL; // 简化时间处理,实际应使用定时器
    float dt = (current_time - previous_time) / 1000.0// 假设SysTick为1ms
    previous_time = current_time;
    
    // 加速度计角度计算
    float accel_angle_pitch = atan2(accel_y, accel_z) * 180 / 3.14159;
    float accel_angle_roll = atan2(accel_x, accel_z) * 180 / 3.14159;
    
    // 互补滤波
    angle_pitch = 0.98 * (angle_pitch + gyro_x * dt) + 0.02 * accel_angle_pitch;
    angle_roll = 0.98 * (angle_roll + gyro_y * dt) + 0.02 * accel_angle_roll;
    
    pitch_mpu = angle_pitch;
    roll_mpu = angle_roll;
}

// 振动电机控制
void Motor_Control(uint8_t state) {
    if (state) {
        MOTOR_GPIO_PORT->BSRR = MOTOR_GPIO_PIN;
    } else {
        MOTOR_GPIO_PORT->BRR = MOTOR_GPIO_PIN;
    }
}

// USART发送字符串
void USART_SendString(USART_TypeDef *USARTx, volatile char *s) {
    while (*s) {
        while (!(USARTx->SR & USART_SR_TXE));
        USARTx->DR = *s++;
    }
}

// SysTick中断处理
void SysTick_Handler(void) {
    if (TimingDelay != 0x00) {
        TimingDelay--;
    }
}

// 延时函数
void Delay_ms(uint32_t nTime) {
    TimingDelay = nTime;
    while (TimingDelay != 0);
}

项目核心代码

#include "stm32f10x.h"

// 引脚定义
#define VIBRATION_GPIO_PORT GPIOA
#define VIBRATION_GPIO_PIN GPIO_Pin_0

// 函数声明
void SystemClock_Config(void);
void GPIO_Init(void);
void I2C1_Init(void);
void USART1_Init(void);
void MPU6050_Init(void);
void JY901_Init(void);
void ESP8266_Init(void);
void MPU6050_Read(float *accel, float *gyro);
void JY901_Read(float *data);
void ESP8266_SendData(char *data);
void Vibration_Motor_On(void);
void Vibration_Motor_Off(void);
void Pose_Analysis(float *mpu_data, float *jy_data, int *is_standard);

int main(void) {
    // 初始化系统时钟
    SystemClock_Config();
    
    // 初始化GPIO
    GPIO_Init();
    
    // 初始化I2C1
    I2C1_Init();
    
    // 初始化USART1
    USART1_Init();
    
    // 初始化传感器和模块
    MPU6050_Init();
    JY901_Init();
    ESP8266_Init();
    
    // 变量定义
    float mpu_accel[3], mpu_gyro[3];
    float jy_data[9];
    int is_standard = 1;
    
    while(1) {
        // 读取传感器数据
        MPU6050_Read(mpu_accel, mpu_gyro);
        JY901_Read(jy_data);
        
        // 姿态分析
        Pose_Analysis(mpu_accel, jy_data, &is_standard);
        
        // 振动反馈
        if (!is_standard) {
            Vibration_Motor_On();
        } else {
            Vibration_Motor_Off();
        }
        
        // 准备数据字符串
        char data_str[100];
        sprintf(data_str, "Accel:%.2f,%.2f,%.2f;Gyro:%.2f,%.2f,%.2f;JY:%.2f,%.2f,%.2f"
                mpu_accel[0], mpu_accel[1], mpu_accel[2],
                mpu_gyro[0], mpu_gyro[1], mpu_gyro[2],
                jy_data[0], jy_data[1], jy_data[2]);
        
        // 发送数据到华为云 via ESP8266
        ESP8266_SendData(data_str);
        
        // 简单延时
        for(int i=0; i<1000000; i++);
    }
}

void SystemClock_Config(void) {
    // 启用HSE
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY));
    
    // 配置FLASH
    FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2;
    
    // 配置PLL
    RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
    RCC->CFGR |= (RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
    
    // 启用PLL
    RCC->CR |= RCC_CR_PLLON;
    while (!(RCC->CR & RCC_CR_PLLRDY));
    
    // 切换系统时钟到PLL
    RCC->CFGR &= ~RCC_CFGR_SW;
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
    
    // 设置分频器
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
}

void GPIO_Init(void) {
    // 启用GPIOA和GPIOB时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN;
    
    // 配置PA0为推挽输出(振动电机)
    GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
    GPIOA->CRL |= GPIO_CRL_MODE0_0;
    GPIOA->CRL &= ~GPIO_CRL_MODE0_1;
    GPIOA->CRL &= ~(GPIO_CRL_CNF0_0 | GPIO_CRL_CNF0_1);
    
    // 配置PB6和PB7为交替开漏输出(I2C1)
    GPIOB->CRL &= ~(GPIO_CRL_MODE6 | GPIO_CRL_CNF6 | GPIO_CRL_MODE7 | GPIO_CRL_CNF7);
    GPIOB->CRL |= (GPIO_CRL_MODE6_0 | GPIO_CRL_MODE7_0);
    GPIOB->CRL |= (GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_1);
    GPIOB->CRL &= ~(GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0);
    
    // 配置PA9为交替推挽输出(USART1 TX),PA10为浮空输入(USART1 RX)
    GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9 | GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
    GPIOA->CRH |= GPIO_CRH_MODE9_0;
    GPIOA->CRH |= GPIO_CRH_CNF9_1;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    GPIOA->CRH &= ~GPIO_CRH_MODE10;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
}

void I2C1_Init(void) {
    // 启用I2C1时钟
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    
    // 配置I2C1
    I2C1->CR1 &= ~I2C_CR1_PE;
    I2C1->CR2 = 36;
    I2C1->CCR = 180;
    I2C1->TRISE = 37;
    I2C1->CR1 |= I2C_CR1_PE;
}

void USART1_Init(void) {
    // 启用USART1时钟
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    
    // 配置USART1
    USART1->CR1 &= ~USART_CR1_UE;
    USART1->BRR = (39 << 4) | 1;
    USART1->CR1 |= USART_CR1_TE | USART_CR1_RE;
    USART1->CR1 |= USART_CR1_UE;
}

void Vibration_Motor_On(void) {
    GPIOA->BSRR = GPIO_BSRR_BS0;
}

void Vibration_Motor_Off(void) {
    GPIOA->BSRR = GPIO_BSRR_BR0;
}

void Pose_Analysis(float *mpu_data, float *jy_data, int *is_standard) {
    // 简单示例:检查加速度计Z轴阈值
    if (mpu_data[2] > 1.5) {
        *is_standard = 0;
    } else {
        *is_standard = 1;
    }
}

void ESP8266_SendData(char *data) {
    while (*data) {
        while (!(USART1->SR & USART_SR_TXE));
        USART1->DR = *data++;
    }
}

// 以下函数假设在其他文件中实现,此处为空函数体
void MPU6050_Init(void) {}
void JY901_Init(void) {}
void ESP8266_Init(void) {}
void MPU6050_Read(float *accel, float *gyro) {}
void JY901_Read(float *data) {}

总结

本系统成功设计并实现了一个基于STM32F103C8T6微控制器的智能健身姿态矫正系统,通过集成多种传感器和云平台技术,实现了对用户健身动作的实时监测与矫正。该系统能够高效采集人体运动数据,进行精准的姿态分析,并提供即时反馈,从而提升健身训练的效果和安全性。

硬件方面,系统以STM32F103C8T6最小系统核心板为主控制器,结合MPU6050六轴传感器和JY901九轴姿态传感器,确保了运动数据的高精度采集与处理。振动电机模块为用户提供实时的触觉反馈,帮助纠正动作偏差,而ESP8266-01S Wi-Fi模块实现了与华为云平台的稳定通信,保障了数据的远程传输与存储。整个硬件架构通过洞洞板焊接和杜邦线连接,实现了模块化的可靠集成。

软件与云集成部分,系统将训练数据和姿态分析结果上传至华为云平台,实现了数据的云端管理与分析。QT上位机应用则进一步丰富了用户体验,通过显示3D运动轨迹、生成详细的训练报告和改进建议,为用户提供了全面的健身辅助和后期分析工具,增强了系统的实用性和智能化水平。

总体而言,该系统不仅满足了实时姿态矫正的核心需求,还通过华为云和QT上位机的结合,拓展了数据可视化和远程监控功能,为智能健身设备的发展提供了有价值的参考,具有广泛的应用前景和市场潜力。

 

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。