ARTICLE DETAIL

资讯详情

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

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源围绕YOLOv8实现渔船作业监控系统可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件约24.21MB以70个Python源码文件为核心辅以4个pt权重文件、5个xml配置、12个pyc缓存及mp4演示视频、ico图标等覆盖模型训练、推理检测与可视化界面模块。项目已完整测试运行包含源码、数据集、可视化页面与部署说明可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图便于答辩展示与结果分析。目前已有40人学习下载。读者可直接获得可运行的一站式方案也可在现有代码基础上修改扩展实现其他检测功能适合小白进阶与二次开发参考。1. 渔船作业监控系统为什么值得用 YOLOv8 重做一遍港口的监控室里值班人员盯着十几路摄像头画面靠肉眼判断哪条船在禁渔区下网、哪条船夜间违规作业——这种场景在不少渔港仍然存在。基于 YOLOv8 的渔船作业监控系统本质上就是把这套「人盯屏幕」的活交给模型用目标检测识别画面里的渔船、人员、网具再叠加区域判断逻辑输出告警。它解决的不是「能不能检测到船」这种基础问题而是「在复杂海况、逆光、雨雾条件下稳定区分作业行为」的工程问题。这套方案适合三类人做毕设或课程设计的学生需要一套能跑通、有界面、有数据集的完整闭环想入门目标检测落地的开发者拿渔船场景练手比跑 COCO 更贴近真实业务以及做渔政信息化的工程人员需要评估 YOLOv8 在边缘设备上的可行性。源码、可视化界面、数据集、部署教程四件套齐全意味着你不用从零标注数据、不用自己写推理服务改改配置就能看到结果。下面按「环境怎么搭 → 数据怎么处理 → 模型怎么训 → 界面怎么接 → 坑在哪」的顺序拆开讲。2. 从零把 YOLOv8 渔船检测环境跑起来2.1 环境选型CPU 版够不够GPU 版怎么配先说结论如果只是跑通推理、验证数据集质量CPU 版完全够用要训练自己的数据集GPU 是刚需。热搜里「ubuntu20.04 搭建 yolov8 环境 cpu 版本」和「gtx1660ti 跑 yolov8」代表了两类典型需求我一般按下面的组合来配。组件CPU 方案GPU 方案说明系统Ubuntu 20.04Ubuntu 20.04 / Windows 11生产部署优先 LinuxPython3.93.93.10 部分依赖有兼容问题框架PyTorch CPUPyTorch CUDA 11.8版本要和驱动对齐推理速度约 300-800ms/帧约 15-40ms/帧取决于输入尺寸和模型规格显存无6GB 起步gtx1660ti 跑 yolov8n/s 没问题CPU 版安装时最容易翻车的是 PyTorch 装成了 GPU 版却找不到 CUDA或者反过来。用官方源装 CPU 版# 创建独立环境避免污染系统 Python conda create -n fishmon python3.9 -y conda activate fishmon # 安装 CPU 版 PyTorch注意 --index-url 指向 cpu 源 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralyticsYOLOv8 的官方封装 pip install ultralytics opencv-python PyQt5这段命令的逻辑是先隔离环境再装框架最后装 YOLOv8 和界面依赖。--index-url那行是关键不加的话 pip 默认拉 GPU 版在没显卡的机器上会报libcudart.so not found。GPU 版把 index-url 换成https://download.pytorch.org/whl/cu118即可装完用torch.cuda.is_available()验证返回 False 就说明驱动或版本没对上。2.2 验证安装三行代码确认模型能推理环境装完别急着上数据集先用官方预训练权重跑一张图确认整条链路通。这一步能提前暴露 80% 的环境问题。from ultralytics import YOLO # 加载最小的 n 版本首次运行会自动下载权重 model YOLO(yolov8n.pt) # 对一张测试图推理saveTrue 会把结果存到 runs/detect/ 下 results model(test.jpg, saveTrue, conf0.25) # 打印检测到的类别和数量确认不是空结果 for r in results: print(r.boxes.cls, r.boxes.conf)conf0.25是置信度阈值低于它的框会被丢弃。渔船场景里如果目标小、遮挡多这个值可以降到 0.15 试试但会引入误检。saveTrue生成的图能直观看到框的位置如果框全歪了多半是图片预处理或坐标系问题不是模型问题。跑通这一步说明 PyTorch、ultralytics、OpenCV 三者协作正常可以进入数据环节。2.3 目录结构源码包拿到手先看这几个文件一套完整的渔船监控系统源码目录通常长这样拿到手先确认这几个关键位置fish_monitor/ ├── datasets/ # 数据集含 images 和 labels ├── weights/ # 训练好的 .pt 权重 ├── ui/ # 可视化界面代码 ├── configs/ # 数据配置 yaml、训练参数 ├── train.py # 训练入口 ├── detect.py # 推理入口 └── requirements.txt # 依赖清单先看configs里的数据 yaml里面path、train、val、nc、names五个字段决定了模型认哪些类别。渔船场景常见类别是boat、person、net、buoy这几类nc要和names长度一致不一致会直接报错。再看weights里有没有现成权重有的话可以先跳过训练直接验证效果这是判断这套源码值不值得深入的最快方式。3. 渔船数据集处理从标注到 YOLOv8 格式的完整链路3.1 数据集长什么样类别怎么定渔船作业监控的数据集核心难点不在数量而在场景覆盖。一个能用的数据集至少要包含不同光照清晨、正午、夜间红外、不同海况平静、有浪、雨雾、不同距离近景大目标、远景小目标、不同作业状态航行、抛锚、下网。热搜里「处理数据集用于 yolov8 训练」和「labelme 标注用于 yolov8」指向的就是这条链路。类别设计上我一般建议从少到多先只标boat和person两类跑通再逐步加net、buoy、flag。类别一多标注一致性就难保证模型容易在相似类之间反复横跳。数据集规模上每个类别至少 500 个实例起步小目标类别要更多因为小目标本身召回就低。3.2 Labelme 标注转 YOLO 格式的脚本Labelme 存的是 JSONYOLOv8 要的是每张图对应一个 txt每行类别id 中心x 中心y 宽 高且都是归一化到 0-1 的值。转换脚本如下import json import os from pathlib import Path # 类别名到 id 的映射顺序必须和训练 yaml 里的 names 一致 CLASS_MAP {boat: 0, person: 1, net: 2, buoy: 3} def convert_labelme(json_path, img_w, img_h, out_dir): with open(json_path, r, encodingutf-8) as f: data json.load(f) lines [] for shape in data[shapes]: label shape[label] if label not in CLASS_MAP: continue # 跳过未定义类别避免训练时报错 pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] # 计算外接矩形并归一化 x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) cx (x_min x_max) / 2 / img_w cy (y_min y_max) / 2 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h lines.append(f{CLASS_MAP[label]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) out_path Path(out_dir) / (Path(json_path).stem .txt) out_path.write_text(\n.join(lines), encodingutf-8) # 批量处理img_w/img_h 需要从对应图片读取 for jf in Path(labels_json).glob(*.json): convert_labelme(str(jf), 1920, 1080, labels_txt)逻辑说明CLASS_MAP是转换的核心它把文字标签映射成数字 id顺序错了模型学到的类别就全乱。归一化用图片实际宽高所以img_w、img_h必须和图片真实尺寸一致写死 1920x1080 只适合固定分辨率的摄像头。:.6f保留六位小数是 YOLO 的惯例精度够用又不至于文件过大。转换完一定要抽查几张用可视化脚本把框画回图上确认没偏移。3.3 数据集划分与 yaml 配置转换完的 txt 和图片要按 8:1:1 划分训练、验证、测试集。划分脚本用random.shuffle打乱后切片即可注意图片和标签要同步移动漏一个就会在训练时报No labels found。import random import shutil from pathlib import Path imgs list(Path(images).glob(*.jpg)) random.seed(42) # 固定种子保证可复现 random.shuffle(imgs) n len(imgs) splits {train: imgs[:int(n*0.8)], val: imgs[int(n*0.8):int(n*0.9)], test: imgs[int(n*0.9):]} for split, files in splits.items(): (Path(dataset) / split / images).mkdir(parentsTrue, exist_okTrue) (Path(dataset) / split / labels).mkdir(parentsTrue, exist_okTrue) for img in files: shutil.copy(img, Path(dataset) / split / images / img.name) lbl Path(labels_txt) / (img.stem .txt) if lbl.exists(): shutil.copy(lbl, Path(dataset) / split / labels / lbl.name)random.seed(42)是为了让每次划分结果一致方便对比实验。划分完写数据 yamlpath: /home/user/dataset train: train/images val: val/images test: test/images nc: 4 names: [boat, person, net, buoy]path用绝对路径最稳相对路径在不同工作目录下容易找不到。nc必须等于names长度这是新手最常犯的错之一。4. 训练渔船检测模型参数怎么设、曲线怎么看4.1 训练命令与关键参数YOLOv8 训练入口很简洁但参数设错会浪费大量时间。渔船场景我一般这样起训yolo detect train \ dataconfigs/fish.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ device0 \ projectruns/fish \ nameexp1逐个说modelyolov8s.pt选 s 版本是精度和速度的平衡点n 太快但小目标漏检多m/l 在 6GB 显存上容易 OOM。imgsz640是默认值如果渔船在画面里很小可以提到 960 或 1280但显存占用会翻倍。batch16在 6GB 显存上跑 s 版本比较稳OOM 就降到 8。lr00.01是初始学习率数据集小可以降到 0.001 防止过拟合。patience30表示 30 轮没提升就早停省时间。4.2 损失曲线怎么读什么时候该停热搜里「yolov8 画损失函数曲线图」是很多人关心的。训练完runs/fish/exp1/下会有results.csv用 pandas 画出来import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/fish/exp1/results.csv) df.columns df.columns.str.strip() # 列名可能带空格先清理 fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labeltrain) ax[0].plot(df[epoch], df[val/box_loss], labelval) ax[0].set_title(Box Loss) ax[0].legend() ax[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) ax[1].set_title(mAP50) ax[1].legend() plt.savefig(curves.png)读曲线的经验train loss 一直降但 val loss 开始升就是过拟合该早停或加数据增强。两条都降但 mAP 不涨可能是学习率太小或类别不平衡。mAP50 在 0.7 以上对渔船场景算可用0.85 以上算不错。如果 mAP 卡在 0.3 不动先查标注质量八成是框歪了或类别标错。4.3 数据增强渔船场景该开哪些YOLOv8 默认开了 mosaic、HSV 抖动、翻转。渔船场景我建议额外注意两点一是关掉上下翻转flipud0.0因为船不会倒着开开了反而引入噪声二是适度开大尺度抖动scale0.5模拟远近不同的目标。夜间红外数据要单独处理不能和可见光混在一起训否则模型会学糊涂。yolo detect train dataconfigs/fish.yaml modelyolov8s.pt epochs150 \ flipud0.0 fliplr0.5 scale0.5 mosaic1.0 hsv_h0.015 hsv_s0.7hsv_h0.015是色调抖动幅度海面颜色变化大可以适当调高。mosaic1.0是四图拼接对小目标提升明显但训练后期建议降到 0.5 让模型适应真实分布。5. 可视化界面与推理部署把模型接进监控系统5.1 PyQt5 界面骨架与推理线程可视化界面是这套系统的门面热搜里「python 爬虫可视化界面」「java 开发可视化界面」说明大家对界面实现很关注。渔船监控用 PyQt5 最省事核心是把推理放在独立线程否则界面会卡死。from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO import cv2 class DetectThread(QThread): frame_ready pyqtSignal(object) # 把带框的帧发回主线程 def __init__(self, model_path, source): super().__init__() self.model YOLO(model_path) self.source source self.running True def run(self): cap cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame cap.read() if not ret: break # 推理并直接画框streamTrue 适合视频流 results self.model(frame, conf0.3, verboseFalse) annotated results[0].plot() self.frame_ready.emit(annotated) cap.release() def stop(self): self.running False self.wait()逻辑说明QThread子类里跑推理循环pyqtSignal把结果帧发回主线程更新 QLabel这样界面不会冻结。conf0.3比训练时略高减少监控画面里的误报。verboseFalse关掉每帧日志否则控制台会刷屏。results[0].plot()直接返回画好框的 numpy 数组省去手动画框的代码。5.2 区域入侵判断从检测框到告警光检测到船不够监控系统要判断「是否进入禁渔区」。思路是把检测框中心点和预设多边形区域做点在多边形内判断import cv2 import numpy as np # 禁渔区多边形坐标按实际画面比例设 FORBIDDEN_ZONE np.array([[200, 300], [800, 300], [800, 600], [200, 600]], np.int32) def check_intrusion(box_xyxy): x1, y1, x2, y2 box_xyxy cx, cy (x1 x2) / 2, (y1 y2) / 2 # pointPolygonTest 返回正数表示在内部 result cv2.pointPolygonTest(FORBIDDEN_ZONE, (cx, cy), False) return result 0FORBIDDEN_ZONE的坐标要根据摄像头实际画面标定不同机位不一样。pointPolygonTest的第三个参数False表示只判断内外不返回距离速度快。判断为 True 就触发告警可以叠加时间戳存证。这里要注意框中心点判断对部分越界的情况会漏严格场景要用框和区域做交并比。5.3 部署到边缘设备模型导出与推理加速热搜里「rk3588 部署 yolov8」「hi3516cv610 yolov8 模型转换」说明边缘部署是刚需。YOLOv8 支持导出 ONNX、TensorRT、OpenVINO 等格式# 导出 ONNXopset 12 兼容性最好 yolo export modelweights/best.pt formatonnx opset12 simplifyTrue # 导出 TensorRT需要 GPU 环境 yolo export modelweights/best.pt formatengine halfTrue device0simplifyTrue会做图优化去掉冗余算子。halfTrue用 FP16 精度速度提升明显但精度略降渔船场景一般可接受。导出后在目标设备上用对应推理引擎加载RK3588 走 RKNN 工具链海思芯片走 NNIE流程都是「ONNX → 厂商工具转换 → 板端推理」中间的对齐和量化校准是最容易出问题的环节。6. 渔船监控系统落地避坑五条血泪经验6.1 现象训练 loss 正常但 mAP 一直是 0原因数据 yaml 里的names顺序和标注转换时的CLASS_MAP不一致模型学到的类别和验证时对不上。解决把 yaml 和转换脚本的类别映射逐字比对顺序必须完全一致改完重新生成标签。6.2 现象推理时框位置整体偏移原因标注时用的图片尺寸和推理时输入尺寸不一致归一化坐标被错误还原。解决确认标注时的img_w、img_h是原图尺寸推理时imgsz只影响模型输入不影响坐标还原两者不要混。6.3 现象夜间画面几乎检测不到船原因训练集里夜间样本太少或者可见光和红外数据混训导致模型混乱。解决夜间数据单独训一个模型或者用图像增强模拟低照度别指望一个模型通吃所有光照。6.4 现象界面跑几分钟就卡死原因推理写在主线程里或者每帧都重新加载模型。解决模型在初始化时加载一次推理放 QThread用信号槽回传结果别在循环里YOLO()。6.5 现象边缘设备上推理速度只有几帧原因模型没量化或者输入尺寸设太大。解决导出时开halfTrue或做 INT8 量化输入尺寸从 640 降到 416速度能翻倍精度损失在渔船场景通常可接受。7. 把告警准确率再提一档时序滤波与误报抑制单帧检测最大的问题是抖动——同一艘船这帧判为越界、下帧又判为正常告警刷屏。我一般加一层时序滤波维护每个目标最近 N 帧的越界状态连续 M 帧都越界才触发告警。用简单的滑动窗口就能实现from collections import deque class TrackFilter: def __init__(self, window10, threshold7): self.history deque(maxlenwindow) self.threshold threshold def update(self, is_intrusion): self.history.append(1 if is_intrusion else 0) # 窗口内越界帧数超过阈值才确认告警 return sum(self.history) self.threshold # 每个跟踪 id 维护一个 filter filters {} def confirm(track_id, is_intrusion): if track_id not in filters: filters[track_id] TrackFilter() return filters[track_id].update(is_intrusion)window10表示看最近 10 帧threshold7表示 7 帧以上越界才确认。这两个值要按帧率调25fps 下 10 帧约 0.4 秒反应够快又不至于误报。跟踪 id 用 YOLOv8 自带的track模式获取model.track(frame, persistTrue)就能拿到稳定的 id。验证滤波效果的方法很直接拿一段有船进出禁渔区的录像跑一遍统计告警次数和真实越界次数算准确率和召回率。我自己的经验是加滤波后误报能降一半以上代价是告警延迟 0.3-0.5 秒对渔政场景完全可接受。另一个提准确率的技巧是给不同类别设不同置信度阈值——船设 0.4人设 0.3网具设 0.25因为小目标本身分数就低一刀切会漏检。这套系统我从环境搭建到告警滤波完整跑过一遍最大的教训是别一上来就追求高 mAP先把数据标注的一致性和推理链路的稳定性搞定模型精度是水到渠成的事。数据集里一张标错的图可能比少一百张图危害还大。希望帮到你。本文还有配套的精品资源点击获取
返回列表