四軸項目總結之三--pid

本篇主要介紹pid基本原理、實現和參數調試。其中主要有這幾個部分的pid要進行實現:姿態的pid實現自穩. 高度pid實現定高. 循跡pid實現尋黑線而行。
一、pid的基本原理
參考:
【1】http://blog.csdn.net/nemol1990/article/details/45131603
【2】http://blog.csdn.net/super_mice/article/details/38436723
(注:將時間常數一併加入Kp,Kd,Ki中了)
1. 比例(P)控制規律:具有P控制的系統,其穩態誤差可通過P控制器的增益Kp來調整:Kp越大,穩態誤差越小;反之,穩態誤差越大。但是Kp越大,其系統的穩定性會降低。m(t) = Kp*e(t);
由上式可知,控制器的輸出m(t)與輸入誤差信號e(t)成比例關係,偏差減小的速度取決於比例係數Kp:Kp越大,偏差減小的越快,但是很容易引起振盪(尤其是在前向通道中存在較大的時滯環節時);Kp減小,發生振盪的可能性小,但是調節速度變慢。單純的P控制無法消除穩態誤差,所以必須要引入積分I控制。
2. 積分(I)控制規律:由於採用了積分環節,若當前誤差e(t)爲0,則其輸出信號m(t)有可能是一個不爲0的常量。需要注意的是,引入積分環節,可以提到系統型別,使得系統可以跟蹤更高階次的輸入信號,以消除穩態誤差。
m(t) = Ki * | e(t)*d(t) .
3. 微分(D)控制規律:可以反應輸入信號的變化趨勢,具有某種預見性,可爲系統引進一個有效的早期修正信號,以增加系統的阻尼程度,而從提高系統的穩定性。(tao爲微分時間常數)
m(t) = Kd*d[e(t)]/dt;
4.P控制對系統性能的影響:
開環增益越大,穩態誤差減小(無法消除,屬於有差調節)
過渡時間縮短
穩定程度變差
5.I控制對系統性能的影響:
消除系統穩態誤差(能夠消除靜態誤差,屬於無差調節)
穩定程度變差
6.D控制對系統性能的影響:
減小超調量
減小調節時間(與P控制相比較而言)
增強系統穩定性
增加系統阻尼程度

二、不同部分的pid實現
參考:
http://forum.eepw.com.cn/thread/268670/1
1、姿態部分PID
採用串級PID,外環是角度環,內環是角速度環。
角度環通過角度誤差pid計算,得到角速度的期望值,送到角速度環;角速度環再做一次pid得到電機輸出。
這樣做的目的是,通過兩級pid串聯,增大了四軸的阻尼(角度誤差(角速度)阻尼、角速度誤差(角加速度)阻尼)。參數調節好,四軸在飛行跟隨手感會很好,機動性強,指哪打哪,也能很好的做到懸停。

    #define angle_max           10.0f     
    #define angle_integral_max  1000.0f   
    /******************************************************* 
    函數原型:   void Control_Angle(struct _out_angle *angle,struct _Rc *rc) 
    功    能: PID控制器(外環) 
    *******************************************************/  
    void Control_Angle(struct _out_angle *angle,struct _Rc *rc)  
    {  
        static struct _out_angle control_angle;  
        static struct _out_angle last_angle;  
    //////////////////////////////////////////////////////////////////  
    //          以下爲角度環  
    //////////////////////////////////////////////////////////////////  
        if(rc->ROLL>1490 && rc->ROLL<1510)    
            rc->ROLL=1500;  
        if(rc->PITCH>1490 && rc->PITCH<1510)      
            rc->PITCH=1500;  
    //////////////////////////////////////////////////////////////////  
        control_angle.roll  = angle->roll  - (rc->ROLL  -1500)/13.0f ;  
        control_angle.pitch = angle->pitch - (rc->PITCH -1500)/13.0f;  
    //////////////////////////////////////////////////////////////////  
        if(control_angle.roll >  angle_max)  //ROLL  
            roll.integral +=  angle_max;  
        else if(control_angle.roll < -angle_max)  
            roll.integral += -angle_max;  
        else  
            roll.integral += control_angle.roll;  

        if(roll.integral >  angle_integral_max)  
           roll.integral =  angle_integral_max;  
        if(roll.integral < -angle_integral_max)  
           roll.integral = -angle_integral_max;  
    //////////////////////////////////////////////////////////////////  
        if(control_angle.pitch >  angle_max)//PITCH  
           pitch.integral +=  angle_max;  
        else if(control_angle.pitch < -angle_max)  
           pitch.integral += -angle_max;  
        else  
            pitch.integral += control_angle.pitch;  

        if(pitch.integral >  angle_integral_max)  
           pitch.integral =  angle_integral_max;  
        if(pitch.integral < -angle_integral_max)  
           pitch.integral = -angle_integral_max;  
    //////////////////////////////////////////////////////////////////  
        if(rc->THROTTLE<1200)//油門較小時,積分清零  
        {  
            roll.integral  = 0;  
            pitch.integral = 0;  
        }  
    //////////////////////////////////////////////////////////////////  
        roll.output  = roll.kp *control_angle.roll  + roll.ki *roll.integral  + roll.kd *(control_angle.roll -last_angle.roll );  
        pitch.output = pitch.kp*control_angle.pitch + pitch.ki*pitch.integral + pitch.kd*(control_angle.pitch-last_angle.pitch);  
    //////////////////////////////////////////////////////////////////  
        last_angle.roll =control_angle.roll;  
        last_angle.pitch=control_angle.pitch;  
    }  




下面是內環代碼:

view plaincopy to clipboardprint?

    #define gyro_max            50.0f     
    #define gyro_integral_max   5000.0f  
    /****************************************************************************** 
    函數原型:   void Control_Gyro(struct _SI_float *gyro,struct _Rc *rc,uint8_t Lock) 
    功    能: PID控制器(內環) 
    *******************************************************************************/   
    void Control_Gyro(struct _SI_float *gyro,struct _Rc *rc,uint8_t Lock)  
    {  
        static struct _out_angle control_gyro;  
        static struct _out_angle last_gyro;  
        int16_t throttle1,throttle2,throttle3,throttle4;  
    //////////////////////////////////////////////////////////////////  
    //          以下爲角速度環  
    //////////////////////////////////////////////////////////////////  
        if(rc->YAW>1400 && rc->YAW<1600)  
            rc->YAW=1500;  
    //////////////////////////////////////////////////////////////////  
        control_gyro.roll  = -roll.output - gyro->y*Radian_to_Angle;  
        control_gyro.pitch = pitch.output - gyro->x*Radian_to_Angle;  
        control_gyro.yaw   = -(rc->YAW-1500)/2.0f - gyro->z*Radian_to_Angle ;  
    //////////////////////////////////////////////////////////////////  
        if(control_gyro.roll >  gyro_max)    //GYRO_ROLL  
            gyro_roll.integral +=  gyro_max;  
        else if(control_gyro.roll < -gyro_max)  
            gyro_roll.integral += -gyro_max;  
        else  
            gyro_roll.integral += control_gyro.roll;  

        if(gyro_roll.integral >  gyro_integral_max)  
           gyro_roll.integral =  gyro_integral_max;  
        if(gyro_roll.integral < -gyro_integral_max)  
           gyro_roll.integral = -gyro_integral_max;  
    //////////////////////////////////////////////////////////////////  
        if(control_gyro.pitch >  gyro_max)//GYRO_PITCH  
            gyro_pitch.integral +=  gyro_max;  
        else if(control_gyro.pitch < -gyro_max)  
            gyro_pitch.integral += -gyro_max;  
        else  
            gyro_pitch.integral += control_gyro.pitch;  

        if(gyro_pitch.integral >  gyro_integral_max)  
           gyro_pitch.integral =  gyro_integral_max;  
        if(gyro_pitch.integral < -gyro_integral_max)  
           gyro_pitch.integral = -gyro_integral_max;  
    //////////////////////////////////////////////////////////////////  
    //  if(control_gyro.yaw >  gyro_max)//GYRO_YAW  
    //      gyro_yaw.integral +=  gyro_max;  
    //  else if(control_gyro.yaw < -gyro_max)  
    //      gyro_yaw.integral += -gyro_max;  
    //  else  
            gyro_yaw.integral += control_gyro.yaw;  

        if(gyro_yaw.integral >  gyro_integral_max)  
           gyro_yaw.integral =  gyro_integral_max;  
        if(gyro_yaw.integral < -gyro_integral_max)  
           gyro_yaw.integral = -gyro_integral_max;  
    //////////////////////////////////////////////////////////////////  
        if(rc->THROTTLE<1200)//油門較小時,積分清零  
        {  
            gyro_yaw.integral  = 0;  
        }  
    //////////////////////////////////////////////////////////////////  
        gyro_roll.output  = gyro_roll.kp *control_gyro.roll  + gyro_roll.ki *gyro_roll.integral  + gyro_roll.kd *(control_gyro.roll -last_gyro.roll );  
        gyro_pitch.output = gyro_pitch.kp*control_gyro.pitch + gyro_pitch.ki*gyro_pitch.integral + gyro_pitch.kd*(control_gyro.pitch-last_gyro.pitch);  
        gyro_yaw.output   = gyro_yaw.kp  *control_gyro.yaw   + gyro_yaw.ki  *gyro_yaw.integral   + gyro_yaw.kd  *(control_gyro.yaw  -last_gyro.yaw  );  
    //////////////////////////////////////////////////////////////////  
        last_gyro.roll =control_gyro.roll;  
        last_gyro.pitch=control_gyro.pitch;  
        last_gyro.yaw  =control_gyro.yaw;  
    //////////////////////////////////////////////////////////////////  
        if(rc->THROTTLE>1200 && Lock==0)  
        {  
            throttle1 = rc->THROTTLE - 1050 + gyro_pitch.output + gyro_roll.output - gyro_yaw.output;  
            throttle2 = rc->THROTTLE - 1050 + gyro_pitch.output - gyro_roll.output + gyro_yaw.output;  
            throttle3 = rc->THROTTLE - 1050 - gyro_pitch.output + gyro_roll.output + gyro_yaw.output;  
            throttle4 = rc->THROTTLE - 1050 - gyro_pitch.output - gyro_roll.output - gyro_yaw.output;  
        }  
        else  
        {  
            throttle1=0;  
            throttle2=0;  
            throttle3=0;  
            throttle4=0;  
        }  
        Motor_Out(throttle1,throttle2,throttle3,throttle4);  
    }  



注意到,pitch和roll用到了內外環,yaw只用到了內環。 

對代碼不做過多的講解,不明白的可以留言或者@我都可以,我們一起討論。

2、高度PID
參考:http://blog.sina.com.cn/s/blog_65e255160100qvi9.html
由於高度PID是最不好調試的,總需要飛起來調,這裏有幾點思路,可以供大家借鑑。高度pid用單級pid就行了,e(t)=target_high - act_high。用act_high = high*(sin(theta1)+sin(theta2)+sin(theta3))進行校準,還有別忘了濾波得到準確合理的數值。還考慮一個問題,如果和目標值相差太大,誤差豈不是很大,反饋量也很大?怎麼辦呢?可以考慮分幾次慢慢疊加。

3、循跡PID
這裏我主要參考了飛思卡爾智能車的實現,其它的還有光流算法,GPS巡航,我沒有去研究。
首先,攝像頭的安裝位置很有講究,有些說要保證什麼前瞻性的,但個人覺得如果飛行速度不是太快(用它循跡也不能太快)裝正下方偏前一點的位置就行了。誤差項即爲採回的圖像的中線和循跡黑線中心線的差值,還是用單級pid就行了。

三、PID參數的調試
其實,這對於很多人來說,算是一件很困難的事。個人覺得快刀可以斬亂麻,我們不妨先把刀磨快。這裏的刀,就是一個調試助手,我們得實時的瞭解各項參數,這樣才知道往哪個方向調,出現了什麼問題。一個寫的不錯的開源軟件叫匿名上位機,很不錯的,現在又新加入了很多功能了,估計,使用別人的上位機只需要符合它的通信協議就行了,這在你的程序中要去實現。當然,如果你自己有興趣,也可以用C#寫一個簡單的上位機,基於串口,或網絡都可以。這裏給出一個參考網址:http://wenku.baidu.com/link?url=8LlIRn1qp8HvRVKWt4owK7FiUpdTRMGzgjTrKKUCwnLEW2RzORht0bxvcfWooI47Iva4Nw-EGE-LDp9rFwjUdDsN3PHjfeo2i5tver7TEB3
調試時,可以先將飛機綁起來,做烤四軸也可以,可以參考上面我介紹的兩篇文章。注意四軸是很危險的,鄙人程序跑飛了兩次,所幸都躲過一劫,並沒有傷到,只是有一個調的時候,割掉了一點小皮。
總的原則是:
1、先調roll或pitch,單一方向的調,調好後複製到另一方向,基本是一樣的參數,yaw方向稍有不同,我的經驗是Kp基本可以不變。
2、調一個方向時,先調內環,調好內環再調外環,內環調好的狀態是,在平衡位置放開時,會慢慢的往一邊倒(大概幾秒),往一邊扳時,會有較大的阻力。這是便可以加一點外環量了,如果發現不對,還需要回到內環,這樣反覆進行。
3、調內環、外環時,都從p->i->d的順序依次進行。增大還是減少按照我們第一部分介紹的基本原理進行。
4、內外環只加p時,四軸基本能夠等幅震盪,這時我們可以加一點點d,引入高頻分量,震動就沒這麼厲害了,最後,如果能在某個位置平衡,但平衡位置不在中心位置時,加一點點i,i能消除穩態誤差。
5、內外環是相對的,一個太硬了,另一個勢必會表現的比較軟,從這裏可以知道調試的方向。
6、經過以上的步驟,如果roll和pitch基本差不多了,就可以去試飛了,注意檢查方向有沒有搞錯,如果出現一起飛就炸,大概率是d太大了,引來很大的震動,試飛,主要觀察能不能較好的自穩,打方向的軟硬,會不會出現過調啊,響應速度怎麼樣,再進行相應的調試。

到這就差不多了,好好玩吧,注意安全哦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章