基于STM32F103C8T6的智能健身姿态矫正系统设计与华为云实现
项目开发背景
随着现代生活节奏加快和健康意识提升,健身已成为许多人日常生活的重要组成部分。然而,在自主训练过程中,由于缺乏专业指导,训练者往往难以确保动作的规范性,错误姿势的长期积累不仅会降低训练效果,还可能引发运动损伤。传统健身辅助设备通常依赖视觉或音频反馈,存在实时性不足、干扰性强等局限,难以满足高效训练的需求。
在此背景下,结合嵌入式技术与物联网的智能健身矫正系统应运而生。本系统通过高精度传感器实时捕捉人体运动数据,利用边缘计算能力进行本地姿态分析,并通过触觉反馈实现无干扰的实时矫正。进一步地,通过接入华为云平台实现训练数据的云端存储与分析,为用户提供长期趋势追踪和科学建议,突破了传统健身设备的单一功能性局限。
该系统以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(100, 100, 1200, 800);
container = new QWidget(this);
setCentralWidget(container);
layout = new QVBoxLayout(container);
view3D = new Qt3DExtras::Qt3DWindow();
QWidget *widget3D = QWidget::createWindowContainer(view3D);
widget3D->setMinimumSize(800, 600);
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.0f, 16.0f/9.0f, 0.1f, 1000.0f);
camera->setPosition(QVector3D(0, 0, 20.0f));
camera->setViewCenter(QVector3D(0, 0, 0));
// 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上位机的结合,拓展了数据可视化和远程监控功能,为智能健身设备的发展提供了有价值的参考,具有广泛的应用前景和市场潜力。
- 点赞
- 收藏
- 关注作者
评论(0)