ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA工业相机开发实战:从传感器到USB3.0高速传输

FPGA工业相机开发实战:从传感器到USB3.0高速传输 1. 一个本来不被看眼的方案为什么值得做1.1 工业相机为什么贵一台像样的工业相机动辄几千进口品牌上万的型号也很常见。Basler、海康这些名字经常出现在选型表里价格并不便宜而且一旦接入产线你很难再改动它内部的图像处理和传输逻辑。很多做机器视觉项目的朋友都问过我能不能自己把相机造出来我去年把这整条链路做通了从传感器物理层一路做到USB3.0出图踩了不少坑也攒了不少能直接复用的经验。这篇就把完整开发过程拆给你看。说实话这个项目在立项时并不被一个做应用开发的朋友看好。他的理由是买个现成的工业相机又快又省心何必折腾传感器、FPGA、USB3.0这一大堆东西但从技术角度讲商用工业相机恰恰是一个“黑盒”。你拿到手的只有图像数据和SDK内部到底是怎样做Bayer插值、怎样做白平衡、怎样控制曝光和帧率你是看不到的。如果哪天真碰上特殊需求比如要接非标传感器、要做逐行触发、要在极低延迟下回传数据黑盒就会变成最大的阻碍。1.2 FPGA做相机核心有哪些优势FPGA做相机前端第一个优势是数据通路天然并行。传感器输出的像素数据是高速串行或并行的位流FPGA可以用硬件逻辑做PIPELINE式处理不像CPU那样逐指令执行。第二个优势是低延迟和确定性。曝光、读出、打包、传输这几件事可以完全由硬件时间轴驱动不会因为操作系统调度产生随机抖动这对产线触发类应用特别重要。第三个优势是接口灵活LVDS、MIPI、Sub-LVDS、并行CMOS接口都能用FPGA的IO去适配换传感器往往只需要改逻辑不用重新打板。所以这个项目不只是一个“自己做相机”的炫技更是一套完整的FPGA图像采集、处理和高速传输参考方案。适合的人群我也说说已经有FPGA基础但没碰过图像传感器的人做机器视觉项目但想脱离黑盒的人以及想弄清楚USB3.0到底怎么和FPGA打交道的人。这篇会讲清楚每个关键节点也把容易翻车的细节放在后面单列一节。2. 开局先定硬件架构从传感器到高速链路2.1 传感器选型要盯住的四个参数工业相机和应用级摄像头最大的差别之一是传感器快门方式。手机摄像头现在大多是卷帘快门逐行曝光逐行读出拍静止场景没问题但拍高速运动的工件会产生果冻效应。工业视觉场景必须用全局快门传感器所有像素同时开始曝光同时结束再统一读出这样才能在运动状态下抓住清晰的轮廓。选传感器时我会重点抓四个参数分辨率、帧率、像素尺寸和噪声表现。分辨率决定了视场内能看到多小的缺陷帧率决定了产线节拍上限像素尺寸直接影响灵敏度和动态范围噪声表现决定了暗部细节是否干净。Sony Pregius系列的IMX系列传感器就属于很省心的选择帧率和噪声都非常稳低预算方案可以用安森美的AR0134之类500万像素全局快门MIPI或者并行接口都有开源资料多适合做第一版。另外别忘了看传感器需要的供电电压和数据接口。早期我因为只盯着分辨率忽略了一个传感器需要多路供电的细节结果画原理图时电源树多出一路LAYOUT时又得很小心区分模拟地和数字地。后面我会单独提。2.2 板级架构与数据流向我的整体方案是这样图像传感器输出MIPI或LVDS数据进入FPGAFPGA负责传感器寄存器配置、曝光控制、数据接收、ISP处理和帧缓存处理完的图像通过USB3.0链路上传到PC端PC端上位机负责显示、存储和参数注入。为了保证帧率稳定中间还加了一颗DDR3颗粒做帧缓存防止USB传输突发时丢帧。这里有一个很重要的设计取舍FPGA内部是否真的需要DDR如果你的分辨率不高、帧率也不高并且USB3.0的传输带宽明显大于数据产生速率那理论上可以不挂DDR直接流式上传。但工业相机往往会遇到“短时间高帧率、长时间低帧率”的场景或者需要触发抓拍后立刻保存一帧这时候帧缓存就是刚需。我选择DDR3的原因很简单便宜、够用、参考资料多。DDR4或DDR4 SDRAM性能更强但对新手来说时序和训练复杂一些第一版没必要上。2.3 USB3.0两条路线怎么选FPGA和USB3.0打交道的方法我实际对比过两条主流路线。第一条是使用带USB3.0 PHY的SoC FPGA比如Intel Cyclone V系列内部自带USB OTG控制器可以直接当作USB设备来枚举。这条路线的好处是不用外挂桥接芯片缺点是逻辑复杂度高你要处理大量USB协议层内容还要面对RAM和FIFO管理开发周期明显拉长。第二条是使用独立的USB3.0桥接芯片比如赛普拉斯的FX3。FPGA只需要通过一个通用从FIFO或者GPIF接口把图像数据推给FX3FX3内部再把数据封装成USB包发出去。这条路线把USB协议栈交给桥接芯片FPGA侧只需要关心数据时序是我最终采用的方式也是目前多数开源工业相机会走的路。选FX3还有一个原因它提供了非常灵活的GPIF II接口可以配置成同步从FIFOFPGA侧只需要拉高写使能、给时钟、给数据就能把字节流灌进去。这种设计把FPGA从繁杂的USB协议堆栈里解放出来让你把更多精力放在图像质量上。如果你只是想把数据送到电脑里做实验也可以试试用Gadget模式的Linux开发板但这里不做重点。3. 传感器端到FPGA的物理链路实现3.1 电源和时钟是成像质量的底座很多第一次做图像传感器的人把原理图一画、PCB一打上电发现图像上有大量条纹和雪花第一反应是FPGA代码写错了其实是电源纹波和时钟抖动把传感器输出毁了。工业相机的传感器通常需要模拟电源、数字电源和IO电源模拟电源一般用低噪声LDO数字电源要关注瞬态响应。我第一版图省事数字电源直接用了开关电源结果MIPI时钟眼图很难看后来加了一颗LDO做二级稳压问题才解决。时钟部分也一样。传感器的工作时钟一般由板上的晶振提供也可以由FPGA通过PLL输出。新手可能会问直接从FPGA输出时钟不行吗可以但要注意PLL输出抖动会不会超传感器规格。很多传感器对输入时钟的phase noise有明确要求如果抖动太大像素时钟恢复会不稳定。我实际测试下来给传感器单独配一颗25MHz或27MHz有源晶振比从FPGA分出去干净得多。3.2 差分信号与PCB布线要避开的坑MIPI或者LVDS这类高速差分信号最基础也最不能错的事情是阻抗控制。MIPI对单端阻抗要求通常在50欧姆对应差分阻抗100欧姆Sub-LVDS也基本是100欧姆差分。如果没有按这个目标去做叠层和线宽眼图会直接劣化甚至达不到接收端的最小灵敏度。定位思路是拿到PCB厂家的叠层建议先算好线宽线距再让厂家做阻抗测试条不要等板子回来才量。除了阻抗还有等长和参考平面。MIPI的时钟通道能容忍的skew很小必须在FPGA引脚附近手动调整蛇形线让整个lane的走线长度差尽量小。参考平面要完整不要在差分线下面切地一但有地平面破口回流路径就会绕远直接变成EMI来源。另一个常见坑是差分线的换层换层处附近必须有回流地孔否则高速信号会在过孔处产生严重阻抗不连续。我踩过一个让我印象很深的坑传感器板子和FPGA板子之间用排线连接排线本身没有阻抗控制MIPI频率一高就全是乱码。后来把排线换成极短的FFC软排线并且严格控制长度才稳定下来。所以如果你想把传感器做成独立小板连接器选型真不能省。3.3 传感器寄存器配置和数据采集工业传感器的寄存器配置通常通过I2C或SPI接口完成。这里我会特意提一下“fpga实现uart_rx接收仿真”这个很多人入坑时做过的练习——UART其实和I2C/SPI一样都是把一个字节按约定时序送出去只要状态机写对了就没什么神秘。传感器寄存器配置模块的核心就是FPGA上电后从一个只读存储器里读出配置表产生时钟和引脚跳变把地址和数据写入传感器。数据采集则要看传感器的输出格式。老式的并行CMOS接口最直接包含一个像素时钟、行同步、场同步和若干位数据线。MIPI接口则要复杂些要先做lane对齐、字节对齐再做包头识别和行打包。设计采集模块时我建议把“采集原始数据”和“后续ISP处理”分开成独立模块中间只留一个FIFO这样调试时可以单独抓每个环节的数据定位问题会快很多。4. FPGA逻辑把原始数据变成干净的图像流4.1 MIPI/LVDS接收模块FPGA里最靠前的模块是物理接收层。MIPI CSI-2接收时D-PHY层要先做高速时钟的采集和DDR采样每个时钟沿采两个bit然后做串并转换把串行的数据流恢复成字节流再交给协议层解析。这部分也是很多做FPGA图像处理的人会觉得难的地方因为它既有高速模拟眼图问题又有协议解析逻辑问题。我在调试MIPI接收时用过一个笨但有效的办法先在传感器配置成输出测试图案模式比如纯色或彩条然后在FPGA里抓解析后的byte流对照传感器手册里的数据格式看是不是预期值。用这个方法能很快把lane mapping、byte order、时序参数这些最容易错的地方测出来。如果你用的是LVDS接口思路类似只是少了D-PHY协议解析直接做deserializer和word对齐就行。不管哪种接口接收模块出来的数据最好统一转成内部像素总线比如32位或64位宽这样后面ISP模块就不用关心传感器接口长什么样。这个抽象层非常值得做因为一旦以后换传感器你只需要改最前面的接收模块。4.2 ISP处理与定点数工业相机不能像手机一样只输出RAW图很多应用需要你在相机端就完成一定程度的图像处理。最基础的三件套是黑电平校正、白平衡和伽马校正。黑电平校正是把没有光照时的像素基线减掉白平衡是让白纸在画面里看起来是白的而不是偏蓝或偏红伽马校正是为了让图像在屏幕上的灰阶变化更符合人的视觉感知。这几个模块看起来简单实际做的时候有一个共同的关键点数据都用定点数来表示别用浮点。FPGA里浮点运算面积大、延迟高而图像像素都是整数用定点数加乘就能覆盖绝大多数需求。打个比方你要做白平衡增益系数1.5倍就把它表示成Q1.7格式也就是数值乘以128然后在乘法器里把像素乘以153最后右移7位。这样精度足够逻辑又非常省。还有一个容易被忽略的点ISP处理要保证数据流的连续性最好不要在中间为了等一个系数而去读内存。合理做法是先把系数算好放在寄存器里让像素流水线一启动就steady运行。这也是FPGA图像处理和写软件很大的区别——软件里你可以随时暂停算一下硬件里一旦流水线断流就会丢像素。4.3 帧缓冲与跨时钟域图像数据经过ISP处理后进入写入DDR3的路径。这里会遇到典型的跨时钟域问题传感器像素时钟是一路DDR控制器的时钟又是一路两者没有任何同步关系。我的做法是在写入端用一个异步FIFO做主时钟域隔离然后用DDR控制器把数据burst写进指定内存地址读端再用另一个异步FIFO把数据送到USB3.0链路。异步FIFO的深度要按最大瞬时突发量来算放太浅会在多帧并发时溢出丢数据。帧缓冲的地址管理也是一门学问。我维护了一套简单的寄存器组记录当前写帧地址、读帧地址和帧状态用ping-pong方式管理当传感器正在写第0帧时USB链路可以读第1帧等第0帧写完立刻切换。这样做的核心目的就是避免“同一帧被边写边读”导致图像撕裂。相比软件里的双缓冲FPGA里做ping-pong要额外考虑DDR的bank管理尽量让两个帧的地址落在不同bank组减少row switch开销。4.4 控制寄存器和复位处理相机肯定不能只用一套固定参数跑到底所以FPGA内部需要一组控制寄存器用来接收上位机下发的曝光、增益、白平衡系数等参数。实现方法通常是内部做一个AXI-Lite或者简单寄存器总线上位机通过USB控制端点修改寄存器的值FPGA再把寄存器值转成传感器I2C时序或内部ISP系数。这里我要专门强调一下“fpga复位信号亚稳态”这件事。图像处理流水线很怕异步复位的释放时间不满足要求如果全局复位在时钟上升沿附近释放就会让部分触发器先退出复位、部分后退出导致状态错乱。我的处理方法是外部异步复位进来后先经过两级同步器再用同步后的复位去复位各个模块同时这条同步复位路径上不要插组合逻辑。实测下来这个细节能省掉大量“时好时坏”的疑难杂症。5. USB3.0链路从FPGA到上位机5.1 桥接方案下的数据传输接口我用的FX3方案中FPGA和FX3之间走的是同步从FIFO接口。在这里FX3作为从设备FPGA作为主设备发起写操作。FPGA内部设计一个状态机拉低FLAGA等标志线准备好数据后拉高SLWR在时钟上升沿把数据送到FD[31:0]总线上等FIFO里的数据达到阈值FX3就自动把数据打包通过U3端口发往PC端。这个接口看起来简单但有一个速度匹配问题。FPGA这边产生图像数据的速率是固定的比如1920x108060fps约2.4GbpsUSB3.0理论带宽5Gbps实际有效带宽能做到3.5Gbps左右带宽是够的。但FX3内部DMA DMA缓冲如果配置不好会出现数据还没被USB取走、FIFO已经满的情况。所以我建议把GPIF接口的数据总线设置为32位时钟频率尽量和图像数据率匹配同时给FX3的DMA通道分配足够大的缓冲区。5.2 DMA与端点配置FX3要通过DMA把GPIF接收到的数据搬运到USB端点。这一层的配置很像嵌入式开发里的描述符链表你需要创建DMA管道把GPIF套接字和USB套接字连起来然后指定DMA缓冲区的大小和数量。缓冲区太小会导致吞吐上不去太大又会占用内存。我第一次配置时用的是默认16KB缓冲区结果高分辨率下帧率死活上不去后来改成32KB丢帧问题立刻缓解很多。想排查USB端点到PC端的瓶颈我建议先不接FPGA直接用FX3自带的USBBulkLoopAuto例程测一遍原始带宽。如果这个例程跑到3GB/s以上都稳定说明硬件链路没问题问题基本出在FPGA侧的数据供给时序。如果例程带宽都不太行就要去查差分走线、终端电阻或者电源供电别一上来就怀疑FPGA代码。5.3 上位机实现pyusb/C#/LabVIEW到了上位机这一侧很多做FPGA的人会有点生疏但其实思路很清晰USB设备枚举成功后用libusb或pyusb打开设备找到BULK传输端点循环接收数据包再按已知的图像格式还原成帧即可。我早期用Python写了一个极简demo只有不到100行代码就能把RAW图存成文件再用OpenCV显示非常适合验证链路是否通。如果要做成工程化工具我建议两个方向C#配合CyUSB或libusb-dotnet可以快速做出带界面的采图软件LabVIEW也有USB读取工具包拖拽节点就能拿到数据适合做验证和测试平台。不管用哪个语言协议设计一定要清晰我在图像帧头里放了固定的magic word、图像宽高、帧号和数据长度上位机解析时先找帧头再按长度切帧这样即使出现偶尔丢包也能快速对齐下一帧。6. 实测过的典型问题和排查思路6.1 现象一帧率卡在某一值不上去我第一次整机联调时1920x1080分辨率下帧率一直跑不满30fps怎么调USB传输都上不去。排查过程是这样的先用compute和benchmark工具测FX3原始USB吞吐确认能跑满3Gbps以上再检查FPGA发给FX3的时钟频率发现GPIF时钟只配到了50MHz理论上32位总线能跑200MB/s但此时像素数据率已经接近带宽上限突发时会把缓冲区打满。把GPIF时钟抬高到80MHz同时把FX3的DMA缓冲区从16KB改到32KB后帧率才稳定跑满。这个案例里值得记住的是USB3.0标称带宽不等于你实际能用的带宽。你需要把协议开销、DMA切换、端点polling这些因素都算进去一般按理论带宽的60-70%做设计余量。如果应用帧率要求稳定60fps那像素率接近或超过有效带宽就必须考虑压缩或者降低位深了。6.2 现象二图像出现横条纹或闪烁图像横条纹很多不是FPGA逻辑问题而是曝光和光源频率打架。工业现场如果用交流供电的荧光灯光源频率是50Hz或60Hz如果传感器的曝光时间刚好不是光源周期的整数倍就会在图像上出现明暗相间的横条纹。解决办法一是把曝光时间设为光源周期的整数倍二是改用直流驱动光源三是在ISP里做anti-banding处理。还有一种是传感器电源纹波引起的横条纹位置固定而且和速度无关。我遇到过一版板子图像顶部有规律亮带查了一圈发现是给传感器供电的LDO输出端电容太小FPGA输出的配置时序切换时拉了一下电源结果是VDD变化直接耦合进了像素信号。换了大容量去耦电容之后横条纹消失。排查这类问题建议先用示波器看传感器电源纹波再看图像特征不要一头扎进代码里。6.3 现象三USB枚举不稳定和丢帧USB枚举不稳定的原因很多最常见的是差分线等长没做好和终端阻抗匹配不对。USB3.0的Tx/Rx差分对如果layout不对称信号眼图会明显闭合电脑可能认成USB2.0设备甚至完全无法识别。另外USB3.0部分也需要参考地平面完整我建议把FPGA板和USB桥接板之间的连接器选成高速专用连接器不要图便宜用普通排针。丢帧问题还有一个容易被忽视的来源上位机读取速度不够。如果你在PC端用Python写循环读取但又没有及时处理数据USB缓冲区满后新数据就会被丢弃。我的处理办法是多开几个读取线程或者用队列模式缓存数据把数据搬运和处理分开。注意这种“软件背压”导致的丢包和硬件丢包很像排查时一定要先在上位机打时间戳确认数据到达节奏是均匀的还是突发的。6.4 一个很容易忽略的时序收敛问题FPGA工程到了最后阶段大概率会遇到时序不收敛的问题。一个项目里如果你的传感器接收、ISP、DDR和USB接口逻辑全部铺满资源布局布线后的时序余量会很紧张。这时候你会深刻理解“fpga布局和布线区别”布局决定了各模块放在芯片哪个区域布线是在布局基础上连起来布局不合理跨区域的长走线会给时序带来很大压力。解决方法不是盲目加约束而是先把关键路径找出来手动调整模块之间的物理位置尽量减少跨die或跨clock region的长路径。如果你的FPGA是较大型器件还可能遇到“多die fpga约束”问题。简单说多die器件内部die和die之间的互连延迟比单die高很多你需要用厂商的特殊接口约束明确哪些路径允许跨die哪些必须保持在同一个die内。时序报表里如果出现大量跨die路径不满足工程上通常会把相关逻辑复制到同样die内或者调整分区避免在关键路径上出现跨die的长连线。7. 扩展方向与一个真实建议做完这台相机后面能扩展的方向其实非常多。比如把ISP里的白平衡、自动曝光做成闭环算法让相机在上位机不干预的情况下自动适应光照变化或者把MIPI接收模块改成支持多路输入做成多目工业相机一旦配合云台和编码器甚至可以做到随机构运动自动调整拍摄角度。这些看起来复杂的场景本质上都在复用这套采集、处理和传输的框架只是传感器数量和算法策略不同而已。我个人在实际操作中的体会是这个项目最值钱的不是“终于能出图了”那个瞬间而是踩完这一路所有坑之后你会建立起对整条图像链路的完整认识。以后不管你用FPGA做工业检测、做相控阵相关的控制计算还是做其他和传感器数据有关的东西都能更快定位瓶颈在哪里。真不建议一上来就追求高分辨率高帧率先用低分辨率把全链路跑通再逐步加参数这样能从根上减少问题排查的复杂度。如果你正好打算做类似的相机项目希望这篇能让你少走几段弯路。
返回列表