ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA图像识别与目标跟踪:颜色识别流水线硬件实现与VGA调试

FPGA图像识别与目标跟踪:颜色识别流水线硬件实现与VGA调试 简介这份资源面向FPGA初学者与嵌入式视觉方向的开发者围绕「如何用可编程逻辑器件实现实时图像处理」这一核心问题给出了一套基于颜色的目标识别与跟踪系统方案。系统以Altera EP2C8系列FPGA为核心配合30万像素CMOS摄像头、SDRAM帧缓冲、ADV7123视频DAC与VGA输出完整覆盖硬件电路搭建、流水线算法设计与调试接口配置适合视频监控、智能交通等对实时性与低功耗有要求的场景。资源包为1个PDF文档大小约389KB内容涵盖系统整体功能概述、FPGA外围电路与VGA、SDRAM接口设计以及平滑滤波、二值化、腐蚀膨胀、包围盒等算法模块的实现思路与流水线结构分析。目前已有2045人学习下载读者可借此理解硬件并行处理相较DSP串行方案在速度与可靠性上的优势并掌握从图像采集到目标定位的完整设计链路。1. 从一块 EP2C8 说起为什么颜色跟踪值得用 FPGA 硬扛摄像头每秒吐出 30 帧、每帧 30 万像素如果让一颗普通 MCU 逐像素跑颜色判断光是遍历一遍就要吃掉大半算力更别提后面还有滤波、腐蚀、包围盒。很多人第一次做目标跟踪习惯性先上 OpenCV 加软件算法跑通了才发现帧率掉到个位数光照一变包围盒就抖成筛子。这套 FPGA 图像识别与目标跟踪系统的思路正好相反把二值化、平滑、腐蚀膨胀、包围盒全部拆成流水线让数据像流水一样在每个时钟沿往前走处理速率直接跟像素时钟对齐做到无延时。它适合谁适合手上有 Altera Cyclone II 开发板、想搞明白硬件并行到底怎么落地的人也适合做智能监控、智能交通这类对实时性和功耗都敏感的从业者。核心器件是 EP2C8摄像头 30 万像素 CMOSSDRAM 做帧缓冲ADV7123 出 VGA整套 5V 供电JTAG 调试、AS 固化。2. 硬件链路怎么搭从摄像头到 VGA 的时钟与带宽账2.1 时钟树与 PLL 分配这套系统最容易翻车的地方不是算法是时钟。板子上给了 40 MHz 和 50 MHz 两路输入50 MHz 走 PLL 倍频到 100 MHz 喂 SDRAM同时分频到 25 MHz 给 CMOS 摄像头当驱动时钟40 MHz 留作后备。为什么摄像头是 25 MHz30 万像素、30 帧每秒像素时钟大致就在这个量级摄像头输出的是 16 bit RGB565 数据5-6-5 分配。SDRAM 跑 100 MHz、16 bit 字宽总带宽 100 MHz × 16 bit摄像头写入只占 12.5 MHz 带宽VGA 读取占 25 MHz剩下 62.5 MHz 留给图像处理流水线。这个账要先算清楚不然后面加一级滤波就发现带宽不够、画面撕裂。# Quartus 里 PLL 的常见配置思路以 MegaWizard 为例 # inclk0 50 MHz # c0 100 MHz - SDRAM 时钟 # c1 25 MHz - CMOS 摄像头 XCLK # c2 100 MHz - 系统处理时钟与 SDRAM 同源便于跨时钟域逻辑说明PLL 的 c0 和 c2 同频但建议分不同输出口方便后面做时序约束时区分时钟域。参数上倍频系数和分频系数要按你板子实际晶振改别照抄。跨时钟域的信号比如摄像头 25 MHz 域到处理 100 MHz 域一定要过两级寄存器同步或者用异步 FIFO这是血泪经验直接连会出亚稳态。2.2 SDRAM 帧缓冲的读写仲裁摄像头 30 帧写入、VGA 60 帧读出帧率不一致必须用 SDRAM 做乒乓或三缓冲。SDRAM 操作里最频繁的是读命令和写命令中间要插激活、预充电、猝发终止、刷新。常见做法是写一个带仲裁的 SDRAM 控制器摄像头写请求优先级略低于 VGA 读请求因为读一旦断流屏幕就黑写丢一两行还能靠下一帧补。100 MHz 下摄像头每行有效像素期间写入行消隐期间让给 VGA 读这样带宽利用率最高。// SDRAM 读写仲裁的简化状态机片段 localparam IDLE 2d0, WRITE 2d1, READ 2d2, REF 2d3; always (posedge clk_100m or negedge rst_n) begin if (!rst_n) state IDLE; else case (state) IDLE: if (cam_wr_req) state WRITE; // 摄像头写优先 else if (vga_rd_req) state READ; else if (ref_req) state REF; // 定时刷新不能省 WRITE: if (wr_burst_done) state IDLE; READ: if (rd_burst_done) state IDLE; REF: if (ref_done) state IDLE; endcase end逻辑说明cam_wr_req和vga_rd_req是各自时钟域同步过来的请求信号。参数上刷新计数器按 SDRAM 手册的 64ms/8192 行算别漏刷新否则数据会莫名其妙丢。注意写请求优先只是简化实际工程里更稳的是给读留固定时间片避免写突发太长把读饿死。2.3 VGA 输出与 ADV7123 偏置ADV7123 本质是三路高速 DA把 RGB 数字量转模拟量。模拟输出端要并 75 Ω 电阻做阻抗匹配DAC 满量程电流由偏置电位器决定调这个电位器就是调最大亮度。常见坑是电位器调太满白色区域过曝二值化阈值跟着飘。建议先把 VGA 直通显示调好确认图像不偏色、不滚动再往里加算法。3. 颜色识别流水线平滑、二值化、腐蚀膨胀怎么串3.1 3×3 平滑滤波器的行缓冲实现软件做滤波要先把整帧读进内存每个输出像素再回读窗口内存带宽被吃光。FPGA 的做法是行缓冲用两个 FIFO 或双口 RAM 缓存前两行加上当前行凑成 3×3 窗口每个时钟推一个像素进来窗口就滑动一格。这样每个输入像素只读一次带宽省到极致。// 3x3 窗口生成的核心两行缓冲 移位寄存器 // line_buf0, line_buf1 各缓存一行像素 // 每来一个像素三行对应位置同时输出组成 3x3 wire [15:0] w00, w01, w02; // 第一行三个 wire [15:0] w10, w11, w12; // 第二行三个 wire [15:0] w20, w21, w22; // 第三行三个 // 平滑九点求和后右移除以9的近似 wire [19:0] sum w00 w01 w02 w10 w11 w12 w20 w21 w22; wire [15:0] smooth_pix sum / 9; // 综合器会转成乘法或移位组合逻辑说明line_buf的深度等于图像一行像素数位宽 16 bit。参数上除以 9 在硬件里不划算常见做法是乘 0.111 的定点近似或者直接用 (sum×57)9。注意行缓冲的读写时钟要一致如果摄像头域和处理域不同FIFO 要跨时钟。3.2 RGB 分量比较与二值化流水线二值化在 RGB 空间做摄像头 16 bit 数据拆成 R5、G6、B5分别和样本颜色的对应分量相减取绝对值下一个时钟沿把三个误差和各自阈值比较三个都满足才输出白色最大值否则黑色。整条流水线延时两个时钟周期。// 二值化两级流水 reg [4:0] r_err, b_err; reg [5:0] g_err; reg r_ok, g_ok, b_ok; always (posedge clk) begin // 第一级求误差绝对值 r_err (r_pix r_ref) ? (r_pix - r_ref) : (r_ref - r_pix); g_err (g_pix g_ref) ? (g_pix - g_ref) : (g_ref - g_pix); b_err (b_pix b_ref) ? (b_pix - b_ref) : (b_ref - b_pix); // 第二级阈值比较 与运算 r_ok (r_err R_TH); g_ok (g_err G_TH); b_ok (b_err B_TH); bin_out r_ok g_ok b_ok ? 16hFFFF : 16h0000; end逻辑说明R_TH、G_TH、B_TH是三个阈值参数光照强时调小光照弱时调大这是这套算法最需要现场调的地方。注意三个通道必须同时满足只满足两个会把背景误判成目标。流水线延时两拍意味着包围盒那边要相应延迟两拍对齐否则坐标错位。3.3 腐蚀膨胀的窗口逻辑二值化后噪点还在尤其光照不足时 CMOS 噪点多。腐蚀用窗口内所有像素相与面积小于窗口的孤立白点被抹掉膨胀用相或把腐蚀后缩小的目标补回来。两者共用同一套 3×3 窗口只是运算换成与/或。// 腐蚀窗口全1才输出1 wire erode_out {w00[0], w01[0], w02[0], w10[0], w11[0], w12[0], w20[0], w21[0], w22[0]}; // 膨胀窗口有1就输出1 wire dilate_out |{w00[0], w01[0], w02[0], w10[0], w11[0], w12[0], w20[0], w21[0], w22[0]};逻辑说明这里取每个像素的最低位当二值标志因为二值化输出是全 0 或全 F。参数上窗口大小决定能滤掉多大的噪点3×3 只能滤单像素噪点噪点成片就要上 5×5。注意腐蚀会让目标变小膨胀要对称补回来先腐蚀后膨胀是开运算顺序别反。3.4 包围盒的坐标极值统计包围盒就是找目标像素横纵坐标的最大最小值。每帧开始时把 min_x 置最大、max_x 置最小扫描过程中遇到白色像素就更新。四个极值一出中心坐标就是 (min_xmax_x)/2 和 (min_ymax_y)/2。// 包围盒极值更新每帧复位一次 always (posedge clk) begin if (frame_start) begin min_x 10d1023; max_x 10d0; min_y 10d1023; max_y 10d0; end else if (bin_pix) begin if (x_cnt min_x) min_x x_cnt; if (x_cnt max_x) max_x x_cnt; if (y_cnt min_y) min_y y_cnt; if (y_cnt max_y) max_y y_cnt; end end逻辑说明x_cnt、y_cnt是行列计数器跟像素时钟同步。参数上位宽按分辨率定30 万像素用 10 bit 够。注意包围盒对噪点极敏感前面腐蚀膨胀没做好这里 min/max 会被单个噪点拉到画面边缘包围盒疯狂晃动这就是为什么预处理不能省。4. 避坑与排查这套流水线最容易翻车的五个地方4.1 现象VGA 显示图像整体偏色或滚动原因ADV7123 的偏置电位器没调好或者行场同步信号的极性配错。解决先用纯色测试图确认三路 DA 输出正常再检查 VGA 时序的行频场频是否匹配显示器极性不对就改同步信号取反。4.2 现象二值化后目标时有时无光照一变就丢原因RGB 三通道阈值是固定值环境光变化直接让误差超出范围。解决把阈值做成可通过键盘动态调整的寄存器现场边看 VGA 边调或者加一级自动白平衡但那是另一个工程量。4.3 现象包围盒抖动厉害中心坐标跳变原因预处理没开或腐蚀膨胀窗口太小噪点被当成目标。解决确认平滑滤波和开运算都使能对比关闭预处理前后的 VGA 输出噪点明显增多就说明预处理在起作用别为了省资源把它关掉。4.4 现象SDRAM 读写偶尔丢数据画面出现横条纹原因刷新命令被读写请求挤掉或者跨时钟域信号没同步。解决刷新计数器优先级设为最高读写突发长度别设太长摄像头域到处理域的信号全部过两级同步或异步 FIFO。4.5 现象JTAG 下载后运行正常AS 固化后上电不工作原因配置数据没真正写进 EPROM或者上电复位时序不够。解决用 Quartus 的 Programmer 确认 AS 模式烧录成功检查复位电路 RC 参数必要时加专用复位芯片。5. 把中间结果引到 VGA动态流水线观察与阈值整定技巧这套系统有个很实用的设计每一步中间处理结果都能直接输出到 VGA。做法是在流水线各级挂一个多路选择器用键盘切换观察点想看平滑输出就看平滑想看二值化就看二值化想看腐蚀膨胀就看腐蚀膨胀。这个技巧对调试的价值极大因为 FPGA 内部信号不像软件能随时打印SignalTap 抓的是离散采样而 VGA 是连续画面噪点、抖动、边界这些动态问题一眼就能看出来。我一般会按这个顺序整定先把摄像头直通显示调清楚确认无偏色无滚动然后打开平滑看噪点是否减少接着开二值化用键盘调 R/G/B 三个阈值直到目标区域稳定为白、背景稳定为黑再开腐蚀膨胀观察小噪点是否被滤掉、目标是否完整最后开包围盒看矩形框是否紧贴目标、中心是否平稳。每一步都通过 VGA 切换观察不靠猜。阈值整定有个经验先在正常光照下调好一组值然后故意把灯关掉一半看目标丢失的程度再决定阈值范围留多大余量。如果余量要求太大说明算法本身对光照太敏感这时候要么加自动增益要么换 HSV 空间做判断但 HSV 的除法在 FPGA 里更费资源得权衡。处理速度上这套流水线做到 900 万像素每秒流水线延时约 64 微秒工作于像素频率功耗比 DSP 方案低。验证方法很简单拿一个秒表或者信号源做参照让目标快速移动看包围盒跟不跟得上跟得上且不抖就说明流水线延时和带宽都够。从那以后我每次做 FPGA 图像流水线都强制先把中间结果引到 VGA 走一遍再谈算法优化。希望帮到你。本文还有配套的精品资源点击获取
返回列表