当前位置: 首页 > ops >正文

基于亚博K210开发板——神经网络加速器

开发板

亚博K210开发板
在这里插入图片描述

实验目的

本次测试主要学习 K210 芯片中的神经网络加速器 KPU 的功能。

实验准备

实验元件

K210 芯片中的神经网络加速器 KPU

元件特性

KPU 是通用神经网络处理器,内置卷积、批归一化、激活、池化运算单元,可以对人脸或物体进行实时检测,具体特性如下:

  • 支持主流训练框架按照特定限制规则训练出来的定点化模型
  • 对网络层数无直接限制,支持每层卷积神经网络参数单独配置,包括输入输出通道数目、输入输出行宽列高
  • 支持两种卷积内核 1x1 和 3x3
  • 支持任意形式的激活函数
  • 实时工作时最大支持神经网络参数大小为 5.5MiB 到 5.9MiB
  • 非实时工作时最大支持网络参数大小为(Flash 容量-软件体积)
    在这里插入图片描述
    在这里插入图片描述

SDK 中对应 API 功能

以头文件 kpu.h为例

• kpu_task_init (0.6.0 以后不再支持,请使用 kpu_single_task_init):初始化 kpu 任务句柄,该函数具体实现在 model compiler 生成的 gencode_output.c 中。
• kpu_run (0.6.0 以后不再支持,请使用 kpu_start):启动 KPU,进行 AI 运算。
• kpu_get_output_buf (0.6.0 以后不再支持):获取 KPU 输出结果的缓存。
• kpu_release_output_buf (0.6.0 以后不再支持):释放 KPU 输出结果缓存。
• kpu_start:启动 KPU,进行 AI 运算。
• kpu_single_task_init:初始化 kpu 任务句柄。
• kpu_single_task_deinit:注销 kpu 任务。
• kpu_model_load_from_buffer:解析 kmodel 并初始化 kpu 句柄。
• kpu_load_kmodel:加载 kmodel,需要与 nncase 配合使用。
• kpu_model_free:释放 kpu 资源。
• kpu_get_output:获取 KPU 最终处理的结果。
• kpu_run_kmodel:运行 kmodel。

实验原理

KPU 可以接收图像的数据,然后经过神经网络卷积计算,返回计算后的数据,LCD 显示输出

实验过程

1.首先是 LCD、摄像头和按键的硬件引脚和软件 GPIO 映射关系。

// 硬件IO口,与原理图对应
#define PIN_LCD_CS             (36)
#define PIN_LCD_RST            (37)
#define PIN_LCD_RS             (38)
#define PIN_LCD_WR             (39)// camera
#define PIN_DVP_PCLK           (47)
#define PIN_DVP_XCLK           (46)
#define PIN_DVP_HSYNC          (45)
#define PIN_DVP_PWDN           (44)
#define PIN_DVP_VSYNC          (43)
#define PIN_DVP_RST            (42)
#define PIN_DVP_SCL            (41)
#define PIN_DVP_SDA            (40)#define PIN_KEYPAD_MIDDLE      (2)/*****************************SOFTWARE-GPIO********************************/
// 软件GPIO口,与程序对应
#define LCD_RST_GPIONUM        (0)
#define LCD_RS_GPIONUM         (1)#define KEYPAD_MIDDLE_GPIONUM  (2)/*****************************FUNC-GPIO************************************/
// GPIO口的功能,绑定到硬件IO口
#define FUNC_LCD_CS             (FUNC_SPI0_SS3)
#define FUNC_LCD_RST            (FUNC_GPIOHS0 + LCD_RST_GPIONUM)
#define FUNC_LCD_RS             (FUNC_GPIOHS0 + LCD_RS_GPIONUM)
#define FUNC_LCD_WR             (FUNC_SPI0_SCLK)#define FUNC_KEYPAD_MIDDLE      (FUNC_GPIOHS0 + KEYPAD_MIDDLE_GPIONUM)
void hardware_init(void)
{/* 按键 */fpioa_set_function(PIN_KEYPAD_MIDDLE, FUNC_KEYPAD_MIDDLE);/* LCD */fpioa_set_function(PIN_LCD_CS,  FUNC_LCD_CS);fpioa_set_function(PIN_LCD_RST, FUNC_LCD_RST);fpioa_set_function(PIN_LCD_RS,  FUNC_LCD_RS);fpioa_set_function(PIN_LCD_WR,  FUNC_LCD_WR);// DVP camerafpioa_set_function(PIN_DVP_RST,   FUNC_CMOS_RST);fpioa_set_function(PIN_DVP_PWDN,  FUNC_CMOS_PWDN);fpioa_set_function(PIN_DVP_XCLK,  FUNC_CMOS_XCLK);fpioa_set_function(PIN_DVP_VSYNC, FUNC_CMOS_VSYNC);fpioa_set_function(PIN_DVP_HSYNC, FUNC_CMOS_HREF);fpioa_set_function(PIN_DVP_PCLK,  FUNC_CMOS_PCLK);fpioa_set_function(PIN_DVP_SCL,   FUNC_SCCB_SCLK);fpioa_set_function(PIN_DVP_SDA,   FUNC_SCCB_SDA);// 使能SPI0和DVPsysctl_set_spi0_dvp_data(1);
}

2.设置摄像头和显示器接口需要的电平电压为 1.8V。

static void io_set_power(void)
{/* Set dvp and spi pin to 1.8V */sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18);
}
  1. 设置系统时钟,初始化系统中断,并使能系统全局中断。
 /* 设置系统时钟和DVP时钟 */sysctl_pll_set_freq(SYSCTL_PLL0, 800000000UL);sysctl_pll_set_freq(SYSCTL_PLL1, 300000000UL);sysctl_pll_set_freq(SYSCTL_PLL2, 45158400UL);uarths_init();/* 系统中断初始化 */plic_init();/* 使能系统全局中断 */sysctl_enable_irq();
  1. 初始化显示屏,并显示图片一秒。
/* 初始化显示屏,并显示一秒图片 */printf("LCD init\r\n");lcd_init();lcd_draw_picture_half(0, 0, 320, 240, gImage_logo);sleep(1);
  1. 初始化摄像头。
/* ov摄像头初始化 */int OV_type;OV_type=OVxxxx_read_id();/* 初始化摄像头 */if(OV_type == OV_9655){ov9655_init();}   else if(OV_type == OV_2640) {ov2640_init();}else {return 0;//打不开摄像头,结束}
  1. 初始化按键并且设置按键中断回调。
/* 按键中断回调 */
int key_irq_cb(void *ctx)
{key_flag = 1;key_state = gpiohs_get_pin(KEYPAD_MIDDLE_GPIONUM);return 0;
}/* 初始化按键 */
void init_key(void)
{// 设置按键的GPIO模式为上拉输入gpiohs_set_drive_mode(KEYPAD_MIDDLE_GPIONUM, GPIO_DM_INPUT_PULL_UP);// 设置按键的GPIO电平触发模式为上升沿和下降沿gpiohs_set_pin_edge(KEYPAD_MIDDLE_GPIONUM, GPIO_PE_BOTH);// 设置按键GPIO口的中断回调gpiohs_irq_register(KEYPAD_MIDDLE_GPIONUM, 1, key_irq_cb, NULL);
}
  1. KPU 初始化并打印剩余内存。
/* kpu初始化 */kpu_task_t task;conv_init(&task, CONV_3_3, conv_data);printf("KPU TASK INIT, FREE MEM: %ld\r\n", get_free_heap_size());printf("Please press the keypad to switch mode\r\n");
  1. 等待摄像头传输数据完成,kpu 开始运算摄像头采集的数据,并输出到g_ai_buf_out 中。
while (g_dvp_finish_flag == 0);/* 开始运算 */conv_run(&task, g_ai_buf_in, g_ai_buf_out, kpu_done);
/* KPU完成 */
static int kpu_done(void *ctx)
{g_ai_done_flag = 1;return 0;
}
  1. 等待 KPU 计算完成,就把数据转化成 RGB565 格式,因为显示器不支持RGB888 格式。
while (!g_ai_done_flag);g_ai_done_flag = 0;g_dvp_finish_flag = 0;/* 转化成LCD支持的RGB565格式 */rgb888_to_565(g_ai_buf_out, g_ai_buf_out + 320 * 240, g_ai_buf_out + 320 * 240 * 2, (uint16_t *)g_display_buf, 320 * 240);
void rgb888_to_565(uint8_t *src_r, uint8_t *src_g, uint8_t *src_b, uint16_t *dst, uint32_t len)
{uint32_t i;for (i = 0; i < len; i += 2){dst[i] = (((uint16_t)(src_r[i + 1] >> 3)) << 11) + (((uint16_t)src_g[i + 1] >> 2) << 5) + (((uint16_t)src_b[i + 1]) >> 3);dst[i + 1] = (((uint16_t)(src_r[i] >> 3)) << 11) + (((uint16_t)src_g[i] >> 2) << 5) + (((uint16_t)src_b[i]) >> 3);}
}
  1. 此时我们需要在左上角写上每个对应的模式,默认是原始 origin。
void draw_text(void)
{char string_buf[8 * 16 * 2 * 16]; //16个字符char title[20];switch (demo_index){case 0:sprintf(title, " origin ");lcd_ram_draw_string(title, (uint32_t *)string_buf, BLUE, BLACK);lcd_ram_cpyimg((char *)g_display_buf, 320, string_buf, strlen(title) * 8, 16, 0, 0);break;case 1:sprintf(title, "  edge  ");lcd_ram_draw_string(title, (uint32_t *)string_buf, BLUE, BLACK);lcd_ram_cpyimg((char *)g_display_buf, 320, string_buf, strlen(title) * 8, 16, 0, 0);break;case 2:sprintf(title, " sharp  ");lcd_ram_draw_string(title, (uint32_t *)string_buf, BLUE, BLACK);lcd_ram_cpyimg((char *)g_display_buf, 320, string_buf, strlen(title) * 8, 16, 0, 0);break;case 3:sprintf(title, "relievos");lcd_ram_draw_string(title, (uint32_t *)string_buf, BLUE, BLACK);lcd_ram_cpyimg((char *)g_display_buf, 320, string_buf, strlen(title) * 8, 16, 0, 0);break;default:break;}// sprintf(title, "% 2d % 2d % 2d", (int)conv_data[3], (int)conv_data[4], (int)conv_data[5]);// lcd_ram_draw_string(title, (uint32_t *)string_buf, BLUE, BLACK);// lcd_ram_cpyimg((char *)g_display_buf, 320, string_buf, strlen(title) * 8, 16, 0, 16);}
  1. 最后是把数据显示到 LCD 上。
/* 左上角写字母提示是哪个模式 */draw_text();/* 显示图像 */lcd_draw_picture(0, 0, 320, 240, g_display_buf);
  1. 那么怎么切换显示的模式呢?那就是通过按键的中断修改按键的状态来切换模式的。
if (key_flag) //使用按键选择的卷积核{if (key_state == 0) //按下{msleep(20); //延迟去抖key_flag = 0;demo_index = (demo_index + 1) % 4;memcpy((void *)conv_data, (void *)(conv_data_demo[demo_index]), 3 * 3 * 3 * 3 * sizeof(float));conv_init(&task, CONV_3_3, conv_data);}else //弹起{msleep(20); //延迟去抖key_flag = 0;}}
  1. 编译调试,烧录运行
    进入自己项目 build目录,运行以下命令编译。
cmake .. -DPROJ=watchdog -G "MinGW Makefiles"
make

实验现象

烧录固件完成后,系统会自动弹出一个终端窗口,并且打印一些初始化的信息,此时我们看显示器的已经显示了摄像头当前采集的画面,而且左上角还有一个‘origin’的单词,当我们按下 keypad 中间的键时,模式切换,LCD 显示的画面会变化,除了原始画面,还有其他三种模式可以显示,每按一次 keypad 都可以切换一次模式。
在这里插入图片描述

实验总结

  • KPU 是可以处理图像,让其产生不一样的视觉效果。
  • KPU 与 LCD 的数据格式是不一样的,需要转化以下才可以正常显示。
http://www.xdnf.cn/news/8109.html

相关文章:

  • MacBookPro上macOS安装第三方应用报错解决方案:遇到:“无法打开“XXX”,因为Apple无法检查其是否包含恶意软件 问题如何解决
  • c#基础04(类型转换)
  • Qt网络编程
  • 复合增长率10.1%!2025 小型电动 VTOL 无人机市场报告深度解读
  • Python包管理新篇章!解析uv工具的高效解决方案
  • 树 Part 8
  • 分贝计如何帮助改善睡眠环境
  • 【推理加速】TensorRT C++ 部署YOLO11全系模型
  • MySQL 单表与多表操作详解
  • Ubuntu Desktop 24.04 常用软件安装步骤
  • Ubuntu 新建用户
  • 体育直播网站如何实现实时数据
  • 卷积神经网络基础(十一)
  • 记录:RK3588无法显示开机logo问题
  • React 第四十六节 Router中useInRouterContext的使用详细介绍及注意事项
  • docker-安装部署于macOS11
  • windows安装clamav
  • 多模态大语言模型arxiv论文略读(八十九)
  • LeetCode 404.左叶子之和的递归求解:终止条件与递归逻辑的深度剖析
  • C++学习:六个月从基础到就业——多线程编程:并发容器与无锁编程
  • Intel oneAPI对OpenCL 的支持
  • 什么是特征工程?
  • Java异常分类与Spring事务回滚机制深度剖析
  • 电子电路:为什么会产生电流超前或者滞后于电压的情况?
  • 实战演练:天猫商品详情页动态数据 API 接口开发与调用
  • Nginx 部署前端项目dist文件到局域网然后ngrok部署到公网
  • 如何使用AI为WordPress生成图片
  • 通过Python 在Excel工作表中轻松插入行、列
  • 编译xiaozhi-esp32-server-java遇到的报错
  • 痉挛性斜颈护理指南:多维度守护颈部健康