1 題目
1.1 任務(wù)
設(shè)計一個由四旋翼無人機(jī)及消防車構(gòu)成的空地協(xié)同智能消防系統(tǒng)。無人機(jī)上安裝垂直向下的激光筆,用于指示巡邏航跡。巡防區(qū)域為40dm×48dm。無人機(jī)巡邏時可覆蓋地面8dm寬度區(qū)域。以縮短完成全覆蓋巡邏時間為原則,無人機(jī)按照規(guī)劃航線巡邏。發(fā)現(xiàn)火情后立即采取初步消防措施,并將火源地點位置信息發(fā)給消防車,使其前往熄滅火源??盏貐f(xié)同巡邏及消防工作完成時間越短越好。
1.2 任務(wù)點
1、基本要求(50分)
- (1)參賽隊需自制模擬火源。模擬火源是用電池供電的紅色光源,如LED等,用激光筆持續(xù)照射可控制開啟或關(guān)閉:持續(xù)照射2秒左右開啟,再持續(xù)照射2秒左右關(guān)閉。(5分)
- (2)展示規(guī)劃的巡邏航線圖,在消防車上按鍵啟動無人機(jī)垂直起飛后,無
人機(jī)以18dm左右高度,在巡防區(qū)域按規(guī)劃的航線完成全覆蓋巡邏。(22分) - (3)無人機(jī)與消防車之間采用無線通信;巡邏期間無人機(jī)每秒向消防車發(fā)
送1次位置坐標(biāo)信息,消防車上顯示器實時更新顯示無人機(jī)位置坐標(biāo)信息。(8分) - (4)巡邏中,消防車顯示器顯示巡邏航跡曲線,計算并顯示累計巡邏航程。(8分)
- (5)完成巡邏后,無人機(jī)返回,準(zhǔn)確降落在起飛區(qū)域內(nèi)。(7分)
2、發(fā)揮部分(50分)
- (1)手動操作激光筆點亮-一個火源。在消防車上啟動無人機(jī)巡邏。無人機(jī)
按規(guī)劃航線巡邏,發(fā)現(xiàn)火情后,前往接近火源(水平距離≤5dm)識別確認(rèn),再在無人機(jī)上用LED指示燈示警。(8分) - (2)無人機(jī)飛至火源地點上方,降低至10dm左右高度,懸停3s后拋灑滅
火包,滅火包落在以火源點為中心、半徑3dm圓形區(qū)域內(nèi);再將火源地點位置坐標(biāo)發(fā)送給消防車,然后繼續(xù)巡邏,完成后返航回到起飛點。(12分) - (3)消防車接收到火情信息,顯示火源地點位置坐標(biāo)后,從消防站出發(fā)前
往火源地點,途中不得碾壓街區(qū)及其邊界線,在5dm距離內(nèi)以激光筆光束照射模擬火源將其熄滅。(15分) - (4)熄滅模擬火源后消防車返回到出發(fā)區(qū)域內(nèi)。發(fā)揮部分限時360s內(nèi)完成。(10分)
- (5)其他。(5分)
1.3 設(shè)計部分
1.3.1 模擬火源
該部分的要求如下:
模擬火源可用電池供電的紅色LED等,需帶向上的喇叭形遮光罩,遮光罩角度約60°左右,高度不超過10cm??捎眉す夤P控制其開啟或關(guān)閉。
示意圖如下:
外部遮光罩可仿照寵物防咬罩進(jìn)行修改:
內(nèi)部LED燈罩是為了擴(kuò)大燈光面積,以便系統(tǒng)內(nèi)圖像識別系統(tǒng)的工作。同時為了提高小車激光跟隨算法的工作效率,需要擴(kuò)大光敏傳感器檢測區(qū)域,建議使用人體微波檢測模塊上的塑料透鏡,以達(dá)到聚光的效果。
電路設(shè)計圖:STC8最小系統(tǒng)板加兩節(jié)1.5V干電池,一個紅色LED,300Ω左右的電阻,一個光敏傳感器就差不多了。
程序設(shè)計:程序中用到一個GPIO輸出、ADC、定時器。
1.3.2 小車部分
該部分要求如下:
消防車要求使用 4 輪電動小車,長寬投影尺寸不大于 20cm×35cm,高度不大于 40cm;不得使用麥克納姆輪。
1、底盤與電機(jī):
(1)底盤:自選。
(2)電機(jī):最好使用帶編碼器的金屬齒輪減速點擊,能夠得到轉(zhuǎn)速和角度,以便估測小車位置,更好地控制小車。
(3)電機(jī)驅(qū)動:H橋電機(jī)驅(qū)動模塊。
(4)電池:12V高倍率鋰電池組。
最好找要帶有電機(jī)驅(qū)動的、編碼器接口、能夠降壓給主控供電的小車底板,比如開山斧電機(jī)驅(qū)動模塊。
四輪小車的前兩個輪子無動力,采用舵機(jī)控制方向,后兩個輪子分別采用直流電機(jī)控制。
2、主控芯片
(1)盤古:
如果選擇TI的,盤古這塊系統(tǒng)板開發(fā)起來還是比較順手的,用起來Bug少。板子上有OLED驅(qū)動芯片和加速度傳感器、蜂鳴器、多個按鍵、串口接口多。
(2)TM4C123GXL:
這塊板子十分不推薦,調(diào)試接口有問題,驅(qū)動也不好打(ICDI,要安裝CCS),性能也比較差,供電端口也少,寫代碼的時候Keil閃退無數(shù)次,硬件跑程序也容易進(jìn)中斷卡死。
要是有別人改過的、開發(fā)好的成品,用用還是可以的。
3、電機(jī)驅(qū)動芯片:
使用的是無名創(chuàng)新的開山斧驅(qū)動板,以下是模塊接口圖:
- 電機(jī)控制信號接口:為四路PWM輸入,兩路PWM控制一個電機(jī)的轉(zhuǎn)速和旋轉(zhuǎn)方向。
- … …
3、顯示屏
建議使用陶晶馳的串口屏,配置方便,代碼簡單,能夠回傳字符串。
引腳:5V、GND、TXD、RXD
4、無線模塊
可以使用藍(lán)牙、WIFI、Lora等等。
無線模塊建議采用有廣播功能的模塊,這樣多機(jī)調(diào)試更加方便。
引腳:VCC(5~3.3V)、GND、TXD、RXD
今年還允許使用UWB,能夠?qū)崿F(xiàn)無人機(jī)和小車的精確定位,還能夠傳輸數(shù)據(jù),只要來得及開發(fā)代碼,可以說是降維打擊,不過價格還是比較昂貴的。
5、機(jī)器視覺和舵機(jī)
對于模擬火源的檢測,有OpenMV方案和K210方案。
OpenMV模塊有控制舵機(jī)的例程,可以控制激光筆關(guān)閉模擬火源,但溢價過多,可以自己DIY。
6、IMU
這個主控板上有就最好,沒有就用模塊化的MPU6050,不過要安裝在小車中心處。
7、其他模塊:
(1)灰度傳感器:白光照射不同顏色的地面,反射回的光強(qiáng)不同,可以進(jìn)行巡線等任務(wù)。
(2)激光頭。
(3)…
1.3.3 無人機(jī)部分
直接購買成品化的TI無人機(jī),主控板可以使用前面提到的主控。
2 程序設(shè)計
我主要是做小車的,因此講一下我小車的設(shè)計方案。
小車上主控板選用TI 盤古的開發(fā)板,板載芯片為TM4C123GH6PZT7,MCU內(nèi)核為ARM Cortex-M4F,MCU最大主頻為80 MHz,工作電壓范圍1.08 V-3.63 V,RAM為32 KB,F(xiàn)lash大小為256 KB,EEPROM為2 KB,核心位寬為32-Bit,ADC為12 bit。
外設(shè)和內(nèi)部資源需要使用串口屏、藍(lán)牙模塊/UWB模塊、定時器、PWM、LED輸出、按鍵輸入(這個可以使用串口屏的按鍵串口信息回傳事件替代)、蜂鳴器驅(qū)動。
2.1 藍(lán)牙模塊配置
本人使用的是大熊智能的雙模藍(lán)牙模塊,兩個藍(lán)牙模塊配對的話需要設(shè)置一主機(jī)一從機(jī),以下藍(lán)牙模塊連接CH340模塊,在電腦上使用AT命令配置兩個模塊。默認(rèn)使用115200波特率連接。
藍(lán)牙從機(jī),連接無人機(jī):
AT+NAME=DX2003-S # 設(shè)置從機(jī)名稱
AT+MASTER=01 # 設(shè)置從機(jī)工作模式
AT+BAUD=115200 # 設(shè)置波特率為115200
AT+LADDR # 讀取從機(jī)藍(lán)牙地址,以便主機(jī)連接
+LADDR=22345000891f
藍(lán)牙主機(jī),連接小車:
AT+NAME=DX2003-M # 設(shè)置主機(jī)名稱
AT+MASTER=04 # 設(shè)置主機(jī)工作模式
AT+BAUD=115200 # 設(shè)置波特率為115200
AT+CONN=22345000891f # 主機(jī)連接從機(jī)地址
連接上之后:
(1)主機(jī)顯示:
IM_CONN:0 # 0代表是BLE連接上,1代表是SPP連接上
(2)從機(jī)顯示:
IM_CONN:8
藍(lán)牙斷開連接命令:
AT+DSCET=1
2.2 小車串口屏界面設(shè)計
串口屏使用USART HMI軟件繪制界面,需要進(jìn)行界面排版,字庫添加,程序編譯。
軟件下載及學(xué)習(xí)鏈接:
http://mall.micromove.cn/start/download_ide.html
2.2.1 串口通信協(xié)議
1、串口屏接收:
協(xié)議為:字符串+HEX標(biāo)識符
HEX標(biāo)識符為:\xff\xff\xff
USART HMI軟件上仿真不需要添加HEX標(biāo)識符(\xff\xff\xff)。
例子:
如果是串口屏使用CH340連接電腦,則電腦上的串口工具輸入(不要加空格):
t0.txt="陶晶馳電子"\xff\xff\xff
b0.txt="Hello World"\xff\xff\xff
j0.val=100\xff\xff\xff
page0.bco=WHITE\xff\xff\xff
如果是MCU串口發(fā)送數(shù)據(jù):在2.4.2
中詳述。
2、串口屏發(fā)送:
(1)prints:從串口打印一個變量/常量。
(2)printh:從串口打印16進(jìn)制。
2.2.2 繪圖函數(shù)
主要使用的有:
(1)cirs:繪制實心圓
cirs x,y,radius,color\xff\xff\xff
cirs 160,266,6,RED\xff\xff\xff
(2)line:繪制直線
line x_start,y_start,x_end,y_end,color\xff\xff\xff
line 185,246,185,26,BLUE\xff\xff\xff
(3)插入圖片
pic x,y,picid\xff\xff\xff # picid為軟件插入的ID號為x的圖片
pic,123,150,0\xff\xff\xff
2.2.3 串口屏界面設(shè)計
小車能夠通過藍(lán)牙串口接收無人機(jī)傳回的航點數(shù)據(jù)(協(xié)議A)和火災(zāi)位置數(shù)據(jù)(協(xié)議B),并在串口屏上顯示出藍(lán)色原點和紅色六角形。
2.3 無人機(jī)與小車之間的通信協(xié)議設(shè)計
1、無人機(jī)通過串口發(fā)送給小車的字符串:
A,160,150,F // 無人機(jī)航點坐標(biāo)(160,150)
B,250,100,F // 模擬火源坐標(biāo)(250,100)
C,1,F // 模擬火源ID: 1
A類表示無人機(jī)航點,F(xiàn)為結(jié)束標(biāo)志位
B類表示無人機(jī)檢測到的火源坐標(biāo),F(xiàn)為結(jié)束標(biāo)志位。
C類表示無人機(jī)檢測到的模擬火源所在的區(qū)域ID,F(xiàn)為結(jié)束標(biāo)志位。這個協(xié)議可以不發(fā)送,直接小車通過B類協(xié)議計算模擬火源位置。
2、小車通過串口發(fā)給無人機(jī)的字符串只需要一個按鍵使能,使用按鍵輸入或者串口屏的點擊,通過藍(lán)牙串口發(fā)送“TakeOff”字符串,無人機(jī)檢測到就能起飛了。
2.4 小車程序設(shè)計
首先進(jìn)行各個部分的初始化,然后進(jìn)入while(1)循環(huán),循環(huán)內(nèi)寫入大部分處理函數(shù)。中斷處理函數(shù)中存放處理函數(shù)的使能位,當(dāng)某些函數(shù)工作時間較長,需要在中斷中使能標(biāo)志位,然后在主函數(shù)main中進(jìn)行處理,以免造成中斷阻塞。
主函數(shù):
#include "stdio.h"
#include <stdint.h>
#include <stdbool.h>
// ......
// 全局變量
char uart4_rec_temp[50]; // 接收到暫存的字符數(shù)組
bool uart4_rec_check_flag = 0; // 接收數(shù)據(jù)解包的標(biāo)志位
// ......
int main(void)
{
ROM_FPUEnable();//使能浮點單元
ROM_FPULazyStackingEnable();//浮點延遲堆棧,減少中斷響應(yīng)延遲
ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);//配置系統(tǒng)時鐘
initTime(); // 初始化滴答定時器
GPIO_Init(); // LED燈初始化
// ......
ConfigureUART4(); // 初始化藍(lán)牙BLE-串口4
ConfigureUART5(); // 初始化串口屏驅(qū)動-串口5
UART4_BLE_CONNECT(); // 串口4連接藍(lán)牙
HMI_Init(); // 串口屏初始化
// ......
while(1)
{
// ......
uart4_data_check(); //串口4數(shù)據(jù)包解包
uart5_uav_point_write(); // 向串口屏寫入無人機(jī)航點
Control_UAV_flight_status(); // 小車控制無人機(jī)起飛
// ......
}
}
2.4.1 藍(lán)牙串口程序
藍(lán)牙串口程序包括串口初始化、串口發(fā)送、串口中斷服務(wù)函數(shù)(串口接收)、串口數(shù)據(jù)解析等部分。
1、串口初始化:
// 藍(lán)牙-串口4驅(qū)動 PC4/PC5
void ConfigureUART4(void)
{
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);// Enable the GPIO Peripheral used by the UART.
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4);// Enable UART0
ROM_GPIOPinConfigure(GPIO_PC4_U4RX);// Configure GPIO Pins for UART mode.
ROM_GPIOPinConfigure(GPIO_PC5_U4TX);
ROM_GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5);
UARTConfigSetExpClk(UART4_BASE,SysCtlClockGet(),115200,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
UARTFIFODisable(UART4_BASE); // 使能UART4中斷
UARTIntEnable(UART4_BASE,UART_INT_RX); // 使能UART4接收中斷
UARTIntRegister(UART4_BASE,UART4_IRQHandler); //UART4中斷地址注冊
IntPrioritySet(INT_UART4,USER_INT3); //中斷優(yōu)先級設(shè)置USER_INT3(0最高)
}
2、串口中斷服務(wù)函數(shù):
void UART4_IRQHandler(void) //UART4中斷函數(shù)-藍(lán)牙BLE接收中斷(無人機(jī)信息發(fā)送給小車)
{
uint32_t flag = UARTIntStatus(UART4_BASE,1);//獲取中斷標(biāo)志 原始中斷狀態(tài) 屏蔽中斷標(biāo)志
UARTIntClear(UART4_BASE,flag);//清除中斷標(biāo)志
char ch;
while(UARTCharsAvail(UART4_BASE))//判斷FIFO是否還有數(shù)據(jù)
{
ch = UARTCharGet(UART4_BASE);
uart4_rec_temp[temp_cnt] = ch;
temp_cnt ++;
}
if(ch == 'F')
uart4_rec_check_flag = 1; // 接收數(shù)據(jù)解包的標(biāo)志位置1
if(temp_cnt >= 50) //數(shù)組存滿后清空
{
memset(uart4_rec_temp, 0, sizeof(uart4_rec_temp)); // 清空字符數(shù)組
temp_cnt = 0;
}
bit_data = !bit_data;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_5, bit_data); //置低位點亮,保持閃爍,如果LED不閃爍了,表示程序卡死了
}
3、串口發(fā)送函數(shù):
void UART4_SendString(const char *string)
{
int len = strlen(string);
while(len--)
{
// 等待UART發(fā)送緩沖區(qū)為空
while(UARTSpaceAvail(UART4_BASE) == 0);
// 發(fā)送字符到UART
UARTCharPut(UART4_BASE, *string++);
}
}
4、解析串口接收到的數(shù)據(jù):
首先檢測第一個數(shù)是否是“A”或“B”,然后檢測字符串最后一個字符是否是“F”。接著索引字符串中的“,”,將中間的兩個數(shù)字字符串拆分出來,使用atof()函數(shù)將字符串轉(zhuǎn)換為浮點數(shù)。最后將獲得的浮點數(shù)存入對應(yīng)的變量中,或者使用調(diào)試接口輸出,或使用OLED屏輸出。
// 串口4數(shù)據(jù)包解包
void uart4_data_check(void)
{
if(uart4_rec_check_flag == 1) // 分析uart4_rec_temp中的數(shù)據(jù)
{
uart4_rec_check_flag = 0;
if(uart4_rec_temp[0] == 'A') // 是無人機(jī)XY坐標(biāo)數(shù)據(jù)包
{
int len = strlen(uart4_rec_temp);
if(uart4_rec_temp[len-1] == 'F')
{
char* token;
token = strtok(uart4_rec_temp,",");
token = strtok(NULL,",");
uart4_rec_x[uav_cnt] = atof(token);
token = strtok(NULL,",");
uart4_rec_y[uav_cnt] = atof(token);
uart4_flight_dist = uart4_flight_dist + sqrt(pow(uart4_rec_x[uav_cnt]-uart4_rec_x[uav_cnt-1],2) + pow(uart4_rec_y[uav_cnt]-uart4_rec_y[uav_cnt-1],2));
printf("A-X: uart4_rec_x[%d]:%f\r\n",uav_cnt,uart4_rec_x[uav_cnt]); // 測試
printf("A-Y: uart4_rec_y[%d]:%f\r\n",uav_cnt,uart4_rec_y[uav_cnt]); // 測試
printf("A-D: uart4_flight_dist[%d]:%fm\r\n",uav_cnt,uart4_flight_dist/100); // 測試
// ......
}
// 處理完成后,清空uart4_rec_temp
memset(uart4_rec_temp, 0, sizeof(uart4_rec_temp));
temp_cnt = 0;
}
else if(uart4_rec_temp[0] == 'B') // 是火源XY坐標(biāo)數(shù)據(jù)包
{
int len = strlen(uart4_rec_temp);
if(uart4_rec_temp[len-1] == 'F')
{
char* token;
token = strtok(uart4_rec_temp,",");
token = strtok(NULL,",");
uart4_fire[0] = atof(token);
token = strtok(NULL,",");
uart4_fire[1] = atof(token);
printf("B-X: uart4_fire[0]:%f\r\n",uart4_fire[0]); // 測試
printf("B-Y: uart4_fire[1]:%f\r\n",uart4_fire[1]); // 測試
// ......
}
// 處理完成后,清空uart4_rec_temp
memset(uart4_rec_temp, 0, sizeof(uart4_rec_temp));
temp_cnt = 0;
}
else
{ // 沒找到A/B數(shù)據(jù)包,清空uart4_rec_temp
memset(uart4_rec_temp, 0, sizeof(uart4_rec_temp));
temp_cnt = 0;
}
}
}
2.4.2 串口屏控制程序
串口屏界面:
1、串口驅(qū)動:
// 串口屏-串口5 PE4/PE5
void ConfigureUART5(void)
{
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);// Enable the GPIO Peripheral used by the UART.
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART5);// Enable UART6
ROM_GPIOPinConfigure(GPIO_PE4_U5RX);// Configure GPIO Pins for UART mode.
ROM_GPIOPinConfigure(GPIO_PE5_U5TX);
ROM_GPIOPinTypeUART(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5);
UARTConfigSetExpClk(UART5_BASE,SysCtlClockGet(),115200,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
UARTFIFODisable(UART5_BASE); // 使能UART6中斷
UARTIntEnable(UART5_BASE,UART_INT_RX); // 使能UART6接收中斷
UARTIntRegister(UART5_BASE,UART5_IRQHandler); //UART6中斷地址注冊
IntPrioritySet(INT_UART5,USER_INT5); // 中斷優(yōu)先級設(shè)置USER_INT6
}
2、串口中斷服務(wù)函數(shù):
函數(shù)內(nèi)包含串口接收,由于接收數(shù)據(jù)分析較為簡單,解析就在函數(shù)內(nèi)解決了,如果數(shù)據(jù)解析較為復(fù)雜,建議在中斷服務(wù)中給個flag,在主函數(shù)內(nèi)處理。
void UART5_IRQHandler(void)
{
uint32_t flag = UARTIntStatus(UART5_BASE,1);//獲取中斷標(biāo)志 原始中斷狀態(tài) 屏蔽中斷標(biāo)志
UARTIntClear(UART5_BASE,flag);//清除中斷標(biāo)志
char ch;
while(UARTCharsAvail(UART5_BASE))//判斷FIFO是否還有數(shù)據(jù)
{
ch = UARTCharGet(UART5_BASE);
uart5_hmi_rec[uart5_cnt] = ch; //串口屏發(fā)送回的數(shù)據(jù)存儲到buffer中
}
uart5_cnt ++;
if(strstr(uart5_hmi_rec,"TO")) // 數(shù)據(jù)解析,如果uart5_hmi_rec中有"TO",則設(shè)置flag
{
key_flag = 1; // 藍(lán)牙串口發(fā)送數(shù)據(jù)的flag
}
if(uart5_cnt >= 5)
{
uart5_cnt = 0;
memset(uart5_hmi_rec,0,sizeof(uart5_hmi_rec)); // 清空buffer
}
}
3、串口發(fā)動函數(shù):
// UART5 主控串口5發(fā)送給串口屏HMI-----字符串
void UART5_SendString(const char *string)
{
int len = strlen(string);
while(len--)
{
// 等待UART發(fā)送緩沖區(qū)為空
while(UARTSpaceAvail(UART5_BASE) == 0);
// 發(fā)送字符到UART
UARTCharPut(UART5_BASE, *string++);
}
}
// UART5 主控串口5發(fā)送給串口屏HMI------二進(jìn)制
void UART5_SendBinary(void)
{
u8 i;
for(i=0;i<3;i++)
{
// 等待UART發(fā)送緩沖區(qū)為空
while(UARTSpaceAvail(UART5_BASE) == 0);
// 發(fā)送二進(jìn)制0xff到UART
UARTCharPut(UART5_BASE, 0xff);
}
}
4、串口屏啟動程序:
函數(shù)UART5_SendString(cmd)的后面必須跟著函數(shù)UART5_SendBinary(),為的是給串口屏發(fā)送三個0xff結(jié)束標(biāo)志符。
// 串口屏初始化
void HMI_Init(void)
{
char cmd[50];
const int point_num = 5;
int UAVwaypoint[point_num][2] = {{185, 246},
{185, 70},
{400, 70},
{400, 246},
{185, 246}};
UART5_SendString("rest"); // 重啟屏幕
UART5_SendBinary();
Delay_Ms(1000);
UART5_SendString("line 100,20,150,20,BLUE"); // 繪制巡邏軌跡樣例
UART5_SendBinary();
for(int i=0;i<point_num-1;i++) // 繪制規(guī)劃航點
{
sprintf(cmd,"line %d,%d,%d,%d,BLUE",UAVwaypoint[i][0],UAVwaypoint[i][1],UAVwaypoint[i+1][0],UAVwaypoint[i+1][1]);
UART5_SendString(cmd);
UART5_SendBinary();
Delay_Ms(250);
}
sprintf(cmd,"line %d,%d,%d,%d,BLUE",UAVwaypoint[point_num-1][0],UAVwaypoint[point_num-1][1],UAVwaypoint[0][0],UAVwaypoint[0][1]);
UART5_SendString(cmd);
UART5_SendBinary();
}
5、向串口屏寫入無人機(jī)航點位置和火源位置:
void uart5_uav_point_write(void)
{
if(uav_flag == 1) //無人機(jī)航點位置
{
uav_flag = 0;
char cmd[30];
memset(cmd,0,sizeof(cmd));
// X、Y數(shù)據(jù)處理
float x_fixed,y_fixed;
if(uart4_rec_x[uav_cnt-1] > 480)
x_fixed = 480;
else if(uart4_rec_x[uav_cnt-1] < 0)
x_fixed = 0;
else
x_fixed = uart4_rec_x[uav_cnt-1];
uint16_t x_pixel = (uint16_t)(x_fixed/480*312);
if(uart4_rec_y[uav_cnt-1] > 400)
y_fixed = 400;
else if(uart4_rec_y[uav_cnt-1] < 0)
y_fixed = 0;
else
y_fixed = uart4_rec_y[uav_cnt-1];
uint16_t y_pixel = (uint16_t)(y_fixed/256*400);
sprintf(cmd,"cirs %d,%d,6,BLUE",160+x_pixel,262-y_pixel);
//printf("uav_cmd: %s\r\n\r\n",cmd);
UART5_SendString(cmd); // 繪制藍(lán)色圓形 x,y,radius,color
UART5_SendBinary();
sprintf(cmd,"t16.txt=\"(%d,%d)\"",(int)(x_fixed/10),(int)(y_fixed/10));
//printf("uav_position: %s\r\n\r\n",cmd);
UART5_SendString(cmd); // 寫入無人機(jī)位置坐標(biāo)(x,y)
UART5_SendBinary();
sprintf(cmd,"t10.txt=\"%.3f\"",uart4_flight_dist/100);
//printf("uav_flight_dist: %s\r\n\r\n",cmd);
UART5_SendString(cmd); // 寫入無人機(jī)航程
UART5_SendBinary();
}
if(fire_flag == 1) // 火源位置
{
fire_flag = 0;
char cmd[30];
memset(cmd,0,sizeof(cmd));
float x_fixed,y_fixed;
if(uart4_fire[0] > 480)
x_fixed = 480;
else if(uart4_fire[0] < 0)
x_fixed = 0;
else
x_fixed = uart4_fire[0];
uint16_t x_pixel = (uint16_t)(x_fixed*312/480);
if(uart4_fire[1] > 400)
y_fixed = 400;
else if(uart4_fire[1] < 0)
y_fixed = 0;
else
y_fixed = uart4_fire[1];
uint16_t y_pixel = (uint16_t)(y_fixed*256/400);
sprintf(cmd,"pic %d,%d,1",160+x_pixel,262-y_pixel);
//printf("fire_cmd: %s\r\n\r\n",cmd);
UART5_SendString(cmd); // 繪制紅色火災(zāi)圖像
UART5_SendBinary();
sprintf(cmd,"t8.txt=\"(%d,%d)\"",(int)(x_fixed/10),(int)(y_fixed/10));
//printf("fire_position: %s\r\n\r\n",cmd);
UART5_SendString(cmd); // 寫入火源位置坐標(biāo)
UART5_SendBinary();
}
}
2.4.3 小車控制無人機(jī)
依舊是藍(lán)牙串口的部分,藍(lán)牙串口發(fā)送“TakeOff\r\n”字符串,無人機(jī)串口檢測到后能夠解鎖。
// key_flag = 1時,小車控制無人機(jī)起飛
void Control_UAV_flight_status(void)
{
if(key_flag == 1)
{
key_flag = 0;
UART4_SendString("TakeOff\r\n");
}
}
2.4.4 小車電機(jī)控制
小車控制電機(jī)最好需要檢測編碼器信號、讀取IMU的姿態(tài)和加速度信號,但由于時間限制,這一些部分并沒有編寫,完全處于開環(huán)控制。
開山斧模塊能夠直接獲取電機(jī)的轉(zhuǎn)速和方向:
此處對小車直流電機(jī)進(jìn)行控制,采用定時器控制PWM波占空比,以調(diào)整直流電機(jī)轉(zhuǎn)速。
1、PWM初始化:
void PWM0_Init(void)
{
SysCtlPWMClockSet(MOTOR_PWM_SYSCTL_PWMDIV); // Set divider to 80M/8=10M=0.1us
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0); // Enable PWM peripheral
SysCtlDelay(2); // Insert a few cycles after enabling the peripheral to allow the clock to be fully activated
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // Enable GPIOB peripheral
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); // Enable GPIOB peripheral
SysCtlDelay(2); // Insert a few cycles after enabling the peripheral to allow the clock to be fully activated
// Use alternate function
GPIOPinConfigure(GPIO_PB4_M0PWM2);
GPIOPinConfigure(GPIO_PB5_M0PWM3);
GPIOPinConfigure(GPIO_PD0_M0PWM6);
GPIOPinConfigure(GPIO_PD1_M0PWM7);
// Use pin with PWM peripheral
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_4);//M0PWM2
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_5);//M0PWM3
GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0);//M0PWM6
GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_1);//M0PWM7
// Configure the PWM generator for count down mode with immediate updates to the parameters
PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE
| PWM_GEN_SYNC_MODE);
PWMGenConfigure(PWM0_BASE, PWM_GEN_1, PWM_GEN_MODE
| PWM_GEN_SYNC_MODE);
PWMGenConfigure(PWM0_BASE, PWM_GEN_2, PWM_GEN_MODE
| PWM_GEN_SYNC_MODE);
PWMGenConfigure(PWM0_BASE, PWM_GEN_3, PWM_GEN_MODE
| PWM_GEN_SYNC_MODE);
// The period is set to 100us (10 KHz)
period = MOTOR_PERIOD_MAX_800US;
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, period); // Set the period
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, period);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_2, period);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_3, period);
// Start the timers in generator 0 and 1
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
PWMGenEnable(PWM0_BASE, PWM_GEN_1);
PWMGenEnable(PWM0_BASE, PWM_GEN_2);
PWMGenEnable(PWM0_BASE, PWM_GEN_3);
// Enable the outputs
PWMOutputState(PWM0_BASE,
PWM_OUT_0_BIT | PWM_OUT_1_BIT
| PWM_OUT_2_BIT | PWM_OUT_3_BIT
| PWM_OUT_4_BIT | PWM_OUT_5_BIT
| PWM_OUT_6_BIT | PWM_OUT_7_BIT
, true);
}
2、PWM輸出:
// width1/2/3/4 為四個PWM通道設(shè)置的脈沖寬度(0-1000)
void PWM_Output(uint16_t width1, uint16_t width2, uint16_t width3, uint16_t width4)
{
uint16_t pwm[4]={0};
pwm[0] = width1;
pwm[1] = width2;
pwm[2] = width3;
pwm[3] = width4;
//EPWM1端口M0PWM0、M0PWM1、M0PWM2、M0PWM3
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_2, pwm[0]);//PH0——M0PWM0
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_3, pwm[1]);//PH1——M0PWM1
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_6, pwm[2]);//PH2——M0PWM2
PWMPulseWidthSet(PWM0_BASE,PWM_OUT_7, pwm[3]);//PH3——M0PWM3
}
3、小車運動:
簡單編寫了小車向前/后/左/右運動的函數(shù)。
// 向前,value為脈寬,換算一下相當(dāng)于速度;delay_ms為持續(xù)時間
void Car_Forward(int value,int delay_ms)
{
PWM_Output(0,0,0,0);
PWM_Output(0,value,value,0);
Delay_Ms(delay_ms);
PWM_Output(0,0,0,0);
}
// 向后,value為脈寬,換算一下相當(dāng)于速度;delay_ms為持續(xù)時間
void Car_Backward(int value,int delay_ms)
{
PWM_Output(0,0,0,0);
PWM_Output(value,0,0,value);
Delay_Ms(delay_ms);
PWM_Output(0,0,0,0);
}
// 左轉(zhuǎn),value為脈寬,換算一下相當(dāng)于轉(zhuǎn)速;delay_ms為持續(xù)時間
void Car_TurnLeft(int value,int delay_ms)
{
PWM_Output(0,0,0,0);
PWM_Output(0,value,0,value);
Delay_Ms(delay_ms);
PWM_Output(0,0,0,0);
}
//右轉(zhuǎn),value為脈寬,換算一下相當(dāng)于轉(zhuǎn)速;delay_ms為持續(xù)時間
void Car_TurnRight(int value,int delay_ms)
{
PWM_Output(0,0,0,0);
PWM_Output(value,0,value,0);
Delay_Ms(delay_ms);
PWM_Output(0,0,0,0);
}
4、小車移動到指定街區(qū)的函數(shù):
這一部分就是整活了,近處的街區(qū)還能將就一下,遠(yuǎn)的街區(qū)很容易跑偏。
int WaitTime = 10000; // 小車靜止等待的時間為10s
// 在小車等待的時候,視覺模塊控制舵機(jī)上的激光筆照射模擬火源
void Car_Move(int position) // position為火源id
{
if(car_move_flag == 1)
{
car_move_flag = 0;
switch(position){
case 0: //無火災(zāi),不用動
break;
case 1: // 1號街區(qū)
Car_Forward(500,1000);
Delay_Ms(1000);
Car_TurnLeft(500,1000);
Delay_Ms(WaitTime);
Car_TurnRight(500,1000);
Delay_Ms(1000);
Car_Backward(500,1000);
Delay_Ms(1000);
break;
case 2: // 2號街區(qū)
Car_TurnRight(500,1000);
Delay_Ms(500);
Car_Forward(500,500);
Delay_Ms(500);
Car_TurnLeft(500,500);
Delay_Ms(500);
Car_Forward(500,3000);
Car_TurnLeft(500,1000);
Delay_Ms(500);
Delay_Ms(WaitTime);
Car_TurnRight(500,1000);
Delay_Ms(500);
Car_Backward(500,3000);
Delay_Ms(1000);
break;
case 3: // 3號街區(qū)
Car_TurnRight(500,1000);
Delay_Ms(500);
Car_Forward(500,1000);
Delay_Ms(500);
Car_TurnLeft(500,1000);
Delay_Ms(500);
Car_Forward(500,3000);
Car_TurnRight(500,1000);
Delay_Ms(500);
Delay_Ms(WaitTime);
Car_TurnLeft(500,1000);
Delay_Ms(500);
Car_Backward(500,3000);
Delay_Ms(1000);
break;
case 4: // 最遠(yuǎn)的4號街區(qū),瞎跑
Car_TurnRight(500,2000);
Delay_Ms(500);
Car_Forward(500,3000);
Delay_Ms(500);
Delay_Ms(WaitTime);
Car_Backward(500,3000);
Delay_Ms(1000);
break;
case 5: // 5號街區(qū)
Car_TurnRight(500,2000);
Delay_Ms(500);
Car_Forward(500,3000);
Delay_Ms(500);
Delay_Ms(WaitTime);
Car_Backward(500,3000);
Delay_Ms(1000);
break;
case 6: // 6號街區(qū)
Car_TurnRight(500,1000);
Delay_Ms(500);
Car_Forward(500,1500);
Delay_Ms(500);
Delay_Ms(WaitTime);
Car_Backward(500,1000);
Delay_Ms(1000);
break;
case 7: // 旋轉(zhuǎn)測試
Car_TurnRight(500,6000);
Delay_Ms(1000);
Car_TurnRight(500,5000);
Delay_Ms(1000);
break;
default:
break;
}
}
}
2.5 無人機(jī)程序設(shè)計
由于無人機(jī)程序大部分已經(jīng)編寫的差不多了,直接使用購買無人機(jī)的代碼修改。
無人機(jī)上需要安裝藍(lán)牙、光流、OpenMV或K210、激光筆、LED燈等外設(shè)。文章來源:http://www.zghlxwxcb.cn/news/detail-629901.html
2.5.1 藍(lán)牙串口程序
用來發(fā)送無人機(jī)位置信息和無人機(jī)監(jiān)測到的火源信息,接收小車發(fā)來的起飛解鎖信息。文章來源地址http://www.zghlxwxcb.cn/news/detail-629901.html
到了這里,關(guān)于空地協(xié)同智能消防系統(tǒng)——無人機(jī)、小車協(xié)同的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!