尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ZYNQ7020 PL侧帧差法运动目标检测与HDMI显示实现

ZYNQ7020 PL侧帧差法运动目标检测与HDMI显示实现 简介这套基于ZYNQ7020的帧差法运动目标检测系统是答辩得分98分的个人毕业设计成果代码经调试测试可正常运行。系统涵盖OV5640图像采集、帧差法算法处理、HDMI显示输出等完整链路适合电子信息、计算机、自动化等专业学生用于课程设计或毕业设计参考也可作为FPGAARM嵌入式开发学习者从入门到进阶的实践案例。压缩包共56个文件以C源文件10个与头文件12个为核心另有Verilog/VHDL硬件逻辑文件、XDC约束、TCL脚本及SDK工程配置等FPGA逻辑与嵌入式软件一一对应整体约110KB结构精炼便于阅读。项目包含自建IP核axi_dynclk、rgb2lcd、DVI_TX等、图像采集与显示模块及完整的Vivado工程配套详细文档可帮助理解从硬件搭建到算法实现的全过程。已有141人学习使用对于希望掌握ZYNQ异构平台图像处理开发流程的读者具有较好的参考价值。1. 帧差法在ZYNQ7020上为什么值得从PL侧做起帧差法可能是运动目标检测里最朴素的方法了当前帧与前一帧做逐像素灰度差分超过阈值的区域判定为运动目标。算法本身十分钟就能在PC上用Python写出来但放到ZYNQ7020上问题的性质就变了——它要在像素时钟节拍下逐像素流水完成不经过CPU调度不走操作系统。这套高分毕设项目的完整链路是OV5640摄像头采集视频PL侧完成帧差法检测HDMI实时输出结果SDK只负责上电配置和AXI外设驱动。对做FPGA图像处理毕设或者刚接触视频通路的人来说真正值钱的不光是帧差法那几十行逻辑而是这套源码把采集—缓存—算法—显示的硬件链路完整打通了。下文按数据流向拆解每个模块从IP选型讲到SDK调试。2. 从OV5640到DDR3视频采集链路的IP选型与axi_dynclk配置解压工程后ip_repo目录下能看到本项目自定义封装的一批IP核ov5640_cap_data、DVI_TX、rgb2lcd、axi_dynclk_v1_0配合Xilinx官方VDMA构成了完整的视频通路。视频数据从传感器到DDR3缓存这条链路决定了帧差法拿到的究竟是不是一帧干净完整的灰度数据。很多初次做图像处理的同学习惯把摄像头输出直接接进算法模块结果帧率稍高就丢帧、行错位问题往往不是算法写错了而是采集侧时序没对齐。所以这一章从时钟、时序、数据打包三个层面把采集链路讲透。2.1 OV5640在DVP模式下的时钟与同步信号时序OV5640支持MIPI和DVP两种输出接口这套设计走的是DVP并行8位接口。DVP模式下传感器输出四组信号PCLK是像素时钟数据在PCLK上升沿稳定有效HREF拉高表示当前行有效VSYNC是帧同步信号D[7:0]是像素数据总线。分辨率确定后PCLK频率也随之确定以常见的1280x72060fps为例像素时钟约为74.25MHz如果工作在1920x108030fps像素时钟也是74.25MHz但1080p60需要148.5MHz。OV5640的XVCLK外部输入一般接24MHz晶振传感器内部通过PLL倍频出所需的像素时钟。问题在于镜头模组的分辨率、帧率、输出格式都是通过SCCB寄存器动态配置的每次修改配置PLL输出也会跟着变。如果像素时钟由FPGA侧的PLL固定生成就会出现在720p下能正常出图、切到1080p就花屏或无信号的尴尬情况。axi_dynclk_v1_0正是为了解决这个问题出现的。它是Xilinx Wiki上维护的动态时钟控制器IP通过AXI4-Lite寄存器在运行时修改MMCM的D、M、Q分频倍频系数输出时钟频率可以动态调整分辨率切换时不需要重新综合整个工程。表2-1列出了本工程常见的时钟配置对应关系。表2-1 axi_dynclk典型输出配置目标场景输入参考时钟D(分频)M(倍频)Q(输出分频)输出频率720p60100MHz(FCLK_CLK0)2151075MHz1080p30100MHz(FCLK_CLK0)2372574MHz1080p60100MHz(FCLK_CLK0)2279150MHz这里需要说明MMCM的VCO频率通常在600MHz到1200MHz之间所以D、M、Q的取值必须保证VCO Fclk_in / D * M落在合法区间内。表中第一行的VCO是750MHz正好落在范围内。实际工程中OV5640的DVP接口对像素时钟精度并不苛刻输出74MHz或75MHz都能正常出图但DVI_TX对时钟稳定度敏感切换分辨率后一定要重新锁定PLL再启动显示链路。2.2 axi_dynclk_v1_0的寄存器配置与驱动方式在SDK工程里对axi_dynclk的初始化本质上是往几个寄存器写值。寄存器0x00写D分频0x04写M倍频0x08写Q分频0x10写使能位。驱动初始化完成后PL侧的逻辑时钟才真正稳定下来。常见做法如下#include xil_io.h #define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR #define REG_DIVCLK_DIVIDE 0x00 #define REG_DIVF 0x04 #define REG_DIVQ 0x08 #define REG_DIVR 0x0C #define REG_ENABLE 0x10 void dynclk_set_75mhz(void) { /* 输出 100 / D * M / Q此处配置为 75MHz */ Xil_Out32(DYNCLK_BASEADDR REG_DIVCLK_DIVIDE, 0x02); Xil_Out32(DYNCLK_BASEADDR REG_DIVF, 0x0F); /* M 15 */ Xil_Out32(DYNCLK_BASEADDR REG_DIVQ, 0x0A); /* Q 10 */ Xil_Out32(DYNCLK_BASEADDR REG_ENABLE, 0x01); }这段代码先配置分频倍频系数再使能PLL使能后PL侧时钟会在一小段锁定时间内从无到有过渡。实际调试中DIVF和DIVQ的取值应当以Vivado工程中Block Design的地址映射为准不同版本的axi_dynclk寄存器偏移可能存在差异写之前先打开IP的xparameters.h确认基地址。另一个容易踩的坑是OV5640的PCLK方向是传感器输出给FPGA而DVI_TX需要的像素时钟方向是FPGA内部生成两者不是同一个时钟不能混用。2.3 ov5640_cap_data模块的AXI4-Stream打包逻辑OV5640输出的是8位DVP数据流而DDR3缓存和VDMA处理的是AXI4-Stream协议。ov5640_cap_data模块的作用就是完成这两者的转换。VDMA的AXI4-Stream数据位宽通常配成32位对8位像素来说相当于每4个像素打包成一个32位字。模块内部需要根据HREF和VSYNC生成行同步信号tlast和帧同步信号tuser这两个信号直接决定VDMA能不能正确切分每一行和每一帧。以下是该模块核心逻辑的简化描述实际工程中还需处理PCLK跨时钟域到AXI4-Lite时钟域的问题// ov5640_cap_data 核心打包逻辑节选 module ov5640_cap_data #( parameter H_ACTIVE 1280, parameter V_ACTIVE 720 )( input wire pclk_i, input wire rst_n_i, input wire cam_vsync_i, input wire cam_href_i, input wire [7:0] cam_data_i, output reg m_axis_tvalid, output reg m_axis_tlast, output reg [31:0] m_axis_tdata ); reg [1:0] pix_cnt; reg [31:0] data_buf; reg [10:0] h_cnt; always (posedge pclk_i or negedge rst_n_i) begin if (!rst_n_i) begin pix_cnt 2d0; h_cnt 11d0; end else if (cam_href_i) begin /* 每4个8位像素拼成一个32位数据h_cnt计数到H_ACTIVE-1时拉高tlast */ data_buf[7:0] cam_data_i; data_buf[15:8] data_buf[7:0]; data_buf[23:16] data_buf[15:8]; data_buf[31:24] data_buf[23:16]; pix_cnt pix_cnt 1d1; if (h_cnt H_ACTIVE - 1) begin m_axis_tlast 1b1; h_cnt 11d0; end else begin h_cnt h_cnt 1d1; end end end endmodule这段代码的要点是data_buf通过移位寄存器的方式把连续4个像素拼成32位pix_cnt指示当前拼接位置h_cnt一行计数到H_ACTIVE-1时拉高tlast。需要特别注意的是tlast必须与最后一个像素数据在同一拍有效否则VDMA会把行长度算错导致图像整行错位。H_ACTIVE参数必须与VDMA中设置的行长度严格一致否则会出现图像斜切现象。3. 帧差法在FPGA上的像素级实现两帧缓存与差分阈值逻辑帧差法落到FPGA上核心难题不是减法运算本身而是怎么拿到前一帧。FPGA内部Block RAM容量有限ZYNQ-7020总共只有4.9Mbit存不下完整的一帧720p画面所以帧缓存必须放在DDR3里。这章讲清楚为什么选帧差法而不是混合高斯背景建模、VDMA双缓冲怎么组织、差分与二值化的硬件实现以及阈值参数怎么设。3.1 帧差法为什么比背景建模更适合片上实现运动目标检测的常见方法有帧差法、光流法、混合高斯背景建模。光流法计算每个像素的运动矢量硬件开销极大混合高斯模型需要为每个像素维护多个高斯分布参数存储量在DDR3上实现虽然可行但逻辑复杂度高实时性很难保证。帧差法的优势在于只需要两帧图像做减法运算单元只需要减法器、比较器和简单的状态机流水线深度可以压得非常短适合逐像素实时处理。帧差法检测效果受两个因素影响明显。一是物体运动速度物体移动过慢时两帧差分值接近于零目标会消失二是环境光照突变全局亮度变化会造成大面积虚假运动区域。针对前者若在SDK中配置VDMA每2帧做一次比较等效于扩大了帧间间隔慢速目标也能检出针对后者通常配合中值滤波或形态学开运算去除孤立噪点。3.2 基于AXI VDMA的乒乓帧缓存设计帧差法要求同时持有当前帧和前一帧。工程中的做法是将DDR3划分为两个帧缓冲区域一个存当前帧一个存上一帧。VDMA的S2MM写通道把ov5640_cap_data送来的当前帧写入空闲缓冲MM2S读通道从另一个缓冲读出上一帧两路数据在PL侧对齐后做逐像素差分。乒乓缓存的地址切换在SDK里配置核心代码如下#include xil_io.h #define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR /* MM2S 控制寄存器启动读通道 */ Xil_Out32(VDMA_BASEADDR 0x00, 0x00001003); /* RS|Reset|GenLock */ /* S2MM 控制寄存器启动写通道 */ Xil_Out32(VDMA_BASEADDR 0x30, 0x00001003); /* MM2S 帧地址读上一帧 */ Xil_Out32(VDMA_BASEADDR 0x5C, DDR_BASEADDR FRAME0_START); /* S2MM 帧地址写当前帧到另一缓冲 */ Xil_Out32(VDMA_BASEADDR 0xAC, DDR_BASEADDR FRAME1_START); /* 帧垂直尺寸720 行 */ Xil_Out32(VDMA_BASEADDR 0x04, 720); /* 行长度一行 1280 像素 × 2 字节(16bit) 2560 */ Xil_Out32(VDMA_BASEADDR 0x08, 2560);这里需要注意VDMA的帧地址寄存器必须在VDMA处于复位状态时写入写完再启动通道。行长度寄存器HSIZE的单位是字节假设每个像素在DDR3中以16位YUV格式存放一行1280像素对应2560字节。如果HSIZE配置成像素个数而不是字节数读出来的数据会逐行错位画面呈现阶梯状撕裂。3.3 差分、阈值二值化与形态学去噪的硬件实现两路像素流在PL侧对齐后进入帧差法运算核心。该模块在工程中位于ov5640_hdmi_frame_difference顶层内部包含差分计算、阈值比较和邻域滤波三个部分。差分模块本质上是一个带符号减法器由于像素是无符号数需要先做绝对值运算再与阈值比较// frame_diff_core.v 核心运算逻辑 module frame_diff_core #( parameter THRESHOLD 32 // 差分阈值可调 )( input wire clk_i, input wire rst_n_i, input wire [7:0] cur_pixel_i, // 当前帧像素 input wire [7:0] pre_pixel_i, // 上一帧像素 output reg [7:0] diff_pixel_o // 二值结果255运动 / 0静止 ); wire [8:0] diff_abs; assign diff_abs (cur_pixel_i pre_pixel_i) ? (cur_pixel_i - pre_pixel_i) : (pre_pixel_i - cur_pixel_i); always (posedge clk_i or negedge rst_n_i) begin if (!rst_n_i) diff_pixel_o 8d0; else if (diff_abs THRESHOLD) diff_pixel_o 8d255; // 判定为运动目标 else diff_pixel_o 8d0; // 背景区域 end endmodule这段代码先计算两帧灰度差的绝对值再用参数化的THRESHOLD做比较。THRESHOLD取32意味着灰度变化超过32级就被判定为运动适用于光照稳定的室内场景。单独的二值化结果会有大量离散噪点主要是传感器暗电流和场景微小抖动造成。工程在二值化后接了一个3x3中值滤波或腐蚀膨胀模块两者选一即可。腐蚀操作会缩小运动目标边缘去除孤立噪点膨胀操作会填充目标内部空洞。对帧差结果建议先腐蚀后膨胀即开运算保留目标整体轮廓的同时去掉噪点。3.4 阈值参数怎么定灰度级与场景光照的关系阈值参数决定了检测灵敏度这个参数在工程中可以从两个位置调整一是直接改Verilog里的THRESHOLD参数后重新综合二是把阈值做成AXI GPIO寄存器在SDK运行时动态修改。后者的好处是调试时不用每次改代码重新跑实现配合串口打印可以快速标定。表3-1给出了不同阈值下的典型表现供调参时对照。阈值环境特征检测效果副作用816室内恒定光照慢速运动也能检出噪声较多出现散斑2448普通室内照明人体走动清晰轻微阴影可能漏检64128光线变化较明显只检出快速运动慢速目标丢失实际调试时先把阈值调低观察画面中噪点密度再逐步调高直到静止场景下画面基本全黑、人体走动时轮廓连续。如果光照条件变化频繁可以后续在此基础上加入背景更新模块做自适应阈值这是该项目的常见扩展方向。4. DVI_TX与rgb2lcd运动检测结果的HDMI显示通路检测结果必须让肉眼看到才具备工程意义。本工程的显示通路由rgb2lcd和DVI_TX两个自定义IP组成前者负责把帧差结果叠加成RGB像素流后者负责把RGB信号按DVI/HDMI时序发送出去。这一章看显示链路的像素格式转换与时序约束。4.1 rgb2lcd模块中的像素格式转换OV5640输出的是RAW RGB或YUV格式而帧差法模块处理后得到的是8位灰度二值数据。rgb2lcd模块要做的事情有两个把灰度二值结果从8位扩展到24位RGB把原来的YCbCr/YUV数据转换成HDMI所需的RGB888格式。为了便于观察运动目标在原图中的位置常见做法是将二值检测结果叠加到原始画面上——检测为运动区域的像素直接输出为红色或绿色背景像素保留原图色彩。工程中这个模块同时完成两种任务正常显示时直接透传OV5640经过色彩转换的RGB数据调试模式下把二值结果映射为红色通道。这里的转换流水线涉及两个时钟域——OV5640的PCLK域和DVI_TX的像素时钟域为了保证跨时钟域不丢像素模块内部使用FIFO或握手信号同步。显示分辨率与采集分辨率不一致时这里还要做行场计数缩放但本工程保持1280x720直接透传省去了缩放逻辑。4.2 DVI_TX的时序参数与管脚约束DVI_TX模块本质上是把并行RGB数据和行场同步信号按照DVI规范串行化输出。7系列FPGA上通常使用OSERDESE2原语将28位并行数据转成4:1串行数据再配合TMDS驱动输出。该IP的输入侧需要五个信号像素时钟pclk、行同步hsync、场同步vsync、8位红绿蓝数据。输出侧产生三对TMDS差分数据通道和一对TMDS时钟通道。写约束文件时以下约束是必须的create_clock -period 13.333 [get_ports pclk_i] set_property PACKAGE_PIN AE15 [get_ports {tmds_clk_p}] set_property IOSTANDARD TMDS_33 [get_ports {tmds_clk_p}] set_property PACKAGE_PIN AE16 [get_ports {tmds_clk_n}]这里的13.333ns对应75MHz像素时钟。需要特别提醒DVI_TX模块对管脚位置非常敏感如果使用的开发板不是标准ZYNQ-7020核心板HDMI座子的管脚分配必须按板卡原理图重新约束直接套用项目提供的xdc文件大概率会报管脚冲突。调试时如果HDMI无信号优先检查pclk频率是否被MMCM正确锁定其次是TMDS输出引脚是否被软件误配为普通IO。4.3 显示偏色、花屏的常见定位方法显示通路的问题绝大多数集中在数据位宽和时序对齐上。偏色通常是色度空间转换时系数配错YUV转RGB时如果系数四舍五入方式不一致会导致整体色偏帧差叠加模式下背景像素和检测像素来自两个不同模块如果叠加优先级写错会出现运动区域颜色被背景覆盖的问题。花屏则多为行时序不匹配VDMA输出的行长度、rgb2lcd内行计数器的终点值、DVI_TX的行显示周期三者必须满足实际解析度行数 显示有效区 消隐区。任何一层不一致都会出现整帧斜切或上下半屏错位。5. 从Vivado到SDK一个验证帧差效果的调试套路5.1 SDK初始化三件事拿到工程后SDK侧的软件工作按固定顺序执行先初始化OV5640寄存器列表让传感器输出720p RGB数据再配置axi_dynclk输出75MHz像素时钟最后启动VDMA并读取帧差结果。这个顺序不能颠倒因为VDMA启动后就开始搬运数据如果此时时钟还没有稳定采集模块会在不稳定的PCLK上采出大量错误数据。OV5640的寄存器配置一般通过IIC总线完成核心代码如下int ov5640_init(void) { /* 常用关键寄存器完整配置序列见工程内 ov5640_reg.c */ ov5640_write_reg(0x3103, 0x11); /* 系统时钟分频 */ ov5640_write_reg(0x3008, 0x42); /* PLL 使能 */ ov5640_write_reg(0x3034, 0x1A); /* PLL 配置 */ ov5640_write_reg(0x3820, 0x40); /* 垂直翻转 */ ov5640_write_reg(0x3821, 0x00); /* 水平镜像 */ return 0; }这段代码只列出几个标志性寄存器完整的ov5640_reg.c在SDK源码目录下包含上百个寄存器写操作。调试时建议先注释掉除复位外的所有配置让摄像头输出默认的VGA格式确认采集链路通了再切到720p这样定位问题会快很多。5.2 用走动的人验证实时性与阈值合理性验证检测效果最有效的方法是固定摄像头、让测试者在画面中横向移动。把阈值调到32观察显示画面人体轮廓应显示为高亮区域背景保持原图颜色。此时需要注意移动速度太快会导致帧差出现双边缘慢速移动则会丢目标这是因为帧差法固有的时间分辨率局限。更细的验证方法是让测试者手持一张纯色纸板在均匀背景下以不同速度移动观察轮廓的连续性和边缘断裂情况。若轮廓断裂严重优先降低阈值若噪点太多则调高阈值并检查3x3开运算是否生效。5.3 常见故障排查表现象可能原因排查顺序HDMI无输出DVI_TX像素时钟未锁定检查axi_dynclk寄存器是否写入正确ILA看pclk画面斜切错位VDMA行长度配置与像素位宽不符确认HSIZE为字节数一行720p需设2560全屏噪点闪烁阈值过低或中值滤波未生效阈值调高确认3x3窗口寄存器已实际例化检测不到任何运动读地址和写地址指向同一帧缓冲检查MM2S和S2MM帧地址是否分属不同缓冲本文还有配套的精品资源点击获取
返回列表