拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

YOLOv11智慧养殖实战:水下鱼群检测与Jetson部署

YOLOv11智慧养殖实战:水下鱼群检测与Jetson部署 简介面向智慧养殖领域开发者的一份30页技术文档系统讲解如何基于YOLOv11实现鱼类行为识别与养殖密度统计。内容从智慧养殖背景与需求入手覆盖YOLO系列演进及YOLOv11网络结构、多尺度特征融合、损失函数优化等原理再按数据采集、预处理、模型训练、行为分类与密度计算给出Python与OpenCV实践代码并附实验结果分析、不同光照与密度对比及水产养殖、渔业资源保护等应用场景兼具原理、代码与工程落地方向。资源为单个PDF压缩包约1.95MB支持目录章节跳转与大纲定位可在阅读器左侧快速定位章节已有85人学习。整份文档从数据采集、模型训练到计数评估链路完整行为识别与密度统计的关键代码均可对照复现适合正在做目标检测、智慧渔业或水产智能化项目的开发者参考。1. 智慧养殖场里的YOLOv11先解决数得清再谈看得懂养鱼池边最磨人的事不是投喂是巡塘。一个十亩的池塘凭肉眼数鱼群密度、看鱼是否浮头靠的是老师傅的经验到了上百个池的养殖基地人工巡塘根本排不过来。水下相机加上YOLOv11目标检测把「数鱼」和「看行为」这两件原来只能靠人的事交给算法一套系统同时出鱼的边界框、类别和行为标签。密度统计这根线是区别于普通目标检测项目的地方——要的不是框准不准是框完以后的计数和热力分布能不能指导投喂和增氧。这篇文从数据标注讲到Jetson部署把整条链路拆开附命令和参数照着做能把一套识别系统跑起来。适合两类人已经在做智慧养殖项目、想换掉Faster R-CNN这类老模型的以及手里有水下视频素材、想验证YOLOv11到底能不能扛住低光照和高密度场景的。鱼类检测的难点不在模型选型在数据采集和标签策略——水里的目标比路面上的车难伺候得多。2. 从水下视频到可训练数据集标注策略决定模型上限2.1 为什么直接用公开权重跑不动水下鱼群很多团队拿到YOLOv11第一件事是拿COCO预训练权重直接predict结果不出所料淡水鱼、海水鱼在COCO的80类里一个都没有模型能输出的只有人、车、猫狗这类陆地目标。就算拿Imagenet预训练权重做迁移水下场景的光谱分布和空气里差别极大——水对红光的吸收让画面整体偏蓝绿悬浮颗粒造成局部模糊鱼体反光区域在RGB图像里跟背景的对比度低。这些分布差异让模型在前几次迭代里损失下不去。更隐蔽的问题是标注标准。路面目标检测的惯例是「框住完整目标」但鱼群场景里一条鱼被另一条鱼挡住大半只露出头部或者尾部这时候标不标标了模型学到的是残缺目标不标密度统计永远偏低。我一般定三条标框规则遮挡超过50%不标只露出头部且能判断类别才标贴壁的鱼如果身体有弯曲框要贴合实际轮廓而不是用矩形硬套。这三条规则看似简单直接决定后续密度统计误差是±5%还是±20%。2.2 数据采集夜间红外、白天自然光、高密度三路并进水下相机装的位置很关键。正对鱼群活动区的侧面视角能拍到完整鱼体适合行为识别俯视视角对密度统计更友好因为鱼体重叠最少但俯视画面里的鱼形变严重——从上方看一条鲈鱼轮廓从细长变成椭圆。我的做法是两类视角都采集侧视用于行为分类俯视用于密度计数各自训练一个模型避免一个模型学两种空间分布。采集时段上凌晨和傍晚各拍一批中午强光拍一批夜间开红外补光拍一批覆盖鱼全天活动的光照范围。高密度场景单独拍投喂机启动后鱼群聚拢的1-2分钟是密度统计最需要但最难处理的画面。每个场景录10-15分钟视频隔帧抽成图片一个池子能抽出300-500张有效帧。多个池子、多个品种的素材混合模型才不会过拟合到某一个池子的水色上。2.3 标注工具选型与导出格式转换LabelStudio和X-AnyLabeling是水下场景用下来比较顺手的工具。LabelStudio适合团队协作多人同时标注时能看到彼此进度X-AnyLabeling的优势是自带SAM分割辅助鱼体轮廓复杂时先用SAM自动生成掩膜再手动修正标注速度能快一倍。但要注意用SAM辅助标注时容易把背景水草也圈进去导出前要逐张检查类别属性。导出格式上LabelStudio默认输出COCO JSONX-AnyLabeling输出的是自己的格式。YOLOv11训练要的是txt格式的标签文件每行一个目标类别id、中心点x、中心点y、宽、高前四个值都是相对图片尺寸归一化后的0-1小数。转换脚本的常见写法import json from pathlib import Path # 读取LabelStudio导出的COCO格式标注 with open(annotations.json, r, encodingutf-8) as f: coco json.load(f) img_id2name {img[id]: img[file_name] for img in coco[images]} img_id2size {img[id]: (img[width], img[height]) for img in coco[images]} cat_id2idx {cat[id]: idx for idx, cat in enumerate(coco[categories])} # 每张图生成一个txt标签文件 for ann in coco[annotations]: img_name img_id2name[ann[image_id]] txt_path Path(labels) / (Path(img_name).stem .txt) w, h img_id2size[ann[image_id]] # COCO的bbox格式是[x, y, width, height]需转为YOLO的归一化中心点格式 x, y, bw, bh ann[bbox] cx (x bw / 2) / w cy (y bh / 2) / h bw_norm bw / w bh_norm bh / h class_idx cat_id2idx[ann[category_id]] with open(txt_path, a) as f: f.write(f{class_idx} {cx:.6f} {cy:.6f} {bw_norm:.6f} {bh_norm:.6f}\n)这段脚本的核心在坐标系换算。COCO的bbox存的是左上角坐标和宽高YOLO需要的是归一化的中心点坐标。cx和cy的计算必须加括号——x bw / 2如果漏了括号中心点会偏移半个框的位置训练时模型看到的标签全是歪的Loss曲线显示收敛但实际效果一塌糊涂。另外注意多次运行这段脚本前要清空旧的labels目录open(txt_path, a)是追加模式重复跑会叠加出多余行。2.4 数据集划分与验证集采样水下数据集的划分比普通视觉项目更讲究。同一个池子不同时间段的画面相关性很高如果随机切分训练集和验证集里混着同一批鱼在不同帧的影像验证集的指标会虚高到失真。正确做法是按视频片段划分每一段视频抽出的帧整体属于同一个集合保证验证集里出现的鱼是训练阶段完全没见过的个体。数据量上单类别例如「鱼」检测10-20个池子的素材3000-5000张标注图基本够用如果按品种分多类别每类至少保证800-1000张。类别不平衡在水下场景常见——某个池子主养鲈鱼标注了4000张另一个池子的鲫鱼只有200张模型会把鲫鱼误检成鲈鱼。解决方式是用datasets目录里的train: val:比例划分加上mosaic1.0的数据增强让模型多看混合样本。3. YOLOv11网络结构与训练参数小目标优化的关键配置3.1 YOLOv11相比v8改了哪些东西YOLOv11在骨干网络里用C3k2模块替换了v8的C2f同时加入了C2PSA注意力模块。C3k2的核心是把梯度流分成两路一路走标准卷积提取细节特征另一路走捷径保留原始信息在参数量增加不明显的前提下让特征金字塔每一层的信息更完整。C2PSA则是在空间注意力机制上做了轻量化改造——水下场景里鱼体边缘模糊注意力机制能引导模型把权重放在鱼头、鱼尾这些辨识度高的区域。对鱼类检测来说涨点真正明显的是检测头部分。v11的检测头在训练阶段默认启用DFLDistribution Focal Loss的改进版本边界框回归更加保守对遮挡严重的鱼群会产生更紧致的预测框。直观感受是同样一批重叠鱼群的图片v8的预测框会互相套叠v11的框更贴合单条鱼的可见区域这对后续密度统计的精度有帮助。改进的另一个体现是Anchor-Free策略的稳定化——v11沿用了v8的解耦头结构但对正负样本分配做了调整小目标的正样本匹配阈值放宽让只有几十个像素的小鱼也能参与训练。3.2 训练前的环境配置与权重选择环境配置的坑主要在ultralytics包版本上。pip install ultralytics默认装最新版但YOLOv11在2024年末之后的小版本迭代里对旧显卡的CUDA兼容性做了一些调整实测RTX 3090在CUDA 11.8下跑v11.0-v11.2没问题新版本如果报AssertionError: cuda available优先检查torch版本而不是显卡驱动。推荐组合# CUDA 11.8 torch 2.4.0 ultralytics 8.3.x 的组合稳定 pip install torch2.4.0 torchvision0.19.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.3.30权重选择上yolov11n.pt是nano版本模型文件约5MB适合Jetson Nano这类边缘设备但精度有限yolov11s.pt是small版本精度和速度的平衡点最好训练时间也友好。做水产养殖项目的常见起点是yolov11s.pt预训练权重在COCO上收敛过的特征对水下目标迁移有帮助——毕竟鱼的纹理和背景纹理的区分能力是从陆地目标上学到的边缘特征迁移过来的。3.3 训练命令与关键参数详解训练命令的写法yolo detect train \ modelyolov11s.pt \ datafish.yaml \ epochs150 \ imgsz640 \ batch16 \ patience30 \ optimizerSGD \ lr00.01 \ mosaic1.0 \ close_mosaic10 \ mixup0.2 \ valTruefish.yaml的内容包括path指向数据集根目录、train和val的路径、nc填写类别数、names列出类别名。训练参数里close_mosaic10是容易被忽略的——它表示最后10个epoch关闭mosaic增强mosaic在训练后期会导致小目标被拼接缝隙切割模型学不到完整的鱼体语义关闭后让模型微调收敛最终mAP能提升1-2个点。mixup0.2让两张图混合训练增强模型对半透明鱼体的适应力水下场景里鱼鳍和鱼尾的透明度高mixup模拟了这种半遮挡状态。3.4 小目标优化切片推理和anchor参数调整水下鱼群图片里大量目标是小于32x32像素的这类目标在YOLOv11的P3特征层stride 8上只有4x4个格子特征是稀疏的。提升小目标召回率常规手段有两条路。第一条是训练阶段的anchor参数——YOLOv11虽然用的Anchor-Free但数据预处理的锚框分配策略会影响小目标的匹配。在yaml文件里调整anchors参数默认设置对中大型目标友好小目标多的数据集建议改成anchors: [5,5, 10,10, 15,15, 25,25, 45,45]这类尺度更密集的锚框组合让浅层特征层承担更多小目标匹配任务。第二条是推理阶段的SAHI切片策略。SAHI把大图切分成若干带重叠的小块每块单独推理再合并结果去除重叠框。鱼类检测场景里整池画面分辨率往往在1920x1080以上直接缩放成640x640输入小目标会缩到几个像素SAHI保持原分辨率切片推理小目标在切片里依然有足够像素。用SAHI配合YOLOv11的推理命令from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction # 加载训练好的v11模型权重 detection_model AutoDetectionModel.from_pretrained( model_typeultralytics, model_pathruns/detect/train/weights/best.pt, confidence_threshold0.25, image_size640, ) # 切片尺寸和重叠率slice_size512保证小目标像素充足 result get_sliced_prediction( pond_01.jpg, detection_model, slice_size512, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_typeNMS, postprocess_match_threshold0.5, )slice_size512是常见配置里兼顾速度和效果的选择256更适合极小目标但对算力消耗翻几倍overlap_*_ratio0.2的意义是防止目标恰好落在切片边界被截断。这里的postprocess_match_threshold是NMS的IoU阈值0.5对密集鱼群是安全值——设高了会把同一目标在相邻切片的重复框都保留设低了会把相邻两条鱼的框合并成一个。4. 鱼类行为识别与密度统计从检测框到业务指标的转换4.1 行为类别定义与模型输出设计鱼类行为识别不能靠一套通用动作分类解决得跟养殖业务指标挂钩。常规做法是定义四类行为正常游动swimming、抢食feeding、浮头缺氧gasping、离群停滞stagnant。每类行为对应不同的投喂和增氧决策——抢食意味着投喂机可以继续浮头必须立即开启增氧机。模型输出设计上我推荐检测头只负责「鱼」这个类别行为分类另接一个轻量分类分支而不是把所有行为都塞进同一个检测器的类别列表里。原因有两个同一帧画面里不同区域可能有不同行为比如池子左侧在抢食、右侧在浮头一个目标一个类别标不现实行为类别的标注成本远高于目标类别数据量短期内不足以支撑多类别检测。架构上主模型负责输出每个鱼目标的位置框行为分类模型对每个位置框内的图像块做四分类属于「检测ROI分类」的组合路线。4.2 密度统计的两种方法直接计数与热力图回归密度统计有两种主流路线。直接的路线是把检测框的数量求和但这种方法在鱼群高度重叠时严重偏低——一个框中可能挤着3-5条鱼。我的经验是当单帧中重叠率超过30%时直接计数误差会超过15%。改进方式是给每个检测框加一个置信度加权高置信度的框计1.0低置信度的框计0.5-0.7这个系数要在实际池子里校正——对同一个池子拍50帧人工数出真实数量再回归出置信度和计数值的关系。更稳健的路线是密度图回归密度统计的本质是估计鱼群分布的连续函数而不是输出离散整数。把鱼头的位置做成高斯核渲染的密度图用深度学习回归密度图再对密度图积分得到总数。高斯核的带宽sigma是超参数鱼体长50像素时sigma取8-12像素效果较好sigma太大会让密度图过于平滑、相邻鱼的高斯峰融合积分总数偏低。YOLOv11先检测出可靠的目标框用这些框的位置生成密度图的种子点再用一个轻量的UNet回归细化比纯端到端密度回归收敛更快。4.3 密度统计在养殖池里的实用部署方案实际项目里我不推荐在单帧上做密度统计而是做时间窗口的滑动平均。水面波动、鱼群瞬间聚散会让单帧计数抖动30%以上。按秒级采样统计过去10秒的平均密度抖动降到一个稳定的区间。具体流程每秒抽2帧进行检测和计数维护一个10帧的环形缓冲区输出缓冲区平均值密度超过预设阈值时触发告警阈值按池子面积和规格标定这个方案对算力的要求低嵌入式设备也能跑。YOLOv11的检测频率在Jetson Nano上约8-10 FPSTensorRT优化后2 FPS的采样不会成为瓶颈。4.4 跟踪与多目标ID分配的辅助作用如果要输出「某条鱼持续20秒浮头」这样的事件级行为判断需要目标跟踪来保持ID稳定性。YOLOv11配合ByteTrack是现阶段比较顺手的组合。ByteTrack的思路是同时用高置信度和低置信度的检测框参与关联先关联高置信度框再用低置信度框填补被遮挡目标的位置这在鱼群互相遮挡时尤其有效。from ultralytics import YOLO from boxmot import BYTETracker model YOLO(runs/detect/train/weights/best.pt) tracker BYTETracker() cap cv2.VideoCapture(pond_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, conf0.3, iou0.5)[0] # 提取检测框和置信度交给ByteTrack关联ID dets results.boxes.data.cpu().numpy() # [x1, y1, x2, y2, conf, cls] tracks tracker.update(dets) for track in tracks: track_id, x1, y1, x2, y2, conf int(track[0]), *track[1:6] # 按track_id统计每一条鱼的停留时间 cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, fID:{track_id}, (int(x1), int(y1)-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow(tracked, frame)conf0.3在跟踪场景里比默认的0.25略高可以减少误检干扰ID关联iou0.5控制NMS的框合并强度鱼群密集时可以放到0.4合并得更激进一些减少同一目标输出多个框导致ID分裂的情况。boxmot包的BYTETracker需要输入[x1,y1,x2,y2,conf,cls]格式的检测结果输出的每个track除了ID还包括卡尔曼滤波预测后的框坐标。5. 训练与部署避坑水下场景里常见的六个翻车现场5.1 水面反光和倒影让模型把影子当鱼现象模型在测试集上mAP有0.85拿到养殖池现场一跑水面上漂浮的树叶、池壁的反光区域全被框出来。原因训练数据里没有加入包含水面反光的负样本。水下相机装在池边时水面波光粼粼的区域在模型眼里和鱼体的高反光区域纹理相似。解决在训练集中加入500张以上完全没有鱼、只有水面波动和反光的背景图标注文件为空txt。让模型学到「有反光不等于有鱼」。同时推理时开启augmentTrue模型会对输入做水平翻转和色彩抖动对反光的敏感度会降低。5.2 鱼群密度太高时个别鱼的框被NMS压掉现象投喂时段鱼群聚拢单帧画面有几十条鱼重叠交叉输出的框数明显比实际鱼数少。原因NMS的IoU阈值默认0.5两条鱼的重叠面积超过50%时置信度低的框直接被抑制。解决推理时把IoU阈值调低到0.3-0.4保留更多重叠框如果还是不够切换到SAHI切片推理把高密度区域放大后再检重叠鱼在切片里会被拆开。5.3 夜间红外补光下模型掉点明显现象白天训练的模型在夜间红外画面下漏检率超过40%。原因红外图像是单通道灰度映射成的伪彩色分布和白天的RGB图像差异巨大模型在骨干网络第一层卷积上没学到红外的纹理模式。解决训练数据里加入30%比例的夜间红外帧mixup增强把白天和夜间帧混合让模型对光照变化鲁棒。如果夜间场景占比高单独用红外数据finetune一个专用权重更稳妥。5.4 标签框太小导致训练不收敛现象Loss曲线在第50个epoch后仍然震荡验证集的recall一直上不去。原因大量鱼目标只有十几个像素标注框尺寸小于默认锚框尺度正样本匹配不上。解决在模型配置里调整anchors为更小的尺度同时把imgsz从640调到960——图像分辨率增大后小目标在特征图上的响应区域扩大匹配难度下降。代价是训练显存增加约40%batch需要相应调小。5.5 Jetson部署时TensorRT推理报错现象yolo export转TensorRT成功在PC上推理正常部署到Jetson Nano上跑到engine加载阶段报显存不足或维度不匹配。原因Jetson的GPU架构和PC不同TensorRT的engine文件是硬件绑定的在PC上生成的engine直接拷贝到Jetson用不了。解决在Jetson上重新执行导出流程用device0指定在Jetson上生成engine。Jetson的算力有限输入分辨率建议降到416x416输出精度略有损失但在可接受范围内。部署脚本固定用yolo predict modelfish.engine source...把导出和推理分开两步避免运行时反复转engine。5.6 密度统计值在生产环境漂移现象第一周密度统计误差±5%一个月后误差扩大到±20%。原因水质变化导致图像浑浊度上升相机镜头被藻类附着输入分布偏移后模型检测率下滑。解决把部署做成带反馈的闭环定期抽帧人工复核误检率高时用最近数据增量训练。镜头位置固定后可以加一个水质浊度传感器浊度超标时先做图像去雾预处理再进模型——比重新训练划算得多。6. Jetson Nano上的实际部署TensorRT导出与帧率调优边缘端部署首选NVIDIA Jetson系列功耗低且natively支持TensorRT。Jetson Nano在鱼池边可以做到8-10 FPS的推理速度配合2 FPS的采样频率绰绰有余。部署链路是PyTorch权重 → ONNX → TensorRT engine → DeepStream管道接入。# 在Jetson Nano上训练好的权重导出为TensorRT yolo export modelruns/detect/train/weights/best.pt formatengine device0 # 导出时为FP16精度推理速度约提升1.5倍 # 精度损失在鱼类检测上表现不明显但输出框的置信度会比FP32低0.03-0.05导出时要注意Jetson的显存只有4GBimgsz640的engine导出是上限超过会报显存溢出。我的经验是明确部署目标是「数数」和「看行为」而非高精度检测时输入分辨率降到416能跑到15FPS此时5像素以下的小鱼会漏但用SAHI在池子重点区域切块补偿整体密度统计精度能保住。推理脚本在Jetson上的写法和PC有差异。开启CUDA内存池复用、关闭PIL的线程池推理延迟能再降10%。部署时顺便开启fp16True做TensorRT的FP16推理注意不要在CPU上做预处理缩放——用Jetson的GPU做CUDA缩放能省下每帧的延时。视频流接RTSP时设置GST_DEBUG3查看pipeline状态拉流失败优先查摄像头IP和编码格式H.265的流在Jetson上硬解比H.264吃显存4GB版本建议强制转码H.264。部署时还有一个尺寸取舍鱼密度统计的精度和输入分辨率强相关。一个1920x1080的池子画面直接缩到640推理和切成512的三块分别推理后者能数出多20%的鱼但推理耗时也变成三倍。折中做法是平时用640全局推理做实时监控只在密度阈值触发时切高分辨率精确复核——日常和告警两档模式切换算力和精度都能兼顾。这套方案做了三个养殖周期以后我最大的教训是别把模型当一次性交付池塘水质、光照、鱼体大小都在变化定期抽帧复盘、把新场景增量训练数据喂回去是必须的运维流程。希望帮到你。本文还有配套的精品资源点击获取
返回列表