STM32與PID算法

一、總體原則
    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,這個可以通過定時器來設定的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章