淺談全國大學生智能車競賽-攝像頭組圖像處理及控制算法

    由於是第一次寫技術博客,先做一下自我介紹。本人本科專業是光電信息科學與工程(工),就讀於長春理工大學光電工程學院,研究生爲華中科技大學光學工程專業。本科期間參加參加過各類比賽,其中印象最深的還是智能車競賽。參加了十屆和十一屆的智能車競賽,當然結果是都沒有出校賽這讓我很尷尬。做了兩屆的攝像頭,第一年跑的速度是1.8m/s第二年跑到了3m/s以上。雖然最後有一些可惜但是我希望把我自己的一些感想和遇到的問題給大家介紹一下,分享一下我的經驗,希望能夠對學弟學妹有一些幫助。

  步入主題,智能車競賽攝像頭組主要包括幾個部分,我就以採集圖像;賽道模式識別;控制算法;PID算法;機械結構調整等幾個方面爲大家介紹我的整體思路。


  首先我先說一下我第二年用的是B車具體車型自己可以百度一下吧,在這裏我簡單介紹一下,它用的是一個大電機,速度個人認爲是其他車無法比擬的,然後舵機用的是SD-5舵機,是一個數字舵機它的響應頻率很快,可以滿足我用很高的頻率去控制轉角,這樣也對於控制方面提供了方便。

  接下來談談如何調整機械部分,首先攝像頭的位置,個人把攝像頭放在了整個車的重心位置,也就是整個車的中間。舵機採用臥式結構,推薦採用臥式或立式,舵機與攝像頭之間放置的電池,爲什麼將電池放在這是因爲爲了讓整個車的重心靠近中間,攝像頭我直接開到了電路板上,將電路板固定在底盤上,整個主控上面包含了所有的模塊,節省了空間降低了重心。整車拼起來後最高的位置是舵機和輪子,個人噴了黑色噴漆感覺很帥(這個是看個人了)。前輪調整的時候很多人很迷茫在這我給大家說一下,前輪內八,讓最小的轉彎半徑越大越好,可以得到很好地轉彎效果,又因爲我的電池是靠近舵機的所以增加了轉彎時候的側滑力量,內八的程度是多少呢就是當打到最大角度的時候輪子稍微給點離心力就可以所有的面都着地,這樣就是最好的狀態,還有就是舵機的拉桿,要V字型,爲什麼呢,就是爲了當擺角最大的時候舵機給的推力是最大的,當擺角小的時候舵機反應不是特別靈敏(吉林大學車輛工程的一位車友告訴的方法),這樣調整完前輪後就是後輪的調整,調整到什麼狀態呢,當電機給最大佔空比的時候只要按住車瞬間電機就能停住,這樣就可以讓以後編碼器測出來的數據不會有空轉的情況(自己慢慢測試就好沒有什麼很好地方法),安裝編碼器的時候編碼器齒輪與後車輪的齒輪齧合要怎麼檢測呢,就是當你轉動編碼器齒輪時候看不出空回,如果有空回就要繼續調整直道看不出空回爲止。到此爲止只有一個事情要解決了,那就是B車底盤的問題,這個東西的話大家很多人是不知道的,就是買車模的時候它會給兩個小塊通常是黑色的(我當時以爲這個東西是雞肋,後來才知道我太天真了。),它是用來墊前輪的底盤的(具體名詞不知道想了解可以百度),然後後輪如果拆下來可以看見輪軸與底盤連接那個位置有個類似橢圓的一個件把它倒過來裝上你會發現,咦??我的底盤低了好多。對就是這樣你的底盤調整的更低更有賽車的樣子了。至此所有的機械結構基本調整完畢。特別提醒,前後底盤的連接一定要在底盤的上部連接否則你會很不開心,會遇到各種問題,儘量做到的就是用最少的料讓前後的底盤連接的最穩定。在此不多做介紹了。

以下這種固定方法是不可取的。


舵機拉桿效果圖


整車的結構效果圖


   接下來就應該說說軟件部分了,第一個就是攝像頭圖像採集問題,個人用過OV7620,藍宙捕食者3代,山外鷹眼硬件二值化(不要問我爲什麼做了兩次用了三種攝像頭)。這個圖像採集的問題呢,我覺得很值得一提,我首先介紹一下三種攝像頭吧,7620這款攝像頭是最經典的一種攝像頭,30幀每秒COMS傳感器,個人認爲用這個攝像頭做到3m/s的大神真的是太牛逼了,這個攝像頭如果想要用首先你必須有很強的圖像處理能力,因爲它採集速度慢,圖像質量很渣,個人用它跑到的最大速度是2.2m/s。再就是藍宙捕食者3代,這個不吹不黑,個人感覺是最渣最渣的一款攝像頭,會出現各種各樣的問題,而且你不知道該怎麼去做,不推薦使用。接下來就是重頭戲山外鷹眼攝像頭,我採用的是60*80像素,採集速度是148幀每秒,採集速度都快比線性CCD快了。它的噪聲如何呢,個人測試過沒有濾波算法跑到了3m/s的速度,不解釋。這個攝像頭首先個人覺得需要一個比較大的廣角鏡頭,其次就是前瞻的遠處1.2M就足夠了,當然也可以用1.4m個人喜好問題和算法問題。這個攝像頭屬於動態的閾值,閾值可以通過一個參數進行修改,這個自己多看看山外鷹眼攝像頭的介紹就知道了。然後還有一個比較頭疼的問題就是不能用山外直接給的攝像頭採集程序否則達不到148幀/s這部分只能靠自己去研究時序然後改正了,思路呢就是在初始化的時候直接初始化所有的中斷源,然後用DMA一隻採集可以實現理論上的圖像處理與圖像採集的並行執行。這部分是很重要的當你的採集速度達到148幀/s後車速達到3m/s你每個圖像衝出去的距離也不過2.03cm,這樣相對於7620就有了一個明顯的優勢,你可以提高你的極限速度,如果7620跑3m/s你可以跑到更高。

  賽道模式識別,這部分是很多人最頭疼的部分,我也走過很多的彎路,在處理十字的時候自己看着圖像看了一個月的時間最後才解決的,還有就是彎道如何識別,什麼入彎什麼出彎都是什麼情況,本人介紹的方法都是按照我以上介紹的配置才適用的,我問過很多司機師傅問他們怎麼開車,他們給的回答都很模糊,那麼我們是怎麼看的呢,我的理解就是我們看的實際上就可以理解爲直線和彎道的分量,當前的圖像中直線的分量是多少,只要我能夠提取出來這個分量我就能得到很好地一個模糊的數值去控制當前的電機轉速(個人認爲賽道類型就是用來控制電機轉速的),那我是怎麼做到的識別出來這個分量的呢?我應用了三種方式,第一個就是採用《兩點算法求智能車賽道曲率》-蔣旭 吳濤 論文中的計算曲率算法計算了賽道前部和後部的曲率,更信任前部分的曲率,然後加上有效行數據,有效行數據與賽道類型建立一個三次函數的映射關係求出其所佔的權重,這樣三個算法算出來的數值所佔的權重不同最終就能得到一個當前賽道直線分量的權重,得到這個權重後就能通過模糊的方式控制電機了。個人採用的是三次函數映射,在過彎的時候能夠達到很好地效果,不會出現不連續的現象,這樣就能有一個很好的系統響應。具體算法需要自己去寫了,

這是屏幕顯示的部分,當時在十字問題卡主的時候這個時候提取出來了直線的分量。

在此我貼一部分代碼

     //找到A B C三個點   對應於  《兩點算法求智能車賽道曲率》-蔣旭 吳濤 論文中的 圖 1 
      Down_A_hang=left_line[0][0];//A點所在行數
      Down_A_lie=left_line[1][0];//A點所在列數
      Down_C_hang=left_line[0][(60-youxiaohang)/2];//C點所在行數
      Down_C_lie=left_line[1][(60-youxiaohang)/2];//C點所在列數
      L_AB=Down_C_hang-Down_A_hang;//求出 L_AB
      L_BC=abs(Down_C_lie-Down_A_lie);//求出 L_BC
      R_qulv=L_AB*L_AB/(2.0*L_BC)+L_BC/2.0;//計算出當前賽道的曲率半徑
      //////////
      Down_A1_hang=left_line[0][1];//A點所在行數
      Down_A1_lie=left_line[1][1];//A點所在列數
      Down_C1_hang=left_line[0][((60-youxiaohang)/2)/2+1];//C點所在行數
      Down_C1_lie=left_line[1][((60-youxiaohang)/2)/2+1];//C點所在列數
      L_AB=Down_C_hang-Down_A_hang;//求出 L_AB
      L_BC=abs(Down_C_lie-Down_A_lie);//求出 L_BC
      R_qulv=L_AB*L_AB/(2.0*L_BC)+L_BC/2.0+R_qulv;//計算出當前賽道的曲率半徑
      /////////
      Down_A2_hang=left_line[0][2];//A點所在行數
      Down_A2_lie=left_line[1][2];//A點所在列數
      Down_C2_hang=left_line[0][((60-youxiaohang)/2)/2+2];//C點所在行數
      Down_C2_lie=left_line[1][((60-youxiaohang)/2)/2+2];//C點所在列數
      L_AB=Down_C_hang-Down_A_hang;//求出 L_AB
      L_BC=abs(Down_C_lie-Down_A_lie);//求出 L_BC
      R_qulv=L_AB*L_AB/(2.0*L_BC)+L_BC/2.0+R_qulv;//計算出當前賽道的曲率半徑
      R_qulv=R_qulv/3.0;//三個點取平均

這部分是我代碼中的一部分,當然需要你們自己去做後面的工作,如果有興趣歡迎聯繫qq:792499178.

應用此算法後在實際實踐中將車子的速度從2.2達到了2.6m/s這時候又出現了一個新的問題就是舵機出現了抖動(抖動情況可以去看QQ空間上面寫的那個號網址

 http://mobile.qzone.qq.com/l?g=1336&appid=311&subtype=0&blog_photo=0&ciphertext=28AE2E611D3234CC4EED2BE09951BC60&uw=792499178&g_f=2000000393 
)以上是賽道類型識別模糊算法。

  接下來就是說說控制算法了,控制算法的話我跟其他人應該沒什麼太大的區別舵機PD,電機PID。首先講講PID吧,PID的話就是P-比例,I-積分,D-微分,三個部分組成,這個算法的提出是在很久以前了,具體是誰提出的自行百度吧。在這裏首先說說我電機閉環採用的是增量式PID算法,代碼呢在這裏給大家貼出來

s8  dspeed;
u32 speed_set;
s16 speed_set_base;
s16 Speed_Error=0;
s16 LastSpeed_Error=0;
s16 PrevSpeed_Error=0;
s32 SpeedOut=500000; //電機PWM
s32 LastSpeedOut=500000;
s32 SpeedOut1=0;
extern u8 Start_line;
extern u8 roadflag;
s32 car_speed=0;
extern u8 dispay_on;
void car_speed_control(s16 car_speed_set,u32 Moto_P,u32 Moto_I,u32 Moto_D)

   car_speed=LPTMR0_CNR;
   lptmr_counter_clean(); 
   s16 Speed_ErrorMax=50;
   s16 Speed_ErrorMin=-50;
  Speed_Error=car_speed_set-car_speed;
  if (Speed_Error>Speed_ErrorMax)Speed_Error=Speed_ErrorMax;
  else if (Speed_Error<Speed_ErrorMin)Speed_Error=Speed_ErrorMin;
  SpeedOut=LastSpeedOut+ Moto_P*(Speed_Error-LastSpeed_Error)+ Moto_I*Speed_Error+Moto_D*(Speed_Error+PrevSpeed_Error-2*LastSpeed_Error);        
    if(SpeedOut>dianji_PWM_MAX) 
  {
    SpeedOut=dianji_PWM_MAX;   
  }
  else if(SpeedOut<dianji_PWM_MIN) 
  {
    SpeedOut=dianji_PWM_MIN;
  }


  if (SpeedOut>0)
  {SpeedOut1=SpeedOut;
   FTM_PWM_Duty(FTM0,CH2,0);
   FTM_PWM_Duty(FTM0,CH0,SpeedOut1);
  }
  else
  {
    SpeedOut1=-SpeedOut;
    FTM_PWM_Duty(FTM0,CH2,SpeedOut1);
    FTM_PWM_Duty(FTM0,CH0,0);
  }
  PrevSpeed_Error = LastSpeed_Error;
  LastSpeedOut=SpeedOut;
  LastSpeed_Error=Speed_Error;  
}

這就是我用的增量式PID算法,爲什麼應用增量式PID算法呢,其實我也不是很明白就是感覺它好吧(個人是光電工程專業的我屬於不務正業的那夥所以還請專業人士勿噴),當然也不是完全的沒有根據,大家可以百度一下位置式PID和增量式PID兩種算法的區別,很容易比較出來哪個有優勢。在接下來就是在增量式PID的基礎上加了一個棒棒算法,這個實際上是我沒有調好的,個人能力問題對PID算法沒有理解到很好所以加了棒棒算法後整個車加速的速度很快但是聲音特別大(但是個人聽起來感覺很爽,如果調好了聲音是很小的),棒棒算法是什麼呢就是當你編碼器返回的速度與你預期速度差距很大的時候直接滿佔空比輸出,這樣可以讓你的車很快的達到目標速度(這就是對應於之前我那個機械結構的設計,在後輪調整中有提到),測試後可以在參數一般的情況下2m的距離內加速到10ms,250個脈衝的速度(採用的512線的迷你編碼器),這個根據車模不同有不同的數值,這裏的數據僅供參考。接下來就介紹一下舵機PD的參數設定了,這個參數難倒了智能車的大部分人在這裏我就透露我的一些經驗吧,首先我採用的是P三次函數算法,自變量是有效行,因變量是P的值,當然更建議的是自己設定P值,首先找到一個效果比較好的函數映射曲線後在用matlab進行擬合取出來相應的數值後用數組的形式導入到程序中,在根據調試的情況具體情況具體分析後在微調數組中的P值,然後D值的選取呢,當你的車速度沒有超過2.6m/s之前個人認爲沒有必要用這個參數,爲什麼這麼說呢,因爲我在2.2m/s的時候完全不會出現抖動的情況,當速度提升後在直線時候舵機抖動厲害,這個時候就需要D的值了,這個D值作用是什麼呢?它的作用就是放大偏差,有人會說放大偏差幹什麼,個人的理解是變相的一種閉環,當速度大的時候慣性比較大拐彎不是很容易,當你需要拐的角度越大需要的力就越大,這樣就可以體現出來D值的重要性,通常D值根據拐彎的大小即賽道類型而變化個人採用的是與P值成一定的正比例關係。採用這個方式後車子的速度提升到了3m/s,從2.2m/s到3m/s一共用了2天時間。

下面我留下我的舵機P算法

 float Kp = (CAMERA_H - info->effectiveLine)*(CAMERA_H - info->effectiveLine)/gServoPid.coefficient+gServoPid.p;
outPWM = (int32_t)(Kp*info->error + gServoPid.d*(info->error - gServoPid.error1)) + gCarParam.servoCenter; gServoPid.error1 = info->error;

具體參數自己慢慢調。有興趣的可以聯繫上面的qq,或聯繫[email protected].


  個人認爲智能車攝像頭組大體上我涉及的就只有這些了,還有一些細節問題我沒有提過,這些就需要自己去創新了,目標只有一個,但是過程我們可以有上百上千種,以上是我的方法,如果有錯誤的地方還請各位大神指正,也希望能夠幫助新手瞭解一下智能車的一些知識。


2016.11.8    22:44

白銀浩

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