[MCU] 【兆易GD32H759I-EVAL】--16.边缘AI目标检测

dirty   2024-6-22 14:17 楼主

      本篇在查阅资源资料情况下,讲述将目标检测模型Yolo-FastestV2部署到GD32H759I-EVAL开发板上,实现AI目标识别。

      GD32 AI ModelZoo是一个用于在GD32设备上快速部署AI模型的开源项目,其在github上发布: https://github.com/HomiKetalys/gd32ai-modelzoo
 

一.了解AI模型与准备
      在部署AI模型方面,GD32H759提供了一颗主频高达600Mhz的MCU,同时提供了64KB的L1 Cache,以及1M的片上RAM。Yolo-FastestV2是一个十分轻量的目标检测模型,其已在github上发布: https://github.com/dog-qiuqiu/Yolo-FastestV2 。对其了解可点这里。在GD32H759上运行一个目标检测模型并展示其检测结果,需要使用GD32H759I-EVAL开发板上的摄像头ov2640以及LCD,故硬件上按前面使用LCD、摄像头接好跳线帽。

 

二.代码准备
1.摄像头程序
(1)摄像头DCI驱动移植官方例程,并做修改。capture_mode将连续捕获模式改为快照捕获模式,以便采集图像可控。dci_config包含DCI的IO口配置、DCI参数配置、DCI DMA配置、使能开启。

/*!
    \brief      configure the DCI to interface with the camera module
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dci_config(void)
{

    dci_parameter_struct dci_struct;
    dma_single_data_parameter_struct dma_single_struct;
    
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOE);
    rcu_periph_clock_enable(RCU_GPIOH);
    rcu_periph_clock_enable(RCU_GPIOG);
    rcu_periph_clock_enable(RCU_DCI);

    /* DCI GPIO AF configuration */
    /* configure DCI_PIXCLK(PE3), DCI_VSYNC(PB7), DCI_HSYNC(PA4) */
    gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_3);
    gpio_af_set(GPIOB, GPIO_AF_13, GPIO_PIN_7);
    gpio_af_set(GPIOA, GPIO_AF_13, GPIO_PIN_4);

    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);

    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);

    /* configure  DCI_D0(PC6), DCI_D1(PH10) DCI_D2(PC8), DCI_D3(PC9), DCI_D4(PE4), DCI_D5(PB6), DCI_D6(PE5), DCI_D7(PE6) */
    gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_6);
    gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_8);
    gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_9);
    gpio_af_set(GPIOB, GPIO_AF_13, GPIO_PIN_6);
    gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_4);
    gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_5);
    gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_6);
    gpio_af_set(GPIOH, GPIO_AF_13, GPIO_PIN_10);

    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);

    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
    gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);

    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);

    ckout0_init();

    /* DCI configuration */
    //    dci_struct.capture_mode = DCI_CAPTURE_MODE_CONTINUOUS;
    dci_struct.capture_mode = DCI_CAPTURE_MODE_SNAPSHOT;
    dci_struct.clock_polarity = DCI_CK_POLARITY_RISING;
    dci_struct.hsync_polarity = DCI_HSYNC_POLARITY_LOW;
    dci_struct.vsync_polarity = DCI_VSYNC_POLARITY_LOW;
    dci_struct.frame_rate = DCI_FRAME_RATE_ALL;
    dci_struct.interface_format = DCI_INTERFACE_FORMAT_8BITS;
    dci_init(&dci_struct);
    nvic_irq_enable(DCI_IRQn, 2, 0);       // 使能DCI NVIC中断
    dci_interrupt_enable(DCI_INT_OVR);     // 使能DCI FIFO溢出中断(非常重要!!!)
    dci_interrupt_flag_clear(DCI_INT_OVR); // 清除溢出中断标志位
    dci_interrupt_enable(DCI_INT_EF);      // 帧结束中断使能,用以绘制一帧图像到LCD上并触发下一次捕获
    dci_interrupt_flag_clear(DCI_INT_EF);  // 清除帧结束中断标志位

    /* DCI DMA configuration */
    rcu_periph_clock_enable(RCU_DMA1);
    rcu_periph_clock_enable(RCU_DMAMUX);

    dma_single_data_para_struct_init(&dma_single_struct);
    dma_deinit(DMA1, DMA_CH7);
    dma_single_struct.request = DMA_REQUEST_DCI;
    dma_single_struct.periph_addr = (uint32_t)DCI_DATA_ADDRESS;
    dma_single_struct.memory0_addr = (uint32_t)0xC0000000;
    dma_single_struct.direction = DMA_PERIPH_TO_MEMORY;
    dma_single_struct.number = IMAGE_HEIGHT * IMAGE_WIDTH / 2;
    dma_single_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_single_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_single_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
    dma_single_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
    dma_single_struct.priority = DMA_PRIORITY_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH7, &dma_single_struct);

    dma_channel_enable(DMA1, DMA_CH7); // 使能DMA传输

    dci_enable();         // 使能DCI
    dci_capture_enable(); // 开启传输
}


(2)用ConfigDCI()来控制图像的采集。

void ConfigDCI()
{
    dci_parameter_struct dci_struct;
    dma_single_data_parameter_struct dma_single_struct;

    // 使能RCC相关时钟
    rcu_periph_clock_enable(RCU_DCI);  // 使能DCI时钟
    rcu_periph_clock_enable(RCU_DMA1); // 使能DMA1时钟

    // DCI配置
    dci_deinit(); // 复位DCI
    /* DCI configuration */
    //    dci_struct.capture_mode = DCI_CAPTURE_MODE_CONTINUOUS;
    dci_struct.capture_mode = DCI_CAPTURE_MODE_SNAPSHOT;
    dci_struct.clock_polarity = DCI_CK_POLARITY_RISING;
    dci_struct.hsync_polarity = DCI_HSYNC_POLARITY_LOW;
    dci_struct.vsync_polarity = DCI_VSYNC_POLARITY_LOW;
    dci_struct.frame_rate = DCI_FRAME_RATE_ALL;
    dci_struct.interface_format = DCI_INTERFACE_FORMAT_8BITS;
    dci_init(&dci_struct);
    nvic_irq_enable(DCI_IRQn, 2, 0);       // 使能DCI NVIC中断
    dci_interrupt_enable(DCI_INT_OVR);     // 使能DCI FIFO溢出中断(非常重要!!!)
    dci_interrupt_flag_clear(DCI_INT_OVR); // 清除溢出中断标志位
    dci_interrupt_enable(DCI_INT_EF);      // 帧结束中断使能,用以绘制一帧图像到LCD上并触发下一次捕获
    dci_interrupt_flag_clear(DCI_INT_EF);  // 清除帧结束中断标志位

    /* DCI DMA configuration */
    rcu_periph_clock_enable(RCU_DMA1);
    rcu_periph_clock_enable(RCU_DMAMUX);

    dma_single_data_para_struct_init(&dma_single_struct);
    dma_deinit(DMA1, DMA_CH7);
    dma_single_struct.request = DMA_REQUEST_DCI;
    dma_single_struct.periph_addr = (uint32_t)DCI_DATA_ADDRESS;
    dma_single_struct.memory0_addr = (uint32_t)0xC0000000;
    dma_single_struct.direction = DMA_PERIPH_TO_MEMORY;
    dma_single_struct.number = IMAGE_HEIGHT * IMAGE_WIDTH / 2;
    dma_single_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_single_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_single_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
    dma_single_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
    dma_single_struct.priority = DMA_PRIORITY_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH7, &dma_single_struct);

    // dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI1);       //绑定DMA通道与相应外设
    // dma_flow_controller_config(DMA1, DMA_CH7, DMA_FLOW_CONTROLLER_DMA);  //DMA作为传输控制器
    dma_channel_enable(DMA1, DMA_CH7); // 使能DMA传输

    dci_enable();         // 使能DCI
    dci_capture_enable(); // 开启传输
}

 

这里配合模型参数,将摄像头像素长宽定义为256*256,并在如上配置中使用到。

#define IMAGE_WIDTH 256
#define IMAGE_HEIGHT 256

 

2.LCD驱动

      LCD驱动初始化与官方例程基本一致。配置屏宽高 480*272。


/*********************************************************************************************************
 * 函数名称:InitLCD
 * 函数功能:初始化LCD模块
 * 输入参数:void
 * 输出参数:void
 * 返 回 值:void
 * 注    意:
 *********************************************************************************************************/
void InitLCD(void)
{
    u32 i;

    // TLI初始化结构体
    tli_parameter_struct tli_init_struct;

    // 初始化LCD设备结构体(默认横屏,当前层为背景层)
    g_structTLILCDDev.pWidth = LCD_PIXEL_WIDTH;
    g_structTLILCDDev.pHeight = LCD_PIXEL_HEIGHT;
    g_structTLILCDDev.dir = LCD_SCREEN_HORIZONTAL;
    g_structTLILCDDev.currentLayer = LCD_LAYER_BACKGROUND;
    g_structTLILCDDev.frameBuf = s_arrBackgroundFrame;
    g_structTLILCDDev.backFrameAddr = BACK_FRAME_START_ADDR;
    g_structTLILCDDev.foreFrameAddr = FORE_FRAME_START_ADDR;
    g_structTLILCDDev.pixelSize = 2;
    for (i = 0; i < LCD_LAYER_MAX; i++)
    {
        g_structTLILCDDev.width[i] = g_structTLILCDDev.pWidth;
        g_structTLILCDDev.height[i] = g_structTLILCDDev.pHeight;
    }

    // 使能RCU时钟
    rcu_periph_clock_enable(RCU_TLI);
    tli_gpio_config();

    // 复位TLI
    tli_deinit();
    tli_struct_para_init(&tli_init_struct);

    // 配置TLI时钟(时钟频率为1MHz * 200 / 5 / 4 = 10MHz,数据手册给的典型值是30MHz,但是10MHz刷新率下更稳定)
    //  if(ERROR == rcu_pllsai_config(240, 4, 4)){while(1);}
    //  rcu_tli_clock_div_config(RCU_PLLSAIR_DIV2);
    //  // if(ERROR == rcu_pllsai_config(200, 4, 5)){while(1);}
    //  // rcu_tli_clock_div_config(RCU_PLLSAIR_DIV4);
    //  // if(ERROR == rcu_pllsai_config(50, 4, 5)){while(1);}
    //  // rcu_tli_clock_div_config(RCU_PLLSAIR_DIV4);
    //  rcu_osci_on(RCU_PLLSAI_CK);
    //  if(ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK)){while(1);}
    rcu_pll_input_output_clock_range_config(IDX_PLL2, RCU_PLL2RNG_1M_2M, RCU_PLL2VCO_150M_420M);
    if (ERROR == rcu_pll2_config(25, 188, 3, 3, 3))
    {
        while (1)
        {
        }
    }
    rcu_pll_clock_output_enable(RCU_PLL2R);
    rcu_tli_clock_div_config(RCU_PLL2R_DIV8);
    rcu_osci_on(RCU_PLL2_CK);

    if (ERROR == rcu_osci_stab_wait(RCU_PLL2_CK))
    {
        while (1)
        {
        }
    }

    // TLI初始化
    // 信号极性配置
    tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;     // 水平同步脉冲低电平有效
    tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;     // 垂直同步脉冲低电平有效
    tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;       // 数据使能低电平有效
    tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI; // 像素时钟是TLI时钟

    // 显示时序配置
    tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;                                                                  // 水平同步脉冲宽度
    tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;                                                                    // 垂直同步脉冲宽度
    tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;                                        // 水平后沿加同步脉冲的宽度
    tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;                                            // 垂直后沿加同步脉冲的宽度
    tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1;                         // 水平有效宽度加后沿像素和水平同步像素宽度
    tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1;                            // 垂直有效宽度加后沿像素和垂直同步像素宽度
    tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1; // 显示器的水平总宽度
    tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1;      // 显示器的垂直总宽度

    // 配置BG层颜色(黑色)
    tli_init_struct.backcolor_red = 0x00;
    tli_init_struct.backcolor_green = 0x00;
    tli_init_struct.backcolor_blue = 0x00;

    // 根据参数配置TLI
    tli_init(&tli_init_struct);

    // 初始化背景层和前景层
    LCDLayerWindowSet(LCD_LAYER_BACKGROUND, 0, 0, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);
    LCDLayerWindowSet(LCD_LAYER_FOREGROUND, 0, 0, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);

    // 使能背景层
    LCDLayerEnable(LCD_LAYER_BACKGROUND);

    // 禁用前景层
    LCDLayerDisable(LCD_LAYER_FOREGROUND);

    // 设置背景层透明度
    LCDTransparencySet(LCD_LAYER_BACKGROUND, 0xFF);

    // 设置前景层透明度
    LCDTransparencySet(LCD_LAYER_FOREGROUND, 0xFF);

    // 开启抖动
    tli_dither_config(TLI_DITHER_ENABLE);

    // //关闭抖动
    // tli_dither_config(TLI_DITHER_DISABLE);

    // 立即更新TLI寄存器
    tli_reload_config(TLI_REQUEST_RELOAD_EN);

    // TLI外设使能
    tli_enable();

    // 切换到背景层
    LCDLayerSwitch(LCD_LAYER_BACKGROUND);

    // 横屏显示
    LCDDisplayDir(LCD_SCREEN_HORIZONTAL);

    // 清屏
    LCDClear(LCD_COLOR_BLACK);
}

      这里使用竖屏显示。LCD显示方向函数如下:

/*********************************************************************************************************
 * 函数名称: LCDDisplayDir
 * 函数功能: 设置LCD显示方向
 * 输入参数: dir:显示方向(LCD_SCREEN_HORIZONTAL,LCD_SCREEN_VERTICAL)
 * 输出参数: void
 * 返 回 值: void
 * 注    意:
 *********************************************************************************************************/
void LCDDisplayDir(EnumTLILCDDir dir)
{
    u32 swap;

    // 交换宽度和高度
    if (g_structTLILCDDev.dir != dir)
    {
        // 保存显示方向
        g_structTLILCDDev.dir = dir;

        // 交换背景层宽度和高度
        swap = g_structTLILCDDev.width[LCD_LAYER_BACKGROUND];
        g_structTLILCDDev.width[LCD_LAYER_BACKGROUND] = g_structTLILCDDev.height[LCD_LAYER_BACKGROUND];
        g_structTLILCDDev.height[LCD_LAYER_BACKGROUND] = swap;

        // 交换前景层宽度和高度
        swap = g_structTLILCDDev.width[LCD_LAYER_FOREGROUND];
        g_structTLILCDDev.width[LCD_LAYER_FOREGROUND] = g_structTLILCDDev.height[LCD_LAYER_FOREGROUND];
        g_structTLILCDDev.height[LCD_LAYER_FOREGROUND] = swap;
    }
}

3.在gd32h7xx_it.c实现了像素转换映射片外RAM地址LCD显示
4.模型Yolo-FastestV2在GD32H759I上进行部署。

      进入对应模型网址 https://github.com/HomiKetalys/gd32ai-modelzoo ,克隆获取资源如下:

1_AI模型gd32ai-modelzoo.jpg
图1:gd32ai-modelzoo 获取

      GD32AI-ModelZoo提供了该模型的训练,导出,以及近乎一键的部署。在COCO80上训练的模型中,提供了Yolo-FastestV2模型在COCO2017上训练的检测80种目标的模型。提供了两种分辨率192*192 和256*256,我们这里使用256*256.

2_AI模型移植.jpg
图2:模型移植

      工程如下:

3_AI模型工程部署.jpg
图3:AI模型工程部署

5.边缘AI目标检测实现

      这里采用帧获取轮询检测方式。获取一帧图片后,给到AI_Run函数处理,返回结果,包含识别物个数及种类。

4_AI目标识别.jpg
图4:AI目标识别

      在一次检测结果出来后,将结果在图中框出,并显示探测到的个数与识别物名称。detect_once函数如下。在检测完显示后开始下一帧传输。

//检测一次
void detect_once(void)
{
    while(!((1 == get_frame_flag())));
    u32 i,x, y,t0,t1,t2;
    char str[128];
    u16 *frame=(u16 *) frame_addr;
    
    time_start();
    
    t0=get_time_stamp();
    AI_Run((u8 *)frame);
    t1=get_time_stamp();
    t0=t1-t0;


    //将结果显示在屏幕
    t1=get_time_stamp();
    for(y = 24; y <= LCD_Y-1; y++)
    {
        for(x = 0; x <= LCD_X-1; x++)
        {
            if((x>=SHOW_X0)&&(x<=SHOW_X1)&&(y>=SHOW_Y0)&&(y<=SHOW_Y1))
                LCDDrawPoint(x, y, frame[IMAGE_HEIGHT*IMAGE_WIDTH-1-(y-SHOW_Y0)*IMAGE_WIDTH-(x-SHOW_X0)]);
            else
                LCDDrawPoint(x, y, 0xffff);
        }
    }
#ifndef TEST_TIME
    LCDColorFillPixel(0,0,272,24*7,LCD_COLOR_YELLOW);
	sprintf(str,"detect object_num:%d",object_num);
	LCDShowString(0,24*7,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_MAGENTA,LCD_COLOR_CYAN,str);
    for(i=0;i<object_num;i++)
    {
        LCDShowString(SHOW_X0+(u32)results[i].bbox.x_min,SHOW_Y0+(u32)results[i].bbox.y_min-16,480-1,16,LCD_FONT_16,LCD_TEXT_TRANS,0x07f0,0xffff,(char *) activities[results[i].cls_index]);
        LCDDrawRectangle(SHOW_X0+(u32)results[i].bbox.x_min,SHOW_Y0+(u32)results[i].bbox.y_min,SHOW_X0+(u32)results[i].bbox.x_max,SHOW_Y0+(u32)results[i].bbox.y_max,0xfff0);
		sprintf(str,"%s  ",(char *) activities[results[i].cls_index]);
	}

    if(object_num>0)
    {
        LCDShowString(0,24*8,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_RED,LCD_COLOR_CYAN,str);
    }
    else
    {
        sprintf(str,"none");
	    LCDShowString(0,24*8,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_BLUE,LCD_COLOR_CYAN,str);

    }
    
#endif
    t2=get_time_stamp();
    t1=t2-t1;
    
    u32 sys=rcu_clock_freq_get(CK_SYS)/1000000;
    u32 ahb=rcu_clock_freq_get(CK_AHB)/1000000;
    u32 apb1=rcu_clock_freq_get(CK_APB1)/1000000;
    u32 apb2=rcu_clock_freq_get(CK_APB2)/1000000;
 
	
    LCDShowString(0,0,LCD_X-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLUE,LCD_COLOR_YELLOW,"results:");
    sprintf(str,"infer_time:%dms",t0);
    LCDShowString(0,24*1,24*20-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
    sprintf(str,"display_time:%dms",t1);
    LCDShowString(0,24*2,24*20-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
    sprintf(str,"SYS:%dMHz",sys);
    LCDShowString(0,24*3,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
    sprintf(str,"AHB:%dMHz",ahb);
    LCDShowString(0,24*4,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
    sprintf(str,"APB1:%dMHz",apb1);
    LCDShowString(0,24*5,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
    sprintf(str,"APB2:%dMHz",apb2);
    LCDShowString(0,24*6,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);

	
    
    //开启下一帧传输
    clear_frame_flag();
    ConfigDCI();
}

6.main函数如下

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
    ov2640_id_struct ov2640id;

    /* enable the CPU cache */
    cache_enable();
    
    systick_config();

    usart_config();

    /* SDRAM initialization */
    exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
    delay_1ms(1000);

    /* camera initialization */
    dci_ov2640_init();
    dci_ov2640_id_read(&ov2640id);


    /* LCD configure and TLI enable */
    InitLCD();
    LCDDisplayDir(LCD_SCREEN_VERTICAL);
    /* Timer Init */
    timer_config();
    /* AI Init */
    AI_Init(IMAGE_WIDTH,IMAGE_HEIGHT,0);
    LCDClear(0xffff);
    
    

    void detect_once();
    while(1)
    {
        detect_once();
    }
}

 

三.测验

1.编译烧录后,在没有80种识别物摄像头下,可以看到LCD屏显示如下。这里分为256*256摄像头显示区,和上方黄色背景时间间隔、时钟频率展示区和中间粉色、蓝色/红色  两行检测数量、目标的展示区。

5_边缘AI目标检测显示界面.jpg
图6:边缘AI目标识别界面

 

2.取一张照片如下图7,用开发板摄像头对准,可检测到结果如下图8

人车狗.jpg
图7:检测图
7_检测测试.jpg
图8:边缘AI目标检测结果

 

      至此,借用网络资源加以理解运用,实现了兆易GD32H759I边缘AI目标识别功能,评估了其具备AI的能力。

 

附参考源及代码工程:

【1】http://www.360doc.com/content/24/0416/15/70238708_1120553573.shtml

【2】https://mp.weixin.qq.com/s/mi6TFCmWUw9HQ8TMb9djXA

本帖最后由 dirty 于 2024-6-22 14:33 编辑

回复评论 (8)

600MHz,图片视频频率能达到多少帧?

点赞  2024-6-23 12:04

能跑AI这开发板性能不错呀。这个做人脸识别,效果如何?

点赞  2024-6-24 07:35

大部分还可以,存在少数情况下误识别

点赞  2024-6-24 09:11
wangerxian 发表于 2024-6-23 12:04 600MHz,图片视频频率能达到多少帧?

帧率这应用没直接一直刷帧测,因做AI,获取一帧后AI处理,用时在360ms左右,然后显示结果用时在30ms左右,依此循环。

本帖最后由 dirty 于 2024-6-24 23:32 编辑
点赞  2024-6-24 09:27
引用: dirty 发表于 2024-6-24 09:27 帧率这应用没直接一直刷帧测,因做AI,获取一帧后AI护理,用时在360ms左右,然后显示结果用时在30ms左右, ...

那600MHz处理图像还是很吃力的。

点赞  2024-6-24 15:20
引用: wangerxian 发表于 2024-6-24 15:20 那600MHz处理图像还是很吃力的。

毕竟不是带NPU的,上的也是轻量模型。这芯片能做边缘AI方面的,一般场景还是够用,相比较其他同类芯片而言,还是很不错了

点赞  2024-6-24 23:35
引用: dirty 发表于 2024-6-24 23:35 毕竟不是带NPU的,上的也是轻量模型。这芯片能做边缘AI方面的,一般场景还是够用,相比较其他同类芯片而 ...

嗯嗯,能体验一下确实也不错。

点赞  2024-6-25 09:46

厉害~~~~~~~~~~

点赞  2024-6-25 20:48
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复