资讯详情

资讯详情

基于YOLOv8与PyQt5的锂电池表面缺陷检测系统设计与实现

1. 系统整体设计与技术选型思路1.1 为什么选YOLOv8做缺陷检测锂电池表面缺陷检测这件事放在工业质检场景里其实非常典型。极片划痕、凹坑、脏污、气泡、破损这些缺陷直接关系到电池的安全性和使用寿命所以产线上对这一块的检测要求一直很高。但传统机器视觉做法是图像差分加人工设计特征遇到反光不均匀、背景复杂的情况就特别容易误检而且换一个型号的电池就要重新调一套规则维护成本相当高。YOLOv8能在这种场景下胜出核心原因在于它把目标检测做成了一条非常顺滑的流水线。作为单阶段检测器它不像Faster R-CNN那样需要单独跑区域提议网络而是一个前向推理直接输出类别和边界框速度上有天然优势。在GTX 1660 Ti这种6G显存的卡上yolov8s模型跑640分辨率输入基本能稳定到30-40 FPS完全够用。另外ultralytics这个官方库把数据加载、增强、训练、验证、导出全部封装好了不用自己写一堆训练循环这对毕业设计来说能省下大量时间。还有一点很实际YOLOv8的工程生态非常成熟。无论是PyPI一键安装、自带预训练权重、支持ONNX/TensorRT导出还是社区里铺天盖地的教程都意味着你遇到问题基本都能搜到答案。对于毕设这种强交付导向的项目选它绝对比从零搭一个检测网络靠谱得多。1.2 为什么选PyQt5做桌面界面到了界面这一层核心诉求其实就三个开发效率要高、能和OpenCV/NumPy方便互操作、打包成exe不要太痛苦。PyQt5在这三点上都很能打。它基于Qt C库性能没问题同时Python绑定做得非常成熟QImage转OpenCV的BGR格式只需要一行代码图像数据直接共享内存避免了序列化和拷贝的开销。有人会问那PySide6呢确实PySide6是Qt官方支持的Python绑定而且LGPL协议比GPL更宽松。但PyQt5在社区资料量上还是明显占优网上几乎任何界面问题都能搜到现成答案这对时间紧张的毕设来说太重要了。还有一点是PyQt5对Python 3.8-3.10的兼容性非常稳很多坑都已经被人踩平了而PySide6在一些旧依赖上偶尔会有小毛病。所以我个人推荐稳妥起见选PyQt5除非你有需要Qt 6新特性的硬需求。再说为什么不用Web前端或者Electron。虽然Web界面好看但部署时还得带一个浏览器内核而且和本地的摄像头、文件系统交互要实现跨语言桥接复杂度一下子上去了。PyQt5直接在Python进程内跑摄像头用OpenCV VideoCapture图片用cv2.imread检测结果直接画框整个链路完全不用出Python环境稳且快。1.3 自适应界面设计的真实需求自适应界面听起来像是个加分项但放到实际场景里其实是刚需。实验室的显示器可能是1920x1080答辩教室的投影仪分辨率可能是1280x800导师笔记本的缩放比例可能是125%或150%。如果你用固定像素坐标布局一旦分辨率变了控件要么挤成一团要么超出屏幕边界现场演示翻车就非常尴尬。解决这个问题的核心思路有三个层次。第一层是用Qt布局管理器QHBoxLayout、QVBoxLayout、QGridLayout这套东西让控件自动跟随窗口拉伸。第二层是处理图像显示区域QLabel显示检测结果时要在resizeEvent里重新缩放QPixmap按比例缩放并居中显示。第三层是处理高DPI在main.py开头设置Qt.AA_EnableHighDpiScaling属性否则在150%缩放的屏幕上字体会发虚。这三层都做到位界面才算真正做到了自适应。2. 环境搭建与依赖配置2.1 CUDA和PyTorch版本组合实测环境配置是整个项目最容易被卡住的第一道关其中CUDA、cuDNN、PyTorch三者的版本匹配关系是重灾区。先给出我实测稳定的一套组合操作系统Windows 11显卡GTX 1660 TiCUDA 11.8cuDNN 8.6.0Python 3.10PyTorch 2.0.1ultralytics 8.0.x。这套组合下yolov8n和yolov8s都能正常跑训练和推理都没发现兼容性问题。安装CUDA和cuDNN的要点是这样的。先下载CUDA 11.8安装包安装后命令行执行nvidia-smi确认显卡能识别到。cuDNN不用单独安装到系统目录直接把解压出来的bin、include、lib文件夹里的文件复制到CUDA安装目录对应文件夹下就行。千万别装CUDA 12.x再回头配PyTorch 2.0虽然有些配置能跑但踩坑概率会高很多。PyTorch安装建议用官方推荐的pip镜像命令pip install torch2.0.1cu118 torchvision0.15.2cu118 --index-url https://download.pytorch.org/whl/cu118装完验证一下import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果cuda.is_available()返回False说明PyTorch和CUDA版本没对上或者环境变量里CUDA路径有问题。检查一下系统环境变量Path里有没有C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\bin没有的话手动加进去然后重启终端。这个坑我见过太多人踩了就是环境变量加得不对导致torch始终认不到GPU。2.2 PyQt5安装与OpenGL导致界面无显示PyQt5的安装相对简单但有一个非常经典的坑界面闪退或显示黑屏。正常情况下这样装就够了pip install pyqt55.15.9 pip install pyqt5-tools5.15.9.3.3如果你在Windows服务器或者某些远程虚拟环境下运行会发现程序能启动但窗口显示不出来或者直接崩溃报错。这个问题的根源在于PyQt5的QtWebEngine组件依赖系统OpenGL而虚拟机或远程桌面环境里往往没有完整的OpenGL驱动。更常见的情况是直接闪退没有任何提示。解决方法是把Qt的渲染模式切换到软件渲染。在main.py的最开头加上这几行import os os.environ[QT_OPENGL] software os.environ[QT_QUICK_BACKEND] software如果还是不行可以安装opengl32sw.dll替代系统的OpenGL实现pip install PyQt5-opengl然后在环境变量里设置os.environ[QT_OPENGL_DLL] opengl32sw这个问题虽然不常发生但一旦遇到就很迷惑我先写在这里给大家排雷。2.3 工程目录结构规划一个清晰的目录结构能让你少掉很多头发。我推荐这么划分lithium_battery_defect/ ├── main.py # 程序入口 ├── config/ │ └── settings.py # 全局配置路径、模型参数、阈值 ├── models/ │ └── best.pt # 训练好的权重YOLOv8格式 ├── ui/ │ ├── main_window.py # 主窗口界面 │ ├── widget_utils.py # 自定义控件辅助函数 │ └── style.qss # 界面样式 ├── detect/ │ ├── detector.py # 模型推理封装类 │ └── thread_utils.py # 推理线程、视频线程 ├── datasets/ │ ├── images/ # 原始图片 │ ├── labels/ # 标注txt文件 │ └── data.yaml # 数据集配置文件 ├── resources/ │ └── icon.png # 界面图标 └── output/ └── result/ # 检测结果输出目录建议把模型权重、数据集、代码分开存。很多人习惯把所有文件堆在一个目录后面找东西非常痛苦。而且等你要打包exe的时候目录结构越清晰越容易配置打包参数。3. 数据集制作与模型训练3.1 锂电池表面缺陷类型与标注规范锂电池表面常见的缺陷类型包括划痕、凹坑、脏污、气泡、破损、压伤等不同厂家对缺陷的定义会有些差异但大致类别是差不多的。这里需要注意一个关键点缺陷类别的粒度不要分得太细。比如划痕和细划痕如果它们的形态差异不够明显模型很难区分反而会把精度拉低。建议合并成5-6个大类每类保证至少500-1000张样本。标注工具我推荐直接用labelImg或labelme。labelImg适合做矩形框标注保存为YOLO格式的txt文件labelme适合多边形标注但需要自己转格式。对YOLOv8来说用labelImg就好因为YOLOv8原生就是水平矩形框检测。标注过程中的细节很重要。第一缺陷区域要用尽量紧密的框框住整个可见区域不要留太多背景也不要切掉缺陷的边缘。第二避免一个目标只框了一部分特别是长条形的划痕尽量框完整。第三标注时要保持一致性同样的形态在所有图片里都标同一类别。如果标注不一致模型会学得很混乱。数据集的目录结构要符合YOLO格式要求datasets/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标注 │ └── val/ # 验证集标注 └── data.yaml # 数据集配置data.yaml内容train: datasets/images/train val: datasets/images/val nc: 6 names: [scratch, dent, dirty, bubble, crack, breakage]注意data.yaml里面的路径如果你在Windows下跑建议写绝对路径或者相对训练命令的执行路径避免出现反斜杠转义问题。3.2 预训练权重选择与训练参数配置YOLOv8有n/s/m/l/x五个尺寸有的同学一上来就直接用yolov8x结果1660Ti根本带不动batch只能设1训练速度慢到怀疑人生。根据我的实测336张图、6个类别、训练300轮的话GTX 1660Ti上yolov8n大概4小时能跑完yolov8s大概8-10小时yolov8m可能要2天。对毕业设计来说yolov8s是最推荐的平衡点精度比n高一截速度也足够。训练参数这块关键的是这几个from ultralytics import YOLO model YOLO(yolov8s.pt) # 加载预训练权重 model.train( datadatasets/data.yaml, epochs200, imgsz640, batch8, lr00.01, patience50, device0, workers4, )epochs我建议设200左右配合早停机制不用真的跑满通常到150轮左右就收敛了。batch1660Ti 6G显存上yolov8s最大能跑到16但建议保守一点设8留出显存给验证过程。lr0默认0.01就好不用特意调。patience早停的容忍轮数设为50表示50轮mAP没提升就提前结束。workersWindows下建议设4过高容易报DataLoader worker错误。训练产生的结果保存在runs/detect/train/目录下。里面有last.pt和best.pt别搞混了。best.pt是根据验证集mAP保存的最优权重我们部署时使用的就是它。3.3 损失函数曲线的可视化与保存训练过程中yolov8默认会输出很多指标包括box_loss、cls_loss、dfl_loss以及precision、recall、mAP50、mAP50-95。但是训练完成后很多人不知道怎么看这些曲线或者想单独生成损失函数曲线的图。其实ultralytics在训练过程中已经把曲线图保存在了runs/detect/train/results.png里这里面包含了所有指标的曲线。如果想要单独画某一条损失曲线比如box_loss可以读取训练时保存的CSV文件import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.plot(df[epoch], df[train/box_loss], labeltrain_box_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.grid(True) plt.title(Box Loss Curve) plt.savefig(box_loss.png, dpi150) plt.show()这个CSV文件在训练结束后会生成在results.csv里记录每一个epoch的指标。答辩的时候把这个图放在PPT里导师会认为你真的理解了训练过程的迭代逻辑加分作用非常明显。训练结束后验证一下模型表现model YOLO(runs/detect/train/best.pt) metrics model.val(datadatasets/data.yaml) print(fmAP50: {metrics.box.map50:.4f}, mAP50-95: {metrics.box.map:.4f})如果mAP50超过0.85说明模型已经比较可靠了如果只有0.6左右就需要回来看是不是标注有问题、类别太相似、或者样本量不够。3.4 模型导出与单张图测试训练完成后为了让推理速度更快可以把权重导出为ONNX格式或者如果部署在NVIDIA显卡上导出为TensorRT engine格式model YOLO(runs/detect/train/best.pt) model.export(formatonnx, imgsz640, halfTrue) model.export(formatengine, imgsz640, halfTrue)ONNX格式的好处是跨平台、跨框架通用PyQt5程序里调用也方便。但要注意导出的onnx文件在推理时需要匹配的opencv版本或onnxruntime版本养成好习惯就是用requirements.txt锁定版本。用导出后的模型跑一张图测试from ultralytics import YOLO model YOLO(best.pt) results model.predict(test.jpg, conf0.25, iou0.45) for r in results: boxes r.boxes.xyxy.cpu().numpy() confs r.boxes.conf.cpu().numpy() cls_ids r.boxes.cls.cpu().numpy().astype(int) for box, conf, cls_id in zip(boxes, confs, cls_ids): print(f类别: {model.names[cls_id]}, 置信度: {conf:.3f}, 坐标: {box})conf设0.25比较合理太高会漏检太低会多很多误检。4. PyQt5自适应界面设计与检测模块集成4.1 主窗口布局从原型到完整界面在动手写界面代码之前先把界面的功能分区想清楚。一个完整的检测系统界面至少需要有以下几个模块文件操作区打开图片、打开文件夹、打开摄像头、检测控制区开始检测、停止检测、保存结果、结果显示区原图展示、检测结果展示、状态信息区模型信息、推理耗时、检测计数、日志输出区运行日志。基于这几个功能模块主窗口我推荐用QMainWindow搭配中央部件。左侧固定宽度的控制面板用QVBoxLayout排列按钮和选项右侧用QSplitter分上下两个QLabel显示原图和检测结果。QSplitter的好处是用户可以拖动分割条调整图像显示区域的比例这本身就是一种自适应的体验。底部用QTextEdit作为日志输出区域。具体布局伪代码如下class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(锂电池表面缺陷检测系统) self.resize(1280, 800) self.setup_ui() self.setup_detector() self.setup_thread() def setup_ui(self): # 中央部件 central_widget QWidget() self.setCentralWidget(central_widget) main_layout QHBoxLayout(central_widget) # 左侧控制面板 left_widget QWidget() left_widget.setFixedWidth(240) left_layout QVBoxLayout(left_widget) # 文件按钮 self.btn_open_image QPushButton(打开图片) self.btn_open_dir QPushButton(打开文件夹) self.btn_open_camera QPushButton(打开摄像头) self.btn_stop_detect QPushButton(停止检测) self.btn_save_result QPushButton(保存结果) ... # 右侧显示区域 right_splitter QSplitter(Qt.Vertical) self.label_original QLabel(原始图像) self.label_result QLabel(检测结果) right_splitter.addWidget(self.label_original) right_splitter.addWidget(self.label_result) main_layout.addWidget(left_widget) main_layout.addWidget(right_splitter)有一点经验可以分享按钮不要用太花哨的样式但是一定要给按钮设一个固定高度比如40px不然在高DPI缩放下按钮会变得拥挤或者变形。可以写一个统一按钮高度的函数。4.2 自适应分辨率的三种实现手段自适应界面设计不是一句话的事得从三个层面都做到位。第一个层面是布局管理器自动伸缩。这一点用好QVBoxLayout、QHBoxLayout和QSplitter就基本实现了。不要在布局管理器里给控件设置固定的绝对坐标。如果某个控件确实需要固定宽度比如左侧面板可以设置setFixedWidth。但主显示区域一定要跟着窗口走用QSplitter加addWidget的方式就能做到。第二个层面是图像显示区域的等比例缩放。直接给QLabel设置setPixmap后窗口缩放时图片并不会自动缩放需要重写resizeEvent事件class ScalableLabel(QLabel): def __init__(self, text, parentNone): super().__init__(text, parent) self._pixmap QPixmap() self.setMinimumSize(1, 1) self.setAlignment(Qt.AlignCenter) # 保证不拉伸变形 self.setScaledContents(False) def set_image(self, img_bgr): # 将OpenCV BGR图像转为QImage h, w, ch img_bgr.shape rgb_img cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) qimage QImage(rgb_img.data, w, h, ch * w, QImage.Format_RGB888) self._pixmap QPixmap.fromImage(qimage.copy()) self.update_display() def update_display(self): if not self._pixmap.isNull(): scaled self._pixmap.scaled( self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.setPixmap(scaled) def resizeEvent(self, event): super().resizeEvent(event) self.update_display()这段代码的核心是Qt.KeepAspectRatio保证图片在缩放时按比例缩放不变形。同时重写resizeEvent窗口大小变化时自动触发重新缩放。这样无论你把窗口拉多大多小图像都会完整、等比例地显示在里面。第三个层面是高分屏适配。在main.py的入口处加上import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) app QApplication(sys.argv)这两个属性要放在创建QApplication之前设置否则不生效。另外字体方面建议统一设置应用字体比如微软雅黑避免默认字体在125%缩放下线条过细。4.3 检测线程封装不卡界面的关键做桌面检测系统最容易犯的错误就是直接在主线程里调用model.predict()。虽然代码写起来简单但推理一次少说也要50-100ms加上画图操作界面会明显卡顿拖拽窗口都会掉帧。更严重的是如果图片分辨率大或者连续处理视频帧界面直接无响应假死这是答辩现场最忌讳的。正确做法是用QThread将推理放到子线程通过信号与槽返回结果。我封装了两个类DetectionThread负责跑模型推理和VideoThread负责读视频帧它们之间用Python标准信号连接到UI更新。class DetectionThread(QThread): result_ready pyqtSignal(dict) log_message pyqtSignal(str) def __init__(self, model_path, conf0.25, iou0.45): super().__init__() self.model_path model_path self.conf conf self.iou iou self.model None self._running False self._img None def run(self): # 在子线程中加载模型避免启动卡顿 if self.model is None: from ultralytics import YOLO self.model YOLO(self.model_path) while self._running: if self._img is not None: img self._img self._img None results self.model.predict( img, confself.conf, iouself.iou, verboseFalse ) # 处理结果 for r in results: boxes r.boxes.xyxy.cpu().numpy() confs r.boxes.conf.cpu().numpy() cls_ids r.boxes.cls.cpu().numpy().astype(int) names self.model.names detections [] for box, conf, cls_id in zip(boxes, confs, cls_ids): detections.append({ box: box, conf: float(conf), cls_id: int(cls_id), cls_name: names[int(cls_id)] }) drawn_img r.plot() # YOLOv8内置画框 self.result_ready.emit({img: drawn_img, detections: detections}) else: self.msleep(10) def update_image(self, img): self._img img def start_detection(self): self._running True self.start() def stop_detection(self): self._running False self.wait() def load_model(self): # 预加载模型避免首次推理缓慢 if self.model is None: from ultralytics import YOLO self.model YOLO(self.model_path)主线程中连接信号self.detect_thread DetectionThread(models/best.pt) self.detect_thread.result_ready.connect(self.update_result_display) self.detect_thread.log_message.connect(self.log_output.append) # 打开图片的逻辑 def open_image(self): file_path, _ QFileDialog.getOpenFileName( self, 选择图片, , 图片文件 (*.jpg *.jpeg *.png *.bmp) ) if file_path: img cv2.imread(file_path) # 先显示原图 self.label_original.set_image(img) # 发送给检测线程 self.detect_thread.update_image(img) if not self.detect_thread.isRunning(): self.detect_thread.start_detection()这里有个小细节模型加载不要放在__init__里因为ultralytics第一次加载模型需要初始化CUDA上下文放到子线程run方法里加载可以让窗口先弹出用户体验好很多。如果加载过程太久用户以为程序卡死了反而不好。4.4 视频与摄像头实时检测帧率优化策略处理视频文件或者摄像头实时流时逻辑和单张图片不同需要用一个单独的线程持续读帧把每一帧发送到检测线程。注意两个线程之间不要直接用队列传输大数组因为Python多线程受GIL限制传输大数组会拖慢整体速度。更好的做法是使用共享变量加锁或者直接用带锁的img变量。class VideoThread(QThread): frame_ready pyqtSignal(object) video_end pyqtSignal() def __init__(self): super().__init__() self.cap None self._running False self.mode None # camera or video def start_camera(self, camera_index0, width1280, height720): self.cap cv2.VideoCapture(camera_index) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) self.mode camera self._running True self.start() def start_video(self, video_path): self.cap cv2.VideoCapture(video_path) self.mode video self._running True self.start() def run(self): while self._running and self.cap.isOpened(): ret, frame self.cap.read() if not ret: self.video_end.emit() break if self.mode camera: # 摄像头画面可以做一次镜像 frame cv2.flip(frame, 1) self.frame_ready.emit(frame) # 控制读取帧率避免CPU占用过高 self.msleep(30) # ~33FPS def stop(self): self._running False if self.cap: self.cap.release() self.wait()实时检测场景下完整跑一次yolov8s 640推理大约需要80-120ms而视频读帧只需要15ms左右两者叠加会导致帧率下降。我的做法是检测线程本身只处理最新的一帧丢掉积压的旧帧保证实时性优先。这也是上面DetectionThread中_img变量用覆盖而非队列的原因。5. 常见问题与排查技巧实录5.1 PyQt5界面显示异常OpenGL导致的界面无显示在前面环境配置部分已经讲过这里再补充一个常见表现程序启动后有窗口黑屏或者点击按钮后整个界面变透明。除了设置QT_OPENGLsoftware之外还有一种情况是显卡驱动太旧导致QtWebEngine初始化失败。建议优先升级显卡驱动仍然不行就卸载重装PyQt5。pip uninstall pyqt5 pyqt5-tools pyqt5-sip -y pip install pyqt55.15.9 pyqt5-tools5.15.9.3.3另外注意不要同时安装pyqt5和pyside6两个包都装在同一个环境里会引起模块冲突导致随机崩溃。5.2 界面字体模糊与控件挤在一起高分屏下如果没设置Qt.AA_EnableHighDpiScaling界面看起来就像马赛克一样字是糊的按钮间距也不正常。这个必须在QApplication创建之前设置。如果设置之后字体还是偏小可以全局设置字体大小from PyQt5.QtWidgets import QApplication font QApplication.font() font.setPointSize(10) QApplication.setFont(font)另外在125%/150%缩放下QLabel显示图片的边缘可能会出现1px的白边这是QPixmap缩放时的抗锯齿问题。可以在QLabel周围加一个QFrame边框来掩盖视觉上会干净很多。5.3 模型训练时显存不足或训练速度极慢1660Ti 6G显存跑yolov8sbatch设8是比较稳的。如果报CUDA out of memory按顺序排查第一其他程序是否占用显存比如浏览器、微信偶尔会调用GPU第二降低batch到4或2第三换用yolov8n模型第四在train参数中加cacheFalse关闭数据缓存。训练速度极慢还有一个容易被忽略的原因数据集的图片路径存在中文或者空格导致ultralytics的加载进程频繁崩盘重试。解决方案是数据集目录和训练项目目录全部用英文路径不要有空格。5.4 推理时模型加载慢或反复加载第一次调用model.predict()时ultralytics会做一次完整的模型初始化包括加载权重、初始化CUDA上下文这个过程耗时1-2秒很正常。如果你发现每检测一张图都要等1-2秒说明每次predict时都重新实例化了模型。正确做法是创建一次YOLO对象然后在循环里反复调用它的predict方法。上面的DetectionThread里就是这样做的模型在run方法里只加载一次。另外如果要追求更快的响应速度可以先把第一帧放到线程里做预热num_samples 2 result model.predict(img, ...) # 前两帧用于预热后面的推理速度会显著提升这种预热策略在工业项目中很常见。5.5 打包成exe时的各种坑答辩前想把项目打包成exe这个需求太常见了。但PyQt5 ultralytics打包时坑非常多。推荐用PyInstallerpip install pyinstaller pyinstaller -D -w main.py --add-data models/best.pt;models --add-data datasets/data.yaml;datasets几个关键问题-w参数是去掉控制台窗口但是调试阶段建议不要加不然看不到报错信息。--add-data用来打包模型和数据文件Windows下分隔符用分号Linux/macOS用冒号。ultralytics进去之后体积巨大打包出来可能超过500M这是正常的。如果嫌大可以只导入推理需要的模块但效果有限。打完包双击exe没反应可以先在命令行里运行exe看报错。最常见的是缺DLL用dumpbin /dependents查看依赖项或者把dist目录下的文件手动补充到打包目录。另一种方式是降级ultralytics版本有些版本在PyInstaller下兼容性更好。6. 一些额外的经验与建议我一直觉得毕业设计真正锻炼人的地方不是按部就班跑通流程而是在遇到各种莫名其妙的问题之后能快速定位问题根源并解决。拿这个项目来说从数据标注到模型训练再到界面整合每一步都有很多细节而正是这些细节决定了一个系统是demo级别还是能拿到高分级的作品。有一点特别想强调模型训练时的数据质量永远比模型结构重要。如果你发现效果不好先别急着换backbone或者改attention先回去看看标注是否合理、各类别样本量是否均衡、图片的对比度光照是否太多样。我见过有人为了刷那0.01的mAP去魔改网络结构结果在答辩现场的测试集上反而翻车。扎实的数据整理和标注统一性才是项目稳定性的最大保障。在界面上我建议在状态栏加入一个实时的“检测帧率”显示。PPT答辩的时候现场演示评委看到FPS数值在跳会比看到一张静态截图更有说服力。这个效果实现起来很简单就是在检测线程返回结果时记录时间戳计算一帧的时间倒数。最后再分享一个小技巧。在PyQt5界面里显示检测结果时YOLOv8已经提供了内置的r.plot()方法画出带标注的图像。但它是加载了自己默认的字体和颜色对于锂电池缺陷检测这种工业场景我建议手动重新画一遍指定缺陷类别的颜色和边框粗细。比如划痕用红色、凹坑用黄色、脏污用蓝色这样不同类别一眼就能区分开展示效果好很多。手动画框代码也很简单直接用cv2.rectangle和cv2.putText在检测结果图上绘制就行。这个系统后续还有很多可以扩展的方向。比如把缺陷统计结果导出成Excel报表或者接入PLC做自动分拣联动甚至用LiteRT做边缘端部署。对于毕业设计而言先把自己定位成完整的桌面应用把检测链路和界面交互做好就已经足够了。过程中踩过的坑才是这个项目最大的收获。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →