1. 项目概述:基于FPGA的实时车牌识别系统
去年在智能交通项目上折腾车牌识别时,我试过各种方案——从树莓派跑OpenCV到STM32移植轻量级模型,最后发现FPGA方案在实时性和功耗上优势明显。这次用正点原子达芬奇Pro开发板(Xilinx Artix-7 XC7A100T)实现的系统,从图像采集到字符识别全程硬件加速,实测延迟低于8帧(1080P@30fps下约267ms),功耗稳定在2.3W左右。
核心功能模块包括:
- OV5640摄像头数据采集与预处理
- 动态阈值二值化算法
- 基于形态学的字符分割
- 并行模板匹配识别
- HDMI实时显示与串口输出
特别适合需要低延迟、高可靠性的场景,比如高速公路ETC系统或停车场智能道闸。开发环境选用Vivado 2020.2,所有逻辑都用Verilog HDL实现,没有使用任何软核处理器,充分发挥了FPGA的并行计算优势。
2. 硬件架构设计
2.1 核心硬件选型
开发板选用正点原子达芬奇Pro主要考虑三点:
- 芯片资源:XC7A100T-2FGG484I提供101,440个逻辑单元,足够部署完整流水线
- 外设接口:原生支持HDMI 1.4a输出和DVP摄像头输入
- 扩展能力:板载USB-JTAG编程口和UART转USB,方便调试
摄像头选择OV5640的三大理由:
- 支持1080P@30fps输出
- 自动曝光/白平衡功能
- 通过SCCB总线可编程配置
实测发现需要特别注意摄像头的供电质量。当使用开发板自带LDO供电时,图像偶尔会出现横纹噪点。后来改用外接3.3V低噪声电源后问题解决。
2.2 系统时钟架构
整个系统涉及多个时钟域,必须谨慎处理跨时钟域同步:
verilog复制// 主要时钟生成模块
clk_wiz_0 instance_name
(
.clk_out1(clk_100M), // 主处理时钟
.clk_out2(clk_200M), // DDR控制器时钟
.clk_out3(clk_74M25), // HDMI像素时钟
.resetn(sys_rst_n),
.locked(pll_locked),
.clk_in1(sys_clk) // 板载50MHz晶振
);
关键时序约束示例:
code复制create_clock -period 10.000 -name clk_100M [get_ports clk_100M]
set_input_delay -clock clk_100M -max 2.000 [get_ports {cmos_data[7:0]}]
set_multicycle_path -setup 2 -from [get_clocks clk_cmos] -to [get_clocks clk_100M]
3. 核心算法实现
3.1 动态阈值二值化
传统固定阈值法在光照变化场景下表现糟糕,我们改进的动态算法包含三个关键点:
- 亮度统计窗口:仅分析图像顶部10行像素(约0.6%的图像面积),既降低计算量又避免车牌区域干扰
- 自适应系数:通过实测确定0.7倍均值最优,平衡了字符清晰度和噪声抑制
- YUV域处理:直接使用摄像头的Y分量,省去RGB转换开销
verilog复制// 动态阈值计算核心代码
reg [31:0] y_sum;
always @(posedge cmos_pclk) begin
if(!rst_n) begin
y_sum <= 32'd0;
pixel_cnt <= 0;
end else begin
if(pixel_cnt < FRAME_WIDTH*10) begin
y_sum <= y_sum + cmos_y;
pixel_cnt <= pixel_cnt + 1;
end else if(pixel_cnt == FRAME_WIDTH*10) begin
threshold <= (y_sum / (FRAME_WIDTH*10)) * 7 / 10; // 均值×0.7
pixel_cnt <= pixel_cnt + 1;
end
binary <= (cmos_y > threshold) ? 1'b1 : 1'b0;
end
end
实测技巧:在隧道等明暗交替场景下,可以给threshold寄存器添加α滤波(threshold <= α*new_val + (1-α)*threshold),避免阈值突变导致识别中断
3.2 字符分割优化
车牌字符分割面临三大挑战:
- 固定螺丝造成的伪影
- 车牌边框干扰
- 字符粘连情况
我们的解决方案:
verilog复制// 三级处理流水线
module char_segmentation(
input clk,
input binary_in,
output reg [7:0] char_boxes
);
// 第一级:水平投影滤波
reg [15:0] h_proj [0:IMG_HEIGHT-1];
always @(posedge clk) begin
if(binary_in) h_proj[row] <= h_proj[row] + 1;
end
// 第二级:垂直投影+腐蚀滤波
wire eroded = binary_in & binary_left & binary_right;
// 第三级:边界检测
always @(posedge clk) begin
if(eroded && !prev_eroded) left_edge <= col;
if(!eroded && prev_eroded) right_edge <= col;
end
endmodule
实测数据对比:
| 方法 | 准确率 | 处理延迟 |
|---|---|---|
| 传统投影法 | 78% | 2ms |
| 腐蚀+投影 | 93% | 2.4ms |
| 加入边缘检测 | 97% | 3.1ms |
4. 字符识别引擎
4.1 模板匹配加速
字符库存储在Block RAM中,采用并行比对架构:
verilog复制// 并行匹配核心逻辑
genvar i;
generate
for(i=0; i<36; i=i+1) begin : CHAR_TEMPLATES
always @(posedge clk) begin
if(template_en[i]) begin
match_cnt[i] <= (pixel == char_rom[i][addr]) ?
match_cnt[i] + 1 : match_cnt[i];
end
end
end
endgenerate
// 最大值选择
always @(posedge clk) begin
max_index <= 0;
for(int j=0; j<36; j=j+1) begin
if(match_cnt[j] > match_cnt[max_index])
max_index <= j;
end
end
模板生成技巧:
- 使用20×40像素模板分辨率
- 对每个字符采集100个真实车牌样本生成平均模板
- 加入3像素的容错边界
4.2 识别后处理
为提高输出准确性,加入三类后处理:
- 车牌规则校验:首位必须为汉字/字母,第二位必须为字母
- 时序滤波:连续3帧识别结果一致才输出
- 相似字符优化:区分'0'与'D'、'8'与'B'等易混淆字符
verilog复制// 车牌规则检查示例
function check_plate_rules;
input [7:0] chars[7];
begin
// 第一位检查
if(!(is_chinese_char(chars[0]) || is_letter(chars[0])))
return 0;
// 第二位检查
if(!is_letter(chars[1]))
return 0;
// 其他位检查...
end
endfunction
5. 系统集成与调试
5.1 视频流水线架构
verilog复制module video_pipeline(
input cmos_pclk,
input cmos_vsync,
input [7:0] cmos_data,
output hdmi_clk,
output [23:0] hdmi_data
);
wire [7:0] y_channel;
wire binary_out;
// 处理流水线
cmos_decoder decoder(.*);
dynamic_threshold threshold(.*);
char_segmentation segmentation(.*);
template_matching matching(.*);
// HDMI输出控制
hdmi_driver driver(
.clk_74M25(clk_74M25),
.video_data({char_boxes, binary_out, y_channel}),
.*);
endmodule
5.2 调试技巧
- Xilinx ILA使用:关键信号添加在线逻辑分析仪
tcl复制create_debug_core u_ila_0 ila
set_property C_DATA_DEPTH 1024 [get_debug_cores u_ila_0]
set_property C_TRIGIN_EN false [get_debug_cores u_ila_0]
- 串口调试协议:
code复制[00:03:45] PLATE: 京A12345 CONF:98%
[00:03:46] DEBUG: Threshold=0x7A
[00:03:47] WARN: Char[3] low_conf(65%)
- 功耗优化:
- 对非关键路径设置多周期约束
- 使用时钟使能替代门控时钟
- 对Block RAM启用写时钟门控
6. 实测性能与优化方向
6.1 资源占用报告
| 模块 | LUT | FF | BRAM | DSP |
|---|---|---|---|---|
| 图像预处理 | 3,212 | 4,567 | 0 | 0 |
| 字符分割 | 1,785 | 2,901 | 2 | 0 |
| 模板匹配 | 8,712 | 12,456 | 36 | 0 |
| HDMI输出 | 2,345 | 3,678 | 1 | 2 |
| 总计 | 16,054 | 23,602 | 39 | 2 |
6.2 识别率测试数据
测试环境:地下停车场,光照范围50-1000lux
code复制| 场景 | 样本数 | 正确数 | 准确率 |
|----------------|--------|--------|--------|
| 正面光照 | 200 | 198 | 99% |
| 侧光 | 150 | 142 | 94.7% |
| 逆光 | 100 | 89 | 89% |
| 夜间补光 | 50 | 46 | 92% |
6.3 后续优化方向
-
算法升级:
- 加入CNN加速器实现端到端识别
- 尝试YOLOv3-tiny的车牌检测
- 引入LSTM进行序列识别
-
硬件改进:
- 改用K7系列FPGA提升处理能力
- 增加红外摄像头支持夜间模式
- 添加GPS模块记录位置信息
-
功能扩展:
- 支持新能源车牌识别
- 增加车牌颜色识别
- 开发无线远程升级功能
这个项目最让我惊喜的是纯硬件方案的效率——没有操作系统开销,没有线程调度,所有模块像精密钟表一样协同工作。下次准备尝试用HLS实现部分算法模块,看看能否在保持性能的同时提升开发效率。