我们将基于 **STM32CubeMX + HAL 库 + 蓝牙模块(如 HC-05/HC-06 或 ESP8266)** 实现一个完整的 **蓝牙遥控无人机飞行控制系统**,支持:
- 通过手机 APP 发送指令
- 解析蓝牙串口数据
- 控制无人机的 **油门、偏航、俯仰、横滚**
- 集成你已有的 **MPU6050 姿态解算** 和 **串级 PID 控制系统**
---
## ✅ 硬件与软件环境
| 项目 | 配置 |
|------|------|
| MCU | STM32F103C8T6 / F4系列等 |
| 开发工具 | STM32CubeMX + Keil / STM32CubeIDE |
| 蓝牙模块 | HC-05 / HC-06(SPP 协议),连接到 USART2 |
| 通信方式 | UART 接收字符串或二进制协议 |
| 手机端APP | 可使用“蓝牙串口助手”或自定义APP |
---
## ✅ 功能目标
实现以下功能:
- 手机发送控制指令(例如:`R:50,P:30,Y:20,T:70`)
- STM32 解析指令 → 提取 Roll, Pitch, Yaw, Throttle
- 结合 MPU6050 姿态 → 运行串级PID → 输出PWM控制电机
---
## ✅ 第一步:STM32CubeMX 配置
### 1. 时钟配置(以 F103 为例)
- SYS: 选择 `Serial Wire` 调试接口
- RCC: HSE 外部晶振使能
- Clock Configuration: 设置系统主频为 72MHz
### 2. UART 配置(用于蓝牙)
- USART2:
- Mode: Asynchronous
- Baud Rate: 9600 或 115200(根据蓝牙模块设置)
- NVIC: 使能中断(优先级高于主循环)
> 📌 引脚连接示例:
```
HC-05 TX → PA3 (USART2_RX)
HC-05 RX → PA2 (USART2_TX)
```
### 3. 生成代码并添加外设句柄
```c
extern UART_HandleTypeDef huart2;
```
---
## ✅ 第二步:蓝牙接收模块(非阻塞方式)
我们使用 **中断 + 缓冲区** 方式接收数据,避免阻塞主循环。
### 文件:`bluetooth.h`
```c
// bluetooth.h
#ifndef __BLUETOOTH_H
#define __BLUETOOTH_H
#include <stdint.h>
#include <string.h>
// 最大命令长度
#define BT_BUFFER_SIZE 64
#define BT_CMD_TERMINATOR '\n'
// 控制结构体
typedef struct {
int16_t roll; // -100 ~ +100
int16_t pitch; // -100 ~ +100
int16_t yaw; // -100 ~ +100
int16_t throttle; // 0 ~ 100
uint8_t updated; // 标志位:是否接收到新指令
} BT_Control_t;
// 函数声明
void Bluetooth_Init(void);
void Bluetooth_Parse(void); // 在主循环中调用解析
extern BT_Control_t bt_cmd;
#endif
```
---
### 文件:`bluetooth.c`
```c
// bluetooth.c
#include "bluetooth.h"
#include "usart.h" // 自动生成的头文件
#include "string.h"
#include "stdio.h"
BT_Control_t bt_cmd = {0};
uint8_t bt_rx_byte = 0;
uint8_t bt_rx_buffer[BT_BUFFER_SIZE];
uint8_t bt_rx_index = 0;
void Bluetooth_Init(void) {
// 启动 UART 中断接收
HAL_UART_Receive_IT(&huart2, &bt_rx_byte, 1);
}
// UART 回调函数(需在 main.c 中重写)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == huart2.Instance) {
if (bt_rx_byte == BT_CMD_TERMINATOR || bt_rx_index >= BT_BUFFER_SIZE - 1) {
bt_rx_buffer[bt_rx_index] = '\0';
// 触发解析
Bluetooth_Parse();
// 重置缓冲区
bt_rx_index = 0;
} else {
bt_rx_buffer[bt_rx_index++] = bt_rx_byte;
}
// 继续开启下一次中断接收
HAL_UART_Receive_IT(&huart2, &bt_rx_byte, 1);
}
}
void Bluetooth_Parse(void) {
char *buffer = (char*)bt_rx_buffer;
// 示例格式: R:50,P:30,Y:20,T:70\n
int r = 0, p = 0, y = 0, t = 0;
int parsed = sscanf(buffer, "R:%d,P:%d,Y:%d,T:%d", &r, &p, &y, &t);
if (parsed == 4) {
bt_cmd.roll = (int16_t)(r > 100 ? 100 : (r < -100 ? -100 : r));
bt_cmd.pitch = (int16_t)(p > 100 ? 100 : (p < -100 ? -100 : p));
bt_cmd.yaw = (int16_t)(y > 100 ? 100 : (y < -100 ? -100 : y));
bt_cmd.throttle = (int16_t)(t > 100 ? 100 : (t < 0 ? 0 : t));
bt_cmd.updated = 1; // 标记更新
}
// 可选:打印调试信息
// printf("Parsed: R%d P%d Y%d T%d\r\n", r, p, y, t);
}
```
> ⚠️ 注意:你需要在 `main.c` 的 `HAL_UART_RxCpltCallback` 中调用 `Bluetooth_Parse()` 或确保回调被正确注册。
---
## ✅ 第三步:整合到主控逻辑(`main.c`)
```c
// main.c
#include "main.h"
#include "mpu6050.h"
#include "imu.h"
#include "pid.h"
#include "cascade_pid.h"
#include "bluetooth.h"
#include "motor.h" // 自定义电机混控
extern UART_HandleTypeDef huart2;
CascadePID_Controller cpid;
_st_AngE Angle = {0};
float base_throttle = 0.0f;
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init(); // 蓝牙串口
MPU_Init();
MPU_Calibrate(100);
imu_rest();
CascadePID_Init(&cpid);
Motor_Init(); // 初始化PWM输出
Bluetooth_Init(); // 启动蓝牙接收中断
float dt;
uint32_t last_tick = HAL_GetTick();
while (1) {
uint32_t now = HAL_GetTick();
dt = (now - last_tick) / 1000.0f;
if (dt < 0.015f) { // 最小时间间隔保护
HAL_Delay(1);
continue;
}
last_tick = now;
// --- 1. 获取IMU数据并更新姿态 ---
MpuGetData();
GetAngle((const _st_Mpu*)&MPU6050, &Angle, dt);
// --- 2. 检查蓝牙是否有新命令 ---
if (bt_cmd.updated) {
bt_cmd.updated = 0; // 清除标志
// --- 3. 将遥控输入映射为目标角度 ---
float target_roll = ((float)bt_cmd.roll) * 30.0f / 100.0f; // ±30°
float target_pitch = ((float)bt_cmd.pitch) * 30.0f / 100.0f;
float target_yaw = ((float)bt_cmd.yaw) * 90.0f / 100.0f; // ±90°快速转向
base_throttle = ((float)bt_cmd.throttle) * 1000.0f; // 映射到PWM范围
// --- 4. 运行串级PID ---
float out_roll, out_pitch, out_yaw;
CascadePID_Update(&cpid,
target_roll, target_pitch, target_yaw,
Angle.roll, Angle.pitch, Angle.yaw,
MPU6050.gyroX, MPU6050.gyroY, MPU6050.gyroZ,
dt,
&out_roll, &out_pitch, &out_yaw);
// --- 5. 混控输出到四个电机 ---
Motor_Mixer(out_roll, out_pitch, out_yaw, base_throttle);
} else {
// 无指令时停机(安全机制)
Motor_StopAll();
}
HAL_Delay(20); // 控制频率 ~50Hz
}
}
```
---
## ✅ 手机端操作建议
### 使用任意蓝牙串口APP(如:Serial Bluetooth Terminal)
发送指令格式(每行一条):
```
R:0,P:0,Y:0,T:20
R:50,P:0,Y:0,T:40
R:0,P:-30,Y:60,T:50
```
含义:
- `R`: Roll(左负右正)
- `P`: Pitch(前负后正)
- `Y`: Yaw(左转负,右转正)
- `T`: Throttle(油门百分比)
> 💡 你可以开发一个简单的 Android/iOS App 实现摇杆控制,自动拼接并发送这些命令。
---
## ✅ 安全增强建议
| 功能 | 实现方法 |
|------|----------|
| **超时保护** | 如果超过1秒未收到指令 → 自动降落 |
| **指令校验** | 添加校验和或固定起始符(如 `$`) |
| **最小油门检测** | 防止意外启动 |
| **状态反馈** | 通过蓝牙回传当前姿态 `printf("A:%.1f,%.1f,%.1f\r\n", Angle.roll, ...);` |
---
## ✅ 扩展功能设想
1. **双向通信**:手机显示当前姿态角、电池电压等
2. **模式切换**:通过蓝牙切换“定高模式”、“航向锁定”等
3. **参数调节**:手机发送 `KP:3.0,KI:0.02,KD:0.5` 修改PID参数
4. **日志记录**:通过蓝牙导出飞行数据用于分析
---请问该蓝牙部分怎么在e调试软件上使用,请详细说明
最新发布