PID控制算法理论学习基础——单级PID控制
这是一篇我在学习PID控制算法的过程中的学习记录。在一开始学习PID的时候,我也看了市面上许多的资料,好的资料固然有,但是更多的是不知所云。(有的是写的太过深奥,有的则是照搬挪用,对原理则一问三不知)这一直让我对PID摸不着头脑。所以我打算从0开始去一层层学习它,直到自己掌握它的精髓。我不认为我有多聪明,所以相当多的部分我都写的很详细,觉得过于冗余的小伙伴可以跳着看。
我并不是计算机科学\电控类专业的学生,所以对知识的理解可能有不到位的地方,还请大家指正。
最后,希望这篇长文对大家有所帮助,这也是我完成它的目标之一。
1.1 单级PID的原理理解
这里强烈建议还不理解PID基本原理的同学观看视频:【自动控制原理】12_PID控制器_Matlab/Simulink仿真【开场三分钟闲话】_哔哩哔哩_bilibili
单级PID也就是只使用一个PID控制块。一般讲到PID,大家都喜欢用调节洗澡水的水温来举例:
-
Kp(比例项),即希望调节到的温度与现在的温度差的越大,我们拧动加热旋钮的幅度就越大,这个差值越小,我们拧动加热旋钮的幅度就越小;
-
Ki(积分项),即P比例调节控制了一段时间之后,发现实际值与期望值还是有误差,那就继续拧懂加热旋钮,这是一个在时间上不断累积的过程,故为积分项;
-
Kd(微分项),当实际值与期望值的误差的导数,也就是水温变化的速度,这一项即控制达到期望值,即控制当前水温到达目标水温的速度。
然后将上面三项相加,就得到了基本的PID控制:
即下图
谈谈我对于PID控制的一些理解:
-
首先面对一个需要PID的控制系统的时候,增大P(比例控制系数)可以使得系统的反应灵敏,但是相应地会无法避免地产生稳态误差并且单纯使用比例控制的系统无法消除这种误差;
-
在得到一个较为灵敏同时拥有较小的稳态误差的系统时,这时引入I(积分控制项),积分控制可以有效地消除稳态误差,但是会使得系统达到稳态的时间延长,也是就是出现了震荡;
-
在PI控制完成之后,一般来说对于一些对时间不是很敏感的系统都已经拥有较好的控制表现了,对于时间要求苛刻的系统就需要引入D(微分控制项)来进一步缩短系统达到稳态的时间(减少震荡);
-
D项的一个很不友好的特性就是对环境噪声极其敏感,引入D之后很容易就会使得先前已经稳定的系统突然变混乱起来,所以通常也需要较多的时间类来获取试验D的取值。
以上就是一个简单的PID控制系统的介绍。从实现来看,其实有着两种不同的实现方式,一种叫位置式PID,另外一种则为增量式PID。其中位置式PID我较少在平衡控制等项目中见过,而增量式PID则大量运用在电机的控制上(驱动平衡类项目),下面来简单说一下这两种PID控制的实现。
1.2 单级PID的代码实现
其中我们知道PID的公式如下:
但是我们会选用下面这个式子来进行实际的工程运用。可以看到除了积分变为累加之外(毕竟现实中数据都是一段一段不是连续的),D(微分项)中的形式变了,这是为了避免一个被称为“微分冲击”的现象,具体的原因在系列3中会有提到,这里可以暂时不去管它。
这里使用的是以tank_like模型(即四个电机的角度固定,靠转动的差速转弯的小车模型,与之相对的是Car_like模型,前面两个轮子可以偏转一定角度转弯)的四轮小车为例,需要控制其四轮转动同步(也就是能走直线)。
tank_like模型与Car_like模型:
为了能够判断小车每个轮子的转动方向以及转动的幅度,需要使用带有编码器的电机,或者使用光电门也是同样的原理。这样就能获取到小车每一个轮子的实时速度值,与目标的速度 作差 后就是这个PID系统的输入。
而小车上单片机向电机输入的PWM波(可以控制小车电机油门),就这个PID系统的输出。
如下图:
//位置式PID
//定义所需要的变量
float Kp, Ki, Kd;
float Motor1_speed;//电机当前的速度
float target_speed;//我们需要电机达到的速度
float err_now;//当前速度与电机期望值的差,也就是当前的误差值
float err_last;//上一次计算的误差值
float err_Sum;//积分项,将累计时间内所有的误差值
float output;//经过PID算法后输出的数据,其实是一个控制PWM占空比的参数,我们只需要直接放在输出PWM的函数里就好了//PID算法
err_now = target_speed -Motor1_speed;
err_Sum += err_now;
if(err_Sum > x) err_Sum = x;//这里需要设定一个极限值,以防积分变量溢出,其实一般来说并不会达到这个极限值,因为误差并不都是正数
//没有对D进行操作
output = Kp * err_now + Ki * err_Sum +Kd * (err_now - err_last);//PID公式
if(output > x) output = x;//这里需要对输出也进行一个限制,这里倒不是限制溢出,而是PWM输出的参数本身就有最大值,超过了就无效了。
TIM_SetCompare1(TIMx, output);//对产生PWM的定时器通道一进行输出
err_last = err_now;//将使用完的当前误差值赋给上一次的误差值,进行下一轮循环
增量式PID的原理就是不直接使用每一次PID产生的输出,而是将两次PID产生的输出相减,得到一个PID增量,再使用这个增量对系统进行输出。与位置式PID不同的地方就在于因为需要使用到前后两次的PID公式,那么也就需要当前误差、上一次误差与上上次的误差。在得到输出的增量之后就类似使用积分项一样直接将时间上的每一次增量相加,得到的结果在TIM_SetCompare1()中输出就可以,代码就不赘述了。