作品簡(jiǎn)介:道路和高速公路是城市的重要基礎(chǔ)設(shè)施,但是交通事故不可避免會(huì)發(fā)生。隨著時(shí)間的增長(zhǎng),道路惡化出現(xiàn)坑洼、裂縫和不平坦的路面,那么道路的安全性和可靠性可能會(huì)受到影響,這樣將會(huì)大大增加交通事故發(fā)生概率,并且造成交通不便,雖然各種因素會(huì)導(dǎo)致道路惡化,但影響最大的因素之一是道路接收的整體交通負(fù)荷,也就是車輛通過(guò)的數(shù)量。本設(shè)計(jì)基于MAX78000通過(guò)在本地部署Ai模型,可以快速準(zhǔn)確的對(duì)過(guò)往車輛進(jìn)行檢測(cè),了解當(dāng)前車輛通過(guò)的數(shù)量。邊緣Ai可以很好保護(hù)數(shù)據(jù)隱私性,只需要將結(jié)果接入到全球網(wǎng)絡(luò)中,就可以實(shí)時(shí)了解城市交通網(wǎng)絡(luò)的負(fù)載情況。
系統(tǒng)框圖:
系統(tǒng)框圖使用得捷Scheme- it在線設(shè)計(jì)工具完成完成框圖繪制。
硬件設(shè)計(jì):
硬件設(shè)計(jì)主要由兩部分組成,基于MAX78000核心板和OV5640-TFT顯示擴(kuò)展板組成,本次主要介紹基于MAX78000核心板的硬件設(shè)計(jì)部分。核心板使用Kicad繪制的四層板。
供電電路:
供電電路采用的是兩個(gè)TI的DC-DC電源芯片TLV62569DBVR,Typec接口提供5V,分別作為芯片3V3供電電路和CNN 1.2V升壓電路。芯片3V3供電電路經(jīng)過(guò)一個(gè)電感L2和電容濾波網(wǎng)絡(luò)來(lái)減少輸入的噪聲并提供穩(wěn)定的電源,R5和R6電阻組成分壓電路,用于控制輸出電壓,并且還帶有一個(gè)LED指示燈和限流電阻R7,用做電源指示燈。CNN 1.2V升壓電路,主要是為CNN加速器供電,提供充足的能量,EN腳由P2_5控制,在不使用CNN加速器的時(shí)候可以關(guān)閉以減少能耗。CNN加速器由64個(gè)并行處理器和512KB基于sram的存儲(chǔ)組成,每個(gè)處理器包括一個(gè)池化單元和一個(gè)具有專用權(quán)重存儲(chǔ)器的卷積引擎。
芯片外圍電路:
芯片外圍電路由多個(gè)去耦電容,濾除電源噪聲,提供穩(wěn)定的供電,保各電壓域的穩(wěn)定性,提供可靠的供電。晶振部分采用的是一顆32.768kHz的無(wú)源晶振,兩個(gè)10pF的負(fù)載電容保證晶振能可靠啟動(dòng)和維持振蕩。
復(fù)位及按鍵電路:
兩個(gè)按鍵電路,一個(gè)復(fù)位電路,通過(guò)一個(gè)SN74LVC1G07DCKR 緩沖器來(lái)控制系統(tǒng)的復(fù)位信號(hào),起到在復(fù)位信號(hào)傳輸中起到穩(wěn)定和驅(qū)動(dòng)的作用,確保復(fù)位信號(hào)的可靠性。一個(gè)用戶按鍵電路由P0_2控制。
效果圖:
物料清單:
軟件設(shè)計(jì):
軟件主體部分采用FreeRTOS,先初始化時(shí)鐘、LED、CNN加速器升壓電路等等,再切換到CNN推理的任務(wù)中,Task_Cnn任務(wù)首先打開(kāi)RGB燈進(jìn)行補(bǔ)光,其中最主要的函數(shù)是App_Cnn_Execute(),用來(lái)使用CNN加速器執(zhí)行推理,清空屏幕(MXC_TFT_ClearScreen())以顯示新結(jié)果。接著,調(diào)用 cnn_start() 啟動(dòng) CNN 推理,然后通過(guò) App_Camera_Cnn_Get() 獲取攝像頭數(shù)據(jù)并傳入 CNN。接下來(lái)設(shè)置系統(tǒng)控制寄存器,確保處理器不會(huì)進(jìn)入深度睡眠,以便 CNN 正常運(yùn)行。 然后,程序進(jìn)入低功耗等待狀態(tài),直到 CNN 完成推理。之后,調(diào)用 get_priors() 生成先驗(yàn)框。
static void Task_Cnn(void *pvParameters)
{
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
MXC_GPIO2->out_clr = MXC_GPIO_PIN_0;
MXC_GPIO2->out_clr = MXC_GPIO_PIN_1;
MXC_GPIO2->out_clr = MXC_GPIO_PIN_2;
while (1)
{
App_Cnn_Execute();
vTaskDelayUntil(&xLastWakeTime, 10);
}
}
目標(biāo)檢測(cè)是計(jì)算機(jī)視覺(jué)領(lǐng)域的核心問(wèn)題之一,其任務(wù)就是找出圖像中所有感興趣的目標(biāo),確定他們的類別和位置。由于各類不同物體有不同的外觀,姿態(tài),以及不同程度的遮擋,加上成像是光照等因素的干擾,目標(biāo)檢測(cè)一直以來(lái)是一個(gè)很有挑戰(zhàn)性的問(wèn)題。SSD、YOLO系列目標(biāo)檢測(cè)需要生成大量的預(yù)測(cè)框在MCU上并不適合部署,為了減少內(nèi)存資源的消耗,所以在此次設(shè)計(jì)中采用的是TinySSD目標(biāo)檢測(cè)模型,主要使用了論文中的Fire4、Fire8、Fire9、Fire10、Conv12-2和Conv13-2層搭建核心模型。
MAX78000的邊緣Ai開(kāi)發(fā)主要分為以下幾個(gè)部分,數(shù)據(jù)集制作、模型搭建以及訓(xùn)練量化生成C代碼
數(shù)據(jù)集制作中使用擴(kuò)展板上的OV5640進(jìn)行拍照,通過(guò)SD卡保存數(shù)據(jù),用labelImg對(duì)于PNG圖片進(jìn)行標(biāo)注,再通過(guò)python腳本將數(shù)據(jù)集按照8:2劃分為訓(xùn)練集和測(cè)試集,并且按照順序排序。
int main(void)
{
int ret = 0;
int slaveAddress;
int id;
g_app_settings.dma_mode = USE_DMA;
g_app_settings.imgres_w = IMAGE_XRES;
g_app_settings.imgres_h = IMAGE_YRES;
g_app_settings.pixel_format = PIXFORMAT_RGB565;
MXC_ICC_Enable(MXC_ICC0);
MXC_SYS_Clock_Select(MXC_SYS_CLOCK_IPO);
SystemCoreClockUpdate();
Bsp_Led_Init();
MXC_GPIO2->out_clr = MXC_GPIO_PIN_0;
MXC_GPIO2->out_clr = MXC_GPIO_PIN_1;
MXC_GPIO2->out_clr = MXC_GPIO_PIN_2;
MXC_DMA_Init();
g_app_settings.dma_channel = MXC_DMA_AcquireChannel();
camera_init(CAMERA_FREQ);
slaveAddress = camera_get_slave_address();
ret = camera_get_manufacture_id(&id);
g_app_settings.imgres_w = 74;
g_app_settings.imgres_h = 74;
while (1) {
if(id_car < 50)
{
cnn_img_data_t img_data = stream_img(g_app_settings.imgres_w, g_app_settings.imgres_h,
g_app_settings.pixel_format,
g_app_settings.dma_channel);
save_stream_sd(img_data, id_c);
id_car ++;
MXC_Delay(50000);
}
}
}
訓(xùn)練量化生成C代碼均在算力云上搭建的環(huán)境中,生成相應(yīng)的C代碼,目標(biāo)檢測(cè)任務(wù)中只需要 cnn.c cnn.h weight.h,分別包含了CNN加速器啟動(dòng)過(guò)程和加載權(quán)重所需要的參數(shù)。在App_Camera_Cnn_Get函數(shù)中獲取相機(jī)圖像,將分別相機(jī)圖像格式輸入給CNN加速器和TFT屏。CNN加速器推理出來(lái)的結(jié)果通過(guò)localize_objects函數(shù),通過(guò)非極大值抑制(NMS)過(guò)濾掉多余的檢測(cè)框,然后根據(jù)剩下的結(jié)果生成物體的邊界框坐標(biāo),并在圖像上繪制檢測(cè)到的物體邊框以及識(shí)別出的類別。
void App_Camera_Cnn_Get(void)
{
uint8_t *frame_buffer;
uint8_t *buffer;
uint32_t imgLen;
uint32_t w, h, x, y;
uint8_t r, g, b;
uint32_t *cnn_mem;
uint32_t *fifo_addr = (volatile uint32_t *) 0x50000008;
camera_start_capture_image();
camera_get_image(&frame_buffer, &imgLen, &w, &h);
for (int y = 0; y < IMAGE_SIZE_Y; y++)
{
for (x = 0; x < IMAGE_SIZE_X; x++)
{
r = *buffer++;
g = *buffer++;
b = *buffer++;
buffer++; // skip msb=0x00
while (((*((volatile uint32_t *) 0x50000004) & 1)) != 0);
*fifo_addr = ((b << 16) | (g << 8) | r) ^ 0x00808080;
color = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
MXC_TFT_WritePixel(x * IMG_SCALE, y * IMG_SCALE, IMG_SCALE, IMG_SCALE, color);
}
}
}
void localize_objects(void)
{
float prior_cxcy[4];
float cxcy[4];
float xy[4];
int class_idx, prior_idx, global_prior_idx;
nms();
for (class_idx = 0; class_idx < (NUM_CLASSES - 2); ++class_idx)
{
for (prior_idx = 0; prior_idx < num_nms_priors[class_idx]; ++prior_idx)
{
if (nms_removed[class_idx][prior_idx] != 1)
{
global_prior_idx = nms_indices[class_idx][prior_idx];
get_cxcy(prior_cxcy, global_prior_idx);
gcxgcy_to_cxcy(cxcy, global_prior_idx, prior_cxcy);
cxcy_to_xy(xy, cxcy);
x1 = 74 * xy[0];
test_2[class_idx] = class_idx + 1;
draw_obj_rect(xy, class_idx, IMAGE_SIZE_X, IMAGE_SIZE_Y, IMG_SCALE);
}
}
}
}
功能展示:
制作花絮:
1:第一次手動(dòng)貼BGA封裝的芯片,雖然能正常亮燈但CNN加速器不正常,把芯片拆了又拆,懷疑這個(gè)那個(gè)的,只能花錢SMT了,SMT加沉金感覺(jué)就是不一樣。
2:之前用來(lái)測(cè)試的TFT小PCB板,弄了半天背光不亮,三極管還發(fā)燙嚴(yán)重,最后才發(fā)現(xiàn)是限流電阻的電阻值搞錯(cuò)了加FPC接口方向反了,還弄壞了一個(gè)TFT屏幕。
總結(jié):最后感謝電源網(wǎng)與得捷電子此次舉辦的大賽,祝越來(lái)越好!