PID控制结构&角度环实现直立

平衡车PID控制结构

平均PWM:左右轮PWM的平均值,控制的是前进和后退的快慢。(简单来说就是平均PWM单独控制前后)

差速PWM:两个电机PWM的差值,很明显控制的是左右转弯的快慢。(简单来说差速PWM单独控制左右)

速度环与角度环的关系

角度环(内环)

  • 任务:保持车身直立

  • 原理:车身前倾→轮子向前转;后倾→向后转

  • 输入:俯仰角

  • 输出:差分PWM(左右轮差速)

速度环(外环)

  • 任务:控制前进/后退/静止

  • 原理:检测实际速度,与目标速度比较,调整驱动力

  • 输入:编码器速度

  • 输出:平均PWM(左右轮同速)

两者如何配合

复制代码
左轮PWM = 平均PWM + 差分PWM / 2 
右轮PWM = 平均PWM - 差分PWM / 2

平均速度 = (左轮速度 + 右轮速度) / 2

差分速度 = 左轮速度 -- 右轮速度

  • 角度环负责站稳

  • 速度环负责跑多快

  • 两者叠加输出给电机,互不干扰

一句话总结

角度环让车不倒,速度环让车听话,两者叠加驱动电机,实现直立行走。

转向环:**控制小车左转和右转。**右边输入的是用户设定的目标转弯速度,如果小车实际走的是直线,测量得到左轮速度和右轮速度绝对一致(差分速度为0)。如果不是0,就说明小车走偏了,转向环会比对目标转弯速度和实际转弯速度。如果向左走偏了,就控制小车往右走。如果向右走偏了,就控制小车往左走。也就是通过控制差分PWM来控制小车左右转弯

举例(转向环):如果没有转向环的闭环控制,那么,你让这个平衡车走直线的时候,左右轮的PWM就可能会不一样(电机差异导致),这就导致了小车可能不能走直线。

加入PID后,互补滤波由原来的1ms调整一次变成了10ms调整一次,所以需要调整α的大小(由0.001变为0.01)。

虽然这样子之后小车的角度测量效果不错,但是后期却出现了小车无论如何直立不了的问题。问题出在了MPU6050上。

这个值是0x06,对应滤波器配置最大,输出数据最平滑,会导致Delay,导致小车无论怎么调都无法保持直立。

我们这里将0x06改成0x00,不使用自带的低通滤波器。

再看采样率分频,不使用自带的滤波器后,陀螺仪时钟的频率是8kHz,那么采样率就是8kHz / (1 + 9)= 800Hz,如果这个值改成7,那么采样率就是8kHz / (1 + 7)= 1kHz(这里的采样率就是MPU6050的刷新频率)。

代码逻辑:

cpp 复制代码
if (RunFlag)
			{
				AnglePID.Actual = Angle;
				PID_Update(&AnglePID);
				AvePWM = -AnglePID.Out;
				
				LeftPWM = AvePWM + DifPWM / 2;
				RightPWM = AvePWM - DifPWM / 2;
				
				if (LeftPWM > 100) {LeftPWM = 100;} else if (LeftPWM < -100) {LeftPWM = -100;}
				if (RightPWM > 100) {RightPWM = 100;} else if (RightPWM < -100) {RightPWM = -100;}
				
				Motor_SetPWM(1, LeftPWM);
				Motor_SetPWM(2, RightPWM);
			}
			else
			{
				Motor_SetPWM(1, 0);
				Motor_SetPWM(2, 0);
			}

之后我们开始调节P、I、D的参数:

cpp 复制代码
if (BlueSerial_RxFlag == 1)
		{
			char *Tag = strtok(BlueSerial_RxPacket, ",");
			if (strcmp(Tag, "key") == 0)							//收到按键数据包
			{
				char *Name = strtok(NULL, ",");
				char *Action = strtok(NULL, ",");
				
			}
			else if (strcmp(Tag, "slider") == 0)					//滑杆数据包
			{
				char *Name = strtok(NULL, ",");
				char *Value = strtok(NULL, ",");
				
				if (strcmp(Name, "AngleKp") == 0)
				{
					AnglePID.Kp = atof(Value);
				}
				else if (strcmp(Name, "AngleKi") == 0)
				{
					AnglePID.Ki = atof(Value);
				}
				else if (strcmp(Name, "AngleKd") == 0)
				{
					AnglePID.Kd = atof(Value);
				}
			}
			else if (strcmp(Tag, "joystick") == 0)					//摇杆数据包
			{
				int8_t LH = atoi(strtok(NULL, ","));
				int8_t LV = atoi(strtok(NULL, ","));
				int8_t RH = atoi(strtok(NULL, ","));
				int8_t RV = atoi(strtok(NULL, ","));
				
				AnglePID.Target = LV / 10;
				DifPWM = RH / 2;
			}
			
			BlueSerial_RxFlag = 0;
		}
		
		BlueSerial_Printf("[plot,%f,%f]", AnglePID.Target, Angle);

这里我们通过蓝牙模块进行调节:

相关推荐
羽获飞2 小时前
从零开始学嵌入式之STM32——29.通用定时器-输入捕获模式测量信号的周期和频率
stm32·单片机·嵌入式硬件
易水寒陈2 小时前
使用vscode开发stm32
ide·vscode·stm32
BUG_yechiyu2 小时前
RT-Thread Nano版本使用RTC,同时解决复位日期丢失,时间并正确问题
stm32
LCG元2 小时前
STM32实战:基于STM32CubeMX的串口通信与DMA传输优化
stm32·单片机·嵌入式硬件
heanyu2 小时前
STM32学习 1 ----串口通讯--阻塞式收发+支持printf
stm32·嵌入式硬件·学习
Suifqwu3 小时前
stm32进阶-FLASH存储优化
stm32·单片机·嵌入式硬件
FreakStudio14 小时前
不用费劲编译ulab了!纯Mpy矩阵micronumpy库,单片机直接跑
python·嵌入式·边缘计算·电子diy
逐步前行14 小时前
STM32_TIM_寄存器操作
stm32·单片机·嵌入式硬件
0南城逆流015 小时前
【STM32】知识点介绍七:PWM功能
stm32·单片机·嵌入式硬件