在用过这么多次PID之后,我对PID唯一的感受就是神奇,它几乎可以应付任何需要进行自动调节的地方,它可以使被控制的对象反应更加灵敏。但是我经常看见有人对这个算法有很多不理解的地方,而网上的资料又杂乱无章,去抱这一本自控理论来啃也不太现实,所以呢我就希望用我自己的经验和对PID这个算法的理解,来帮助大家掌握这个神奇的工具,好让我们在今后的比赛中,或是在工作中,都能设计出一套性能更加优越的自动控制装置。
进入正题。
PID被设计出来的时候,就是为了解决被控制系统的复杂性的问题,被控制系统的响应函数一般都不是线性关系的,就算理论上是线性,实际运行中也会由于摩擦或是其他什么因素,影响系统的线性关系。
举个例子:(图片来自于互联网)
上图是一个旋转倒立摆的模型,这个系统需要做的是,根据角度传感器返回的角度值,来控制直流电机相应的输出,从而使摆杆直立起来,一个最简单的控制策略是,当摆杆往左偏的时候,让旋臂往左转,反之右转。乍一看这个思路好像没什么问题,因为程序可以运行很快,如果摆杆稍微离开我们需要设定的位置,他就可以被迅速调节回来。但是现实是摆杆很快就被甩飞了,囧。在现实中的被控制对象,刚刚的思路忽略的一个因素,那就是物体的惯性,当我们让物体很快回复的过程中,这时物体的动能很大,到达目标位置的时候,根本无法停下来,继续到达相反的,更加偏离目标的位置,这是一个恶心循环,左摆右摆,不断积累动能,最终系统调节不过来,就是刚刚说的被甩飞。要如何应对这种系统呢?答案就是今天的主角:PID登场!
什么是PID:
很简单,就是这么一条公式。关于其中复杂的控制理论,我们也不用去深究它,知道各个参数的意义就可以了。
u(t):某时刻对应的输出,比如单片机的pwm占空比
e(t):某时刻的目标值和实际值的差,比如倒立摆和竖值位置相差的角度
Kp:比例系数
Ki:积分系数
Kd:微分系数
这个公式的意义就是,根据当前的偏差(e(t)),算出系统需要输出的值,比如占空比,这个值等于 一定比例当前的偏差(比例P)+一定比例的偏差从0时刻的积分(积分I)+一定比例的当前偏差的微分(微分D)。
以下图中白色是目标值,绿色是反馈值,红色是PID控制器的输出值:
比例P:
上图,的p设为了2,其他参数设为了0。
比例的意义:
按照我自己的理解,比例的可以想成是弹簧的效果,比如上面的倒立摆,想成是一个弹簧在往左边拉,一个在往右边拉,同时作用可以使其迅速回到中间位置。当然从公式上也能看出来他们的相似性,胡克定理:F=k×Δx。
积分I:
上图,i=0.01,其余全为0。
可以看到图中红色图像是绿色图像的积分。
积分的意义:
当误差不断累计,这时还只是用死板的比例就永远也无法使其调节过来,必须对这个误差进行累积,也就是积分,按照一定比例混合进比例P的作用中,增强系统的效果。
微分D:
D=100,其余全为0。
微分的意义:
我用了一个比较形象的模型来理解这个参数。微分的数学意义是变量的变化快慢,而对于e(t)/dt,就是偏差的变化快慢,PI加上D的效果,就是在抑制PI的控制效果(因为偏差的方向和需要调节的方向相反,公式里写的是加号,实际应该是减号,所以是抑制,千万不要理解成了增强,我当初就犯过这错)。所以整体效果就是,偏差缩小的越快,系统的阻力越大,可以把上述的倒立摆想象成当已经加入了P和I后,又把他们侵入了水中,当摆杆接近中心位置越来越快的时候,由于水的阻力会随着速度的增大而增大,最终结果就是摆杆迅速回到中心位置,但是不会造成过度调节,因此也避免了最开始来回震荡的问题。
我第一次体会到PID的神奇之处就是源于此,当时程序也写的不长,就是用了PID算法,慢慢的调节P、I、D三个参数,最后突然摆杆非常完美的立了起来,瞬间让我对发明这个算法的人无比崇拜。一个公式,简洁明了,却可以适应这么多的控制系统。大家也可以去了解下,我们熟知的两轮直立车、四轴飞行器或是其它什么东西,都能够用PID进行解决。
代码贴出来,有错误还请指正:
pid.h
-
/************************************************************************
-
简介:位置式PID控制程序
-
作者:logic
-
日期:2016年3月1日
-
************************************************************************/
-
#ifndef PID_H
-
#define PID_H
-
-
typedef struct PID_Struct
-
{
-
float P;
-
float I;
-
float D;
-
float IThreshold;
-
float rate_actual_output;
-
-
float aim;
-
float previous;
-
float current;
-
float error;
-
-
float I_count;/*integral from n=0 to n=t*/
-
-
float result;
-
}PID_Struct;
-
-
/*
-
*function:pid初始化
-
*arg:rate_actual_output:开环时,实际反馈与输出的比值,需实测。
-
* 此为自创,比如用于电机调速,因为积分速度过慢会导致系统不够灵敏,加上这个控制效果能使速度变化更明显!
-
* p,i,d:比例积分微分系数
-
* IThreshold:积分阈值
-
*/
-
void PID_Init(PID_Struct *PID,float rate_actual_output,float P,float I,float D,float IThreshold);
-
/*
-
*function:设置目标值
-
*arg:aim:目标值(单位以反馈值为准)
-
*details:和PID_out异步运行
-
*/
-
void PID_SetAim(PID_Struct *PID,float aim);//used by user(asynchronous with PID_Out)
-
/*
-
*function:计算pid输出
-
*arg:current:此刻的反馈值
-
*details:需周期性调用此函数,并且把输出传给执行装置
-
*/
-
float PID_Out(PID_Struct *PID,float current);//used by system
-
-
-
#endif
pid.c
-
#include "pid.h"
-
-
void PID_Init(PID_Struct *PID,float rate_actual_output,float P,float I,float D,float IThreshold)
-
{
-
PID->P=P;
-
PID->I=I;
-
PID->D=D;
-
PID->IThreshold=IThreshold;
-
PID->aim=0;
-
PID->previous=0;
-
PID->current=0;
-
PID->error=0;
-
PID->I_count=0;
-
PID->result=0;
-
PID->rate_actual_output=rate_actual_output;
-
}
-
void PID_SetAim(PID_Struct *PID,float aim)//used by user(asynchronous with Calc)
-
{
-
if(PID->aim==aim)
-
return;
-
PID->aim=aim;
-
PID->I_count=0;//I think the integral should be reset when aim change.
-
-
}
-
float PID_Out(PID_Struct *PID,float current)//used by system
-
{
-
/*declare for variables*/
-
float P_result;
-
float I_result;
-
float D_result;
-
/*calculate for error*/
-
PID->previous=PID->current;
-
PID->current=current;
-
PID->error=PID->current-PID->aim;
-
/*calculate for P,I,D's results separately*/
-
//for P
-
P_result=PID->P*PID->error;
-
//for I
-
PID->I_count+=PID->error;
-
I_result=PID->I*PID->I_count;
-
if(I_result>PID->IThreshold)
-
{
-
PID->I_count=PID->IThreshold/PID->I;
-
I_result=PID->IThreshold;
-
}
-
else if(I_result<-PID->IThreshold)
-
{
-
PID->I_count=-PID->IThreshold/PID->I;
-
I_result=-PID->IThreshold;
-
}
-
//for D
-
D_result=PID->D*(PID->current-PID->previous);
-
/*resulte*/
-
PID->result=P_result+I_result+D_result;
-
//return -PID->result+PID->aim/19.83;
-
if(PID->rate_actual_output==0)
-
return -PID->result;
-
else
-
return -PID->result+PID->aim/PID->rate_actual_output;
-
}
阅读(4318) | 评论(0) | 转发(0) |