PID調試一般原則
a.在輸出不振盪時,增大比例增益P。
b.在輸出不振盪時,減小積分時間常數Ti。
c.在輸出不振盪時,增大微分時間常數Td。
二、各環節作用
[P]比例調節作用:是按比例反應系統的偏差,系統一旦出現了偏差,比例調節立即產生調節作用用以減少偏差。比例作用大,可以加快調節,減少誤差,但是過大的比例,使系統的穩定性下降,甚至造成系統的不穩定。反之,過小,更不上系統需求。
[I]積分調節作用:是使系統消除穩態誤差,提高無差度。因爲有誤差,積分調節就進行,直至無差,積分調節停止,積分調節輸出一常值。積分作用的強弱取決與積分時間常數Ti,Ti越小,積分作用就越強。反之Ti大則積分作用弱,加入積分調節可使系統穩定性下降,動態響應變慢。一般情況是將時間常數設置很小。積分作用常與另兩種調節規律結合,組成PI調節器或PID調節器。
[D]微分調節作用:微分作用反映系統偏差信號的變化率,具有預見性,能預見偏差變化的趨勢,因此能產生超前的控制作用,在偏差還沒有形成之前,已被微分調節作用消除。因此,可以改善系統的動態性能。在微分時間選擇合適情況下,可以減少超調,減少調節時間。微分作用對噪聲干擾有放大作用,因此過強 的加微分調節,對系統抗干擾不利。此外,微分反應的是變化率,而當輸入沒有變化時,微分作用輸出爲 零。微分作用不能單獨使用,需要與另外兩種調節規律相結合,組成PD或PID控制器。
三、細說[分爲PID、PI、PD]
1、確定比例增益P
確定比例增益P 時,首先去掉PID的積分項和微分項,一般是令Ti=0、Td=0(具體見PID的參數設定說明),使PID爲純比例調節。輸入設定爲系統允許的最大值的60%~70%,由0逐漸加大比例增益P,直至系統出現振盪;再反過來,從此時的比例增益P逐漸減小,直至系統振盪消失,記錄此時的比例增益P,設定PID的比例增益P爲當前值的60%~70%。比例增益P調試完成。
【解說】我們知道P是調節與預設值(即輸入值)的偏差的作用的,因此P很大時,當反饋值很小時也會造成很大的波動,最終是震盪狀態,這個可以試出來的,這時的P是Pmax。反之P太小時,根本不可能靠近預設值,當剛好能靠近預設值時,這時的P就是Pmin。一般取P = Pmax(*60~70%)。
2、確定積分時間常數Ti
比例增益P確定後,設定一個較大的積分時間常數Ti的初值,然後逐漸減小Ti,直至系統出現振盪,之後在反過來,逐漸加大Ti,直至系統振盪消失。記錄
此時的Ti,設定PID的積分時間常數Ti爲當前值的150%~180%。積分時間常數Ti調試完成。
【解說】
3、確定積分時間常數Td積分時間常數Td一般不用設定,爲0即可。若要設定,與確定 P和Ti 的方法相同,取不振盪時的30%。
【解說】
【PID算法演示小軟件(綠色版)】
四、具體實踐
01 |
/*2014年7月17日*/ |
02 |
unsigned long lastTime; |
03 |
double Input,
Output, Setpoint; |
04 |
double etSum,
lastErr; |
05 |
double kp,
ki, kd; |
06 |
//output
= kp*et + ki*etSum + kd*det; |
07 |
void PidFunction() |
08 |
{ |
09 |
/*How
long since we last calculated*/ |
10 |
unsigned long now
= millis(); //得到當前時間 |
11 |
double timeChange
= ( double )(now
- lastTime); //得到當前時間與上次時間之間的間隔 |
12 |
/*Compute
all the working error variables*/ // |
13 |
double et
= Setpoint - Input; //反饋值與輸入值的差值
e(t) = 比例 |
14 |
etSum
+= (et* timeChange); //差值*時間間隔乘積累加
= 積分 理論是零 |
15 |
double dEt
= (et - lastErr) / timeChange; //差值-上一次差值
= 微分 |
16 |
/*Compute
PID Output*/ // |
17 |
Output
= kp * et + ki * etSum + kd * dEt; //輸出
= 比例 + 積分 + 微分 |
18 |
/*Remember
some variables for next time*/ |
19 |
lastErr
= et; //下次循環:
當前比例成爲過去比例 |
20 |
lastTime
= now; //下次循環:
當前時間成爲過去時間 |
21 |
} |
22 |
void ParaSet( double Kp, double Ki, double Kd) //設置比例、積分、微分的係數 |
23 |
{ |
24 |
kp
= Kp; |
25 |
ki
= Ki; |
26 |
kd
= Kd; |
27 |
} |
上面的程序代碼是整個PID控制的模板。具體的看你的系統需求。下面我講講倒立擺(還不知道倒立擺是什麼的可以去網上了解下)的PID控制:
【單個傳感器實現倒立擺】
角度傳感器(精密電位器):我們知道一般的精密電位器就可以當作傳感器來使用,電位器三個腳,其中一端接GND,一端接VCC,那麼中間端作爲電壓輸出,通過AD採樣既可以獲取擺轉過的角度位置。自然在擺的最高點就有一個角度值。爲了獲得較好的控制週期,我們採用定時器中斷的方式,即在定時器中斷中進行採樣和數據處理,並進行電機控制(放心,STM32運行速度足夠了)。這裏取定時器三毫米中斷一次,注意,AD採樣的時間一定要小於定時器中斷時間,否則會出現時序錯亂,可以在AD初始化中設定採樣週期,或者在中斷函數裏面進行適當的處理,比如分三次採樣取平均值(這個需要設定更小的中斷時間,如一毫秒中斷一次,我就是這樣的做的)。定時器初始化和一些變量的定義我就沒貼代碼了,具體的代碼如下:
01 |
void TIM3_IRQHandler( void ) |
02 |
{ |
03 |
static int timercount; |
04 |
05 |
if (TIM3->SR&0X0001){ //溢出中斷 |
06 |
LED1
= ~LED1; |
07 |
timercount++; |
08 |
} |
09 |
TIM3->SR
&= ~(1<<0); //清除中斷標誌位 |
10 |
|
11 |
if (timercount
== 1){ |
12 |
ang[0]
= Get_Adc(ADC_CH1); |
13 |
} |
14 |
if (timercount
== 2){ |
15 |
ang[1]
= Get_Adc(ADC_CH1); |
16 |
} |
17 |
if (timercount
== 3){ |
18 |
////////////angle
PID/////////// |
19 |
ang[2]
= Get_Adc(ADC_CH1); //採集AD值 |
20 |
angleLast
= angle; //保存上一次的採樣角度值 |
21 |
angle
= (ang[0]+ang[1]+ang[2])/3; //取三次採集的平均值 |
22 |
degreeLast
= degree; //保存上一次的計算角度值 |
23 |
degree
= calcAngle(angle); //獲取新的計算角度值 |
24 |
|
25 |
velocity
= (angle - angleLast); //得到採樣角速度差值(微分值) |
26 |
if ( abs (angle-setPoint)<=4) //輸入值與反饋值差的絕對值
< 4 |
27 |
error_p
= 0; //p
= 0 |
28 |
else |
29 |
error_p
= setPoint-angle; //p
= 輸入值與反饋值差 |
30 |
error_d
= velocity; //d
= 微分值 |
31 |
error_i
= error_i + (error_p*timechange); |
32 |
|
33 |
myspeed
= - (kp*error_p + ki*error_i + kd*error_d); |
34 |
|
35 |
setMotorSpeed(myspeed); //電機PID控制 |
36 |
|
37 |
//printf("[%d
%d %d %7d]\t",angle,degree,myspeed,error_i); |
38 |
} |
39 |
if (timercount
== 3){ |
40 |
timercount
= 0; |
41 |
error_i
= isOverFlow(error_i); |
42 |
} |
43 |
} |
01 |
void setMotorSpeed( long mySpeed) |
02 |
{ |
03 |
long output
= 0; |
04 |
mySpeed
= constrain(mySpeed,-41665,41665); //限制速度上下限 |
05 |
if (mySpeed
== 0)mySpeed = 1; //保證速度不爲零,也可以不用 |
06 |
if (mySpeed>0){ |
07 |
output
= mySpeed*1.0; //這個具體的看系統進行調整放大 |
08 |
//printf("R:%d\t",output); |
09 |
TurnLeft(output); |
10 |
} |
11 |
else { |
12 |
output
= mySpeed*(-1.0); |
13 |
//printf("L:%d\t",output); |
14 |
TurnRight(output); |
15 |
} |
16 |
} |
速度控制的話,我的速度值調節範圍爲0~5000,這個可以通過定時器來設定的。