1. 程式人生 > >PID演算法原理、調整規律及程式碼

PID演算法原理、調整規律及程式碼

PID演算法簡介

要想讓智慧車根據賽道不斷變化靈活的行進,PID演算法的採用很有意義。

控制器公式 為:

PID演算法原理、調整規律及程式碼

PID演算法原理、調整規律及程式碼

  • 比例(P)控制

比例控制是一種最簡單的控制方式。其控制器的輸出與輸入誤差訊號成比例關係。當僅有比例控制時系統輸出存在穩態誤差(Steady-state error)。

  • 積分(I)控制

在積分控制中,控制器的輸出與輸入誤差訊號的積分成正比關係。對一個自動控制系統,如果在進入穩態後存在穩態誤差,則稱這個控制系統是有穩態誤差的或簡稱有差系統(System with Steady-state Error)。為了消除穩態誤差,在控制器中必須引入“積分項”。積分項對誤差取決於時間的積分,隨著時間的增加,積分項會增大。這樣,即便誤差很小,積分項也會隨著時間的增加而加大,它推動控制器的輸出增大使穩態誤差進一步減小,直到等於零。因此,比例+積分(PI)控制器,可以使系統在進入穩態後無穩態誤差。

  • 微分(D)控制

在微分控制中,控制器的輸出與輸入誤差訊號的微分(即誤差的變化率)成正比關係。自動控制系統在克服誤差的調節過程中可能會出現振盪甚至失穩。其原因是由於存在有較大慣性元件(環節)或有滯後(delay)元件,具有抑制誤差的作用,其變化總是落後於誤差的變化。解決的辦法是使抑制誤差的作用的變化“超前”,即在誤差接近零時,抑制誤差的作用就應該是零。這就是說,在控制器中僅引入“比例”項往往是不夠的,比例項的作用僅是放大誤差的幅值,而目前需要增加的是“微分項”,它能預測誤差變化的趨勢,這樣,具有比例+微分的控制器,就能夠提前使抑制誤差的控制作用等於零,甚至為負值,從而避免了被控量的嚴重超調。所以對有較大慣性或滯後的被控物件,比例+微分(PD)控制器能改善系統在調節過程中的動態特性。

PID演算法原理、調整規律及程式碼

PID控制器的引數整定

PID除錯一般原則

a. 在輸出不振盪時,增大比例增益P。

b. 在輸出不振盪時,減小積分時間常數Ti。

c. 輸出不振盪時,增大微分時間常數Td。

PID控制器的引數整定是控制系統設計的核心內容。它是根據被控過程的特性確定PID控制器的比例係數、積分時間和微分時間的大小。PID控制器引數整定的方法很多,概括起來有兩大類:一是理論計算整定法。它主要是依據系統的數學模型,經過理論計算確定控制器引數。這種方法所得到的計算資料未必可以直接用,還必須通過工程實際進行調整和修改。二是工程整定方法,它主要依賴工程經驗,直接在控制系統的試驗中進行,且方法簡單、易於掌握,在工程實際中被廣泛採用。PID控制器引數的工程整定方法,主要有臨界比例法、反應曲線法和衰減法。三種方法各有其特點,其共同點都是通過試驗,然後按照工程經驗公式對控制器引數進行整定。但無論採用哪一種方法所得到的控制器引數,都需要在實際執行中進行最後調整與完善。現在一般採用的是臨界比例法。利用該方法進行PID控制器引數的整定步驟如下:

(1)首先預選擇一個足夠短的取樣週期讓系統工作﹔

(2)僅加入比例控制環節,直到系統對輸入的階躍響應出現臨界振盪,記下這時的比例放大係數和臨界振盪週期﹔

(3)在一定的控制度下通過公式計算得到PID控制器的引數。

PID引數的設定:是靠經驗及工藝的熟悉,參考測量值跟蹤與設定值曲線,從而調整PID的大小。

PID控制器引數的工程整定,各種調節系統中P.I.D引數經驗資料以下可參照:

溫度T: P=20~60%,T=180~600s,D=3-180s

壓3-18P=30~70%,T=24~180s,

液位L: P=20~80%,T=60~300s,

流量L: P=40~100%,T=6~60s。

書上的常用口訣:

引數整定找最佳,從小到大順序查

先是比例後積分,最後再把微分加

曲線振盪很頻繁,比例度盤要放大

曲線漂浮繞大灣,比例度盤往小扳

曲線偏離回覆慢,積分時間往下降

曲線波動週期長,積分時間再加長

曲線振盪頻率快,先把微分降下來

動差大來波動慢。微分時間應加長

理想曲線兩個波,前高後低4比1

一看二調多分析,調節質量不會低

PID演算法原理、調整規律及程式碼

這裡介紹一種經驗法。這種方法實質上是一種試湊法,它是在生產實踐中總結出來的行之有效的方法,並在現場中得到了廣泛的應用。

這種方法的基本程式是先根據執行經驗,確定一組調節器引數,並將系統投入閉環執行,然後人為地加入階躍擾動(如改變調節器的給定值),觀察被調量或調節器輸出的階躍響應曲線。若認為控制質量不滿意,則根據各整定引數對控制過程的影響改變調節器引數。這樣反覆試驗,直到滿意為止。

經驗法簡單可靠,但需要有一定現場執行經驗,整定時易帶有主觀片面性。當採用PID調節器時,有多個整定引數,反覆試湊的次數增多,不易得到最佳整定引數。

下面以PID調節器為例,具體說明經驗法的整定步驟:

⑴讓調節器引數積分系數S0=0,實際微分系數k=0,控制系統投入閉環執行,由小到大改變比例係數S1,讓擾動訊號作階躍變化,觀察控制過程,直到獲得滿意的控制過程為止。

⑵取比例係數S1為當前的值乘以0.83,由小到大增加積分系數S0,同樣讓擾動訊號作階躍變化,直至求得滿意的控制過程。

(3)積分系數S0保持不變,改變比例係數S1,觀察控制過程有無改善,如有改善則繼續調整,直到滿意為止。否則,將原比例係數S1增大一些,再調整積分系數S0,力求改善控制過程。如此反覆試湊,直到找到滿意的比例係數S1和積分系數S0為止。

⑷引入適當的實際微分系數k和實際微分時間TD,此時可適當增大比例係數S1和積分系數S0。和前述步驟相同,微分時間的整定也需反覆調整,直到控制過程滿意為止。

注意:模擬系統所採用的PID調節器與傳統的工業 PID調節器有所不同,各個引數之間相互隔離,互不影響,因而用其觀察調節規律十分方便。

PID演算法原理、調整規律及程式碼

PID引數是根據控制物件的慣量來確定的。大慣量如:大烘房的溫度控制,一般P可在10以上,I=3-10,D=1左右。小慣量如:一個小電機帶

一水泵進行壓力閉環控制,一般只用PI控制。P=1-10,I=0.1-1,D=0,這些要在現場除錯時進行修正的。

提供一種增量式PID供參考

△U(k)=Ae(k)-Be(k-1)+Ce(k-2)

A=Kp(1+T/Ti+Td/T)

B=Kp(1+2Td/T)

C=KpTd/T

T取樣週期 Td微分時間 Ti積分時間

用上面的演算法可以構造自己的PID演算法。

U(K)=U(K-1)+△U(K)

PID演算法原理、調整規律及程式碼

對於PID演算法,取樣週期是相當重要的,特別是當被控制物體的運動速度較高時,若取樣週期跟不上,PID演算法的輸出滯後嚴重.但若執行部件有較大滯後時PID的功效將會大打折扣,不改進執行部件的響應速度,將是PID發揮其優異控制性能最大的瓶頸.

PID演算法的定義:

P:比例控制項. I:積分控制項. D:微分控制項.

設當前輸出量為U,我們的期望值或是設定值為U0,則可得當前時刻誤差:E=U-U0;

PID演算法即是對誤差量E及E的歷史進行某種線性組合得到控制量的演算法.

一般形式:

Up=P*E;

Ui=i*(E+E_1+E_2+...) E_n為之前的第n次誤差.

Ud=i*(E-E_1)

U=Up+Ui+Ud; U為PID控制輸出量.

上式中Ui的計算不太方便,長時間單方向的累加將可能出現溢位,於是將上式改為如下所示的增量形式:



Up=p*(E-E_1) 比例項增量

Ui=i*(E-2*E_1+E_2) 微分項增量

Ud=i*E 積分項增量

U=Uout_1+Up+Ui+Ud U為PID控制輸出量,Uout_1為前次PID輸出值

Uout=U 儲存本次值

對於上面的公式或理論,便可得到相應的C語言程式:

//======================定義PID結構=========================

static float MinValue; //最大值限制

static float MaxValue; //最小值限制

static float CurrentValue; //當前取樣值

static struct PID{

float Ki; //定義積分常數

float Kp; //定義比例常數

float Kd; //定義微分常數

float E_2; //儲存前前次誤差

float E_1; //存諸前次誤差

float E; //儲存本次誤差

float OutPut; //本次輸出量

float ValueSet; //設定值或期望值

}Control;

//===========================PID計算函式=====================

void PidWork() {

float Up,Ud,Ui;

Control.E=CurrentValue-Control.ValueSet; //得到本次誤差

Up =Control.Kp*(Control.E-Control.E_1); //得到比例項

Ud=Control.Kd*(Control.E-2*Control.E_1+Control.E_2); //得到微分項

Ui=Control.Ki*Control.E; //得到積分項

Control.E_2=Control.E_1; //歷史儲存

Control.E_1=Control.E;

Control.OutPut+=Up+Ud+Ui; //計算增量和

if(Control.OutPut<MinValue)Control.OutPut=MinValue; //值域限制

else if(Control.OutPut>MaxValue)Control.OutPut=MaxValue;

}

//==========================初始化速度=========================

void PidInit() {

MinValue=0;

MaxValue=1000;

CurrentValue=0;

Control.Kp=-6;

Control.Ki=-1.5;

Control.Kd=-0.5;

Control.E=0;

Control.E_2=0;

Control.E_1=0;

Control.ValueSet=100;

Control.OutPut=0;

}

以上三個函式為PID的主體函式,也是萬用PID函式.程式碼量已經相當精簡了.注意上面的PID初始化函式中有Kp,Ki,Kd的符號一定要正確,否則輸出量方向相反,後果不堪設想!!!


附上一段完整程式碼:

#include <stdio.h>

struct _pid {

int pv; /*integer that contains the process value*/

int sp; /*integer that contains the set point*/

float integral;

float pgain;

float igain;

float dgain;

int deadband;

int last_error;

};

struct _pid warm,*pid;

int process_point, set_point,dead_band;

float p_gain, i_gain, d_gain, integral_val,new_integ;;

/*------------------------------------------------------------------------

pid_init

DESCRIPTION This function initializes the pointers in the _pid structure

to the process variable and the setpoint. *pv and *sp are

integer pointers.

------------------------------------------------------------------------*/

void pid_init(struct _pid *warm, int process_point, int set_point)

{

struct _pid *pid;

pid = warm;

pid->pv = process_point;

pid->sp = set_point;

}

/*------------------------------------------------------------------------

pid_tune

DESCRIPTION Sets the proportional gain (p_gain), integral gain (i_gain),

derivitive gain (d_gain), and the dead band (dead_band) of

a pid control structure _pid.

------------------------------------------------------------------------*/

void pid_tune(struct _pid *pid, float p_gain, float i_gain, float d_gain, int dead_band)

{

pid->pgain = p_gain;

pid->igain = i_gain;

pid->dgain = d_gain;

pid->deadband = dead_band;

pid->integral= integral_val;

pid->last_error=0;

}

/*------------------------------------------------------------------------

pid_setinteg

DESCRIPTION Set a new value for the integral term of the pid equation.

This is useful for setting the initial output of the

pid controller at start up.

------------------------------------------------------------------------*/

void pid_setinteg(struct _pid *pid,float new_integ)

{

pid->integral = new_integ;

pid->last_error = 0;

}

/*------------------------------------------------------------------------

pid_bumpless

DESCRIPTION Bumpless transfer algorithim. When suddenly changing

setpoints, or when restarting the PID equation after an

extended pause, the derivative of the equation can cause

a bump in the controller output. This function will help

smooth out that bump. The process value in *pv should

be the updated just before this function is used.

------------------------------------------------------------------------*/

void pid_bumpless(struct _pid *pid)

{

pid->last_error = (pid->sp)-(pid->pv);

}

/*------------------------------------------------------------------------

pid_calc

DESCRIPTION Performs PID calculations for the _pid structure *a. This function uses the positional form of the pid equation, and incorporates an integral windup prevention algorithim. Rectangular integration is used, so this function must be repeated on a consistent time basis for accurate control.

RETURN VALUE The new output value for the pid loop.

USAGE #include "control.h"*/

float pid_calc(struct _pid *pid)

{

int err;

float pterm, dterm, result, ferror;

err = (pid->sp) - (pid->pv);

if (abs(err) > pid->deadband)

{

ferror = (float) err; /*do integer to float conversion only once*/

pterm = pid->pgain * ferror;

if (pterm > 100 || pterm < -100)

{

pid->integral = 0.0;

}

else

{

pid->integral += pid->igain * ferror;

if (pid->integral > 100.0)

{

pid->integral = 100.0;

}

else if (pid->integral < 0.0) pid->integral = 0.0;

}

dterm = ((float)(err - pid->last_error)) * pid->dgain;

result = pterm + pid->integral + dterm;

}

else result = pid->integral;

pid->last_error = err;

return (result);

}

void main(void)

{

float display_value;

int count=0;

pid = &warm;

// printf("Enter the values of Process point, Set point, P gain, I gain, D gain ");

// scanf("%d%d%f%f%f", &process_point, &set_point, &p_gain, &i_gain, &d_gain);

process_point = 30;

set_point = 40;

p_gain = (float)(5.2);

i_gain = (float)(0.77);

d_gain = (float)(0.18);

dead_band = 2;

integral_val =(float)(0.01);

printf("The values of Process point, Set point, P gain, I gain, D gain ");

printf(" %6d %6d %4f %4f %4f ", process_point, set_point, p_gain, i_gain, d_gain);

printf("Enter the values of Process point ");

while(count<=20)

{

scanf("%d",&process_point);

pid_init(&warm, process_point, set_point);

pid_tune(&warm, p_gain,i_gain,d_gain,dead_band);

pid_setinteg(&warm,0.0); //pid_setinteg(&warm,30.0);

//Get input value for process point

pid_bumpless(&warm);

// how to display output

display_value = pid_calc(&warm);

printf("%f ", display_value);

//printf(" %f%f%f%f",warm.pv,warm.sp,warm.igain,warm.dgain);

count++;

}

}