跳到主要内容

ESP32-S3无刷云台项目

基于ESP32-S3芯片开发的二轴无刷云台解决方案,集成IMU惯性测量、姿态解算、闭环控制等功能。

项目概述

项目特点

  • 主控芯片:ESP32-S3 (双核240MHz + AI向量指令)
  • 电机驱动:FOC驱动器 (BLDC无刷直流)
  • 姿态传感:MPU6050 / BMI088 (6轴/9轴IMU)
  • 控制算法:串级PID + 姿态解算
  • 通信接口:Wi-Fi实时调参 + 蓝牙遥控

功能实现

无刷云台功能清单

├── 基础控制
│ ├── 俯仰角控制 (Pitch): ±90°
│ ├── 横滚角控制 (Roll): ±45°
│ └── 航向角控制 (Yaw): 360°

├── 传感器融合
│ ├── 加速度计数据采集
│ ├── 陀螺仪数据采集
│ └── 互补滤波/卡尔曼滤波

├── 控制算法
│ ├── 内环:角速度环 (P)
│ ├── 外环:角度环 (PID)
│ └── 前馈控制 (加速度前馈)

└── 扩展功能
├── Wi-Fi无线调参
├── 蓝牙遥控
└── 摄像头防抖扩展

硬件方案

硬件选型清单

模块型号数量说明
主控ESP32-S3-DevKitC-11开发板
IMUMPU6050 / BMI08816轴/9轴惯性测量
电机无刷直流电机 2206 / 22082云台专用
驱动器DRV8313 / L62342三相H桥
编码器AS56002磁编码器
电源3S锂电池 (11.1V)1供电
稳压MP1584 / AMS11172降压3.3V/5V

云台机械结构

俯仰轴 (Pitch)
┌──────────────┐
│ 摄像头 │ ◄── 负载
│ ┌────────┐ │
│ │ 电机 │──┼──► 俯仰运动
└──┼────────┼──┘
└────┬───┘

横滚轴 (Roll)

┌───────┼───────┐
│ │ │
│ ┌──┴──┐ │
│ │ 电机 │───┼──► 横滚运动
│ └─────┘ │
│ │
│ 底座 │
│ │
└─────────────┘

硬件连接图

ESP32-S3                     MPU6050
┌─────────────┐ ┌──────────────┐
│ │ │ │
│ GPIO 1 ───┼────────────► SCL │
│ GPIO 2 ───┼────────────► SDA │
│ GPIO 3 ───┼────────────► INT │
│ │ │ │
│ │ └──────────────┘
│ GPIO 4 ───┼────────────► DRV8313
│ GPIO 5 ───┼────────────► PWM_A (U)
│ GPIO 6 ───┼────────────► PWM_B (V)
│ GPIO 7 ───┼────────────► PWM_C (W)
│ GPIO 8 ───┼────────────► FAULT
│ │
│ GPIO 9 ───┼────────────► AS5600_SDA
│ GPIO 10 ──┼────────────► AS5600_SCL
│ │
└─────────────┘

DRV8313 AS5600
┌─────────────┐ ┌──────────────┐
│ PWM_U │ │ SDA │◄──► GPIO 9
│ PWM_V │ │ SCL │◄──► GPIO 10
│ PWM_W │ │ VCC 3.3V │
│ ENABLE │ │ GND │
│ FAULT │────────────► │
└─────────────┘ └──────────────┘


三相无刷电机

软件设计

ESP-IDF项目结构

esp32_gimbal/
├── main/
│ ├── main.c # 主程序
│ ├── imu/
│ │ ├── mpu6050.c # MPU6050驱动
│ │ ├── mpu6050.h
│ │ ├── fusion.c # 姿态解算
│ │ └── fusion.h
│ ├── motor/
│ │ ├── bldc.c # 无刷电机驱动
│ │ ├── bldc.h
│ │ ├── foc.c # FOC控制
│ │ └── foc.h
│ ├── control/
│ │ ├── pid.c # PID控制器
│ │ ├── pid.h
│ │ └── cascade_control.c # 串级PID
│ ├── communication/
│ │ ├── wifi_server.c # Wi-Fi调参
│ │ └── ble_control.c # 蓝牙遥控
│ └── config/
│ └── parameters.h # 参数配置

├── components/
│ └── esp-dsp/ # DSP库 (卡尔曼)

├── CMakeLists.txt
└── Kconfig.projbuild

核心代码实现

1. MPU6050驱动

// mpu6050.h
#ifndef MPU6050_H
#define MPU6050_H

#include "driver/i2c.h"
#include <stdint.h>

// MPU6050寄存器
#define MPU6050_ADDR 0x68
#define MPU6050_PWR_MGMT_1 0x6B
#define MPU6050_ACCEL_XOUT 0x3B
#define MPU6050_GYRO_XOUT 0x43
#define MPU6050_CONFIG 0x1A
#define MPU6050_GYRO_CONFIG 0x1B
#define MPU6050_ACCEL_CONFIG 0x1C

// 配置结构体
typedef struct {
int16_t accel_x, accel_y, accel_z;
int16_t gyro_x, gyro_y, gyro_z;
float temp;
} mpu6050_data_t;

esp_err_t mpu6050_init(i2c_port_t i2c_num);
esp_err_t mpu6050_read_raw(mpu6050_data_t *data);
esp_err_t mpu6050_read_scaled(mpu6050_data_t *data);

#endif
// mpu6050.c
#include "mpu6050.h"

static i2c_port_t s_i2c_num;

esp_err_t mpu6050_init(i2c_port_t i2c_num)
{
s_i2c_num = i2c_num;

// 配置I2C
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 2,
.scl_io_num = 1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
i2c_param_config(i2c_num, &conf);
i2c_driver_install(i2c_num, conf.mode, 0, 0, 0);

// 唤醒MPU6050
uint8_t data[2] = {MPU6050_PWR_MGMT_1, 0};
i2c_master_write_to_device(i2c_num, MPU6050_ADDR, data, 2, 100);

// 配置陀螺仪 ±250°/s
data[0] = MPU6050_GYRO_CONFIG; data[1] = 0x00;
i2c_master_write_to_device(i2c_num, MPU6050_ADDR, data, 2, 100);

// 配置加速度计 ±2g
data[0] = MPU6050_ACCEL_CONFIG; data[1] = 0x00;
i2c_master_write_to_device(i2c_num, MPU6050_ADDR, data, 2, 100);

return ESP_OK;
}

esp_err_t mpu6050_read_raw(mpu6050_data_t *data)
{
uint8_t raw_data[14];
uint8_t reg = MPU6050_ACCEL_XOUT;

i2c_master_write_read_device(s_i2c_num, MPU6050_ADDR,
&reg, 1, raw_data, 14, 100);

data->accel_x = (raw_data[0] << 8) | raw_data[1];
data->accel_y = (raw_data[2] << 8) | raw_data[3];
data->accel_z = (raw_data[4] << 8) | raw_data[5];
data->temp = ((raw_data[6] << 8) | raw_data[7]) / 340.0 + 36.53;
data->gyro_x = (raw_data[8] << 8) | raw_data[9];
data->gyro_y = (raw_data[10] << 8) | raw_data[11];
data->gyro_z = (raw_data[12] << 8) | raw_data[13];

return ESP_OK;
}

2. 姿态融合 (互补滤波)

// fusion.h
#ifndef FUSION_H
#define FUSION_H

#include <stdint.h>

// 姿态角结构体
typedef struct {
float pitch; // 俯仰角 (X轴)
float roll; // 横滚角 (Y轴)
float yaw; // 航向角 (Z轴)
} attitude_t;

void fusion_init(float alpha);
void fusion_update(float ax, float ay, float az,
float gx, float gy, float gz,
float dt);
attitude_t fusion_get_attitude(void);

#endif
// fusion.c
#include "fusion.h"
#include "math.h"

static attitude_t attitude;
static float alpha = 0.98; // 互补滤波权重

#define RAD_TO_DEG 57.29578f

void fusion_init(float a)
{
alpha = a;
attitude.pitch = 0;
attitude.roll = 0;
attitude.yaw = 0;
}

void fusion_update(float ax, float ay, float az,
float gx, float gy, float gz, float dt)
{
// 加速度计计算角度 (低频)
float accel_pitch = atan2(-ax, sqrt(ay*ay + az*az)) * RAD_TO_DEG;
float accel_roll = atan2(ay, az) * RAD_TO_DEG;

// 陀螺仪积分 (高频)
attitude.pitch += gx * dt;
attitude.roll += gy * dt;
attitude.yaw += gz * dt;

// 互补滤波融合
attitude.pitch = alpha * attitude.pitch + (1 - alpha) * accel_pitch;
attitude.roll = alpha * attitude.roll + (1 - alpha) * accel_roll;
}

attitude_t fusion_get_attitude(void)
{
return attitude;
}

3. FOC控制

// foc.h
#ifndef FOC_H
#define FOC_H

#include <stdint.h>

void foc_init(void);
void foc_set_angle(float angle); // 设置目标电角度
void foc_set_pwm(int u, int v, int w);
float foc_electrical_angle(float mechanical_angle, uint8_t pole_pairs);

#endif
// foc.c
#include "foc.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"

// FOC Park变换
typedef struct {
float alpha, beta;
float d, q;
float u_alpha, u_beta;
} foc_t;

static foc_t foc;

void foc_set_pwm(int u, int v, int w)
{
// 设置三相PWM (需要反park变换后的值)
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, u);
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, v);
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_C, w);
}

4. 串级PID控制

// cascade_control.c
#include "pid.h"

typedef struct {
// 外环 (角度环)
pid_controller_t angle_pid;

// 内环 (角速度环)
pid_controller_t speed_pid;
} cascade_pid_t;

static cascade_pid_t pitch_cascade = {
.angle_pid = {
.kp = 10.0f, .ki = 0.0f, .kd = 0.5f,
.target = 0.0f, .feedback = 0.0f,
.output = 0.0f,
.integral = 0.0f, .prev_error = 0.0f,
},
.speed_pid = {
.kp = 1.0f, .ki = 0.05f, .kd = 0.0f,
.target = 0.0f, .feedback = 0.0f,
.output = 0.0f,
.integral = 0.0f, .prev_error = 0.0f,
},
};

float cascade_control_update(float target_angle, float current_angle,
float current_speed, float dt)
{
// 外环: 角度环
pid_update(&pitch_cascade.angle_pid, target_angle, current_angle, dt);
float angle_output = pitch_cascade.angle_pid.output;

// 内环: 角速度环 (目标角速度 = 角度环输出 + 前馈)
float target_speed = angle_output;
pid_update(&pitch_cascade.speed_pid, target_speed, current_speed, dt);

return pitch_cascade.speed_pid.output;
}

项目开源方案参考

优质GitHub项目

项目链接说明
SimpleFOC for ESP32https://github.com/simplefoc/Arduino-FOCFOC控制库
ESP32 Gimbalhttps://github.com/mjhxmjh/ESP32-gimbal云台控制
BLDC Controllerhttps://github.com/rikorg/bldc-controller无刷直流控制
MPU6050 Fusionhttps://github.com/k根本原则越好/ESP32-MPU6050姿态解算
Gimbal Controllerhttps://github.com/C蛮/brushless-gimbal云台控制器

学习资源

资源链接
simplefoc.comhttps://simplefoc.com/
FOC算法详解https://zhuanlan.zhihu.com/p/147659863
卡尔曼滤波https://zhuanlan.zhihu.com/p/37777039

云台调试技巧

PID参数整定

调试顺序: 先内环后外环

1. 内环 (角速度环) 调试
├── P参数: 从小到大, 出现振荡则回调
├── I参数: 消除稳态误差
└── D参数: 抑制超调

2. 外环 (角度环) 调试
├── P参数: 控制响应速度
├── D参数: 增强阻尼
└── 前馈: 加速度前馈减少跟踪误差

典型参数参考:
角度环: Kp=8-15, Ki=0, Kd=0.3-1.0
角速度环: Kp=0.5-2.0, Ki=0.02-0.1, Kd=0

常见问题处理

现象原因解决方法
电机抖动换向不平滑/PWM分辨率低增加死区补偿/提高PWM频率
角度漂移积分饱和/陀螺仪零偏积分限幅/校准零偏
响应迟缓P值太小/负载太大增大P/减小负载
自激振荡增益过大减小P/D值

无刷云台是电赛和毕业设计中的热门项目,ESP32-S3的FOC支持和Wi-Fi调参功能大大简化了开发难度。