今年准备参加FPGA大赛,选了车牌识别方向。现在用Zynq实现了YOLO检测和字符识别,但字符分割模块在PL端处理时总是丢帧,尤其是多车牌场景。想问下高手,字符分割的流水线怎么设计才能保证实时性?比如二值化、投影分割这些步骤是串行还是并行好?资源占用和帧率怎么平衡?求具体方案和代码思路。
2026年FPGA大赛备赛,用Zynq做实时车牌识别时,字符分割模块的流水线怎么优化才能不丢帧?
提问
回答 3

做车牌识别的Zynq流水线,字符分割丢帧最常见的原因不是算法慢,而是数据流没对齐。YOLO检测完输出的是不定长的候选框,PL端二值化加投影分割如果按固定时序跑,遇到多车牌时后一帧的检测结果已经来了,前一轮分割还没收尾。个人建议你把二值化和水平投影做成纯流水线,用FIFO缓存一整个二值化后的行数据,然后对每一行做累加——这样每个像素进来时就能并行算出行投影累加值,而不需要等整帧结束。垂直投影稍微麻烦一点,因为要等水平分割出的字符区域再单独过垂直投影,这里可以用一块Block RAM做局部缓存,只存当前字符列的列累计值,不存全图。资源上,BRAM和DSP尽量留给YOLO,字符分割多用LUT和移位寄存器实现累加器,一般不会超过3000个LUT。另外注意一个坑:如果YOLO输出的候选框有重叠,分割前要做NMS后处理,否则投影会粘连。你目前YOLO检测是纯PL还是PS端跑?如果是PS跑DPU,那字符分割的输入接口最好用AXI-Stream加TLAST信号来对齐帧边界,不然中断式搬运很容易丢。

既然选了Zynq做实时车牌识别,字符分割丢帧的根源往往不是分割算法本身慢,而是整个流水线的吞吐量和握手设计没匹配上。你提到YOLO检测和字符识别都已经实现,那说明这两个模块的帧率是够的——问题出在中间那个字符分割模块成了瓶颈。我建议你重新审视一下数据流的组织形式:不要等一帧完整图像的所有YOLO框都出来之后才做分割,而是每检测到一个车牌候选框,就立刻把这个框的ROI区域传给分割模块流水线。这样做的关键是ROI提取要快,用Zynq的VDMA加行缓存,只把框内的行数据喂给分割模块,而不是整帧搬运。对于分割流水线本身,二值化和水平投影可以完全并行:二值化是逐像素的,投影累加是逐行的,两者之间用一个小型行FIFO(深度等于图像宽度)做缓冲,这样二值化结果一出来就能累加进行投影寄存器,不需要等整帧二值化完成。垂直投影则需要等水平分割确定了每个字符的列起止位置之后再做,这部分没法完全并行,但可以用双缓冲机制——当当前字符做垂直投影时,下一字符的列数据已经在另一个缓冲区里准备好了。资源占用方面,字符分割模块尽量不要用DSP48,那些留给YOLO更值,用LUT加移位寄存器就能实现累加器,BRAM用来存投影直方图,单通道128深度就够。最后提一下帧率平衡:如果你YOLO跑30fps,分割模块至少要能处理每帧4-6个车牌,每个车牌7个字符,也就是每秒约180个字符的吞吐。按我的经验,上述流水线在100MHz时钟下能轻松做到每字符50个时钟周期以内,足够用。不过你最好先确认YOLO输出给分割模块的接口是用AXI-Stream还是直接FIFO,如果是前者,注意TLAST和TREADY的信号时序要对齐,否则丢帧往往是因为分割模块没及时拉低ready导致上游数据被覆盖。你目前YOLO用的哪个网络结构?YOLOv3-tiny还是YOLOv8-nano?这两个对BRAM和DSP的占用差别挺大,会影响分割模块能用的资源余量。

看你描述,YOLO检测和字符识别都跑通了,那瓶颈大概率不在算法本身,而在PL端数据流的握手方式上。一个容易被忽视的点是:字符分割模块的输入输出接口是不是用了AXI4-Stream且带了valid-ready握手?如果分割模块内部是固定延迟的,外部YOLO输出的候选框又是突发式的,那当分割模块还在处理上一帧的某个字符时,新来的框数据可能会被覆盖或丢弃。建议你在分割模块的入口加一个深度不大的异步FIFO(比如深度16,用BRAM或LUT实现),只缓存YOLO检测出的车牌候选框坐标和对应的ROI地址,而不是缓存整帧图像。这样分割模块可以按自己的节奏逐个消化框,不会阻塞YOLO的检测流水线。另外,二值化和投影分割完全可以并行:二值化每个像素独立,投影累加只需要一行一行的局部和,用一个深度等于图像宽度的行缓存做滑动窗,每来一个新像素就更新当前行的累加值,这样分割延迟只取决于一行的像素数,而不是整帧。资源上,行缓存用分布式RAM,投影累加器用LUT加进位链,整体开销能控制在2000个LUT以内。你目前用的Zynq具体是哪款型号?如果是7020的话,LUT资源相对紧张,可能需要复用一部分寄存器来做累加,避免全用LUT。
发表回答
登录后可在本页底部提交回答
