转载自B站up主“在天上飞的TTTTT”大佬的笔记,转载请取得原作者授权
1 写在前面
好多佬们问我开不开源,这个等我考完研吧,先写一篇学习路线给大家参考,同时鼓励大家多多参与科创竞赛,相信自己,只要肯学,都会有的。
up算是一个小白,用了八个月时间全栈过了一遍FPGA,上位机开发,YOLO模型部署,本来以为能够稳3争2, 没想到拿了国一,也算是运气比较好吧。当然也付出了相当多的努力,别人开始考研的时候我还在学FPGA,每天早上8点学到晚上11点,有时候会熬夜加班,分赛区决赛和全国总决赛基本上通宵干活的。
//************************************************防杠声明**************************************************//
up只是小白,如果文中有技术错误or讲不清楚的地方,欢迎各位大佬评论区批评指正
//************************************************防杠声明**************************************************//
1.1 专业背景
up是微电子学院,集成电路设计与集成系统专业的,大二(2022年)有FPGA的专业课,学习了一些基础的verilog语法,用zynq7020做了一个硬件串口。
实际上up的基础一般,跟视频相关,接口相关(VESA HDMI PCIE ETH HSST)也是一点点学起来的,大家大可以把我当作小白。
因为对FPGA有那么点熟悉,所以集创赛选择赛道的时候选择了FPGA赛道,又对视频处理有那么一点点兴趣,所以选择了紫光同创杯。
1.2 团队分工
up本人:全栈过了一遍FPGA设计,上位机设计,YOLO模型部署和优化;
队友A:六七月去了夏令营,PCIe上位机没做完,被up接手了,八月回来做上位机GUI优化和一部分视频算法;
队友B:前期完成了yolo的部署和部分功能的实现,另一方面为三人小队顶住了课业压力(小学期实习课程团队答辩),七八月暑假回家了,在他指导下up一周速通了yolo的环境搭建和部署,后续又做了软件的优化,模型的量化和加速;
1.3 心路历程
- 1~2月:选定赛题,开始学习FPGA(可以看看我B站笔记的),学习VESA规格,X家的DMA IP,VDMA IP等等,初步了解帧缓存、AXI协议等等;
- 3月:忙着开学考试、做学院的PPT,我们跟组委会借了板子,也自己买了一块,三月底开始尝试PDS和板子的一些基础功能,跟着奇哥复习AXI总线(奇哥的AXI真的讲的很透彻,建议大家都看看,资料汇总我放在后面),三月底基本上了解紫光DDR IP的使用,开始尝试经过DDR帧缓存的环回;
- 4月:四月初做完了一路AXI主机的DDR缓存,实现了HDMI视频环回;然后开始了解视频缩放,根据双线性插值算法,自己写了verilog逻辑(有点费资源);四月中下旬完善了四路AXI主机和AXI仲裁器,做完了HDMI四路的视频环回,后续买了双目摄像头,又改成了摄像头输入两路,不过bug不断,debug了很长时间;四月下旬开始学习PCIe协议,同时了解紫光PCIe IP的使用;这个时候视频环回的链路已经基本实现了;
- 5月:校运会来了,学生会忙起来了,做项目时间减少,同时五月底还有专业课考试,忙起来了;继续研究PCIe,写了几个PCIe demo让队友做测试(有点像黑箱,一点点试,不过看紫光IP核给的仿真会清晰很多,配置过程,DMA过程等等,笔记放后边);初赛交作品前已经实现了PCIe抓取单帧图片传输并保存为bmp文件,不过由于当初没考虑到大小端转换,所以传的图片有撕裂,而且色彩有问题;另外没有考虑好上位机怎么和YOLO传输数据;
- 6月:一边开发FPGA的以太网UDP协议,一边摸鱼,等分赛区决赛结果,跟着正点原子过了MDIO,RGMII转GMII,ARP和UDP协议;进分赛区决赛后开始加足马力继续肝项目,这时候队友A去夏令营了,所以PCIe上位机被我接手了,摸清楚上位机结构之后,修修补补实现了PCIe抓取视频流并显示,通过虚拟摄像头传递给YOLO识别(这里是GPT提醒我们能够使用v4l2,队友B最后通过ffmpeg实现了视频流传递)
- 7月:一边是小学期生产实习课程压力(这里感谢队友B帮我顶住了小组答辩的压力,让我有时间专注项目),一边是继续肝项目,在紫光PCIe测试平台的基础上,up改了一套测试平台v2.0,另外以太网遇到了点问题,分赛区决赛前没调出来(国赛也没调出来);分赛区决赛,东北赛区五进一,没想到我们完成度相对来说很高,此时已经实现了HDMI输入,双目OV5640输入拼接,HDMI输出,PCIE输出,上位机能够顺利抓取和给YOLO识别,识别帧率40FPS左右(已经通过TensorRT加速),最后一等奖进了国赛。七月下旬队友B放假回家,我留校继续做项目,一个周速通YOLO,一个周对软件代码做了优化,帧率从40FPS提高到60FPS。
- 8月:放假了,专注项目,队友A回归,开始做视频算法,做了直方图均衡,但是对彩色的并不友好,放弃了,做了简单的RGB YUV转换,在Y分量上操作,简单的视频增强,接着去做上位机GUI优化;我继续用一个周调网口,环回输入没调出来,但是做了以太网上位机;我继续用三天速通了光口,参考IEEE 802.3写了握手协议。最后只给了半个周来做PPT和文档完善,时间实在不够,我一边做文档一边又再看yolov8,最后提供了多种模型以供选择,19号从大连飞重庆,在机场延误四个小时,差点没交上作品,PPT和文档没做好是遗憾之一。
2 FPGA设计
2.1 结构框图
提供了两种设计方案,由于PLG50H只有一个HSST资源,所以PCIE 光口不可兼得。
以双板方案为例,开发板1负责视频采集和拼接,提供了HDMI输入、缩放后通过光口环回,两路视频输入,OV5640两路视频输入,通过四路AXI主机仲裁实现帧缓存,输出时对四路画面进行拼接;开发板2负责图传,视频信号通过HDMI与开发板1连接,有一路AXI主机帧缓存,提供HDMI、PCIE、ETH三种输出方式。
视频链路 | 分辨率 | 刷新率 | 色彩格式/色深 |
HDMI | 1920×1080 | 60Hz | RGB888 |
OV5640 | 960×540 | 30Hz | RGB565 |
内部数据链路 | – | 与视频源相同 | 32bits |
*HSST光纤环回 | 960×540 | 60Hz | RGB565 |
2.2 双线性插值算法
双线性插值算法是根据网上的原理来写的,用了RAM资源来缓存,主要是确保好RAM或者FIFO预读出正确就行,保证de和像素数据同步,最后实现了1080P->540P,但是我这样写似乎比较浪费资源,有大佬有更好的写法
2.3 AXI主机仲裁器与帧缓存
为了保证视频正常输出,通过DDR缓存是必要的,紫光的DDR IP是精简的AXI FULL接口,所以AXI协议肯定要整明白,建议大家啃ARM的手册,另外可以和奇哥学,B站有视频(FPGA奇哥,链接放后面),讲的很透彻。
使用四路帧缓存设计,AXI主机能够实现地址自增、帧缓存切换等功能,同时读写状态机独立设计
为了保证四路视频正确缓存,还需要有序地把AXI主机接入DDR IP,多路视频缓存可以参考正点原子双目OV5640的例程,看看他的状态机设计和接入方法,这里我是自己写了仲裁器的读写状态机,对于不同的输入都有较好的兼容性。仲裁器根据每路FIFO的水位控制AXI握手。
2.4 视频拼接和HDMI输出
这里是用了一个能够产生VESA时序的模块(本质上是计数器),能供提供vs,hs,de和输出扫描的xy坐标,根据上述信号控制每路读FIFO输出即可。要控制好输出像素和de信号对应,不然输出会出现倾斜或者错位。
2.5 PCIe接口
这部分可以算是比赛的一个难点之一,但是大家进国赛了基本上都做出来了。
首先是要学习PCIe协议,这里推荐一个大佬写的:http://blog.chinaaet.com/justlxy/p/5100053251,主要要理解PCIe TLP 和 Header类型,发送数据用Mwr 32比较多
然后还要看紫光的PCIe怎么用,这里要看他仿真的过程,RC如何配置EP,RC如何操控EP进行DMA,仿真看懂了可以上板子,通过debug核抓信号,进一步验证。最后再自己魔改,写verilog和上位机,发送符合协议要求的TLP包,紫光这个PCIe IP给的是AXI-S接口,还算好用。
另外要多看IP手册,了解引脚定义和功能,以及例程的相关说明;
而且PCIe需要搭配上位机食用,上位机主要涉及配置PCIe,分配DMA空间,控制FPGA采集,释放DMA空间。
2.6 以太网接口
以太网可以跟着正点原子的FPGA课程学习,了解MDIO、RGMII转GMII、ARP、UDP等,RGMII转GMII可以参考小眼睛官方给的例程。
这里遇到的一个问题,就是开发板的两个网口对接之后(本来想做环回的),一个网口gmii转出来的rx_error信号一直拉高,另一个网口gmii的valid信号一直拉高,根本没法用,但是跟电脑网口或者其他开发板网口对接就没有这种问题。这个问题困扰了两个月,一直没有解决。决赛答辩的时候问了这个问题,徐工说可能是rx_delay设置的问题,这个还没有尝试。
由于上面这个问题,环回输入没做成,想让开发板和另一块开发板做远端环回的,但是存在断连的情况,有时候发送着发送着灯就灭了,画面停顿,过了几秒两个网口重新自协商继续传输。
最后只做了以太网发送视频到上位机,基于UDP协议,实现540P视频的发送,但是丢包严重,不得不在每个UDP包里面加上序号保证丢包不会造成大规模画面撕裂;
2.7 HSST 光模块
首先是看IP手册,然后看例程的仿真,观察HSST怎么工作的。
这里有个坑点是HSST IP不完整,还需要用户自己实现握手,在复位后发送有序集和组码进行握手,字节对其,参考IEEE 802.3 36小节。
在这基础上可以把HSST当成一个提供了GMII接口的PHY芯片,我在上面又套了一个UDP协议。
3 上位机设计
3.1 GUI设计
紫光的PCIe测试平台魔改过来的,可视化界面用的gtk库,用法和QT很像,照猫画虎地增加了很多功能。
3.2 PCIe上位机
紫光原本提供的PCIe测试平台只能在ubuntu16.04使用,后来我迁移到了ubuntu23.04,主要是linux PCIe分配DMA内存和释放内存的函数有了更新,旧版函数没办法在新系统里面使用。
PCIe上位机主要是要看懂官方给的代码和功能对应的关系,如何分配内存,如何通知FPGA开始DMA操作,看懂了才能魔改驱动。原本限制DMA大小4096Byte,我们改成了DMA大小是4096x1024Byte,刚好大于1920x1080x2Byte,可以用于视频传输。
上位机同样是用了帧缓存设计,根据PCIe TLP包带的数据判断哪一帧是最新帧,从而让程序把数据从内核态传递给用户态,再在用户态对视频数据做处理。
3.3 以太网上位机
以太网上位机比PCIe上位机好写多了,由于底层驱动网卡厂家给你写好了,只需要会用socket编程就行,网上大把资料,用socket绑定好网口之后接手就行。
另外如果有能力可以用ARP和开发板交换一下IP和MAC,不然用静态IP也行。
3.4 上位机的数据链路
在GPT的提醒下,我们使用了V4L2和FFMPEG,将视频流传递给/dev/video0,YOLO和其他应用调用虚拟摄像头就可以读取到视频流。
上位机集成的YOLO开关通过.sh脚本实现C对python的调用。
4 AI算法部署
4.1 模型选择和数据集处理
这里我们选择了YOLO的模型,提供了YOLO V5S V5M V8N V8S V8M多个模型以供调用,均训练了150轮。(下面这个图我们费了老大劲了,在AUTODL平台上租了卡连着跑了好久,虽然理论上v8要好不少,但是实际检测的时候感觉v5效果更好一些)
关于YOLO,CSDN上有很多资料,保姆级手把手教你配环境,标注,划分数据集,训练,检测。部署好YOLO之后可以看看YOLO检测代码(detect.py)的解读,如果不需要深入理解,看懂检测代码也就够了。
另外YOLO V8把各种功能都封成API了,所以建议参考YOLO官方文档,看懂了v5,学v8也很快。
我们选用了官方提供的交通灯数据集(VOC数据集中的一个小类好像),但是确实一般,训练好后map只有0.65+,国一的几支队伍好像没用官方给的,而是直接用了COCO。
4.2 模型量化和加速
量化主要是将FP32转换为FP16,上面的图可以看见速度提高了很多;
加速使用了英伟达的TensorRT推理框架,原理不多介绍,在降低一些精度的情况下大幅提高了推理速度,部署CSDN上也有很多。
另外16:9输入的视频,在YOLO选择输入图片参数的时候可以将640×640改成384×640,这样速度会快很多,反正宽度不够的也是补无用数据。
4.3 软件优化
其实v5官方提供的代码有一个问题,用opencv库进行数据的载入和显示(cv2.read()和cv2.imshow()),这很吃CPU和内存,通过line_profiler性能分析工具可以分析每段代码的用时,尝试了多种方法之后发现子线程输入,主线程推理,子进程显示的帧数最高,从40FPS提高到了60FPS,这算是我们一个创新点。
5 学习资料汇总
5.1 他山之石,可以攻玉
- 小眼睛官方例程
- 紫光IP手册(不管是谁家,手册肯定是要啃的)
- 正点原子FPGA开发指南(正点原子资料开源,而且对新手友好,很多设计可以参考),视频教程:https://www.bilibili.com/video/BV1Ec411K791/?spm_id_from=333.999.0.0&vd_source=789bfbff82733918b995c9c278a70ea0
- 各种协议的原始文档(如AMBA IEEE 802.3等)
- AXI和其他FPGA设计学习资料:FPGA奇哥
- PCIe学习资料:http://blog.chinaaet.com/justlxy/p/5100053251
- 手把手搭建yolov5:https://www.bilibili.com/video/BV1f44y187Xg/?spm_id_from=333.999.top_right_bar_window_custom_collection.content.click&vd_source=789bfbff82733918b995c9c278a70ea0(这个up在CSDN有图文教程)
- 一行行读懂yolov5:https://www.bilibili.com/video/BV1Dt4y1x7Fz/?spm_id_from=333.337.top_right_bar_window_custom_collection.content.click&vd_source=789bfbff82733918b995c9c278a70ea0(看懂了就可以瞎改v5了)
5.2 UP自己的笔记
我认为养成做工程写blog的习惯很重要,所以一路上做了很多笔记,之前是在b站记的,但是感觉编辑器不友好,所以改用有道云了。
防杠声明,由于笔记是随着工程一点点写的,早期工程有bug,后续改进后笔记没更新,大家做个参考即可。
https://note.youdao.com/s/9yuHHCgu
2.VESA Monitor Timing Standard 时序实现与HDMI输出:
https://note.youdao.com/s/ExhGhZTu
https://note.youdao.com/s/KmRKDEAm
https://note.youdao.com/s/LZGig5Lo
https://note.youdao.com/s/XnX3b3fq
https://note.youdao.com/s/U6H3m1a6
https://note.youdao.com/s/aGw6AUt2
https://note.youdao.com/s/BCYt4itk