作者dyy_hh參加了校內智能車比賽,這是為這段時間做的總結。在此感謝dyy_hh的總結分享。
一.硬件部分
必需:STM32F103C6T6(或者STM32F103C8T6),舵機(MG 996R),電機(TT馬達 130電機),L298n驅動,電磁桿(可以自己制作),干簧管,兩節18650電池,基礎四輪車模。
二.軟件部分
必需:ADC多路采集的DMA配置,定時器PWM波輸出,普通GPIO口,濾波,歸一化,差比和,PID算法。
輔助:OLED驅動,串口打印。
1.舵機
三根線:VCC,GND,信號線。 我們給VCC接的6V。信號線接相應PWM波輸出口。
舵機調中值:可以使用編碼器調節占空比,看舵機一共能夠轉動多少占空比的范圍(注意!舵機不是可以360度旋轉的)。然后取最中間的占空比輸出給舵機。小菜花當時是設置的20ms為周期,Counter Period 設置的20000-1。
2.電機
開環控制兩個電機:兩個PWM波輸出,四個普通GPIO口控制高低電平。
3.L298n驅動
可以自己去了解如何接線,這里推薦一篇小菜花看到的文章http://t.csdn.cn/Anzwy
4.干簧管
用于在終點處停車。話不多說,上鏈接https://share.weiyun.com/2m5eUtRv
5.電磁桿的電感值采集
我們組開始準備使用兩個水平電感,兩個豎直電感;但是最后由于種種原因,我們使用了兩個水平電感,兩個內八字電感。(最后我們環島沒有進,所以就只使用了兩個水平電感完成了最基礎的尋跡)。
ADC給四個電感 開了四路DMA采集 ,分別是A1,A2,A3,A4。
如果有條件可以在電磁桿中間使用第五路電感,就是小菜花開的A5(雖然我最后沒有用到)。
還有兩路ADC 是采集 兩個干簧管的IO口 高低電平。
6.一些算法
軟件濾波
濾波(Wave filtering)是將信號中特定波段頻率濾除的操作,很大程度上保證了采集到的數據的穩定與真實,是抑制和防止干擾的一項重要措施。
這是參考的別的博主的一種濾波算法: 中位算數平均濾波 。 即結合了中位值濾波和算數平均值濾波的一種算法。
這一篇文章非常詳細的講了濾波http://t.csdn.cn/a8DbF
void Get_ADC(void) //得到的ADC電壓存儲在ADC_Val中
{
int num = 0,count = 0;
for(num = 0; num < 10; num++)
{
HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adci,7);//開啟七路DMA
HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adcj,7);
HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adck,7);
for(count = 0; count < 7;count++)//取中值
{
if (adci[count] > adcj[count])
{
adctmp[count] = adci[count]; adci[count] = adcj[count]; adcj[count] = adctmp[count];
}
if (adck[count] > adcj[count])
adctmp[count] = adcj[count];
else if(adck[count] >adci[count])
adctmp[count] = adck[count];
else
adctmp[count] = adci[count];
sum1[count]+=adctmp[count];
}
}
for(count = 0; count < 7;count++)
{
AD_Val[count]=sum1[count]/10;
sum1[count]=0;
}
}
歸一化
關于為什么要使用歸一化:我看到的很多文章說使用歸一化可以提高對不同賽道的適應性。我的理解是,在不同的賽道只用去測每一路電感的最大最小值就可以正常跑了,對于特殊元素的特征值不用再去測量。大大提高了對不同賽道的適應性。
歸一化的定義:將數據映射到0-1范圍之內處理,可以更快速便捷地觀察數據。
歸一化的公式:(X - Min) / (Max - Min).
其中 X為某一路電感 濾波后的ADC值;Min / Max為某一路電感 濾波后ADC采集到的最小 / 大值。
/*歸一化后每一路ADC的值*/
uint16_t AD_left1;
uint16_t AD_left2;
uint16_t AD_right1;
uint16_t AD_right2;
/*歸一化算法*/
void GuiYi_ADC(void)
{
AD_left1 = (uint16_t) (100 * (AD_Val[1] - AD_left1_min) / (AD_left1_max - AD_left1_min));
AD_left2 = (uint16_t) (100 * (AD_Val[2] - AD_left2_min) / (AD_left2_max - AD_left2_min));
AD_right1 = (uint16_t) (100 * (AD_Val[3] - AD_right1_min) / (AD_right1_max - AD_right1_min));
AD_right2 = (uint16_t) (100 * (AD_Val[4] - AD_right2_min) / (AD_right2_max - AD_right2_min));
}
可以進行適度放大(一般是放大100倍),使車能夠更容易的根據電磁值判斷路況。
差比和
電磁智能車 是根據電磁桿上電感 采集到的值判斷路況,可以說電磁桿上的電感就是一輛電磁智能車的眼睛。差比和值能夠讓車更直觀的判斷路況。
差比和公式:(L-R)/(L+R)。差比和值范圍0-1。
原理:當電感離中心磁感線越近,采集到的值就越大,反之越小。
所以當差比和值為負的時候,可以判斷到車向左偏移了,為正則向右偏移了(公式里面的左右交換了則反之)。
int16_t ad_1_sum;
int16_t ad_1_diff;
double count_1;
double position_1;
/*差比和算法*/
void ChaBiHe_ADC(void)
{
ad_1_sum = (int16_t)AD_left2 + (int16_t)AD_right1+1;
ad_1_diff = (int16_t)AD_left2 - (int16_t)AD_right1;
count_1 = (double)ad_1_diff / ad_1_sum;
position_1 = count_1 * 100;
HAL_Delay(5);
}
小菜花給差比和值乘了100,這里的100可以自行修改,根據需要調整。
PID算法
小菜花采用的位置式PD控制舵機。
小菜花將差比和值乘100后(就是上段代碼中的position)直接傳給PD算法中作為誤差error。
那么為什么可以這樣呢,因為我把 差比和值為0作為目標,采集回來的差比和值作為實際值,那么誤差error就是 實際值-目標值。 注意,我的目標值為0,所以error可以直接為采集回的差比和實際值。
typedef struct
{
double PID_P; /* 比例常數 */
double PID_D; /* 微分常數 */
double LastError; /* 前一項誤差 */
double PrevError; /* 前第二項誤差 */
}PID;
double PID_Vertical (PID *pp)
{
double dError1, Error1;
Error1 = position_1;//差比和的值作為error /*目標值為差比和值為0,所以可以直接將差比和實際所得值作為error*/
dError1 = pp->LastError - pp->PrevError;
pp->PrevError = pp->LastError;
pp->LastError = Error1;
PWMValue1 = pp->PID_P * Error1 + pp->PID_D * dError1;
return PWMValue1;
}
然后就開始調P和D,如果P D調得好,車就跑得很絲滑。小菜花建議可以使用分段PD來調車,親測有效!車跑起來確實絲滑。
PD算法返回的值傳給
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,PWMValue1+745);
745是小菜花當時找到的舵機中值,PWMValue1可正可負,調節舵機可以左右轉動。
7.特殊元素
十字路口
小菜花當時是用兩路水平電感尋跡,到了十字路口也不會出現誤判,能夠正常行進。
三岔路口
當兩路水平電感采集的歸一化后的值 出現同時突然下降時,強制打角。
環島檢測
小菜花是設置了三個標志位,到賽道上去采集閾值。當三個標志位都滿足時,進行了強制打角(可以在此時切換為內八字電感尋跡,由內八電感的PD算法輸出占空比拐彎進環島。不推薦強制打角,會降低對不同賽道的適應性)。
然后用水平電感跑環島。出環的時候檢測兩條磁感線重合的位置,到賽道測閾值,設置標志位,滿足條件則強制直行,延時控制出環島(依舊不推薦強制控制,但小菜花的能力有限,只能想到這個辦法)。
出庫
小菜花當時的思路是:開機就強制打角,延時控制直到出庫,隨后正常水平巡線。(所以出庫函數只運行一遍!)
void chuKu(void)
{
uint16_t i;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,7000);//先低速跑電機
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,7000);
for(i=155;i<192;i++)
{
Value1=i+745;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,Value1);
HAL_Delay(15);
}
}
用了一個for循環(也可以不用,直接輸出占空比)。for循環 使舵機打角時不會突然一下就轉到相應角度,而是更加絲滑地轉過去。
入庫
干簧管經過終點磁鐵,會由高電平變為低電平。因此,檢測到干簧管的IO口有一個電平變化,標志位加一。在while循環里面標志位變為一的時候進行強制打角,延時控制入庫,舵機轉回中值,電機停止轉動。
/*干簧管標志位*/
static uint16_t ganflag = 0;
if ((AD_Val[0]<300||AD_Val[6]<300) )//干簧管檢測出入庫
{
ganflag++;
HAL_Delay(100);
if ((AD_Val[0]<300||AD_Val[6]<300) && ganflag == 1)
{
ruKu();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);//電機GPIO口高低電平
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,745);
}
}
void ruKu(void)
{
uint16_t i;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,7000);//先低速跑電機
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,7000);
for(i=150;i<220;i++)
{
Value1=i+745;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,Value1);
HAL_Delay(15);
}
}
干簧管標志位檢測可以放在中斷里面更好,檢測到一次下降沿,觸發一次中斷,標志位加一。
小菜花放在while循環里面會存在一個問題:過一次磁鐵,標志位不只加一(我猜測是干簧管檢測到一次電平變化,但是代碼已經刷過好幾遍了,所以標志位加的次數不定)。所以,我加了一個延時,HAL_Delay(100),試了一下,可以過一次干簧管,標志位加一。
8. 一些建議
電磁桿
我們當時是自己做了電磁桿,但是由于其中有一路重要電感不能用,所以最終放棄,買了一個電磁桿(所以被迫使用水平+內八字電感)。 建議大家有條件的可以買一個電磁桿,不要把過多的時間都放在修電磁桿上面了!!!
OLED
可以用OLED來顯示數據,觀察起來很方便。 不幸的是,小菜花當時的OLED不知道為啥用不了,
插核心板上面一點反應都沒有!!!
HC-05藍牙模塊
可以使用空閑中斷,用藍牙與手機通信,直接在手機上面調節PD值,十分方便。
Debug
如果你跟我一樣,不幸地 OLED用不了,藍牙串口不,打印那么就用Debug看變量的值吧!!!
文末
還有一個電腦端上位機VOFA+,推薦使用(由于小菜花能力和時間有限,沒有深入了解這個VOFA+),讀者可以自己去了解使用。
-
adc
+關注
關注
98文章
6503瀏覽量
544868 -
STM32
+關注
關注
2270文章
10904瀏覽量
356338 -
電磁
+關注
關注
15文章
1135瀏覽量
51845 -
智能車
+關注
關注
21文章
404瀏覽量
76983 -
舵機
+關注
關注
17文章
270瀏覽量
41062
發布評論請先 登錄
相關推薦
評論