STM32二轴陀螺仪云台

基于STM32的二轴陀螺仪云台设计

基于STM32F103C8T6最小核心板设计的二轴陀螺仪云台,基于江协科技的中断和OLED例程基础上改进而成的。

江协科技的个人空间-江协科技个人主页-哔哩哔哩视频

省流版:网盘分享的文件:定时器云台.rar
链接: https://pan.baidu.com/s/14EF_TK2dO8duZfw14ER-gg?pwd=s9cg 提取码: s9cg

可以直接去获取。

本项目设计的硬件部分有,MPU6050模块、两个SG90_180度舵机、OLED(IIC协议)屏幕

屏幕主要是输出MPU6050模块的X和Y的倾斜角度,用于观察和调试。

本程序获取MPU6050的以及舵机的驱动,都是在中断里面完成的,不会阻塞主程序,这也方便我们去拓展其他的功能,比如把舵机换成步进电机,添加新的传感器等,方便后续的拓展,当然,程序也采用模块化,方便我们去做移植。

舵机采用PWM驱动,另外说一下,舵机得靠5V驱动这一点注意一下,两个舵机的信号线分别连接了STM32的PA0和PA1引脚,连接舵机的为淘宝购买的舵机打印套件。

首先让我们来看一下时钟开启部分

void Timer_Init(void)
{
	//开启TIM2 时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//开启 GPIOA 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
//nvic	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

开启TIM2 时钟和开启 GPIOA 时钟,当然可以根据自己的需求去更改时钟和引脚,但要提前去查看STM32的引脚分布和复用功能。

void GPIO_Config_PWM(void)
{
	//引脚配置
    GPIO_InitTypeDef GPIO_InitStructure;

    // 配置 A0 引脚(TIM2_CH1)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 A1 引脚(TIM2_CH2)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOA, &GPIO_InitStructure);	
}

void TIM2_PWM_Config(void)
{
    TIM_OCInitTypeDef TIM_OCInitStructure;

    // 配置 TIM2_CH1 为 PWM 模式(对应舵机 1 的 A0 引脚)
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;  // 初始占空比为 0
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

    // 配置 TIM2_CH2 为 PWM 模式(对应舵机 2 的 A1 引脚)
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OC2Init(TIM2, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
}

这里设置对应的引脚模式以及开启PWM的模式,这样一来,时钟和PWM部分就完成了,接下来让我们来看一下舵机驱动部分

#include "stm32f10x.h"                  // Device header
#include "Timer.h"


void Servo_Init(void)
{
	Timer_Init();
	GPIO_Config_PWM();
	TIM2_PWM_Config();
}

void Set_Servo_Angles(float angleX, float angleY)
{
	uint16_t pulse_widthX,pulse_widthY;
	//X舵机计算(A0)
	pulse_widthX = (500 + ((2500 - 500) * angleX / 180));
	TIM_SetCompare1(TIM2,pulse_widthX);
	//Y舵机计算(A1)
	pulse_widthY = (500 + ((2500 - 500) * (angleY+90) / 180));
	TIM_SetCompare2(TIM2,pulse_widthY);
}

注意,这里要提前引用Timer定时中断的头文件,防止后续编译的报错,第一部分就是初始化,第二部分就是通过传入的float的值来让舵机做出对应的驱动,值得注意的是,我这里的Y舵机,初始让处于中间的位置,所以在angle Y中加入了90的相位,这样可以保证Y舵机的左右转向,然后舵机函数传入的数值,是去捕获陀螺仪获取的,这里因为我们侧重讲的舵机方面,所以陀螺仪部分就先不侧重讲述,后续再看(挖坑中。。。。),这里我们会引用会调用就行,也可去看看江协科技里面讲陀螺仪部分的视频[10-2] MPU6050简介_哔哩哔哩_bilibili,我这里,做了一部分的改进,因为我们是想通过中断的方式去捕获这个数值,所以我在这选择再封装一个函数,

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

void MPU6050_Tick(void)
{
	MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
}

通过MPU6050_Tick函数,去获取这个数值,然后让变量的值通过extern的方式,可以让其他文件可以直接调用

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		MPU6050_Tick();
		Set_Servo_Angles(AngleX, AngleY);
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

主函数中,我们通过中断的方式去掉用陀螺仪的数值以及去驱动舵机,主函数需要去实现显示的功能,这样一来不会阻塞主程序,这里我们通过i++的形式,来观察,主函数的状态

#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Key.h"
#include "MPU6050.h"
#include "Servo.h"

uint8_t KeyNum;
uint16_t i;

float AngleX,AngleY;

int main(void)
{
	OLED_Init();
	Key_Init();
	MPU6050_Init();
	Servo_Init();

	OLED_ShowString(0,0,"i:",OLED_8X16);
	OLED_ShowString(0,15,"X:",OLED_8X16);
	OLED_ShowString(0,31,"Y:",OLED_8X16);	
	
	while (1)
	{
		OLED_ShowNum(15,0,i++,6,OLED_8X16);
		OLED_ShowSignedNum(16, 15,AngleX,5,OLED_8X16);
		OLED_ShowSignedNum(16, 31,AngleY,5,OLED_8X16);
		OLED_Update();
		if(AX<0)AX=0;
		if(AY>1800)AY=1800;
		if(AY<-900)AY=-900;
		AngleX = AX/10;
		AngleY = AY/10;//下舵机		
	}
}

在实际的运行时,我们发现并不会阻塞主函数,这样一来,我们这给例程就可以进行拓展了。

最后,感谢你能看到这里,因为这个项目也是突发奇想的,可能有很多不足的地方以及没完善的地方,希望能多多指正,下面是本套案例的全套代码

网盘分享的文件:定时器云台.rar
链接: https://pan.baidu.com/s/14EF_TK2dO8duZfw14ER-gg?pwd=s9cg 提取码: s9cg

<think>好的,我现在需要帮助用户解决关于STM32自稳云台的控制算法、PID调试和实现方案的问题。首先,用户提到了参考的站内引用,里面提到了三云台和PID控制的相关内容。我需要结合这些信息来构建回答。 首先,用户的需求是自稳云台,可能是指俯仰和横滚两个,或者横滚和偏航?需要先明确通常指的是哪两个。不过通常云台可能需要稳定俯仰和横滚,比如在无人机或摄像设备中保持水平稳定。所以,接下来我需要从硬件选型开始,包括STM32型号、传感器(比如MPU6050或更高级的IMU)、电机(无刷电机和驱动电路)。这些硬件部分在引用中提到的三云台项目里可能已经有类似的内容,可以参考。 然后,算法部分,姿态解算和PID控制是关键。姿态解算可能需要用到四元数或欧拉角,通过加速度计和陀螺仪的数据进行融合,比如使用卡尔曼滤波或互补滤波。引用1提到了姿态解算和PID控制,所以这里可以引用相关内容。PID部分,用户提到位置式和增量式,根据引用2,需要说明选择哪种PID更适合云台控制。通常云台需要快速响应,增量式可能更合适,因为计算量小,适合实时控制。 接着是控制流程:数据采集(传感器)、姿态解算、PID计算、PWM输出驱动电机。这部分需要详细说明每个步骤的实现,特别是如何将姿态误差转换为PID的输出,进而调整电机。调试部分需要分步骤,先调P,再I,再D,同时注意积分饱和的问题,可能需要抗积分饱和处理。另外,用户可能需要代码示例,比如初始化PID参数、中断服务函数读取传感器数据、计算PID输出等。 在实现方案中,要提到可能的难点,比如传感器数据的噪声处理,电机响应延迟,以及如何通过滤波算法来优化。此外,引用中的STM32程序和PID控制资料可以作为参考,特别是代码注释部分,用户可能需要查看具体的实现细节。 最后,生成的相关问题需要围绕用户可能的后续疑问,比如如何选择传感器、滤波算法、PID参数整定方法,以及云台的机械结构影响等。确保问题覆盖用户可能的下一个步骤或遇到的困难。 现在需要检查是否有遗漏的部分,比如是否涵盖了硬件和软件两方面,调试的具体步骤是否清晰,以及是否引用了提供的参考资料。另外,确保使用正确的中文术语,避免技术术语错误。最后,代码示例部分要符合用户的请求,提供STM32的相关代码片段,但需要确保语法正确,比如使用HAL库或者标准库的函数。</think>### STM32自稳云台实现方案 #### 1. 硬件选型 - **主控芯片**:推荐使用STM32F4系列(如STM32F405/F407),支持浮点运算且外设资源丰富[^1]。 - **传感器**:采用MPU6050(六IMU)或BMI088(高精度IMU)获取姿态数据。 - **电机驱动**:选择无刷电机+电调(ESC),或步进电机+驱动器(如DRV8825)。 - **机械结构**:云台需保证俯仰(Pitch)和横滚(Roll)的自由度。 #### 2. 核心算法实现 1. **姿态解算** 通过加速度计和陀螺仪数据融合计算欧拉角: $$ \theta = \arctan\left(\frac{a_x}{\sqrt{a_y^2 + a_z^2}}\right) \quad (\text{俯仰角}) $$ 使用互补滤波或卡尔曼滤波减少噪声干扰[^1]。 2. **PID控制** - **增量式PID公式**(适合实时控制): $$ \Delta u(k) = K_p[e(k)-e(k-1)] + K_i e(k) + K_d[e(k)-2e(k-1)+e(k-2)] $$ - 将姿态误差输入PID控制器,输出PWM信号驱动电机[^2]。 #### 3. 控制流程 ```c // 伪代码示例(基于STM32 HAL库) void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim == &htim3) { // 10ms中断 // 1. 读取传感器数据 IMU_GetData(&acc, &gyro); // 2. 姿态解算(互补滤波) pitch = 0.98*(pitch + gyro.y*dt) + 0.02*atan2(acc.x, acc.z); // 3. PID计算 pid_pitch.SetPoint = 0; // 目标角度 pid_pitch.Input = pitch; PID_Compute(&pid_pitch); // 4. 输出PWM __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1500 + pid_pitch.Output); } } ``` #### 4. PID调试步骤 1. **初始化参数**:$K_p=0.5, K_i=0, K_d=0$ 2. **调P**:逐步增大$K_p$直至系统开始振荡,取振荡临界值的50% 3. **调D**:加入$K_d$抑制超调(建议$K_d=0.1K_p$) 4. **调I**:微量增加$K_i$消除静差(通常$K_i<0.1K_p$) 5. **抗积分饱和**:增加积分限幅或条件积分 #### 5. 关键优化点 - 传感器校准:开机时自动校准零偏 - 死区补偿:电机启动阈值处理 - 动态调参:根据运动状态切换PID参数组 - 机械限位保护:防止电机过转
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值