基于XC7Z100+OV5640(DSP接口)YOLO人脸识别前向推理过程(部分3)
基于ZYNQ的摄像头显示系统
- 本文介绍了如何使用ZYNQ开发板、OV5640摄像头和HDMI显示器搭建一个摄像头显示系统
- 本文的内容主要分为以下几个部分:
- 硬件介绍
- Vivado工程创建
- Vitis工程创建
- 实验结果展示
硬件介绍
- ZYNQ开发板
- 使用的是ZINC 7100芯片,具有双核ARM Cortex-A9处理器(PS)和可编程逻辑(PL)
- 使用的是HDMI接口作为显示输出,支持720P分辨率
- 使用的是DDR3内存,用于存储摄像头采集的图像数据
- OV5640摄像头
- 使用的是DVP接口作为图像输入,支持多种分辨率,本实验中使用720P
- 使用的是SCCB接口作为配置接口,通过PS端的EMIO连接
- 使用的是两个外部时钟源,一个是50MHz,一个是24MHz,用于驱动摄像头工作
Vivado工程创建
-
在Vivado中创建一个新的工程,选择ZYNQ芯片型号为XC7Z100FFG900-2
-
添加ZYNQ IP核,并对其进行配置,主要包括以下几个方面:
- PS端配置
- 勾选HP接口和DDR3接口,用于连接VDMA IP核
- 勾选EMIO接口,用于连接SCCB IP核
- 勾选FCLK_CLK0,设置为200MHz,用于提供时钟给HDMI接口
- PL端配置
- 设置时钟输入为50MHz,来自PR端的晶振
- 设置DDR3型号为MT41J128M16HA-125,位宽为16位
- PS端配置
-
添加摄像头OV5640配置模块(OV5640_config),并连接 DVP 接口和时钟信号,用于通过SCCB接口对摄像头进行初始化和设置
-
添加摄像头数据转换Video In to AXI4-Stream IP模块(video_in_to_axis),用于将DVP接口的数据转换为AXI Stream接口的数据
-
添加VDMA IP核,并对其进行配置
添加 VDMA IP,并配置读写通道,将 AXI Stream 接口转换为 AXI Full 接口,并通过 AXI Interconnect 连接到 ZYNQ IP
- 主要包括以下几个方面:
- General配置
- 设置Number of Channels为1,表示只使用一帧缓冲区
- 设置Stream Data Width为16,表示每个像素占用16位
- 设置Memory Map Data Width为32,表示与DDR3的数据位宽一致
- Write Channel配置
- 设置Fixed Frame Store Addresses为true,表示使用固定的内存地址作为帧缓冲区
- 设置Base Address为0x10000000,表示帧缓冲区的起始地址
- 设置Horizontal Size为1280*2,表示一行的字节数(注意要乘以2,因为每个像素占用2个字节)
- 设置Vertical Size为720,表示一帧的行数
- 设置Stride为1280*2,表示两行之间的字节数(注意要乘以2,因为每个像素占用2个字节)
- Read Channel配置
- 设置Fixed Frame Store Addresses为true,表示使用固定的内存地址作为帧缓冲区
- 设置Base Address为0x10000000,表示帧缓冲区的起始地址(与写通道一致)
- 设置Horizontal Size为1280*2,表示一行的字节数(注意要乘以2,因为每个像素占用2个字节)
- 设置Vertical Size为720,表示一帧的行数
- 设置Stride为1280*2,表示两行之间的字节数(注意要乘以2,因为每个像素占用2个字节)
-
添加视频时序控制模块(VTC),用于生成720P的视频时序信号给HDMI接口
添加 VTC IP,并配置为 720P 分辨率,生成视频时序信号
-
添加视频数据转换模块(axis_to_video_out),用于将AXI Stream接口的数据转换为DVP接口的数据(类似于VGA接口)
添加 AXI4-Stream to Video Out IP,将 AXI Stream 接口转换为 DVP 接口
-
添加HDMI输出模块(hdmi_out),用于将DVP接口的数据转换为HDMI接口的数据,并输出到显示器上
添加 HDMI Top 和 HDMI Out 模块,将 DVP 接口转换为 HDMI 接口,并连接到板子上的 HDMI 显示器
-
添加颜色转换模块(rgb565_to_rgb888),用于将RGB565格式的数据转换为RGB888格式的数据(因为HDMI输出需要RGB888格式)
-
连接各个模块之间的信号线,并设置相应的时钟、复位、使能等信号
-
运行Connection Automation命令,自动连接ZYNQ IP核和VDMA IP核之间的AXI总线信号
-
验证设计是否有错误或警告,并生成比特流文件
Vitis工程创建
- 在Vitis中创建一个新的工程,并导入Vivado生成的比特流文件和硬件描述文件(如果没有自动导入,则手动添加)
- 添加main.c文件,并编写相应的代码,主要包括以下几个方面:
- 初始化PS端和PL端的硬件设备,并获取相应的设备句柄和内存映射地址
- 配置ZYNQ IP核的寄存器,使能HP接口和DDR3接口,并设置FCLK_CLK0为200MHz
- 配置VDMA IP核的寄存器,使能读写通道,并设置相应的帧缓冲区地址和大小等参数
- 配置VTC IP核的寄存器,使能视频时序生成,并设置相应的水平和垂直参数等参数
- 调用摄像头配置模块提供的函数,通过SCCB接口对摄像头进行初始化和设置,并选择720P分辨率和RGB565格式等参数
- 启动VDMA IP核和VTC IP核,并等待视频显示正常
实验结果展示
- 将ZYNQ开发板、OV5640摄像头和HDMI显示器连接好,并上电启动开发板
- 将Vitis工程下载到开发板上,并运行程序
- 观察HDMI显示器上是否有摄像头采集到的图像正常显示
ZYNQ实现YOLOv3-Tiny的加速方案介绍
-
加速方案的总体原则:
加速方案参考了之前手写数字识别的CNN项目,采用了类似的数据预处理、参数传输、卷积计算、激活查找表等模块 加速方案需要考虑数据尺寸、存储资源、传输带宽、计算延迟等因素,进行设计空间探索,找到最优的设计点
-
加速方案的总体原则是利用ZYNQ的PS和PL分工合作,实现YOLOv3-Tiny的前向计算
-
在ZYNQ的PL端实现网络结构中的卷积、激活、池化等计算密集型的操作,利用硬件加速计算,使用Verilog代码进行硬件描述
-
在ZYNQ的PS端实现数据调度、解析yolo层输出结果、后处理、显示输出结果、非极大值抑制等控制逻辑型的操作,使用C/C++代码进行软件编程
-
在ZYNQ的DDR3中存储网络参数、输入图像数据、中间结果等信息,利用外部存储资源
- DDR3作为外部存储器,缓存网络参数、输入图像和中间结果
-
在ZYNQ的HDMI接口上显示检测结果,利用视频输出设备
-
-
加速方案的具体步骤:
- 参数传输:将Pytorch训练得到的权重、偏置、缩放因子等参数存储在SD卡中,通过HIDMA从PS端传输到PL端,并缓存在BRAM中
- 卷积计算:从DDR3中读取输入图像数据,并根据需要进行填充操作,然后与缓存的权重参数进行卷积计算,并将结果写回DDR3中
- 激活查找表:根据卷积计算的结果,在PL端使用查找表进行激活操作,查找表由Pytorch生成并存储在SD卡中
- 池化操作:在卷积计算后直接进行最大池化操作,减少数据量,并将结果写回DDR3中
- 上采样操作:在PL端使用双线性插值法进行上采样操作,增加特征图的尺寸,并将结果写回DDR3中
- 连接操作:在PL端将两个不同尺度的特征图进行连接操作,形成一个大的特征图,并将结果写回DDR3中
- 解析操作:在PS端对最终的两个特征图进行解析操作,得到目标的类别、置信度和边界框坐标
- 非极大值抑制:在PS端对解析得到的多个边界框进行非极大值抑制,去除重叠度高的冗余框,保留最终的检测结果
- 显示操作:在HDMI驱动模块上将检测结果绘制在原始图像上,并显示在屏幕上
-
数据预处理
- 采用了全局缩放的方法,而不是局部裁剪的方法,以便对整幅图像进行检测
- 将摄像头输出的720P图像(1280x720)通过一个resize模块缩放为416x416,以适应网络的输入尺寸,作为YOLOv3-Tiny的输入。Resize模块使用双线性插值算法进行缩放,保证整幅图像都能被检测。这个resize模块可以利用FPGA的并行性来加速图像缩放的过程
- 使用resize模块实现缩放操作,并将缩放后的图像数据通过VDMA(Video Direct Memory Access)模块写入DDR3中。VDMA模块可以实现高速的视频数据传输,同时减少处理器的负担。
- 缩放后的图像数据与显示用的图像数据分开存储,避免冲突
-
PL端(FPGA)计算
在PL端(可编程逻辑端)负责卷积,激活,池化和上采样等计算密集型的操作
加速器还对原始的YOLO算法进行了一些优化,包括减少位精度、融合归一化层和卷积层、添加一个新的DLQ层等,以减少内存空间和带宽需求,保持精度。
-
PL端通过AXI-DMA接口从DDR3读取所需的数据,包括输入图像和网络参数,并通过Stream_rx模块接收到FPGA内部。AXI-DMA是一种高带宽,高效率的数据传输接口。
-
PL端根据PS端发送的控制信号,在FPGA内部进行相应层的计算,实现所有的YOLO层,包括卷积、激活、池化、上采样等操作。对于卷积层,PL端先对输入图像进行填充(如果需要),然后使用通用矩阵乘法GEMM(General Matrix Multiplication)原理,设计了一个基于systolic array的GEMM处理器。进行卷积运算,并使用查找表实现激活函数。对于池化层和上采样层,PL端使用简单的逻辑电路进行实现。对于YOLO层,PL端直接将输出结果写入DDR3。
- 对于激活层,PL端使用了一个查找表文件来实现leaky ReLU函数。查找表文件是在Pytorch中生成的,包含了不同输入值对应的输出值。
- 对于池化层和上采样层,PL端使用了简单的硬件逻辑来实现最大值池化和双线性插值上采样。
- 对于YOLO层和路由层,PL端不进行任何计算,只是将输入数据原样输出。
-
PL端将每一层的计算结果通过VDMA写入DDR3的相应位置,作为下一层的输入或最终的输出。同时,PL端通过AXI-LITE模块向PS端发送信号,告知PS端该层的计算已经完成。
-
-
PS端(ARM处理器)调度
在PS端(处理器端)负责数据的预处理,调度,解析和显示
-
PS端从摄像头获取720P的图像数据,通过VDMA(Video Direct Memory Access)将其写入DDR3内存,并通过Video In to AXI Stream IP将其转换为AXI Stream接口的数据。
-
PS端从DDR3读取图像数据,并通过Resize模块将其缩放为416*416的尺寸,然后再通过VDMA将其写入DDR3的另一部分。这样就得到了YOLOv3-Tiny网络的输入图像。
-
PS端从SD卡(存储设备)读取Pytorch训练得到的网络参数(包括权重、偏置、缩放因子等)文件(bin文件),包括卷积核的权重和偏置,以及激活层的scale,zero point等参数,并通过VDMA将其写入DDR3的另一部分。并通过SD卡和AXI-DMA将其传输到PL端(Programmable Logic)。
-
PS端通过AXI-Lite接口向PL端发送控制信号,告知PL端要进行哪一层的计算,以及需要哪些数据。AXI-Lite是一种低延迟,低开销的通信接口。
- 对于YOLO层,PS端从DDR3内存中读取输出结果,并进行解析,得到检测出来的物体类别和边界框坐标。
- 对于路由层,PS端从DDR3内存中读取两个输入特征图,并进行拼接,得到一个输出特征图。然后将输出特征图写回DDR3内存中,以供下一层使用。
- 对于其他层,PS端不进行任何处理,只是准备下一层的指令。
-
PS端从DDR3读取YOLOv3-Tiny网络的输出结果,并对其进行解析,得到物体类别,置信度和边界框坐标等信息。然后PS端通过HDMI驱动显示器进行显示,并在原始图像上绘制检测到的物体和边界框。
-
-
Layer计算过程
Layer 是神经网络中的一个基本单元,它由多个神经元组成,每个神经元都有一个激活函数,用于对输入数据进行非线性变换。Layer 的作用是提取输入数据的特征,并将其传递给下一层或输出层。
Layer计算过程是指在深度学习中,使用神经网络对输入数据进行一系列的变换和操作,从而得到输出数据的过程。每一层都有自己的参数和激活函数,可以实现不同的功能,如卷积、池化、全连接、归一化等。不同的层可以组合成不同的网络结构,如CNN、RNN、Transformer等,来解决不同的任务,如图像识别、语音识别、自然语言处理等。
-
输入数据:Layer接收上一层的输出数据或者原始数据作为输入,输入数据通常是一个多维的张量(tensor),例如图片、文本、音频等。
-
数据预处理:对输入数据进行一些必要的处理,例如填充、缩放、归一化、编码等。
-
权重参数:Layer有一组可调整的模型参数,通常是一个或多个矩阵(matrix),用来存储Layer学习到的特征和规律。例如卷积层(convolutional layer)的权重参数就是卷积核(kernel),全连接层(fully connected layer)的权重参数就是连接矩阵(connection matrix)等。
-
运算操作:Layer对输入数据和权重参数进行一定的数学运算,从而得到输出数据。不同类型的Layer有不同的运算操作,例如卷积层的运算操作就是卷积(convolution),激活层(activation layer)的运算操作就是激活函数(activation function),池化层(pooling layer)的运算操作就是池化(pooling)等。
-
输出数据:Layer将运算操作得到的输出数据传递给下一层或者作为最终结果。输出数据通常也是一个多维的张量,例如特征图(feature map),分类概率(classification probability)等。
-
本项目中Layer 0、Layer 1和Layer 2的计算步骤:
-
Layer 0:这是一个卷积层,输入通道数为3,输出通道数为16,卷积核大小为3x3,步长为1,填充为1。它的作用是对输入图像进行特征提取。
- 首先,PS端从SD卡中读取Layer 0的权重参数(包括卷积核和偏置),并通过HIDMA模块发送到PL端的Weight Buffer中。同时,PS端通过HLITE模块告知PL端当前传输的是权重参数。
- 然后,PS端从摄像头获取720P的图像数据,并通过Resize模块将其缩放为416x416的大小。然后通过VDMA模块将图像数据写入DDR3中,并通过HIDMA模块发送到PL端的Padding模块中。同时,PS端通过HLITE模块告知PL端当前传输的是图像数据。
- 接着,PL端的Padding模块对图像数据进行填充操作,将其扩展为418x418的大小,并将填充后的数据送入Convolution模块中。
- 然后,PL端的Convolution模块根据Weight Buffer中存储的权重参数对填充后的数据进行卷积计算,并将结果送入Activation模块中。
- 最后,PL端的Activation模块根据Shift、Matter和Zero Point等参数对卷积结果进行激活函数计算,并将结果送入Pooling模块中。Pooling模块对激活结果进行最大值池化操作,并将结果写入DDR3中作为Layer 1的输入。
-
Layer 1:这是一个卷积层,输入通道数为16,输出通道数为32,卷积核大小为3x3,步长为1,填充为1。它的作用是对上一层的特征进行进一步的提取。
- 首先,PS端从SD卡中读取Layer 1的权重参数(包括卷积核和偏置),并通过HIDMA模块发送到PL端的Weight Buffer中。同时,PS端通过HLITE模块告知PL端当前传输的是权重参数。
- 然后,PS端从DDR3中读取Layer 0的输出结果,并通过HIDMA模块发送到PL端的Padding模块中。同时,PS端通过HLITE模块告知PL端当前传输的是图像数据。
- 接着,PL端的Padding模块对图像数据进行填充操作,将其扩展为210x210的大小,并将填充后的数据送入Convolution模块中。
- 然后,PL端的Convolution模块根据Weight Buffer中存储的权重参数对填充后的数据进行卷积计算,并将结果送入Activation模块中。
- 最后,PL端的Activation模块根据Shift、Matter和Zero Point等参数对卷积结果进行激活函数计算,并将结果送入Pooling模块中。Pooling模块对激活结果进行最大值池化操作,并将结果写入DDR3中作为Layer 2的输入。
-
Layer 2:这是一个卷积层,输入通道数为32,输出通道数为64,卷积核大小为3x3,步长为1,填充为1。它的作用是对上一层的特征进行进一步的提取。
- 首先,PS端从SD卡中读取Layer 2的权重参数(包括卷积核和偏置),并通过HIDMA模块发送到PL端的Weight Buffer中。同时,PS端通过HLITE模块告知PL端当前传输的是权重参数。
- 然后,PS端从DDR3中读取Layer 1的输出结果,并通过HIDMA模块发送到PL端的Padding模块中。同时,PS端通过HLITE模块告知PL端当前传输的是图像数据。
- 接着,PL端的Padding模块对图像数据进行填充操作,将其扩展为106x106的大小,并将填充后的数据送入Convolution模块中。
- 然后,PL端的Convolution模块根据Weight Buffer中存储的权重参数对填充后的数据进行卷积计算,并将结果送入Activation模块中。
- 最后,PL端的Activation模块根据Shift、Matter和Zero Point等参数对卷积结果进行激活函数计算,并将结果送入Pooling模块中。Pooling模块对激活结果进行最大值池化操作,并将结果写入DDR3中作为Layer 3的输入。
-
-