1. 项目概述
最近在野火征途Pro FPGA开发板上实现了一个基于帧差法的运动目标检测系统,使用OV5640摄像头作为输入源,同时支持TFT LCD、VGA和HDMI三屏输出。这个项目最吸引我的地方在于它完美展现了FPGA在实时图像处理领域的独特优势——超低延迟、高并行性和确定性时序控制。
运动目标检测是计算机视觉的基础任务之一,在安防监控、智能交通、人机交互等领域都有广泛应用。而帧差法作为一种轻量级的检测算法,特别适合在FPGA上实现。与基于深度学习的目标检测方法相比,帧差法虽然精度稍逊,但资源占用极低,处理速度更快,非常适合对实时性要求高的边缘计算场景。
2. 硬件平台选型与配置
2.1 开发板核心特性
野火征途Pro开发板搭载Xilinx Artix-7系列FPGA芯片,具体型号为XC7A100T-2FGG484I,具有以下关键特性:
- 101,440个逻辑单元
- 4,860 Kb块RAM
- 240个DSP切片
- 6个时钟管理单元
- 支持DDR3内存控制器
这些资源对于实现实时图像处理算法绰绰有余。开发板还提供了丰富的外设接口,包括:
- 40-pin摄像头接口(支持OV5640)
- HDMI输出接口
- VGA输出接口
- 4.3寸TFT LCD显示屏
- 多个用户按键和LED指示灯
2.2 OV5640摄像头配置
OV5640是一款500万像素的CMOS图像传感器,支持输出多种分辨率和格式。在本项目中,我们将其配置为输出720P(1280×720)@30fps的RGB565格式数据流。
摄像头初始化通过I2C接口完成,需要配置约30个寄存器。以下是几个关键配置项:
c复制// 时钟配置
i2c_write(0x3103, 0x11); // 系统时钟分频
i2c_write(0x3035, 0x21); // PLL控制
i2c_write(0x3036, 0x69); // PLL控制
// 分辨率设置
i2c_write(0x3808, 0x05); // H_SIZE[15:8]
i2c_write(0x3809, 0x00); // H_SIZE[7:0] (1280)
i2c_write(0x380a, 0x02); // V_SIZE[15:8]
i2c_write(0x380b, 0xd0); // V_SIZE[7:0] (720)
// 数据格式配置
i2c_write(0x4300, 0x61); // RGB565输出
i2c_write(0x501f, 0x01); // RGB格式选择
在实际调试过程中,发现直接使用厂商提供的参考配置有时会出现色彩偏差。通过示波器抓取I2C波形确认配置确实生效后,最终在寄存器0x5001中强制设置了RGB565格式才解决问题。
3. 帧差法算法实现
3.1 算法原理
帧差法的核心思想是通过比较连续帧之间的差异来检测运动区域。其数学表达式非常简单:
code复制diff = |current_frame - previous_frame|
motion = diff > threshold
在实际应用中,为了减少噪声影响,通常会采用三帧差分法:
code复制diff1 = |frame2 - frame1|
diff2 = |frame3 - frame2|
motion = (diff1 > threshold) && (diff2 > threshold)
这种方法可以有效抑制因光照变化或传感器噪声导致的误检测。
3.2 Verilog实现
在FPGA中,我们使用流水线结构来实现三帧差分算法。以下是核心代码:
verilog复制// 灰度转换模块
always @(posedge clk) begin
if(de) begin
// RGB565转灰度:Y = 0.299R + 0.587G + 0.114B
gray_value <= (77 * rgb[15:11] + 150 * rgb[10:5] + 29 * rgb[4:0]) >> 8;
end
end
// 三帧差分模块
always @(posedge clk) begin
if(de) begin
gray_1 <= gray_2; // 前前帧
gray_2 <= gray_3; // 前帧
gray_3 <= gray_value; // 当前帧
// 计算相邻帧差分的绝对值
diff1 <= (gray_3 > gray_2) ? (gray_3 - gray_2) : (gray_2 - gray_3);
diff2 <= (gray_2 > gray_1) ? (gray_2 - gray_1) : (gray_1 - gray_2);
// 当连续两帧差分都超过阈值时,判定为运动区域
motion <= (diff1 > THRESHOLD) && (diff2 > THRESHOLD);
end
end
实现时需要注意以下几点:
- 灰度转换系数采用整数近似(77,150,29)代替浮点数(0.299,0.587,0.114)
- 差分计算使用绝对值避免有符号数运算
- 所有操作都在像素有效信号(de)的控制下进行,确保时序正确
3.3 阈值选择与动态调整
阈值THRESHOLD的选择对检测效果影响很大。经过实验,我们发现以下经验值效果较好:
- 室内环境:15~30
- 室外环境:30~50
- 高对比度场景:50~80
为了方便调试,我们通过开发板上的按键实现了阈值的动态调整:
verilog复制reg [7:0] threshold = 8'd30;
always @(posedge key_pressed) begin
if(key_inc) threshold <= (threshold < 100) ? threshold + 5 : 100;
if(key_dec) threshold <= (threshold > 5) ? threshold - 5 : 5;
end
4. 形态学处理优化
4.1 膨胀操作实现
原始的帧差分结果往往存在噪声和空洞,通过形态学膨胀操作可以改善这一问题。我们使用3×3的结构元素进行膨胀处理:
verilog复制// 行缓存实现
reg [7:0] line0 [0:1279];
reg [7:0] line1 [0:1279];
reg [7:0] line2 [0:1279];
always @(posedge clk) begin
if(href) begin
// 更新行缓存
line0[col] <= line1[col];
line1[col] <= line2[col];
line2[col] <= motion; // 当前像素运动标记
// 边界检查
if(col > 0 && col < 1279) begin
// 3x3窗口或操作
dilate_out <= |{line0[col-1], line0[col], line0[col+1],
line1[col-1], line1[col], line1[col+1],
line2[col-1], line2[col], line2[col+1]};
end else begin
dilate_out <= motion; // 边界不做处理
end
end
end
4.2 资源优化技巧
行缓存会占用大量Block RAM资源,针对不同分辨率可以采取以下优化措施:
- 对于小分辨率(如640×480),可以使用寄存器实现行缓存
- 对于大分辨率(如1280×720),使用Block RAM实现行缓存
- 采用降采样处理,先降低分辨率再进行形态学处理
在我们的实现中,由于处理的是720P图像,选择使用Block RAM来实现行缓存,三个行缓存共占用:
1280 × 3 = 3840 bits ≈ 4.8 KB
5. 多显示接口输出
5.1 显示接口架构
系统需要同时输出到三个显示设备:
- 4.3寸TFT LCD(800×480)
- VGA(1280×720)
- HDMI(1280×720)
我们采用AXI4-Stream总线架构来实现视频数据的分配:
code复制 +------------+
| |
| 视频处理 |
| 流水线 |
| |
+-----+------+
|
+-----v------+
| |
| AXI4-Stream|
| 分配器 |
| |
+-----+------+
|
+----------------+----------------+
| | |
+-------v------+ +-------v------+ +-------v------+
| | | | | |
| TFT LCD | | VGA | | HDMI |
| 控制器 | | 控制器 | | 控制器 |
| | | | | |
+--------------+ +--------------+ +--------------+
5.2 HDMI输出实现
HDMI输出需要特别注意时钟域转换问题。VGA控制器通常工作在像素时钟(如25MHz),而HDMI的TMDS时钟是其5倍(125MHz)。我们使用异步FIFO来解决跨时钟域数据传输:
verilog复制hdmi_transmitter u_hdmi(
.video_clk(vga_clk), // 25MHz
.tmds_clk(tmds_clk), // 125MHz
.reset_n(~reset),
.rgb_in({red, green, blue}),
.hsync_in(hsync),
.vsync_in(vsync),
.de_in(de),
.tmds_out(tmds_data),
.tmds_clock(tmds_clk_out)
);
async_fifo #(
.DATA_WIDTH(24),
.FIFO_DEPTH(2048)
) u_async_fifo (
.wr_clk(vga_clk),
.rd_clk(tmds_clk),
// ...其他信号连接
);
经过测试,FIFO深度设置为2048时可以有效避免因时钟抖动导致的画面撕裂问题。
6. 系统集成与性能优化
6.1 资源占用分析
整个系统在XC7A100T上的资源占用情况如下:
| 模块 | LUTs | FFs | BRAMs | DSPs |
|---|---|---|---|---|
| 摄像头接口 | 320 | 450 | 0 | 0 |
| 帧差法核心 | 1200 | 980 | 0 | 3 |
| 形态学处理 | 800 | 650 | 3 | 0 |
| 显示控制器 | 1500 | 1200 | 6 | 0 |
| 总计 | 3820 | 3280 | 9 | 3 |
| 占比(%) | 37.6 | 32.2 | 18.8 | 1.25 |
从表中可以看出,系统还有充足的资源可用于算法扩展或功能增强。
6.2 性能指标
系统的主要性能指标如下:
- 处理分辨率:1280×720 @30fps
- 端到端延迟:<100ms(约3帧)
- 功耗:2.1W(静态)+ 1.5W(动态)= 3.6W
- DDR3带宽占用:约500MB/s
与基于CPU的软件实现相比,FPGA方案具有显著优势:
- 延迟降低10倍以上(软件方案通常有300-500ms延迟)
- 功耗仅为软件方案的1/5
- 无需操作系统,启动时间<1s
7. 实际应用与扩展
7.1 典型应用场景
这个系统非常适合以下应用场景:
- 智能监控:实时检测监控画面中的运动物体
- 无人机避障:检测前方障碍物
- 智能门铃:检测门前人员活动
- 工业检测:检测生产线上的物体运动
7.2 扩展方向
基于当前系统,可以考虑以下扩展方向:
- 结合光流法实现运动轨迹预测
- 添加目标跟踪算法(如KCF)
- 集成简单的分类器实现人/车识别
- 增加网络接口实现远程监控
在实际部署中,我发现帧差法对光照变化比较敏感。一个实用的技巧是在算法前端添加自动曝光控制模块,或者使用HSV色彩空间代替RGB进行处理,这样可以显著提高系统在复杂光照条件下的稳定性。