add TFlite_Micro_Demo移植参考指南.md & tflite user guid.md

This commit is contained in:
yangqingsheng
2020-12-17 21:57:09 +08:00
parent ada3f4fdef
commit 753bff22ac
27 changed files with 287 additions and 15 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"C_Cpp.default.configurationProvider": "go2sh.cmake-integration"
}

View File

@@ -18,7 +18,7 @@ int main(void)
{ {
board_init(); board_init();
printf("Welcome to TencentOS tiny\r\n"); printf("Welcome to TencentOS tiny\r\n");
person_detect_init(); person_detect_init();
osKernelInitialize(); // TOS Tiny kernel initialize osKernelInitialize(); // TOS Tiny kernel initialize
osThreadCreate(osThread(application_entry), NULL); // Create TOS Tiny task osThreadCreate(osThread(application_entry), NULL); // Create TOS Tiny task
osKernelStart(); // Start TOS Tiny osKernelStart(); // Start TOS Tiny

View File

@@ -1,6 +1,6 @@
#include "mcu_init.h" #include "mcu_init.h"
uint16_t camBuffer[OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT]; uint16_t camera_buffer[OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT];
uint8_t frame_flag = 0; uint8_t frame_flag = 0;
uint8_t tensor_flag = 0; uint8_t tensor_flag = 0;
@@ -49,7 +49,7 @@ void board_init(void)
OV2640_OutSize_Set(OV2640_PIXEL_WIDTH,OV2640_PIXEL_HEIGHT); OV2640_OutSize_Set(OV2640_PIXEL_WIDTH,OV2640_PIXEL_HEIGHT);
__HAL_DCMI_DISABLE_IT(&hdcmi, DCMI_IT_LINE | DCMI_IT_VSYNC); __HAL_DCMI_DISABLE_IT(&hdcmi, DCMI_IT_LINE | DCMI_IT_VSYNC);
if (HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)camBuffer , (OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT)/2)) if (HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)camera_buffer , (OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT)/2))
{ {
Error_Handler(); Error_Handler();
} }

View File

@@ -0,0 +1,217 @@
# TensorFlow Lite Micro移植参考指南Keil版
作者:
日期:
联系方式:
## 概述
本教程是基于STM32 NUCLEO-L496ZGCortex-M4, 80Mhz开发板在运行TencentOS tiny的基础上使用Tensorflow Lite Micro框架和CMSIS-NN库算子加速在STM32L496ZG上实现了**行人检测模型**的推理。
关于Tensorflow Lite Micro组件的详细介绍可以参考`TencentOS-tiny\components\tflite_micro`目录下的TFlite_Micro_Component_User_Guide.md文档。
本例程中传入神经网络的RGB图像大小为 18kb96*96 * 2byte在STM32L496平台消耗的内存资源经过优化后如下
- SRAM168 Kbyte
- Flash314 Kbyte
理论上满足以b上内存要求的STM32 Cortex-M系列MCU可以参考本指南进行移植。
## 一、移植前的准备
### 1. 准备目标硬件(开发板/传感器/模组)
需要准备如下硬件:
- 开发板NUCLEO-L496ZGMCU为STM32L496ZG
- Camera获取RGB图像本例程使用OV2640摄像头
- LCD显示RGB图像本例程使用2.4寸LCDSPI通信
硬件实物图如下:
<div align=center>
<img src="image/all.jpg" width=50% />
</div>
### 2.准备TencentOS tiny基础keil工程代码
- 首先参考TencentOS tiny基于keil的移植教程进行移植
https://github.com/Tencent/TencentOS-tiny/blob/master/doc/10.Porting_Manual_for_KEIL.md
- 值得注意的是为了方便初始化MCU的外设后续要继续使用STM32CubeMX软件请确保安装了该软件
- 移植成功后,工程可以进行线程任务切换,通过串口打印"hello world"基础keil工程代码准备完毕。
### 3. 获取Tensorflow Lite Micro
有两种方式获取tflite_micro源码
1. 从TencentOS tiny 代码仓库 `components/tflite_micro`目录获取;
2. 从Tensorflow代码仓库获取TFlite_Micro的源码已经开源github下载地址为https://github.com/tensorflow/tensorflow TFlite_Micro的代码位于`tensorflow/tensorflow/lite/micro/`目录下。
如果没有tflite_micro开发经验建议您以**第一种方式**获取tflite_micro源码如果您希望自行获取最新tflite_micro源码请参考`TencentOS-tiny\components\tflite_micro`目录的TFlite_Micro_Component_User_Guide.md文档。
## 二、BSP准备
### 1. 工程目录规划
以下是整个例程的目录规划:
| 一级目录 | 二级目录 | 三级目录 | 说明 |
| :-------: | :--------------------------: | :------: | :----------------------------------------------------------: |
| arch | arm | | TencentOS tiny适配的IP核架构含M核中断、调度、tick相关代码 |
| board | NUCLEO_STM32L496ZG | | 移植目标芯片的工程文件 |
| | | BSP | 板级支持包外设驱动代码在Hardware目录 |
| component | tflite_micro | | tflite_micro源码 |
| examples | tflitemicro_person_detection | | 行人检测demo示例 |
| kernel | core | | TencentOS tiny内核源码 |
| | pm | | TencentOS tiny低功耗模块源码 |
| osal | cmsis_os | | TencentOS tiny提供的cmsis os 适配 |
完成TencentOS tiny基础keil工程准备工作后在这个keil工程的基础上继续添加外设驱动代码。
### 2. LCD驱动
本例程选用一款2.4寸LCD屏幕分辨率为 240*320 SPI 接口通信内部控制芯片为IL9341。
开发者也可以使用其他LCD自行完成LCD的驱动代码移植方便调试摄像头以及查看图像是否正常。
#### 2.1 SPI初始化
进入`TencentOS-tiny\board\NUCLEO_STM32L496ZG\BSP`目录打开TencentOS_tiny.ioc工程使用STM32CubeMX初始化MCU外设。
<div align=center>
<img src="./image/spi init.png" width=100% />
</div>
#### 2.2 添加驱动代码
添加`lcd_2inch4.c``lcd_config.c`,添加头文件`lcd_2inch4.h``lcd_config.h`路径。
### 3. 摄像头驱动
#### 3.1 外设初始化
#### 3.2 添加驱动代码
**在main函数重写DCMI帧中断回调函数**
```C
/* USER CODE BEGIN 4 */
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
if(hdcmi->State == 2 && frame_flag != 1){
frame_flag = 1;
}
}
/* USER CODE END 4 */
```
### 4. LCD显示摄像头图像
经过以上步骤BSP就准备完毕了。
## 三、Tensorflow Lite Micro移植
### 1. tflite_micro源码加入到keil工程
### 2. 解决编译错误
### 3. 测试行人检测模型
### 4. 编写Person_Detection 任务函数
#### 4.1 图像预处理
<div align=center>
<img src="./image/RGB565.jpg" width=50% />
</div>
在本例程中模型要求输入神经网络的图像为灰度图为完成摄像头获取的RGB彩图到模型输入需要的灰度图转换需从输入的RGB565像素格式中解析出R、G、B三通道的值再根据心理学公式计算出单个像素点的灰度具体代码如下
```c
uint8_t rgb565_to_gray(uint16_t bg_color)
{
uint8_t bg_r = 0;
uint8_t bg_g = 0;
uint8_t bg_b = 0;
bg_r = ((bg_color>>11)&0xff)<<3;
bg_g = ((bg_color>>5)&0x3f)<<2;
bg_b = (bg_color&0x1f)<<2;
uint8_t gray = (bg_r*299 + bg_g*587 + bg_b*114 + 500) / 1000;
return gray;
}
void input_convert(uint16_t* camera_buffer , uint8_t* model_buffer)
{
for(int i=0 ; i<OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT ; i++)
{
model_buffer[i] = rgb565_to_gray(camera_buffer[i]);
}
}
```
#### 4.2 行人检测线程任务函数
```c
void task1(void *arg)
{
while (1) {
if(frame_flag == 1){
printf("***person detection task\r\n");
if(HAL_DCMI_Stop(&hdcmi))Error_Handler(); //stop DCMI
input_convert(camera_buffer,model_buffer);//convert input
person_detect(model_buffer); //inference
LCD_2IN4_Display(camera_buffer,OV2640_PIXEL_WIDTH,OV2640_PIXEL_HEIGHT);
//display
frame_flag = 0;
if(HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,\ //restart DCMI
(uint32_t)camera_buffer ,\
(OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT)/2))
Error_Handler();
}
osDelay(50);
}
}
void task2(void *arg)
{
while (1) {
printf("***task2\r\n");
osDelay(50);
}
}
```
## 四、Benchmark

View File

@@ -18,4 +18,6 @@
### 3. 未完成的工作: ### 3. 未完成的工作:
- 变量名、函数名还没有按照TOS的风格完全统一 - 变量名、函数名还没有按照TOS的风格完全统一
- keil移植指南
- tflite_micro用户指南

View File

@@ -247,7 +247,7 @@
<Group> <Group>
<GroupName>Application/User</GroupName> <GroupName>Application/User</GroupName>
<tvExp>1</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel> <cbSel>0</cbSel>
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
@@ -1051,7 +1051,7 @@
<Group> <Group>
<GroupName>hal</GroupName> <GroupName>hal</GroupName>
<tvExp>0</tvExp> <tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel> <cbSel>0</cbSel>
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>

View File

@@ -16,8 +16,8 @@
<TargetCommonOption> <TargetCommonOption>
<Device>STM32L496ZGTx</Device> <Device>STM32L496ZGTx</Device>
<Vendor>STMicroelectronics</Vendor> <Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32L4xx_DFP.2.5.0</PackID> <PackID>Keil.STM32L4xx_DFP.2.4.0</PackID>
<PackURL>https://www.keil.com/pack/</PackURL> <PackURL>http://www.keil.com/pack/</PackURL>
<Cpu>IRAM(0x20000000-0x2004FFFF) IROM(0x8000000-0x80FFFFF) CLOCK(8000000) FPU2 CPUTYPE("Cortex-M4")</Cpu> <Cpu>IRAM(0x20000000-0x2004FFFF) IROM(0x8000000-0x80FFFFF) CLOCK(8000000) FPU2 CPUTYPE("Cortex-M4")</Cpu>
<FlashUtilSpec></FlashUtilSpec> <FlashUtilSpec></FlashUtilSpec>
<StartupFile></StartupFile> <StartupFile></StartupFile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,50 @@
# 1. 概述
## 1.1. TensorFlow Lite Micro
参考 doc文件夹的AT组件的user guide.md
## 1.2. CMSIS-NN
## 1.3.
# 2. TencentOS-tiny的AT框架
## 2.1. 整体架构
## 2.2. 实现原理
## 2.3. TencentOS-tiny AT框架参数配置
## 2.4. AT框架提供的API
# 3. TencentOS-tiny的SAL框架
## 3.1. 什么是SAL框架
## 3.2. SL框架的实现原理
## 3.3. SAL框架提供的网络编程API
# 4. TFlite_Micro组件加入到keil工程
## 4.1. 移植前的准备
## 4.3. 移植SAL框架
## 4.4. 移植通信模组驱动
## 4.5. 测试网络通信
# 5. 制作TFlite_Micro.lib
## Step1.
## Step2.
## Step3.

View File

@@ -1,9 +1,9 @@
#include "cmsis_os.h" #include "cmsis_os.h"
#include "mcu_init.h" #include "mcu_init.h"
extern uint16_t camBuffer[]; extern uint16_t camera_buffer[];
extern uint8_t frame_flag; extern uint8_t frame_flag;
static uint8_t modBuffer[96*96]; static uint8_t model_buffer[96*96];
#define TASK1_STK_SIZE 1024 #define TASK1_STK_SIZE 1024
void task1(void *arg); void task1(void *arg);
@@ -13,7 +13,7 @@ osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
void task2(void *arg); void task2(void *arg);
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE); osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
uint8_t RGB565toGRAY(uint16_t bg_color) uint8_t rgb565_to_gray(uint16_t bg_color)
{ {
uint8_t bg_r = 0; uint8_t bg_r = 0;
uint8_t bg_g = 0; uint8_t bg_g = 0;
@@ -29,7 +29,7 @@ void input_convert(uint16_t* camera_buffer , uint8_t* model_buffer)
{ {
for(int i=0 ; i<OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT ; i++) for(int i=0 ; i<OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT ; i++)
{ {
model_buffer[i] = RGB565toGRAY(camera_buffer[i]); model_buffer[i] = rgb565_to_gray(camera_buffer[i]);
} }
} }
@@ -39,13 +39,13 @@ void task1(void *arg)
if(frame_flag == 1){ if(frame_flag == 1){
if(HAL_DCMI_Stop(&hdcmi))Error_Handler(); //stop DCMI if(HAL_DCMI_Stop(&hdcmi))Error_Handler(); //stop DCMI
input_convert(camBuffer,modBuffer); input_convert(camera_buffer,model_buffer);
person_detect(modBuffer); person_detect(model_buffer);
LCD_2IN4_Display(camBuffer,OV2640_PIXEL_WIDTH,OV2640_PIXEL_HEIGHT); LCD_2IN4_Display(camera_buffer,OV2640_PIXEL_WIDTH,OV2640_PIXEL_HEIGHT);
frame_flag = 0; frame_flag = 0;
if(HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,(uint32_t)camBuffer ,\ if(HAL_DCMI_Start_DMA(&hdcmi,DCMI_MODE_CONTINUOUS,(uint32_t)camera_buffer ,\
(OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT)/2))Error_Handler(); //restart DCMI (OV2640_PIXEL_WIDTH*OV2640_PIXEL_HEIGHT)/2))Error_Handler(); //restart DCMI
} }
osDelay(50); osDelay(50);