PID定速/定位置控制电机

开始

使用位置式PID和增量式PID实现370减速电机定速和定位置控制。

PID理论

连续形式的PID公式是贴合实际生活的,单片机执行PID调控是按照一个周期进行调控的,所以编程时使用的是离散式的PID公式。

离散公式的一些解释:

k:的取值是0,1,2,等表示的是第几次调控

error(k):表示的是第k次调控时当前的误差值。

T是调控周期 后面这个符号是求和的意思就是0-k次误差的累加。

Kd项乘的是第K次与第K-1次的误差的差值。

开环与闭环控制

开环控制:控制器只单纯的输出来控制被控制的对象,不获取被控对象的状态。

闭环控制:控制器不只单纯输出,而且要获取被控对象的实际状态,并根据实际状态与目标状态的误差,来调控输出值的大小。

比例项

out(t)=K_p∗error(t)

Kp大小决定了比例项的权重,KP越大系统响应越快,并出现超调。纯比例项控制系统会存在稳态误差。因为当KP为0时,PID调控输出0,系统由于内外因素一般都向一个方向的偏移,产生误差。
积分项

out(t)=K_p∗error(t)+K_i∗∫_0^t▒error(t)dt

积分项可以消除纯比例项产生的稳态误差,积分项权重越大,稳态误差消失越快,但系统的响应速度变慢。
微分项

微分项通过判断误差变化趋势来抑制误差的变化。作用类似于阻尼,可以防止系统超调。

为什么Ki要* T

连续形势下0-8次误差积分就是求图中绿色曲线下的面积,离散形势下0-8次误差这一片区域的面积约等于8个矩形的面积

即Terror(0),T error(1)...Terror(7),把T提出来就是T (error(0)+error(1)+...+error(7))。这里也可以看出调控周期越小T累加结果越精确

为什么Kd要除以T

求T5时刻的微分就是求k=5时刻绿线切线的斜率,离散的情况下,就是(error(5)-error(4))/T,所以红线表示T5时刻的微分,蓝线表示T8时刻的微分,同时可以看出微分表示的是变化的趋势。

因为Ki,Kd,T都是常数 所以T合并到Ki,Kd中公式最后如下:

最后合并后的公式输出结果会因为T的变化而变化,如果不希望看到这种结果可以用上面的公式。

位置式PID和增量式PID

位置式PID是从连续形式的PID离散得到的,每次计算的都是全量的输出值。

增量式PID由位置式PID推到得到,每次计算的是输出值的增量。它的计算结果是在上一次的输出值的基础上进行的。

对于电机控制,调控周期一般20-100ms.

PID程序实现

调控周期的确定:1,使用delay函数,适合简单应用场景。2,使用定时器定时,在中断中处理(涉及硬件的操作不能即在主循环 操作又再中断中操作,线程互斥)3,定时中断中置标志位,主循环检测标致位,进行PID调控。


位置式PID

增量式PID

开环控制

即不使用PID调节电机转速。当电机受到阻力时,转速降低,由于PWM没有受到PID的调控,当电机受到阻力时转速降低。为了维持相同的转速当电机受到阻力时应该增大PWM的输出。

这里Speed的值跟编码器的读取周期有直接关系,它的单位是 32个边沿/40ms,如果设定读取周期为20ms,则Speed为16,时间再设定长一点200ms,Speed为162,读取周期越长,读取的边沿数越精确。读取周期比PID调控周期小,数据浪费。读取周期比PID调控周期大,得到的数据不是最新值PID调控就跟不上,所以要将读取周期和调控周期设定一样,40ms就可以。

c 复制代码
int16_t Speed=0;
int16_t PWM=0;

int main(void)
{

	OLED_Init();
	Motor_Init();
	Key_Init();
	Encoder_Init();
	Timer_Init();
	OLED_ShowString(0,0,"Speed Control",OLED_8X16);
	OLED_Update();
	
	while (1)
	{
		if(Key_Check(KEY_1,KEY_SINGLE)){
			PWM+=10;
			if(PWM>=100){
				PWM=100;
			}
		}else if(Key_Check(KEY_2,KEY_SINGLE)){
			PWM-=10;
			if(PWM<=-100){
				PWM=-100;
			}
		}else if(Key_Check(KEY_3,KEY_SINGLE)){
			PWM=0;
		}
		Motor_SetPWM(PWM);
		OLED_Printf(0,16,OLED_8X16,"PWM:%04d",PWM);
		OLED_Printf(0,32,OLED_8X16,"Speed:%04d",Speed);
		OLED_Update();
	}
	
}

void TIM1_UP_IRQHandler(){
	static uint16_t count=0;
	if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET){
		//LED_Tick();
		count++;
		if(count>=200){
			count=0;
			Speed=Encoder_Get();
		}	
		Key_Tick();
		TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
	}
}

位置式PID定速控制

按键设置目标速度,编码器读取实际速度。当电机输出收到阻力时系统会自动增加输出使得实际速度维持在目标速度附件。

1,设定调控周期 40ms

2,定义全局变量

c 复制代码
float Target,Actual,Out;//目标值,实际值,PID输出值
float Kp=0,Ki=0,Kd=0;//PID各项参数
float Error0,Error1,ErrorInt;//本次误差,上一次误差,误差的积分

3,其他代码

c 复制代码
int main(void)
{

	OLED_Init();
	Key_Init();
	RP_Init();
	Encoder_Init();
	Motor_Init();
	Timer_Init();
	
	OLED_Printf(0,0,OLED_8X16,"Speed Control");
	OLED_Update();
	while (1)
	{
		Kp=RP_GetValue(1)/4095.0 *2;
		Ki=RP_GetValue(2)/4095.0 *2;
		Kd=RP_GetValue(3)/4095.0 *2;
		Target=RP_GetValue(4)/4095.0*300 -150;
		OLED_Printf(64,16,OLED_8X16,"Tar:%+04.0f",Target);
		OLED_Printf(64,32,OLED_8X16,"Act:%+04.0f",Actual);
		OLED_Printf(64,48,OLED_8X16,"OUT:%+04.0f",Out);
		OLED_Printf(0,16,OLED_8X16,"Kp:%+4.2f",Kp);
		OLED_Printf(0,32,OLED_8X16,"Ki:%+4.2f",Ki);
		OLED_Printf(0,48,OLED_8X16,"Kd:%+4.2f",Kd);
		OLED_Update();

	}
}
void TIM1_UP_IRQHandler(){
	static uint8_t count;
	if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET){

		count++;
		if(count>=40){
			count=0;
			
			Actual=Encoder_Get();//获取当速度
			Error1=Error0;
			Error0=Target-Actual;//获取当前值与目标值的误差差
			if(Ki!=0){//误差一直在累加,当KI不为0时会产生很大的值,导致系统暂时失去控制
				ErrorInt+=Error0;
			}else{
				ErrorInt=0;
			}
			
			Out=Kp*Error0+Ki*ErrorInt+Kd*(Error0-Error1);//位置式PID计算输出
			
			if(Out>100){Out=100;}//积分限幅
			if(Out<-100){Out=-100;}
			
			Motor_SetPWM(Out);//设置PWM
		}		
		Key_Tick();
		TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
	}
}

几个问题说明一下:

1,极性问题:

现象是:当设定一个目标值或者给电机一个初速度后电机开始疯狂旋转。

原因是:电机旋转极性与编码器的极性不一致。我们设定电机旋转极性顺时针为正,逆时针为负,编码器顺时针旋转计数增加,逆时针旋转计数减小。如果这两个极性不一致,则PID调控由负反馈变为正反馈,当系统出现一点误差时,PID增大输出,结果电机旋转更快,误差变得更大,PID就再次增大输出,很快输出就拉满了。

测试:将编码器的极性修改一下

复制代码
```c
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//极性相反
```
调整编码器极性有两种方法,1调整AB相的接线,2,如上修改代码  3,还可以将PID的输出OUT加个负号
负反馈使得误差减小,正反馈使得误差增大。

2,输入参数与输出参数的关系

代码中PID的输出值OUT在+100 ~ -100 之间,输入值(实际速度值)实测在+180 ~ -180,输入*系数K=暑促,系数K约等于0.几。

3,PID参数整定:

先确定P后I再D

增加代码使用软件查看调控波形

c 复制代码
		Serial_Printf("%f,%f,%f\r\n",Actual,Target,Out);

使用电位器旋钮调整KP,可以看到KP过小系统响应慢,稳态误差大,KP过大则系统超调抖动

要到这样的效果,实际值要快速跟进但在接近目标值时有一个减速平滑的过度。

增量式PID定速控制

红线是实际值,这里有个小台阶,发现调小KP波形更平滑。

c 复制代码
void TIM1_UP_IRQHandler(){
	static uint8_t count;
	if(TIM_GetITStatus(TIM1,TIM_IT_Update)==SET){

		count++;
		if(count>=40){
			count=0;
			
			Actual=Encoder_Get();//获取当速度
			Error2=Error1;
			Error1=Error0;
			Error0=Target-Actual;//获取当前值与目标值的误差差
			
			Out+=Kp*(Error0-Error1)+Ki*Error0+Kd*(Error0-2*Error1+Error2);//位置式PID计算输出
			
			if(Out>100){Out=100;}//积分限幅
			if(Out<-100){Out=-100;}
			
			Motor_SetPWM(Out);//设置PWM
		}		
		Key_Tick();
		TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
	}
}

发现增量式PID在KP,KI,KD都设置为0 以后速度能保持主,原因是增量式PID只计算输出值的增加量,当增加量为0是系统按当前值继续运行。比如目前Kp,Ki,Kd都给O,相当于PID算法暂停了,此时再改变目标值,输出值不会变,实际值也不会跟进操作,这相当于进入了手动控制模式,用户可以凭感觉修改OUT的值,进行手动控制,当修改完OUT的值后,在此启动PID控制就相当于回到了自动控制模式。手动切换到自动后,位置式PID由于内部有一个单独的积分变量ERRorInt,位置式PID这个积分变量需要进行变化,会出现短暂的抖动,过一段时间后才能适应当前状态。而增量式PID内部没有积分变量在切换的过程中可以保证输出值的稳定过度。

增量式PID的OUT+=变化值也是积分的意思,并且不会受到积分饱和的影响。

位置式PID定位置控制

明确一下:

Target:表示目标位置

Actual:表示实际位置,由编码器测得

OUT:表示控制力度

代码需要需要修改两处

c 复制代码
			Actual+=Encoder_Get();//获取当位置
c 复制代码
Target=RP_GetValue(4)/4095.0*816 -408;//电机输出轴转一圈共408个边沿

纯KP调节波形可以看出,如果手动拧输出轴,OUT绿色才输出,ACtual红色贴近目标值紫色,但是与目标值还有一点误差,这就是稳态误差,当输出力太小不足以克服摩擦力驱动电机旋转就会存在。加一点KI就可以了。

如果实际值是逐渐放缓靠近目标值的,则不需要加Kd,如果实际值贴近目标时还不及时放缓,则可以加些Kd进行抑制。

参数个的大,响应快,但是会有抖动,参数给的小,响应慢,更平滑。

纯P项调节小幅度调整Target波形已经很好了:

纯P项大幅度调整时有过冲这里加入D项对比就能看出效果

加入I项反而过冲的很厉害

增量式PID定位置控制

代码修改:

c 复制代码
Actual +=Encoder_Get();//获取当位置

实验现象:

低速调节现象没问题,大幅度调节后,实际值和目标值会出现偏差,只能通过加KI来消除,反而引起了过冲。

增量式PID就算的输出值是在上一次的基础上增加的,如果本次误差小到0,上次误差收到噪声干扰那么增量式PID很难纠正错误的OUT,只有加一点KI。

总结

由开环控制的弊端引出PID闭环控制,PID调控是从经验中积累来的,比例项的调控最直接,过大容易出现过冲。积分项用于消除稳态误差,微分项是阻尼,更具误差变化趋势来调控输出的大小。位置式PID由连续PID离散得来,增量式PID由位置式PID公式推演而来,它们的区别是位置式PID计算的数输出的总量,离散式计算的是输出的增量。PID调控程序实现时需要选择合适的调控周期,采样速率与调控速率要保持一致。进行PID调控时需要明确目标值,输入值,输出值代表的意义以及范围。使用串口绘图软件能直观的展现每项参数对输出结果的影响。

相关推荐
SystickInt4 小时前
32 LCD显示(FSMC应用)-寄存器
stm32·单片机·嵌入式硬件
d111111111d4 小时前
C语言中static修斯局部变量,全局变量和函数时分别由什么特性
c语言·javascript·笔记·stm32·单片机·嵌入式硬件·学习
逐步前行4 小时前
C51_74HC595串口转并口
单片机·嵌入式硬件
阿拉斯攀登6 小时前
STM32 架构概述
stm32·单片机·架构
物联网牛七七7 小时前
STM32 EXTI(外部中断)详解
stm32·单片机·嵌入式硬件·exti
发光小北7 小时前
SG-TCP232-110(单通道串口服务器)特点与功能介绍
服务器·网络·单片机
d111111111d7 小时前
STM32外设学习-读取芯片ID。(学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习
阿拉斯攀登7 小时前
STM32 简单入门
stm32·单片机·嵌入式硬件
阿拉斯攀登7 小时前
三极管:电子信息时代的核心“控制单元”,藏于设备中的关键器件
单片机·嵌入式硬件·三极管