电磁寻迹智能车HAL库基于cubeMX—三轮(分段PID+归一化+差速+均值滤波+多路ADC+三叉+环岛+十字)
一、雜談
拖了好久才來更文章….是因為一直比較忙 ,哈哈。
工程在文末
今年呢,是第二次參加智能汽車校賽,本來也是參加了飛卡的,但是因為某些原因(包括個人的也有包括組隊的一些其實現在看來也就那樣的問題)我退出了,說有遺憾那必然是有的,因為畢竟哪個工科男生沒有一個做車車的想法呢,但不后悔,因為有了更多時間去做其它也想做的事情。所以這個智能車校賽就當作過過車癮了。
說一下大致的情況吧,我寫程序調車,另一個同伴搭車做硬件,我們是高年級組了要求的是做三輪車,去年也參加了做的四輪車,去年調了一個月接近,也是我一個人調的程序,最后拿了三等獎。其實三輪車和四輪車區別不大,無非就改改代碼控制而已。今年的三輪車組別,我調了一個通宵,最后拿了一等獎,因為平時實在太忙,沒有什么時間來管這個,當然也是因為去年做過有點經驗。
二、進入正題
其實,跑電磁組,完賽真的很簡單,不管特殊元素,簡單做一個PID的閉環就能完賽了。可以和飛卡的智能視覺做對比,完全不是一個量級的,飛卡智能視覺就算集三人之力要是實力和所花的時間不夠,連完賽都做不到,大家可以去了解了解飛卡智能視覺,我看著那些大佬的視頻,確實是佩服。RTT操作系統,動態路徑規劃,坐標點識別,包括一些蟻群算法等數據結構所用到的東西,目標檢測模型訓練很考驗一個團隊的綜合實力。
好了,跑題了。
我們來看做這樣一個電磁循跡三輪車所需要哪些硬件吧,以下是我選擇的:
主控STM32F411CEU6單片機、多路電磁桿、三輪車模是向組織方購買的、電機驅動、OLED、干簧管,主要就是這些。
三、硬件、搭車
硬件其實挺簡單的,對于這個比賽我對我們硬件員的要求搭車質量不高,只需要讓他畫個轉接板,電磁桿直接用之前的(但其實這里比較草率了),到時直接把各個模塊插到轉接板上就行,硬件員還有點懵逼覺得自己的工作量是不是有點太少了,哈哈。
下面是原理圖、PCB、實物圖:
這些也不用多說,注意好預留的接口,沒有用到的IO口引出來,干黃管的位置放在車底部為了觸發靈敏,考慮好布局排布就歐克了。還有的話我這里是多加了陀螺儀MPU6050,是為了進環島時的姿態判定,但其實后面時間不夠來不及調了。
電磁桿:
畫的六路,大致是個這樣的擺放位置
其實吧,電磁桿是我們沒有考慮好的,不應該這么隨便,雖然這樣的位置擺放的電磁桿也可以完賽,但很多特殊元素都很難判定,其實電磁最關鍵的就是利用電磁桿去感知賽道,你處在一個什么樣的位置,是否有遇到特殊元素,比如環島,三叉。所以電磁桿的制作就是最考驗每個團隊的觀察分析能力了,其實我們第一次參加這個比賽的時候完全沒有意識到電磁桿的重要性這么強,知道電磁桿很重要,但沒有意識到它才是整個比賽的關鍵,有句話說得好,一個系統里面,硬件結構的搭建決定了整個系統的功能上限,因為軟件調試永遠離不開這些硬件,軟件是驅動硬件的,硬件搞不好軟件是沒有辦法的。
那么呢關于電磁桿我就點到為止,至于電感應該在電磁桿怎么擺放,什么方案最佳,就個人下去探索了,畢竟只有自己思考過的才是最適合自己的。
這里提供一下舉個例子,比如我們之前是那種只有水平和豎直位置的擺放,當我們遇到了三叉應該怎么辦呢。我們發現,如果中間再加一個水平電感,就會有只有在經過三叉時才有的效果了。因為這個水平電感在正常賽道時會一個切割沿線的電磁線,所以一直有一個電壓產生,而當遇到三叉時,中間沿線的電磁線瞬間消失,而這就是三叉的特征,當具體還需要很多細節的處理,以保證判定準確。環島也可以參照這種方法進行處理了。
可以看到我們上面的電磁桿也是臨時改的,把其中一個水平電感放到了中間去。
最后的成品圖:
四、軟件部分
軟件部分核心就是采集電感值,然后將電感值稍微進行一下濾波處理,其實硬件做的電磁感效果比較好的話也不需要怎么濾波。濾波后將實際值通過判斷導入分段PID進行目標值的比較,我們將處理后的結果輸出給電機執行,這就是整體的軟件流程了,其實整體很簡單。
另外的話在執行中再加上三叉,環島等特殊元素的判定就歐克了。
cubeMX部分:
基礎的配置,時鐘等等就不再多說了,這里說一下 ADC多路配置吧
如圖:
使能DMA模式
(1)Data Alignment—>Right alignment 此項選擇右對齊,保持不變。
(2)Scan Conversion Mode—>Enable 此項選擇掃描模式使能,代表對6路ADC輸入分別掃描,如果不使能,其將會只讀取一個輸入的值。
(3)Continuous Conversion Mode —>Enable 此項選擇連續掃描模式,表示將連續不斷的對ADC的值進行轉換。如果此項不使能,將會只采集一次就會停止,直到下一次使能才繼續進行一次ADC轉換。
(4)Discontinuous Conversion Mode—>Disable 此項和第三項是重復的。
(5)Number of Conversion---->6 此處有多少路輸入就選擇多少,而且只有在此處選擇數字之后下面才會出來6個不同的通道。而且此處應該是在進入ADC1中第一個需要操作的步驟,否則(2)(3)是灰色的,無法選擇使能。
使能一個定時器中斷,用于一些功能的周期控制和按鍵的定時掃描
cubemx其他配置
使能了一些如驅動蜂鳴器的普通GPIO口,用的軟件IIC,使能了硬件IIC用于驅動陀螺儀
代碼部分
把用戶代碼單獨封裝出來
代碼的初始化,其實這里mpu6050沒有用到,初始化與否無所謂
while(w_mpu_init()!=mpu_ok) {printf("ERROR\r\n");HAL_Delay(5); } dmp_init(); OLED_Init(); OLED_Clear(); data(); ADC_DMA_Iint(); HAL_TIM_Base_Start_IT(&htim2); MOTO_init();//HAL_Delay(100000); PID_Init(); get_into('R');主循環里面的代碼執行
/* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///讀取mpu姿態Yaw_angle();//采集AD值Ad_Value();Direction_track();//屏幕數據Real_time_data();if(last==1){get_into('L');}//按鍵掃描scan_key();//PID處理}使用干簧管判定停車位這里:用的是標志位,用外部中斷進行觸發
//判斷停車if(stop_sta==0 && stop_falg0==0){HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_SET);stop_falg0=1;stop_falg1=1;}if(stop_sta==1 && stop_falg1==1){stop_falg2=1;HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_RESET);}if(stop_falg2==1 && stop_sta==0){last=1;}判斷環島,這里是用到標志位加定時器中斷計數,但由于整車只調了一個通宵,這里最后是沒有調出來的,其實主要也和電磁桿的位置擺放和特殊點的分析不足有關。
//判斷環島if(PID_direction.time==1){PID_direction.count++;if(PID_direction.count==800){HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_RESET);PID_direction.time=0;PID_direction.count=0;PID_direction.target_speed=70;}}if(Left.firet>=800 && Left.second>=800 && Left.third>=800 && Right.firet <=100) {PID_direction.time=1;PID_direction.target_speed=30;}if(PID_direction.time==1){//HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_SET);PID_direction.flag=2;} if(PID_direction.three_forks==1){if(PID_direction.into_three==1)//已經進入過一次了{//判定是環島PID_direction.cir=1;TIM1->CCR1=30; TIM1->CCR2=60; // TIM1->CCR1=20; //調試 // TIM1->CCR2=35;}else{TIM1->CCR1=45; //調試TIM1->CCR2=10;}PID_direction.flag=3;HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_SET);}判斷三叉路,也是通過標志位和定時器的計數實現的,具體的思路前面也已經說過了,這里通過定時器計數判斷主要是為了穩定過三叉,并且不誤觸發。
//判斷三岔if(PID_direction.three_forks==1){PID_direction.count++;if(PID_direction.count==20) //調試{PID_direction.Kp = 0.022;//0.015PID_direction.count=0;PID_direction.three_forks=0;PID_direction.target_speed=40;PID_direction.run_time_three=1;HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_RESET);}if(PID_direction.cir==1){PID_direction.Kp = 0.023;PID_direction.run_time_three=0;PID_direction.target_speed=40;PID_direction.timer++;if(PID_direction.timer==250){PID_direction.Kp = 0.015;PID_direction.target_speed=70;PID_direction.cir=0;PID_direction.timer=0;}}}if(PID_direction.run_time_three==1){//低速出三岔PID_direction.timer++;if(PID_direction.timer==580){PID_direction.Kp = 0.015;PID_direction.into_three++;PID_direction.target_speed=70;PID_direction.timer=0;PID_direction.run_time_three=0;PID_direction.three_on=0;//允許進入了}}以上代碼的執行均放在了10ms一次的定時器中斷回調函數內部
#include "ISR_CallBack.h"KEY key[4]={0,0,0}; uchar stop_sta,stop_falg0=0,stop_falg1,stop_falg2; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if(htim->Instance == TIM2){//用戶代碼/*..................*/} }PID控制:
這里需要提到的是,某組織方提高的三輪車車模質量不敢恭維,沒有編碼器,要加編碼器也要大改一下車身很麻煩,況且作為一個校賽,自己并沒有花太多精力,于是直接開環跑了,校賽足矣。
電機的初始化
void MOTO_init(void) {HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);HAL_GPIO_WritePin(STBY_GPIO_Port,STBY_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(Moto1_GPIO_Port,Moto1_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(Moto2_GPIO_Port,Moto2_Pin,GPIO_PIN_RESET);HAL_GPIO_WritePin(Moto3_GPIO_Port,Moto3_Pin,GPIO_PIN_SET);HAL_GPIO_WritePin(Moto4_GPIO_Port,Moto4_Pin,GPIO_PIN_RESET);TIM1->CCR1=0;TIM1->CCR2=0;HAL_GPIO_WritePin(Bee_GPIO_Port,Bee_Pin,GPIO_PIN_RESET); }用的位置式PID足夠了,進行了一個簡單的判斷分段
//水平電感if(PID_direction.flag==1){PID_direction.Pwm=PID_direction.Kp*PID_direction.err+PID_direction.Ki*PID_direction.integral+ \PID_direction.Kd*(PID_direction.err-PID_direction.err_last);}//豎直電感if(PID_direction.flag==2){PID_direction.Pwm2=PID_direction.Kp2*PID_direction.err2+PID_direction.Ki2*PID_direction.integral2+ \PID_direction.Kd2*(PID_direction.err2-PID_direction.err_last2);}PID_direction.err_last=PID_direction.err;PID_direction.err_last2=PID_direction.err2;PID的初始化
void PID_Init(void){PID_direction.Kp = 0.015;//0.015PID_direction.Ki = 0;PID_direction.Kd = 0.075;//PID_direction.integral=0;PID_direction.time=0;PID_direction.cir=0;PID_direction.target_speed=70;PID_direction.three_forks=0;PID_direction.run_time_three=0;PID_direction.three_on=0;PID_direction.into_three=0; }限幅和取絕對值函數
uint16_t Limit(uint16_t pwm){ if (pwm <= 50){return 50;}else if(pwm >= 950){return 950;}else {return pwm;} } float Abs(float a){if (a <= 0){return -a;}else{return a;} }最后輸入執行器,控制PWM
if(PID_direction.flag==1){TIM1->CCR1=PID_direction.target_speed-PID_direction.Pwm;TIM1->CCR2=PID_direction.target_speed+PID_direction.Pwm;}if(PID_direction.flag==2){TIM1->CCR1=PID_direction.target_speed-PID_direction.Pwm2;TIM1->CCR2=PID_direction.target_speed+PID_direction.Pwm2;}OLED實時顯示個參數信息
void data(void) {OLED_ShowString(10,0,(uint8_t *)"Smart",16);OLED_ShowString(55,0,(uint8_t *)"Car_MY",16);OLED_ShowString(0,2,(uint8_t *)"V1:",12);OLED_ShowString(0,3,(uint8_t *)"V2:",12);OLED_ShowString(0,4,(uint8_t *)"V3:",12);OLED_ShowString(0,5,(uint8_t *)"V4:",12);OLED_ShowString(0,6,(uint8_t *)"V5:",12);OLED_ShowString(0,7,(uint8_t *)"V6:",12); } void Real_time_data(void) {OLED_ShowNum(30,2,Left.firet,5,12); OLED_ShowNum(30,3,Left.second,5,12); OLED_ShowNum(30,4,Left.third,5,12); OLED_ShowNum(30,5,Right.third,5,12); //差與和的動態信息OLED_ShowNum(30,6,Right.second,5,12);OLED_ShowNum(30,7,Right.firet,5,12);OLED_ShowFloat(67,6,mpu_pose_msg.yaw,3,2,12); }對了,采集ADC的值進行了歸一化如下:
#include "get_adc.h"AD Left,Right; uint32_t ADC_Value[6]; /* =================================================================== 備注:電感AD采集歸一化計算 1、可以方便自己對數據的感知,在普通元素和特殊元素間; 2、在賽道更換后,測新的賽道的最大值,改變max的值即可,有較強的適應性; 3、方便數據處理。 * ===================================================================*/ * //歸一化到0-1000 Left.firet = 1000*(AD_average[5]-Min)/(Max-Min); Left.second = 1000*(AD_average[4]-Min)/(Max-Min); Left.third = 1000*(AD_average[3]-Min)/(Max-Min); Right.third = 1000*(AD_average[2]-Min)/(Max-Min); Right.second = 1000*(AD_average[1]-Min)/(Max-Min); Right.firet = 1000*(AD_average[0]-Min)/(Max-Min);這就是大體的代碼結構了
其它
拿著車,凌晨兩點左右在電梯內準備去改電感的位置,車上發的光好好看,硬件員說,看我搭的車模帥吧,記錄一下,小樣兒~
凌晨五點左右,車調得差不多了,能去休息了,和硬件員一起回實驗室
挺逗的,智能車校賽測評時是在智能視覺的場地
寫得比較雜,與其說是寫技術文章,不如說我只是在寫下一些記錄吧,更多的是感受,這些都是我們電子人的青春。
僅此分享做車的一些想法,有遺憾,也有快樂,不管怎樣選擇了就不后悔,飛卡可能再也打不了了吧,智能車校賽可能也沒時間了,年底要開始準備考研啦!
我的本科階段留給做技術的時間越來越少了,一回頭才發現,從剛剛進入大學那一刻到現在,時間真的好快。
加油呀!追求卓越,成功才會在不經意之間追上你!
工程鏈接:
硬件原理圖PCB
代碼工程
硬件加軟件工程(全套)
總結
以上是生活随笔為你收集整理的电磁寻迹智能车HAL库基于cubeMX—三轮(分段PID+归一化+差速+均值滤波+多路ADC+三叉+环岛+十字)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Foreda Workstation 3
- 下一篇: 苏州企业如何识别不良商标代理机构