1. 项目概述与硬件平台选型
去年接手一个智能停车场项目时,客户要求实现本地化的车牌识别功能。考虑到实时性和功耗要求,最终选择了基于FPGA的解决方案。这次分享的正是基于正点原子达芬奇Artix-7开发板(FPGA型号XC7A35T)实现的车牌识别系统,配套Modelsim仿真验证环境。
选择这款开发板主要基于三点考量:首先是Artix-7系列FPGA在图像处理方面的性价比优势,XC7A35T内置的DSP切片能高效处理卷积运算;其次是正点原子完善的周边生态,包括配套的OV5640摄像头模组和4.3寸RGB显示屏(800×480分辨率);最后是其丰富的接口资源,方便后期扩展其他功能模块。
2. 系统架构设计
整个系统采用流水线架构,各模块通过AXI-Stream接口互联。这种设计在保证200MHz主频下,能实现30fps的实时处理能力。关键数据流路径如下:
code复制OV5640摄像头 → 图像采集 → RGB转Ycbcr → Sobel边缘检测 → 形态学处理 → 字符分割 → 模板匹配 → LCD显示
特别要注意的是在模块间添加了FIFO缓冲,解决各处理环节耗时不均导致的时序问题。实测显示,添加双缓冲后系统稳定性提升40%以上。
3. 核心模块实现细节
3.1 图像采集与预处理
OV5640摄像头配置为输出RGB565格式,640x480@30fps。通过I2C配置寄存器时,有几个关键参数需要注意:
verilog复制// 关键配置参数示例
i2c_write(0x3103, 0x03); // 系统时钟源选择
i2c_write(0x3008, 0x82); // 软件复位
i2c_write(0x3818, 0xC8); // 水平镜像+垂直翻转
i2c_write(0x3621, 0x10); // ISP色彩矩阵控制
实际调试中发现,如果未正确配置PLL分频寄存器(0x3035-0x3037),会导致图像采集出现断层现象。建议先用正点原子提供的默认配置,再逐步调整。
3.2 RGB转Ycbcr优化实现
标准转换公式如下:
code复制Y = 0.299R + 0.587G + 0.114B
Cb = -0.169R - 0.331G + 0.500B
Cr = 0.500R - 0.419G - 0.081B
在FPGA中采用定点数运算优化:
verilog复制// 使用移位代替乘法
assign Y_temp = (R<<1) + (R<<3) + (G<<2) + (G<<4) + (G<<5) + (B<<1) + B;
assign Y = (Y_temp + 128) >> 8; // 加128是为了四舍五入
这种实现方式比直接使用DSP切片节省30%的逻辑资源,适合资源受限的场合。实测转换误差<2%,完全满足车牌识别需求。
3.3 边缘检测的硬件加速
采用改进的Sobel算子,将3x3卷积核拆分为行列分离计算:
code复制Gx = | -1 0 +1 | Gy = | -1 -2 -1 |
| -2 0 +2 | | 0 0 0 |
| -1 0 +1 | | +1 +2 +1 |
Verilog实现时采用流水线设计:
verilog复制always @(posedge clk) begin
// 行缓冲管理
line_buf[0] <= {pixel_in, line_buf[0][23:8]};
line_buf[1] <= line_buf[0];
// 列方向梯度计算
grad_x <= (line_buf[2][23:16] + (line_buf[2][15:8]<<1) + line_buf[2][7:0]) -
(line_buf[0][23:16] + (line_buf[0][15:8]<<1) + line_buf[0][7:0]);
// 阈值处理
edge_out <= (grad_sum > 8'h40) ? 8'hFF : 8'h00;
end
4. 形态学处理优化
车牌识别中,闭运算(先膨胀后腐蚀)能有效连接断裂的边缘。针对FPGA特点,我们采用3x3矩形结构元素,并优化计算顺序:
| 处理步骤 | 逻辑资源消耗 | 处理延迟 |
|---|---|---|
| 基础实现 | 1200 LUTs | 15 clk |
| 行列分离 | 800 LUTs | 20 clk |
| 本方案 | 650 LUTs | 18 clk |
关键实现技巧:
verilog复制// 膨胀运算优化
always @(posedge clk) begin
max_col[0] <= (pixel_in > line_buf[0][7:0]) ? pixel_in : line_buf[0][7:0];
max_col[1] <= (max_col[0] > line_buf[1][7:0]) ? max_col[0] : line_buf[1][7:0];
dilated_out <= max_col[1];
end
5. 字符识别方案对比
测试了三种字符识别方案:
-
模板匹配法:
- 优点:实现简单
- 缺点:旋转不变性差(车牌倾斜>15°时识别率<60%)
-
特征点法:
- 采用7段特征编码
- 识别率85%,但需要大量比较操作
-
改进投影法(最终采用):
- 垂直投影分割字符
- 水平投影定位基准线
- 结合笔画密度特征
- 识别率92%,资源消耗适中
特征提取关键代码:
verilog复制// 垂直投影统计
always @(posedge clk) if (de) begin
if (pixel_in > THRESHOLD)
v_proj[x_pos] <= v_proj[x_pos] + 1;
end
// 字符分割点检测
assign is_split = (v_proj[col] < 2) && (v_proj[col-1] > 5);
6. Modelsim仿真技巧
建立完善的验证环境需要:
- 图像数据导入:
verilog复制initial begin
$readmemh("plate_img.hex", rom);
for (i=0; i<307200; i=i+1) begin
@(posedge clk);
pixel_in <= rom[i];
end
end
- 自动校验机制:
verilog复制always @(posedge done_flag) begin
if (recog_char !== expected_char) begin
$display("Mismatch at %t: Got %h, Exp %h",
$time, recog_char, expected_char);
error_count = error_count + 1;
end
end
- 关键信号监测:
verilog复制always @(posedge clk) begin
if (sof && !in_range) begin
$warning("SOF out of valid range at %t", $time);
end
end
7. 实测性能数据
在Vivado中综合后的资源占用:
| 模块 | LUT | FF | DSP | 频率 |
|---|---|---|---|---|
| 图像采集 | 423 | 688 | 0 | 150MHz |
| 色彩转换 | 857 | 1024 | 3 | 200MHz |
| 边缘检测 | 1342 | 1895 | 8 | 180MHz |
| 字符识别 | 2105 | 2540 | 4 | 120MHz |
| 总计 | 4727 | 6147 | 15 | - |
识别准确率对比:
| 场景 | 白天 | 阴天 | 夜间 |
|---|---|---|---|
| 正面车牌 | 98% | 95% | 90% |
| 倾斜<10° | 96% | 92% | 85% |
| 污损车牌 | 88% | 82% | 75% |
8. 常见问题排查
问题1:图像采集出现横纹
- 检查OV5640的PCLK与FPGA时钟相位关系
- 确认帧缓冲的写指针复位逻辑
- 测量电源纹波(应<50mV)
问题2:边缘检测效果差
- 调整Sobel阈值(推荐值0.3~0.5倍最大梯度)
- 检查3x3窗口同步逻辑
- 确认Y分量动态范围(正常应为16~235)
问题3:字符误识别率高
- 重新校准字符分割阈值
- 检查模板库的字体匹配度
- 增加笔画连续性校验
9. 优化方向
-
算法层面:
- 改用自适应阈值的Canny边缘检测
- 引入HSV色彩空间的车牌定位
- 增加神经网络加速核(需升级至Zynq系列)
-
工程实现:
- 使用Vivado HLS实现部分算法
- 添加DDR3缓存提升吞吐量
- 设计AXI-DMA传输通道
-
扩展功能:
- 增加车牌颜色识别
- 支持多车牌同帧检测
- 添加UART上传识别结果
这个项目最让我意外的是,简单的形态学处理配合优化的投影法,就能达到商业级识别精度。在资源受限的Artix-7上,通过流水线设计和运算优化,完全能满足实时性要求。后续考虑将色彩转换和边缘检测模块封装成IP核,方便在其他项目中复用。