尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

YOLOv8+PyQt5共享自行车识别检测系统:从数据集到告警

发布时间:2026/9/26 15:51:53

资讯中心
01
ARTICLE

YOLOv8+PyQt5共享自行车识别检测系统:从数据集到告警

YOLOv8+PyQt5共享自行车识别检测系统:从数据集到告警
简介这套基于YOLOv8与PyQt5的共享自行车识别检测系统面向计算机视觉相关专业学生、竞赛选手和开发人员适用于共享单车违规停放检测告警、车辆计数等场景也可直接用于课程设计、毕业设计或项目初期演示。压缩包约466.56MB共889个文件包括304张jpg图像样本、247个txt标注文件、87个py源码、9个pt模型权重、47个yaml配置、评估曲线png以及PyQt5界面ui文件并额外提供容器化部署配置、C推理示例和部署说明文档。目前已有1849人学习/使用。除训练好的模型和数据集外还附带详细部署步骤、推理脚本与评估结果按操作说明即可运行模型准确率约98%。在此基础上可扩展违规停放告警等应用也适合作为课程设计和毕设的二次开发起点。1. 共享自行车识别检测系统YOLOv8PyQt5这套组合能落成什么样城市街道上共享单车乱停乱放靠人工巡查成本高靠盯屏又顾不过来。基于YOLOv8PyQt5实现的共享自行车识别检测系统做的事很具体从摄像头画面里把共享单车自动框出来、统计数量、跟预先划定的禁停区比对一旦违规就弹窗告警并写日志。它不是学术Demo而是数据集、模型、GUI三层齐全的完整项目。适合三类人做毕设的学生要交一个能现场演示的系统做智慧城市或物业安防的工程师想快速验证算法加界面这条路线是否可行中小团队想低成本试点违规停放告警。读完这篇你能从零把数据标注、YOLOv8训练、PyQt5界面和告警逻辑串起来也知道哪几个环节最容易翻车。2. 数据集准备用Labelme标注共享自行车并转成YOLOv8训练格式标题里写着含数据集模型这类打包项目通常会附带一批标好的图片和训练好的权重。我的建议是现成权重拿来演示没问题但真要部署到你的街道、你的摄像头角度精度大概率会掉因为场景对不上。所以数据集必须自己过一遍。常见做法是三个来源混用YOLOv8自带的COCO预训练权重里已经有自行车这个类的底子自己拍视频抽帧补目标场景再不够就用开放数据集的街景图补充。共享自行车识别的难点不在是不是车而在是不是共享单车——普通自行车、电动车、三轮车都会干扰告警逻辑类别定义是第一件事。2.1 数据采集与筛选先解决模型看什么的问题共享单车的视觉特征是车架颜色鲜艳黄、橙、蓝、后轮带电子锁、大部分没有后座、车筐是塑料筐。采集时尽量覆盖不同时段清晨逆光、夜间路灯、不同天气雨天反光、阴天、不同遮挡行人挡住半辆车、树影、不同摆放姿态倒地、斜插、车叠车。我自己常用的采集方式拿手机沿街区走一圈录三段视频每段五分钟左右然后每5帧抽一帧能快速攒出上千张候选图。抽完帧先人工删一遍去掉重复度高的、画面糊的、单车目标面积小于整图5%的。目标太小模型学不到特征留着只会拖累训练。筛选完的图片按8:1:1分到train、val、test。有个细节同一辆车在连续帧里出现的画面尽量放进同一组不然训练集和验证集内容太相似验证指标会虚高部署后才发现实际效果没那么好这是典型的数据泄漏。另外不要只标正样本也要留一批不包含共享单车、但包含电动车和三轮车的图做负样本YOLO训练时背景分类才学得准。2.2 Labelme标注与批量转换成YOLO的txt标注这一步业内最常用的工具是Labelme它基于PyQt5图形界面标注结果存成JSON。YOLOv8训练需要的是每张图对应一个同名txt每行格式为类别id x_center y_center width height坐标都归一化到0到1。Labelme默认画多边形但检测任务要矩形框。两种办法在Labelme里选Create Rectangle直接画框或者画多边形后取外接矩形。我一般直接画矩形速度快框的是整车轮廓而不是车轴。下面这个脚本把Labelme的JSON批量转成YOLO格式同时过滤掉非法框import json import os from glob import glob def convert_labelme_json(json_path, out_dir, class_names): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w, img_h data[imageWidth], data[imageHeight] txt_name os.path.splitext(os.path.basename(json_path))[0] .txt lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue cls_id class_names.index(label) # 矩形标注里 points 存的是两个对角点 x1, y1 shape[points][0] x2, y2 shape[points][1] # 转成归一化的中心点坐标和宽高 x_center ((x1 x2) / 2.0) / img_w y_center ((y1 y2) / 2.0) / img_h w abs(x2 - x1) / img_w h abs(y2 - y1) / img_h # 宽高为 0 或坐标越界的框直接丢弃 if w 0 or h 0 or x_center 0 or y_center 0: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) if __name__ __main__: class_names [shared_bike] json_files glob(labelme_json/*.json) os.makedirs(yolo_labels, exist_okTrue) for jf in json_files: convert_labelme_json(jf, yolo_labels, class_names)脚本逻辑不复杂读JSON拿图片宽高遍历shapes里的矩形标注换算成归一化中心坐标和宽高写进txt。容易被忽略的是坐标越界问题归一化后偶尔会出现1.000001这种值PyTorch加载时可能报错或训练出nan所以对中心点做了下界保护宽高为0的框直接弃掉。class_names列表的顺序就是模型输出的类别id后面GUI显示和告警逻辑都依赖这个顺序不要随意调整。转完之后目录按YOLO约定放好data.yaml内容如下# dataset/data.yaml path: dataset train: images/train val: images/val names: 0: shared_bike2.3 标注数量门槛与坏图检查新手最爱问到底要标多少张单类别、场景单一一个固定路口监控视角800张左右就能训到mAP50过0.85要应对多个路段和多个品牌1500张起步其中两成以上覆盖遮挡和夜间。这个量级一个人加一台普通电脑两三天能标完。另外处理数据集用于yolov8训练前一定要做坏图检查图片本身损坏、txt里出现负坐标或超大坐标、图片和txt文件名对不上都会让训练中途崩掉。常见做法是写个小脚本扫一遍所有样本再开训比训练到一半报错回头查图片高效得多。from PIL import Image from glob import glob bad_images [] for p in glob(dataset/images/train/*.jpg) glob(dataset/images/val/*.jpg): try: Image.open(p).verify() except Exception: bad_images.append(p) bad_labels [] for p in glob(dataset/labels/train/*.txt) glob(dataset/labels/val/*.txt): for line in open(p): parts line.split() if len(parts) ! 5: bad_labels.append(p) break print(坏图:, bad_images) print(坏标签:, bad_labels)这段逻辑就干两件事验证图片能不能被PIL正常打开检查标签行是不是恰好5个字段。跑完输出两个空列表再开始训练能省掉后面一整天的排查时间。3. 用YOLOv8训练自己的数据集环境搭建、关键参数与损失曲线判读3.1 环境搭建CPU和GPU两条路线YOLOv8由Ultralytics团队维护训练命令封装得相当完整。环境这一步最常出问题的是PyTorch版本和GPU对不上。先说CPU路线笔记本没有独立显卡时用yolov8n这种nano体量也能训就是慢几百张图的小数据集跑一晚上能出结果。Ubuntu 20.04搭建yolov8环境cpu版本按下面走conda create -n yolov8 python3.9 -y conda activate yolov8 pip install ultralyticsGPU路线要先确认驱动支持哪个CUDA版本再装对应编译的PyTorch。我习惯先跑nvidia-smi看Driver版本再去PyTorch官网选对应的wheel例如pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics装完立刻验证python -c import torch; print(torch.cuda.is_available())输出True再往下走。输出False多半是torch装的CPU版或者CUDA版本与驱动不匹配这时不要硬等直接重装对应版本。还有一句每次都要强调的话项目环境用conda隔离。torch、pyqt5、labelme混装在一个环境里后面会同时出现labelme起不来和界面报错这类连锁问题。3.2 训练命令、关键参数与yolov8网络结构数据准备好后训练本身反而最简单。一条命令就能起yolo detect train datadataset/data.yaml modelyolov8s.pt epochs200 imgsz640 batch16 device0想便于复现就把参数写进Python脚本from ultralytics import YOLO model YOLO(yolov8s.pt) # 加载预训练权重做迁移学习 model.train( datadataset/data.yaml, epochs200, imgsz640, batch16, device0, # cpu 或 0 patience30, # 30轮无提升自动提前停 lr00.01, # 初始学习率 hsv_h0.015, # 色调增强幅度 hsv_s0.7, # 饱和度增强 hsv_v0.4, # 亮度增强 fliplr0.5, # 水平翻转概率 )关键参数按下面这个表调就够用参数推荐值说明modelyolov8s.pt单类检测建议s起步x体量速度太差epochs200配合patience早停不会白跑imgsz640精度和速度的平衡点显存不够降到480batch16 或 86GB显存用820GB以上可以16device0多卡写0,1无GPU写cpulr00.01训练出nan时降到0.001共享单车是单类检测、目标占画面5%到30%yolov8s在GTX 1660 Ti上能跑到40到60帧实时预览够用直接上yolov8x精度提升有限帧率掉一半GUI界面会明显发飘。关于yolov8网络结构图网上很多简单概括backbone用C2f模块提特征neck用PAN-FPN融合多尺度head是解耦头分开输出分类和回归。理解到这个层面对调参足够不用抠每个模块的通道数。3.3 训练结果判读用损失曲线和mAP判断有没有训好训练结束在runs/detect/train*目录里有results.png和results.csv。我习惯自己画一张更细的图把训练集和验证集损失分开看import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) df.columns [c.strip() for c in df.columns] # 列名带前导空格先清理 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].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) ax[1].set_title(mAP) ax[1].legend() plt.savefig(loss_and_map.png, dpi150)判读标准就三条box_loss和cls_loss一路下降且不反弹val/box_loss没有在train继续降的同时明显翘头mAP50训练后期在0.9附近不再剧烈波动。单类场景mAP50低于0.85先别调参回去看数据——大概率某个品牌的车漏标了或者夜间样本太少。调参某些时候带点玄学甚至同一个参数换台机器效果就不一样但损失曲线不会骗人。val指标高只说明数据集内部表现好真实摄像头下的表现还要回到GUI环境里验证这一点放到最后一章讲。4. PyQt5 GUI界面设计把检测模型封装成违规停放告警系统4.1 界面布局与检测线程分离标题里精美GUI界面是另一个重点。PyQt5界面设计可以用Qt Designer拖控件也可以在PyCharm里纯代码写。个人习惯布局用designer画业务逻辑用代码写。界面大概结构左侧大区域放摄像头画面QLabel右侧放检测数量、置信度阈值滑块、告警日志列表QListWidget底部一排按钮——开始、停止、退出、选择视频文件。界面好不好看不重要操作过程不卡才是核心体验。想要界面看起来不那么工程化可以加一个状态指示灯检测线程正常运行亮绿掉线亮红这个比花哨的样式更能让使用方安心。最容易翻车的是把模型推理放进主线程。YOLOv8的predict在CPU或GPU上都要几十毫秒到几百毫秒直接放在槽函数循环里执行界面必然假死。正确做法摄像头读取和推理全部放进QThread子类用信号把画面和结果发回主线程。最小可跑的检测线程import cv2 from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage from ultralytics import YOLO class DetectThread(QThread): frame_ready pyqtSignal(QImage) # 画面信号 result_ready pyqtSignal(dict) # 结果信号 def __init__(self, model_path, source0, conf0.5): super().__init__() self.model YOLO(model_path) self.cap cv2.VideoCapture(source) self.conf conf self.running True def run(self): while self.running: ok, frame self.cap.read() if not ok: break res self.model.predict(frame, confself.conf, verboseFalse) for r in res: for box in r.boxes: x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.frame_ready.emit(qimg.copy()) # 必须 copy否则花屏 self.result_ready.emit({count: int(len(res[0].boxes))}) self.cap.release()主窗口部分from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget from PyQt5.QtCore import Qt from PyQt5.QtGui import QPixmap class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(共享自行车识别检测系统) self.video_label QLabel(等待开启检测) self.count_label QLabel(当前检测数量: 0) self.start_btn QPushButton(开始检测) self.start_btn.clicked.connect(self.start_detect) central QWidget() layout QVBoxLayout(central) layout.addWidget(self.video_label) layout.addWidget(self.count_label) layout.addWidget(self.start_btn) self.setCentralWidget(central) self.thread None def start_detect(self): if self.thread is None or not self.thread.isRunning(): self.thread DetectThread(best.pt, source0, conf0.5) self.thread.frame_ready.connect(self.update_frame) self.thread.result_ready.connect(self.update_result) self.thread.start() def update_frame(self, qimg): self.video_label.setPixmap( QPixmap.fromImage(qimg).scaled( self.video_label.size(), Qt.KeepAspectRatio)) def update_result(self, data): self.count_label.setText(f当前检测数量: {data[count]})两个容易踩的点。QImage装的是rgb.data的指针函数返回后底层数据可能被释放界面就会花屏所以emit前用copy()复制一份。setPixmap如果不scaled画面会跟随原始分辨率窗口拉伸时直接溢出所以一定要用KeepAspectRatio。信号槽的连接要在start之前完成否则第一批帧发出来时槽还没挂上画面会丢最初几帧。提示检测线程里千万别碰UI控件哪怕只是改个文本。跨线程操作界面是PyQt5崩溃和假死的头号来源。4.2 违规停放判定禁停区多边形与告警去抖识别只是第一步违规停放检测告警项目的关键在判定逻辑。最实用的方案在画面里预先用鼠标点出禁停区消防通道、人行道、地铁口顶点坐标存成配置文件。每帧检测到共享单车后取检测框底边中点作为车辆落地点用射线法判断点是否在多边形内def point_in_polygon(x, y, poly): n len(poly) inside False j n - 1 for i in range(n): xi, yi poly[i] xj, yj poly[j] # 向右做水平射线统计与多边形边的交点数 if ((yi y) ! (yj y)) and \ (x (xj - xi) * (y - yi) / (yj - yi) xi): inside not inside j i return inside为什么取底边中点而不是框中心共享单车框的中心落在车座或车筐高度车筐探出禁停线而车轮还在线内时中心点判定会漏报底边中点更接近车轮与地面接触点。这是做精确告警的一个血泪经验。单帧判定不能直接触发告警。行人走过挡一下、车被风吹动半个轮距都是瞬时变化会造成误报。我给告警加三重机制用IOU把相邻帧的检测框关联起来识别是不是同一辆车连续N帧25fps下取15帧约0.6秒判定在同一禁停区内才置为确认违规触发后进入冷却期比如3分钟内同一辆车不再重复告警同时把告警写入CSV并弹窗提示配合QApplication.beep()做声音提醒。告警日志可以接到QListWidget里实时滚动完整字段是时间、摄像头ID、坐标、置信度。检测数量、告警条数、当天误报率这几个指标放在右侧面板使用方一眼就能看到系统干了多少活。这段逻辑跑通后一个能演示的完整项目就闭环了。5. 常见问题排查YOLOv8训练与PyQt5界面最容易翻车的5个坑翻车之后没有后悔药但下面五条能让你提前避开。5.1 labelme装不上PyQt5依赖冲突现象pip install labelme时报错提示PyQt5相关依赖冲突或者安装完成后运行labelme直接闪退。 原因环境里已经存在另一个版本的PyQt5和labelme要求的版本打架无显示服务器的SSH环境也会导致GUI初始化失败。 解决先指定一个已知兼容的版本范围再装labelmepip install PyQt55.15.4,5.16 pip install labelme装完跑labelme如果还报错检查是不是把环境装在了没有桌面会话的机器上。标注这种活直接在本地桌面做别为难服务器。5.2 训练中途loss变nan现象训练十几个epoch后box_loss突然变成nan后面曲线全塌验证指标归零。 原因数据里有宽高为0或坐标越界的标注部分图片读取失败却没被跳过学习率过高也是一个常见诱因。 解决先用第2.2节的过滤逻辑清洗labels再跑一遍第2.3节的坏图脚本最后把lr0从0.01降到0.001重跑。多数情况下坏图是主因少数情况下是学习率过冲两个都查一遍再训。5.3 GPU显存不够直接OOM现象训练刚启动就报CUDA out of memory进程被杀前面几小时白等。 原因batch16、imgsz640加上数据增强显存需求超过显卡容量。 解决batch先降到8imgsz降到480再不行换yolov8n。GTX 1660 Ti这类6GB卡跑yolov8s加imgsz640、batch8是体验比较舒服的组合。注意不要一边训练一边开一堆浏览器标签显存释放会变慢多开几个页面就可能直接OOM。5.4 点击开始后界面无响应现象窗口能打开但点开始检测后整个界面变灰标题栏出现未响应过几秒才恢复。 原因VideoCapture.read和model.predict写在了主线程槽函数里阻塞了Qt事件循环界面刷新不出来了。 解决按第4章的结构改成QThread所有耗时操作放run()UI更新通过信号槽回主线程按钮的槽函数里只负责启动线程。这个坑几乎每个用PyQt5做检测界面的人都会踩一次踩过一次就记住了。5.5 画面偏色或花屏现象界面里画面整体偏蓝绿或者拖动窗口后图像变成破碎的彩色条纹。 原因cv2读出来是BGR顺序QImage默认按RGB解释另一个原因是QImage引用了会被复用的缓冲区。 解决cvtColor转换通道emit前用copy()。记住一句OpenCV是BGRQt是RGB跨库传图像必转通道。如果接的是网络摄像头或RTSP流延迟明显偏大时先检查缓冲区而不是怀疑检测速度调用cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)能明显降低延迟。6. 进阶改造告警验证、ONNX导出与RK3588边缘部署系统跑通之后真正要投入使用前一定要做一次回放验证。我现在的习惯是找一个现场摄像头录一整天视频拿回GUI里用回放模式跑一遍把告警日志导出来跟人工复核结果对比算检出率和误报率。误报率控制在5%以内才敢开放正式告警否则使用方每天被假告警轰炸第二天就把系统关了。为了做这件事我在界面里加了一个可视化调试开关打开后会把禁停区多边形、每帧检测框、是否命中禁停区这些中间结果全部画到画面上并录屏任何一个新增点位都先跑几小时录像再开放。如果现场摄像头点位多、每路都接电脑不现实就要考虑边缘部署。YOLOv8导出ONNX一条命令的事yolo export modelbest.pt formatonnx imgsz640 opset12导出的ONNX可以喂给RK3588这类带NPU的板子转成RKNN格式后用NPU推理单路视频的检测延迟能压到几十毫秒以内一台板卡同时处理四路摄像头也不吃力。这时候PyQt5的角色就从实时检测界面退成管理端配置和告警展示工具两边通过MQTT或HTTP接口通信。要注意的是RKNN做int8量化后mAP会掉几个点量化完一定要拿val集重新测一遍别直接上线。另外一个容易被忽略的细节是告警阈值置信度阈值不要拍脑袋定0.5先在回放数据上把误报和漏报的分布拉出来再决定阈值设多少合适。宁可漏报几条也不要反复误报这是违规停放告警这类项目能长期存活下来的关键。我的教训是第一版就追求一个都不能少结果使用方被误报烦到直接弃用后面改成保守策略反而效果好。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。