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);

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

相关推荐
FreakStudio1 天前
亲测可用!可本地部署的 MicroPython 开源仿真器
python·单片机·嵌入式·面向对象·并行计算·电子diy·电子计算机
rit84324991 天前
STM32 + DS3231 + TM1640 实时时钟数码管显示系统
stm32·单片机·嵌入式硬件
小懒懒️1 天前
嵌入式常见通信协议学习——UART
stm32·uart·通信协议
zjxtxdy1 天前
STM32开发
stm32·单片机·fpga开发
BT-BOX1 天前
STM32简易数字电流表仿真_LCD1602显示
stm32·电流测量·lcd1602显示·电流表
LCG元1 天前
STM32实战:基于STM32F103的I2C通信(AT24Cxx EEPROM读写)
stm32·单片机·嵌入式硬件
飞凌嵌入式1 天前
保姆级教程 | i.MX 93开发板适配Zephyr RTOS全解析
科技·嵌入式
guygg881 天前
基于STM32的智能小区管理系统设计
stm32·单片机·嵌入式硬件
Deitymoon1 天前
STM32——震动传感器控制led
stm32·单片机·嵌入式硬件
BT-BOX1 天前
STM32的温湿度防盗安防报警器仿真_LCD1602显示
stm32·安防·烟雾·防盗·lcd1602显示·dht11温湿度·火焰