2026/9/18 4:04:56

计算机视觉在水产养殖中的应用:鱼类智能计数与监测系统实践

计算机视觉在水产养殖中的应用:鱼类智能计数与监测系统实践 在接触水产养殖和渔业资源调研这个圈子之前我以为“数鱼”就是雇几个人拿抄网捞上来数一数顶多再称个重。直到自己下场做项目才发现人工计数和测量不仅效率低误差还大得吓人——不同的人数同一桶鱼结果能差出百分之十几。后来我慢慢意识到这个行业真正缺的不是硬件而是一套能把“看见鱼”变成“看懂鱼”的自动化方案。于是就有了MiroFish这个项目一套以计算机视觉为核心、面向鱼类行为观测与生物量估算的轻量级智能监测系统。MiroFish这个名字拆开看就是“Miro”加“Fish”。Miro取自“Mirror”的变体也有“观察”的含义合在一起就是“观察鱼”通过水下摄像头持续采集画面用目标检测模型实时识别鱼的位置和种类再用跟踪与计数算法统计数量、估算尺寸最终把数据汇总成养殖户或科研人员能直接看懂的报表。这套系统解决的痛点是传统人工盘点耗时费力、数据滞后、主观误差大适合水产养殖企业、渔业科研机构以及有循环水养殖系统RAS的工厂化养殖场景使用。这篇文章我会把MiroFish从立项到落地的完整过程拆开讲包括为什么选视觉方案而不是声呐或红外、硬件怎么搭、模型怎么训、边缘设备上怎么部署以及我在实际测试里踩过的那些坑。如果你正准备做类似的“视觉养殖”项目或者只是在琢磨怎么把目标检测模型搬到嵌入式设备上这篇文章应该能给你一些可以直接抄作业的参考。1. 项目整体设计与思路拆解1.1 为什么选择“水下视觉”这条技术路线先聊一个绕不开的问题鱼在水里怎么“看”它最靠谱。市面上常见的方案有三种声呐探测、红外/激光围栏、光学视觉。声呐的优势是穿透力强不受水质浑浊影响但分辨率低只能看到一团团回波分不清鱼的种类更别提测量单条鱼的体长。红外和激光方案能触发计数但只能感知“有东西经过”对重叠鱼群几乎无能为力误报率很高。光学视觉的问题则相反它怕水质差、怕光线暗可一旦光照和水质可控它提供的信息密度远超另外两种方案。一条鱼从镜头前游过视觉方案不仅能数出“1”还能留下它的颜色、纹理、体型轮廓甚至能通过连续帧判断游动方向。MiroFish最终的定位是“看得清、数得准、认得出”只有视觉路线能在同一个传感器上同时满足这三项要求。实际项目里我也确实在实验室循环水槽和养殖池两种环境里验证过只要把光照和浑浊度控制在合理范围视觉方案的计数误差能稳定压到5%以内这在人工盘点面前已经是质的提升。1.2 系统架构与模块划分MiroFish整体上分成四层感知层、边缘计算层、服务层和应用层。感知层是水下摄像机和补光灯负责把物理世界变成数字图像流边缘计算层是整台系统的“大脑”跑目标检测和跟踪算法在本地直接产出识别结果服务层负责把边缘端的结果汇总、清洗、存储并通过HTTP接口或MQTT协议往外推送应用层则是养殖户手机上的小程序、车间里的大屏看板或者科研人员的后台分析工具。这套架构有一点值得展开说我刻意把绝大部分计算放在了边缘端而不是把所有视频流都传回服务器。原因很朴素养殖场的网络环境通常不太理想车间里别说千兆网能稳定跑个百兆就不错了而一路1080p视频的码流就要4-8Mbps十路摄像头同时传的话网络和设备成本直接起飞。更关键的是视频里大部分画面是没有鱼的空镜头把原始流全传回去就是在浪费带宽。MiroFish在边缘端只上报“有检测目标”的帧以及结构化数据比如时间戳、鱼的数量、体长估算值单条消息撑死几KB几百路设备同时在线也没有压力。1.3 关键需求拆解计数、测量、分类要分开看很多刚接触这个领域的朋友容易犯一个错误上来就想做一个“全知全能”的系统——既要把鱼数清楚又要测出每条鱼的体长还要能区分出三四种混养的品种。我一开始也这么想结果发现这三个需求对算法和硬件的要求完全不一样硬捏在一起只会让系统变得又慢又不稳定。MiroFish在需求分析阶段就把它们拆开了。计数任务依赖目标检测和跨帧跟踪对单帧精度要求高对ID稳定性要求中等体长估算依赖像素到物理尺寸的标定必须知道摄像头离鱼的大致距离或者在水下放置已知尺寸的参照物品种分类则依赖训练数据的质量和覆盖度不同品种的鱼长相接近时还得额外采集高清特写来补样本。把需求拆开之后每个模块都能用最合适的技术方案单独优化系统整体反而更简单、更可靠。2. 核心细节解析与实操要点2.1 水下图像采集硬件选型与布设细节水下摄像头是整个系统的眼睛这一环省不得。我测试过几种方案普通USB摄像头加防水壳、运动相机改装的采集器、以及工业级水下网络摄像机。最后留下来用的是工业级水下网络摄像机原因有三个一是它支持RTSP标准协议接入OpenCV和GStreamer生态特别顺二是工业级的白平衡和低照度表现明显好于民用设备在浑浊水体里画面依然能保留关键纹理细节三是它可以直接POE供电一根网线同时搞定供电和数据传输现场布线省了很多事。布设位置这件事我踩了不少坑。一开始我图省事把摄像头装在养殖池的中央底部朝上拍结果发现鱼从镜头正上方游过时画面里是一条巨大的白色肚皮检测模型直接懵掉。后来改成池壁侧面斜向下安装镜头轴线和水面成30到45度夹角这样拍到的鱼大多是侧视角度轮廓和纹理特征最完整识别率明显提升。补光灯的安放也要注意别正对着镜头照否则水下悬浮颗粒的反光会让整个画面白茫茫一片。最好让灯光从侧上方打过来形成类似自然界阳光入射的角度。2.2 数据采集与标注别嫌麻烦这是整个项目的地基模型能不能用七成看数据三成看调参。MiroFish的数据来源主要有三个渠道。第一个渠道是自采我在循环水槽里架好设备拍了大概40个小时的视频素材覆盖不同光照强度、不同浑浊度、不同鱼群密度。第二个渠道是公共数据集像AquaPE、DeepFish这些学术界公开的水下鱼类数据集补了大量不同品种和姿态的样本。第三个渠道是数据增强对已有图片做随机裁剪、旋转、色彩扰动和MixUp把原始样本量扩大了8倍。标注阶段我强烈建议用半自动标注工具纯手工框几千张图会让人崩溃。我用的流程是先用一个预训练好的YOLOv5模型对视频帧做粗检测把生成的框存成伪标签再在Label Studio里人工检查和修正。这样单张图的标注时间能从一分钟压到十几秒而且人工只需要盯住“漏检”和“错框”两种情况精力更集中质量反而更高。标注类别上如果目标只是计数其实可以只标一个“fish”类但如果要做品种区分建议把长势接近的品种分开标宁可类别多一点也别混在一起。2.3 目标检测模型选型YOLO系列依然是性价比之王检测模型这一块我横向比较过几类主流方案两阶段的Faster R-CNN、单阶段的YOLO系列、以及基于Transformer的DETR。结论非常直接在边缘计算设备上跑实时视频流YOLO系依然是性价比之王。Faster R-CNN精度不错但推理一张图要几十毫秒到上百毫秒在嵌入式设备上跑到15帧都费劲DETR精度上限高但训练收敛慢、部署依赖重对养殖场这种预算有限的场景不太友好。YOLO系列在精度和速度之间给了很好的平衡而且不同型号之间还能灵活切换。具体到型号选择我最终敲定的是YOLOv5s和YOLOv8n两个版本。v5s精度更高跑在算力强一点的设备上v8n更快更小跑在低功耗设备上。这里有个容易被忽视的点模型精度不是越高越好要看你部署设备的算力上限。精度最高的YOLOv5x在Jetson Nano上跑到1-2帧根本没法做实时跟踪而v8n能跑到25帧以上虽然单帧精度低一两个点但帧率高意味着同一个目标会被更多帧确认误检率反而更低——综合体验比“高精度低帧率”好得多。选型一定要拿着真实设备跑一遍别看跑分。2.4 跟踪与计数逻辑从“检测框”到“数量”检测模型输出的是一堆独立的框但“计数”需要知道这些框之间对应的关系。MiroFish用的是ByteTrack一个基于检测框关联的多目标跟踪算法。它的核心思路很简单对每一帧的检测框按置信度分成高、低两档先用高置信度框做匹配再用低置信度框补漏这样能最大程度减少遮挡和漏检造成的轨迹断裂。实测下来在鱼群密度中等单帧20条以内的情况下ID Switch的次数明显少于经典DeepSort。计数逻辑我自己写了一个轻量级模块当某个跟踪ID第一次出现在画面中计数器加一然后把这个ID标记为“已计数”如果该ID丢失超过一定帧数再出现就当新目标重新计数避免同一批鱼反复进出镜头造成重复统计。这个方案在单向洄游场景下很准但在鱼来回游动的开放水域会有偏差。后来我给MiroFish加了个可配置项允许用户选择“单程计数”或“最大同时在线数”两种统计口径适配不同场景。3. 实操过程与核心环节实现3.1 边缘硬件选型与环境搭建硬件选型直接决定系统成本与算力的平衡。MiroFish前后测试过三款设备NVIDIA Jetson Nano4GB、Jetson Orin Nano、以及瑞芯微RK3588开发板。Jetson Nano是老将150块人民币左右的二手价格很香但算力有限跑v8n模型时CPU占用率偏高Orin Nano是正经的升级款8GB显存版本跑YOLOv8s实时推理毫无压力就是价格贵了一截RK3588的优势是国产芯片供货稳、功耗低但部署时要走RKNN工具链踩坑的过程比较酸爽。如果你预算有限且追求低门槛上手我的建议是先搞一块Jetson Nano或Orin Nano原因就一个生态。NVIDIA的JetPack SDK把CUDA、cuDNN、TensorRT全打包好了安装完就能直接跑AI推理Python接口很全。而嵌入式Linux方案的移植工作量会大不少。系统镜像推荐用JetPack 5.1.2 LTS版本稳定坑少。装好系统后第一步要做的不是装PyTorch而是先用jetson_release确认L4T版本和CUDA版本对上号这一步能避免后面一堆玄学编译错误。3.2 模型训练流程与关键参数数据准备好、硬件就位后训练流程其实很标准化。我用Ultralytics官方仓库训练YOLOv8n核心参数如下输入分辨率640×640batch size设为设备显存能容纳的最大值初始学习率0.01用SGD优化器训练300轮。这里有个关键经验训练集和验证集要按“视频片段”切分而不是按“单帧”随机切分。什么意思呢同一段视频里相邻帧极度相似如果随机切分验证集里会出现大量训练集的“孪生兄弟”评估出来的mAP虚高上线就翻车。训练过程中我每10轮保存一次权重同时盯着验证集的mAP做早停。还有一个实用技巧mAP0.5和mAP0.5:0.95分开看。前者反映“大概框得准不准”后者反映“框得严不严”。对淡水鱼这种外形不那么规整的目标后者一般会低一些不必焦虑只要mAP0.5保持在0.9以上部署后的人眼观感就不会差。训练结束后导出模型时一定要做INT8量化这一步能把模型体积压缩到四分之一推理速度提升两倍多代价是mAP掉1到2个点——对鱼类检测这种任务来说完全能接受。3.3 边缘端推理管线TensorRT加速与前后处理模型训练完只是第一步真正决定体验的是部署推理管线的优化。MiroFish的推理流程是这样的通过GStreamer拉取RTSP视频流解码后用CUDA把帧缩放到640×640送入TensorRT引擎做推理推理输出再经NMS处理得到检测框最后交给ByteTrack做跟踪。整套管线用Python写成核心调用依赖PyTorch、OpenCV、PyCUDA和TensorRT的Python绑定。TensorRT加速这块要说细一点先把PyTorch的.pt权重转成ONNX再用TensorRT的trtexec工具把ONNX转成FP16或INT8引擎。这里最容易翻车的坑是动态尺寸问题。我一开始导出的引擎只支持固定640×640输入当视频流宽高比不同时图像会被拉伸变形检测精度肉眼可见地下降。后来我在ONNX导出时设了动态轴让TensorRT引擎支持640到1280范围内的动态尺寸效果立刻改善。如果你图省事不想碰动态尺寸那就老老实实把原始视频统一裁剪成640×640再送进模型别直接resize。3.4 服务端与可视化数据怎么变成养殖户看得懂的报表边缘端算出来的数据只是一堆JSON和数据库记录要变成养殖户愿意天天打开看的东西还得在服务端和前端下功夫。MiroFish的服务端用FastAPI写的轻量、自带OpenAPI文档、异步性能不错很适合这种体量的项目。数据存储用的SQLite起步等设备数量超过20台再考虑换PostgreSQL。服务端主要做三件事接收边缘端上报的检测结果按时间维度聚合统计提供RESTful API给前端查询。前端我用的是Vue3加ECharts做了一个车间大屏和一个手机H5页面。大屏展示的核心指标有三个当日累计计数、每小时计数趋势折线图、分品种占比饼图。养殖户最关心的其实不是瞬时数量而是“今天这个池子出了多少鱼”“这个月总共过了多少苗”。所以页面设计上我把“趋势”放在视觉中心把“瞬时数据”弱化成角落里的数字。这里有个交互细节点击趋势图上的峰值点可以回放对应时间段的视频片段方便养殖户确认那个异常峰值到底是鱼群经过还是水面反光误检。3.5 尺寸估算从像素到厘米的标定过程体长测量是MiroFish里比较进阶的功能也是不少科研用户关心的点。原理不复杂如果知道摄像头距离鱼所在平面的物理距离D再知道镜头焦距f和传感器像素尺寸就能算出一个像素对应多少毫米。实际做的时候我用的办法更土但更有效——在鱼经常游过的平面放一块已知尺寸的棋盘格标定板拍一段视频然后任选几帧通过棋盘格角点检测直接算出该平面上“每像素对应多少毫米”。有了这个系数鱼检测框的长度乘上去就得到体长。当然这个方案有前提鱼必须贴着标定平面游过离镜头太近或太远都会让测量值偏大或偏小。所以MiroFish的尺寸输出不报单个值而是给一个范围区间同时只统计“完整经过画面且轨迹长度大于阈值”的目标以此过滤掉半路闯入的碎片轨迹。这个功能目前做不到科研级精度误差在正负15%左右但给养殖户做“大致规格分拣”已经完全够用了。4. 常见问题与排查技巧实录4.1 水面反光与气泡干扰导致误检这是我在实际测试中遇到的第一个头疼问题。水面波纹反射阳光时画面上会出现一片片亮白色的高光区域检测模型偶尔会把高光边界当成鱼误检率一度冲到8%。排查的时候我先怀疑模型后来把误检帧截图下来仔细对比才发现高光区域和鱼在灰度纹理上确实有相似之处模型会被“轮廓像鱼”的区域骗到。解决办法有两个方向。第一个是物理层面调整摄像头角度让它尽量俯拍避免直视水面给补光灯加偏振片能大幅削弱反射光。第二个是算法层面在水面区域做个ROI掩码直接把画面顶部容易反光的区域排除在检测范围之外同时加入“轨迹合理性检验”——真实鱼类的运动轨迹是平滑连续的而反光噪点的轨迹往往是闪烁跳变的据此可以在跟踪层过滤掉大部分误检。4.2 夜间低照度环境下检测率骤降MiroFish一开始只留了白天测试数据等真正开始24小时连续运行的时候问题立刻暴露了晚上关了主灯之后画面噪点暴增检测率从白天的92%掉到65%左右几乎不可用。一开始我以为换一个更大功率的补光灯就能解决但灯开大了鱼会被惊扰游动行为明显变化数据反而不自然。最后的方案是分时策略白天依靠自然光侧补光夜间换成红外补光灯同时给摄像头加装红外滤光片切换器ICR。多数水下网络摄像机本身就支持日夜模式白天用红外截止滤镜保证色彩夜间自动切换到全光谱模式。红外光对鱼类的惊扰远小于可见光实测夜间检测率能重新拉回85%以上。如果你用的摄像头没有ICR功能另一个折中方案是把曝光时间拉长到1/30秒再用后端的降噪算法处理但代价是运动模糊会变大得自己权衡。4.3 多目标遮挡严重时计数偏低鱼群密度一大鱼与鱼之间的遮挡就是绕不开的问题。MiroFish在单帧20条以内的场景表现尚可一旦画面里同时出现40条以上的鱼遮挡比例急剧上升很多鱼只露出一半身体检测框重叠严重跟踪算法也会频繁切换ID计数结果会偏低10%到15%。针对这个问题我从经验里总结出几条实用对策。一是物理上分批在过鱼通道设计时把通道收窄到只能容两三条鱼并行通过从源头压缩遮挡概率这是最有效的手段。二是在算法上引入“高低置信度二次匹配”也就是前面提到的ByteTrack思路低置信度框虽然单独看不可靠但能帮助维持轨迹连续性。三是在统计口径上对高密度场景不追求精确计数改为“密度估算”——训练一个回归头直接输出画面中鱼的数量区间准确率反而更有保障。4.4 从训练环境到边缘设备部署的“玄学”报错部署阶段的报错千奇百怪这里分享几个高频坑。第一个是PyTorch版本和JetPack里的CUDA版本不匹配经常出现libcudart.so: cannot open shared object file这类错误解决办法很简单用NVIDIA官方提供的torch-2.x-py3.8-l4t-aarch64.whl别从PyPI直接安装通用版。第二个是TensorRT引擎转换时出现Assertion failed: tensors.count(inputName)十有八九是ONNX导出时动态轴没设对回去检查输入维度别偷懒。第三个是内存泄漏模型长期运行后显存越占越多这个大概率是Python循环里不断创建CUDA流导致的记得把解码、预处理、推理三部分的资源管理器封装好每帧结束后显式释放。4.5 常见问题速查表为了方便现场排查我把高频问题整理成一个速查表贴在控制柜盖板上运维人员照着顺序查就行。现象可能原因排查顺序建议画面全黑/无视频流网线松动、POE供电不足、摄像头上电失败1. 查网线指示灯 2. 查供电电压 3. 用VLC拉流测试检测框乱跳/闪烁帧率过低、模型量化损失过大1. 确认实际推理帧率 2. 换回FP16引擎对比 3. 检查NMS阈值计数明显偏少遮挡严重、ROI掩码误伤、轨迹阈值过大1. 回放视频人工计数对照 2. 调低轨迹丢失阈值 3. 调整ROI计数明显偏多鱼来回游动被重复计数1. 切换计数口径 2. 增加“出场判定”延迟服务端收不到数据MQTT主题配置错、端口不通1. 本地ping服务器 2. 用MQTT客户端测试发布 3. 看防火墙规则5. 影响范围与应用场景扩展5.1 在循环水养殖RAS中的应用价值循环水养殖系统是MiroFish最合适的落地场景之一。RAS车间里的水质、光照、水温都高度可控摄像头工作环境远比室外池塘稳定检测精度天然有保障。RAS模式下养殖密度高、分池频次多每一次分池、转池都是一次生产节点传统做法是抽样称重估算误差大且容易造成鱼体应激。用MiroFish做过鱼通道时系统能自动统计转池的总尾数、估算平均体长还能按体长规格生成分群建议这些数据直接对接车间的ERP系统能把生产计划做得更细。另一个有价值的功能是行为监测。鱼群在特定时间段内突然聚集在进水口或出水口往往是溶氧、温度异常的前兆。MiroFish虽然本身不做水质预测但它提供的“鱼群空间分布密度”数据可以作为一个上游信号输入到水处理的PLC联动逻辑里。当检测到异常聚集时联动打开增氧机或调低进水流量把问题扼杀在早期。5.2 在鱼类科研与资源调查中的辅助作用科研场景对数据的要求比生产场景苛刻MiroFish目前是“辅助工具”而非“科研级测量设备”但它已经能帮研究者省掉大量重复劳动。比如在鱼类行为学实验中研究者需要统计实验鱼在特定区域的停留时间、穿越次数和群体同步性这些指标靠人眼盯监控视频逐帧数效率极低。MiroFish的跟踪数据导出后可以直接生成每一条鱼的运动轨迹热力图和时间序列作为行为分析的中间产物。在野外资源调查场景下水下摄像机配合人工鱼礁投放可以记录鱼类聚集的真实数据。MiroFish在边缘端只保存“有鱼”的关键帧和结构化摘要大大减轻了野外存储和回传的负担。不过要说明白野外水流、能见度、光线变化都远超出室内可控范围这套系统更适合做“定期定点抽样”而不是“全水域普查”输出结果务必结合人工校验别直接当权威数据用。5.3 项目后续迭代方向如果这个项目继续往下走我个人认为有三个值得投入的方向。第一是把尺寸估算做得更严谨引入双目视觉或结构光方案让体长测量误差从正负15%收敛到正负5%以内第二是针对恶劣水体环境开发专门的降质图像增强算法让系统在下雨、浑浊、夜间等场景下也能保持稳定输出第三是把数据价值做深积累长时间跨度的计数与行为数据之后可以尝试训练一个“产量预测”模型把鱼群生长速度、摄食活跃度、疾病异常行为这些指标关联起来真正从“看见鱼”进化到“预见鱼”。写在最后MiroFish这个项目走到现在我最深的体会是做智能化改造别一上来就想搞大而全的平台先解决一个真正让人头疼的小问题用起来再迭代。水产养殖这个行业其实不缺数据缺的是能把数据变成决策的动作。一套系统就算算法再漂亮如果养殖户每天早上打开手机看不懂那个数字是什么意思它就永远是实验室里的玩具。我的经验是先把计数做准让用户尝到甜头再逐步加测量、分类、行为分析这些高阶功能每一步都让用户觉得“多出来的这个功能帮我省了事”项目才能活下来。最后再分享一个实用的小技巧整套系统的所有配置项包括模型路径、摄像头地址、ROI区域、跟踪参数我都放在一个YAML文件里统一管理边缘设备开机时自动加载。现场调优的时候改参数不用重新烧镜像远程改完文件重启一下进程就生效了。这个习惯让我在客户现场调试的沟通成本至少降了一半强烈建议你也这么做。