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

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

相关推荐
WWJA王文举21 分钟前
RS485通信超时全面排查:终端电阻、A/B线、DE方向控制、串口参数和Modbus一次讲透
stm32·通信协议·rs485
Dr.kangder2 小时前
嵌入式面试总结(十六)——AMBA总线
面试·职场和发展·架构·嵌入式·虚拟化·总线
Dr.kangder2 小时前
嵌入式面试总结(十七)——DMA总线
面试·职场和发展·架构·嵌入式·总线
嘉子的秃头日记4 小时前
机械革命“自动修复”无法修复你的电脑
stm32·单片机·电脑
hsjiasb14 小时前
FreeRTOS学习(二十六)——动态内存管理heap_1到heap_5
stm32·单片机·学习·学习笔记·freertos
LCG元15 小时前
STM32+ESP8266+MQTT 物联网气象站:从零搭建温湿度远程监测系统(附完整源码)
stm32·物联网·struts
FakeOccupational17 小时前
【电路笔记 STM32】Cortex-M7 内核上的数据缓存(D-Cache)结构+MPU+DMA&Cache+STM32CubeMX配置
笔记·stm32·缓存
殷忆枫18 小时前
基于K210与STM32的智能垃圾分类与物联网监管系统
stm32·物联网·分类
周洲083018 小时前
STM32 GPIO 外部中断深度解析:边沿触发 / 电平触发、NVIC 优先级配置、中断嵌套实战
stm32·单片机·嵌入式硬件
轻微的风格艾丝凡18 小时前
波形异常记录--零飘过大
嵌入式·硬件工程·dsp