基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
2026/9/24 0:00:07 网站建设 项目流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕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生产部署优先 Linux
Python3.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 python=3.9 -y conda activate fishmon # 安装 CPU 版 PyTorch,注意 --index-url 指向 cpu 源 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics,YOLOv8 的官方封装 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") # 对一张测试图推理,save=True 会把结果存到 runs/detect/ 下 results = model("test.jpg", save=True, conf=0.25) # 打印检测到的类别和数量,确认不是空结果 for r in results: print(r.boxes.cls, r.boxes.conf)

conf=0.25是置信度阈值,低于它的框会被丢弃。渔船场景里如果目标小、遮挡多,这个值可以降到 0.15 试试,但会引入误检。save=True生成的图能直观看到框的位置,如果框全歪了,多半是图片预处理或坐标系问题,不是模型问题。跑通这一步,说明 PyTorch、ultralytics、OpenCV 三者协作正常,可以进入数据环节。

2.3 目录结构:源码包拿到手先看这几个文件

一套完整的渔船监控系统源码,目录通常长这样,拿到手先确认这几个关键位置:

fish_monitor/ ├── datasets/ # 数据集,含 images 和 labels ├── weights/ # 训练好的 .pt 权重 ├── ui/ # 可视化界面代码 ├── configs/ # 数据配置 yaml、训练参数 ├── train.py # 训练入口 ├── detect.py # 推理入口 └── requirements.txt # 依赖清单

先看configs里的数据 yaml,里面pathtrainvalncnames五个字段决定了模型认哪些类别。渔船场景常见类别是boatpersonnetbuoy这几类,nc要和names长度一致,不一致会直接报错。再看weights里有没有现成权重,有的话可以先跳过训练直接验证效果,这是判断这套源码值不值得深入的最快方式。

3. 渔船数据集处理:从标注到 YOLOv8 格式的完整链路

3.1 数据集长什么样,类别怎么定

渔船作业监控的数据集,核心难点不在数量,而在场景覆盖。一个能用的数据集至少要包含:不同光照(清晨、正午、夜间红外)、不同海况(平静、有浪、雨雾)、不同距离(近景大目标、远景小目标)、不同作业状态(航行、抛锚、下网)。热搜里「处理数据集用于 yolov8 训练」和「labelme 标注用于 yolov8」指向的就是这条链路。

类别设计上,我一般建议从少到多:先只标boatperson两类跑通,再逐步加netbuoyflag。类别一多,标注一致性就难保证,模型容易在相似类之间反复横跳。数据集规模上,每个类别至少 500 个实例起步,小目标类别要更多,因为小目标本身召回就低。

3.2 Labelme 标注转 YOLO 格式的脚本

Labelme 存的是 JSON,YOLOv8 要的是每张图对应一个 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", encoding="utf-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), encoding="utf-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_wimg_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(parents=True, exist_ok=True) (Path("dataset") / split / "labels").mkdir(parents=True, exist_ok=True) 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)是为了让每次划分结果一致,方便对比实验。划分完写数据 yaml:

path: /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 \ data=configs/fish.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=30 \ device=0 \ project=runs/fish \ name=exp1

逐个说:model=yolov8s.pt选 s 版本是精度和速度的平衡点,n 太快但小目标漏检多,m/l 在 6GB 显存上容易 OOM。imgsz=640是默认值,如果渔船在画面里很小,可以提到 960 或 1280,但显存占用会翻倍。batch=16在 6GB 显存上跑 s 版本比较稳,OOM 就降到 8。lr0=0.01是初始学习率,数据集小可以降到 0.001 防止过拟合。patience=30表示 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"], label="train") ax[0].plot(df["epoch"], df["val/box_loss"], label="val") ax[0].set_title("Box Loss") ax[0].legend() ax[1].plot(df["epoch"], df["metrics/mAP50(B)"], label="mAP50") 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 抖动、翻转。渔船场景我建议额外注意两点:一是关掉上下翻转(flipud=0.0),因为船不会倒着开,开了反而引入噪声;二是适度开大尺度抖动(scale=0.5),模拟远近不同的目标。夜间红外数据要单独处理,不能和可见光混在一起训,否则模型会学糊涂。

yolo detect train data=configs/fish.yaml model=yolov8s.pt epochs=150 \ flipud=0.0 fliplr=0.5 scale=0.5 mosaic=1.0 hsv_h=0.015 hsv_s=0.7

hsv_h=0.015是色调抖动幅度,海面颜色变化大可以适当调高。mosaic=1.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 # 推理并直接画框,stream=True 适合视频流 results = self.model(frame, conf=0.3, verbose=False) annotated = results[0].plot() self.frame_ready.emit(annotated) cap.release() def stop(self): self.running = False self.wait()

逻辑说明:QThread子类里跑推理循环,pyqtSignal把结果帧发回主线程更新 QLabel,这样界面不会冻结。conf=0.3比训练时略高,减少监控画面里的误报。verbose=False关掉每帧日志,否则控制台会刷屏。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 >= 0

FORBIDDEN_ZONE的坐标要根据摄像头实际画面标定,不同机位不一样。pointPolygonTest的第三个参数False表示只判断内外不返回距离,速度快。判断为 True 就触发告警,可以叠加时间戳存证。这里要注意:框中心点判断对部分越界的情况会漏,严格场景要用框和区域做交并比。

5.3 部署到边缘设备:模型导出与推理加速

热搜里「rk3588 部署 yolov8」「hi3516cv610 yolov8 模型转换」说明边缘部署是刚需。YOLOv8 支持导出 ONNX、TensorRT、OpenVINO 等格式:

# 导出 ONNX,opset 12 兼容性最好 yolo export model=weights/best.pt format=onnx opset=12 simplify=True # 导出 TensorRT,需要 GPU 环境 yolo export model=weights/best.pt format=engine half=True device=0

simplify=True会做图优化,去掉冗余算子。half=True用 FP16 精度,速度提升明显但精度略降,渔船场景一般可接受。导出后在目标设备上用对应推理引擎加载,RK3588 走 RKNN 工具链,海思芯片走 NNIE,流程都是「ONNX → 厂商工具转换 → 板端推理」,中间的对齐和量化校准是最容易出问题的环节。

6. 渔船监控系统落地避坑:五条血泪经验

6.1 现象:训练 loss 正常但 mAP 一直是 0

原因:数据 yaml 里的names顺序和标注转换时的CLASS_MAP不一致,模型学到的类别和验证时对不上。解决:把 yaml 和转换脚本的类别映射逐字比对,顺序必须完全一致,改完重新生成标签。

6.2 现象:推理时框位置整体偏移

原因:标注时用的图片尺寸和推理时输入尺寸不一致,归一化坐标被错误还原。解决:确认标注时的img_wimg_h是原图尺寸,推理时imgsz只影响模型输入,不影响坐标还原,两者不要混。

6.3 现象:夜间画面几乎检测不到船

原因:训练集里夜间样本太少,或者可见光和红外数据混训导致模型混乱。解决:夜间数据单独训一个模型,或者用图像增强模拟低照度,别指望一个模型通吃所有光照。

6.4 现象:界面跑几分钟就卡死

原因:推理写在主线程里,或者每帧都重新加载模型。解决:模型在初始化时加载一次,推理放 QThread,用信号槽回传结果,别在循环里YOLO()

6.5 现象:边缘设备上推理速度只有几帧

原因:模型没量化,或者输入尺寸设太大。解决:导出时开half=True或做 INT8 量化,输入尺寸从 640 降到 416,速度能翻倍,精度损失在渔船场景通常可接受。

7. 把告警准确率再提一档:时序滤波与误报抑制

单帧检测最大的问题是抖动——同一艘船这帧判为越界、下帧又判为正常,告警刷屏。我一般加一层时序滤波:维护每个目标最近 N 帧的越界状态,连续 M 帧都越界才触发告警。用简单的滑动窗口就能实现:

from collections import deque class TrackFilter: def __init__(self, window=10, threshold=7): self.history = deque(maxlen=window) 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)

window=10表示看最近 10 帧,threshold=7表示 7 帧以上越界才确认。这两个值要按帧率调:25fps 下 10 帧约 0.4 秒,反应够快又不至于误报。跟踪 id 用 YOLOv8 自带的track模式获取,model.track(frame, persist=True)就能拿到稳定的 id。

验证滤波效果的方法很直接:拿一段有船进出禁渔区的录像跑一遍,统计告警次数和真实越界次数,算准确率和召回率。我自己的经验是,加滤波后误报能降一半以上,代价是告警延迟 0.3-0.5 秒,对渔政场景完全可接受。另一个提准确率的技巧是给不同类别设不同置信度阈值——船设 0.4,人设 0.3,网具设 0.25,因为小目标本身分数就低,一刀切会漏检。

这套系统我从环境搭建到告警滤波完整跑过一遍,最大的教训是:别一上来就追求高 mAP,先把数据标注的一致性和推理链路的稳定性搞定,模型精度是水到渠成的事。数据集里一张标错的图,可能比少一百张图危害还大。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询