1. 程式人生 > >四軸專案總結之三--pid

四軸專案總結之三--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太大了,引來很大的震動,試飛,主要觀察能不能較好的自穩,打方向的軟硬,會不會出現過調啊,響應速度怎麼樣,再進行相應的除錯。

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