一、项目概述
目标和用途
本项目旨在开发一款基于 STM32 控制的自动平衡机器人,结合步进电机和陀螺仪传感器,实现对平衡机器人的精确控制。该机器人可以用于教育、科研、娱乐等多个领域,帮助用户了解自动控制、机器人运动学等相关知识。
技术栈关键词
-
STM32 单片机
-
步进电机
-
陀螺仪传感器
-
AD 采集电路
-
Qt 人机界面
-
实时数据监控
二、系统架构
系统架构设计
本项目的系统架构设计包括以下主要组件:
-
控制单元: STM32 单片机
-
传感器模块: 陀螺仪传感器
-
驱动模块: 步进电机驱动器
-
人机交互界面: Qt 桌面应用程序
-
通信协议: UART 或 I2C
系统架构图
三、环境搭建和注意事项
环境搭建
-
硬件环境:
-
STM32 开发板(如 Nucleo 或 Discovery)
-
步进电机及驱动模块
-
陀螺仪传感器(如 MPU6050)
-
面包板及连接线
-
-
软件环境:
-
STM32CubeIDE 或 Keil MDK
-
Qt Creator
-
STM32 HAL 库
-
注意事项
-
确保电源供应稳定,避免电源波动导致的设备损坏。
-
在调试过程中,注意步进电机的工作电流,避免过载。
-
传感器的连接线要正确,防止接反导致数据采集错误。
四、代码实现过程
在本部分中,我们将详细介绍 STM32 自动平衡机器人的各个功能模块的代码实现,包括陀螺仪数据采集、步进电机控制以及 Qt 人机界面控制。每个模块的实现都将包含代码示例、说明和算法的详细介绍,最后将展示系统的时序图。
1. 陀螺仪数据采集模块
功能
该模块的主要功能是通过 I2C 总线读取陀螺仪传感器(如 MPU6050)的数据(角速度和角度),并进行滤波处理以提高数据的准确性。
代码实现
#include "mpu6050.h"
#include "i2c.h"
#define MPU6050_ADDR 0x68 // MPU6050 I2C 地址
void MPU6050_Init() {
// 1. 初始化 I2C
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR << 1, 0x6B, I2C_MEMADD_SIZE_8BIT, (uint8_t[]){0}, 1, HAL_MAX_DELAY);
// 2. 设置陀螺仪为正常工作模式
HAL_I2C_Mem_Write(&hi2c1, MPU6050_ADDR << 1, 0x1B, I2C_MEMADD_SIZE_8BIT, (uint8_t[]){0}, 1, HAL_MAX_DELAY); // 设置为 ±250°/s
}
void Read_Gyro_Data(float* gyro_x, float* gyro_y, float* gyro_z) {
uint8_t data[6];
// 读取陀螺仪数据
HAL_I2C_Mem_Read(&hi2c1, MPU6050_ADDR << 1, 0x43, I2C_MEMADD_SIZE_8BIT, data, 6, HAL_MAX_DELAY);
// 数据转换
int16_t rawGyroX = (data[0] << 8) | data[1];
int16_t rawGyroY = (data[2] << 8) | data[3];
int16_t rawGyroZ = (data[4] << 8) | data[5];
// 转换为角速度(°/s)
*gyro_x = (float)rawGyroX / 131.0;
*gyro_y = (float)rawGyroY / 131.0;
*gyro_z = (float)rawGyroZ / 131.0;
}
代码说明
-
MPU6050_Init()
函数用于初始化 MPU6050 陀螺仪,包括设置工作模式。 -
Read_Gyro_Data()
函数读取陀螺仪的原始角速度数据,将其转换为实际的角速度(单位为 °/s)。 -
读取的数据通过 I2C 总线传输,使用 HAL 库函数
HAL_I2C_Mem_Read()
和HAL_I2C_Mem_Write()
进行数据的读写。
2. 步进电机控制模块
功能
该模块负责接收来自陀螺仪的数据,计算控制步进电机的速度和方向,从而实现机器人的平衡控制。
代码实现
#include "stepper_motor.h"
#define STEPS_PER_ROTATION 200 // 步进电机每转一圈的步数
#define MAX_SPEED 1000 // 最大速度
static int current_speed = 0;
static int current_direction = 1; // 1: 正转, -1: 反转
void StepperMotor_SetSpeed(int speed) {
if (speed > MAX_SPEED) speed = MAX_SPEED;
if (speed < 0) speed = 0;
current_speed = speed;
// 根据 speed 设置定时器的频率
__HAL_TIM_SET_PRESCALER(&htim1, (SystemCoreClock / (current_speed * STEPS_PER_ROTATION)) - 1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}
void StepperMotor_SetDirection(int direction) {
current_direction = direction;
// 方向引脚设置
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, current_direction > 0 ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
void StepperMotor_Run() {
// 控制步进电机旋转
while (1) {
if (current_speed > 0) {
// 产生脉冲信号控制步进电机
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
HAL_Delay(1); // 脉冲宽度
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
HAL_Delay(1); // 等待下一个脉冲
}
}
}
代码说明
-
StepperMotor_SetSpeed(int speed)
函数设置步进电机的速度,使用定时器来生成定时脉冲信号来控制电机的转动速度。 -
StepperMotor_SetDirection(int direction)
函数设置步进电机的转动方向,通过控制 GPIO 引脚的高低电平来设置电机的方向。 -
StepperMotor_Run()
函数根据当前速度持续产生脉冲信号,从而使步进电机旋转。
3. Qt 人机界面控制模块
功能
Qt 人机界面模块用于实时显示被测转子的转速、转向和驱动脉冲频率,并提供控制按钮以启动、停止、加速、减速和改变方向。
代码实现
#include <QMainWindow>
#include <QTimer>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void startMotor();
void stopMotor();
void updateDisplay();
private:
QLabel *speedLabel;
QLabel *directionLabel;
QPushButton *startButton;
QPushButton *stopButton;
QTimer *timer;
float currentSpeed;
int currentDirection; // 1: 正转, -1: 反转
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), currentSpeed(0), currentDirection(1) {
speedLabel = new QLabel("转速: 0");
directionLabel = new QLabel("方向: 正转");
startButton = new QPushButton("启动");
stopButton = new QPushButton("停止");
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(speedLabel);
layout->addWidget(directionLabel);
layout->addWidget(startButton);
layout->addWidget(stopButton);
QWidget *centralWidget = new QWidget();
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
connect(startButton, &QPushButton::clicked, this, &MainWindow::startMotor);
connect(stopButton, &QPushButton::clicked, this, &MainWindow::stopMotor);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::updateDisplay);
timer->start(100); // 每100毫秒更新一次显示
}
MainWindow::~MainWindow() {
// 清理
}
void MainWindow::startMotor() {
currentSpeed = 100; // 启动电机
currentDirection = 1; // 正转
// 向 STM32 发送启动和速度指令
}
void MainWindow::startMotor() {
currentSpeed = 100; // 启动电机
currentDirection = 1; // 正转
// 向 STM32 发送启动和速度指令
// 这里可以调用串口通信函数,发送控制命令
// 例如: sendCommandToSTM32("START", currentSpeed, currentDirection);
}
void MainWindow::stopMotor() {
currentSpeed = 0; // 停止电机
// 向 STM32 发送停止命令
// 例如: sendCommandToSTM32("STOP");
}
void MainWindow::updateDisplay() {
// 更新转速和方向标签
speedLabel->setText(QString("转速: %1").arg(currentSpeed));
directionLabel->setText(currentDirection == 1 ? "方向: 正转" : "方向: 反转");
// 这里可以读取 STM32 发送过来的实时数据并更新界面
// 例如: currentSpeed = readSpeedFromSTM32();
// 方向可能需要从 STM32 读取或根据计算结果更新
}
代码说明
-
startMotor()
函数被点击时调用,设置当前速度和方向,并向 STM32 发送启动指令。 -
stopMotor()
函数被点击时调用,停止电机并发送停止指令。 -
updateDisplay()
函数定时更新界面上的转速和方向标签。这一部分可以通过读取 STM32 的串口数据实现实时更新。
4. 数据通信模块
功能
本模块实现 STM32 和 Qt 界面之间的数据通信,使用串口(UART)进行双向数据传输。
代码实现
#include "usart.h"
void sendCommandToSTM32(const char* command) {
HAL_UART_Transmit(&huart2, (uint8_t*)command, strlen(command), HAL_MAX_DELAY);
}
void receiveDataFromSTM32(char* buffer, uint16_t size) {
HAL_UART_Receive(&huart2, (uint8_t*)buffer, size, HAL_MAX_DELAY);
}
代码说明
-
sendCommandToSTM32(const char* command)
函数用于向 STM32 发送指令。 -
receiveDataFromSTM32(char* buffer, uint16_t size)
函数用于接收来自 STM32 的数据,存储在指定的缓冲区中。
5. 时序图
以下是系统时序图,展示了用户与 Qt 界面、Qt 界面与 STM32 之间的交互过程。
五、项目总结
主要功能
本项目的主要目标是设计并实现一款基于 STM32 的自动平衡机器人。项目的核心功能包括:
-
实时数据采集:
-
使用陀螺仪传感器(如 MPU6050)获取机器人的角速度和角度数据,实现对机器人的实时姿态监测。
-
通过 I2C 接口与 STM32 进行数据通信,确保数据的准确传输。
-
-
步进电机控制:
-
利用步进电机驱动模块,实现机器人的加速、减速及转向控制。
-
根据陀螺仪反馈的数据,动态调整电机的速度和方向,以保持机器人的平衡状态。
-
-
人机交互界面:
-
使用 Qt 框架设计用户界面,提供启动、停止、加速和减速等操作按钮。
-
实时显示机器人的转速、转向和驱动脉冲频率等信息,增强用户体验。
-
-
数据通信:
-
通过 UART 串口实现 STM32 和 Qt 界面之间的双向通信,支持实时数据传输。
-
确保用户可以通过界面直接控制机器人,并获取其状态信息。
-