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