基于YOLOv8的商场扶梯梳齿板异物卡滞预警系统研究
发布时间:2026/10/8 1:09:06 锦皓数字建站

简介面向计算机视觉与深度学习方向的在校学生、教师及企业开发者这份基于YOLOv8的商场自动扶梯梳齿板异物卡滞预警系统针对自动扶梯梳齿板异物卡滞这一具体的安全监测场景提供了从模型训练、指标评估到可视化部署的完整流程。压缩包共八个文件包括三个脚本文件分别承担可视化界面、模型训练和视频检测功能、三个模型权重文件以及两个说明文档整体仅十五兆左右轻量易用适合毕业设计、课程设计及大作业场景。项目源码均经过运行验证可直接生成混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果和标签分布图等关键图表同时附有完整数据集、部署教程和启动说明帮助读者快速理解从数据准备到模型推理的各个环节。目前已有三十六人学习下载上手门槛低既适合零基础学习者也便于在原有代码基础上扩展功能进行二次开发。1. 商场自动扶梯梳齿板异物卡滞预警系统YOLOv8 在这套毕设方案里到底解决什么扶梯梳齿板一旦卡进硬币、钥匙或发卡不是小事。异物会让梳齿变形梯级在梳齿板处顶起轻则停梯检修重则乘客被绊倒。很多商场现在的办法还是人工定时巡检但高峰时段根本看不过来。基于YOLOv8的商场自动扶梯梳齿板异物卡滞预警系统就是把检测模型对准扶梯出入口的梳齿板区域实时输出异物的类别和位置检测到卡滞就弹窗、语音、亮灯联动。它之所以适合毕设或课程设计是因为它不只有算法还带可视化界面、完整数据集和部署教程解压后把环境一配就能跑通整个流程从数据标注到训练、预警界面再到部署闭环完整。2. 把梳齿板异物识别拆成目标检测问题YOLOv8 的选型理由与检测头设计2.1 为什么选 YOLOv8 而不是帧差法或两阶段模型梳齿板异物卡滞检测第一反应是运动检测在固定区域做帧差看看有没有“多出来的东西”。这个思路在实验室场景能跑但商场扶梯入口有大量干扰——梳齿板本身有金属反光梯级在持续运动乘客的鞋和裙摆会在画面边缘进进出出。帧差法会把扶梯本身的运动也当成前景环境光一变更是满屏误报。所以正经做法是把异物识别当成目标检测任务让模型直接学习“梳齿板上的硬币/钥匙/发夹”长什么样而不是靠手工特征去猜。选型上两阶段检测器比如Faster R-CNN在精度上有优势但要做到实时视频流检测CPU或边缘设备基本跑不动训练和部署也复杂。YOLOv8属于单阶段检测器使用 anchor-free 的检测机制不需要预设大量锚框检测头把分类和回归分支解耦收敛更快。更重要的是 Ultralytics 官方库把训练、验证、导出、推理封装成一条命令对毕设和课程设计来说最省时间。下面这个表格是我通常做对比时的参考。方案推理速度640×640小异物表现部署灵活度YOLOv8n高CPU可跑中需调输入尺寸高ONNX/RKNN都有YOLOv5s较高中中Faster R-CNN低难实时较好低帧差轮廓快但误报多差中YOLOv8n 的 n 是 nano模型文件只有6M左右如果训练集里异物尺寸小可以换更大的 s 或 m但边缘部署的帧率会下降。这里建议先拿 nano 跑通再根据漏检情况决定是否上调模型复杂度。2.2 完整数据集怎么组织COCO/VOC 标签转成 YOLO 能用的 txt拿到完整数据集后第一步是把标注格式统一。常见的数据集是 COCO JSON 或 VOC XMLYOLO 训练需要的是每个图片同名 txt每行一个目标类别id、归一化中心x、归一化中心y、归一化宽高。项目里如果已经整理好直接可以看到 labels 目录如果没有这段脚本可以救场。import json from pathlib import Path def coco_to_yolo(coco_path: str, out_dir: str): data json.loads(Path(coco_path).read_text(encodingutf-8)) # image_id - 文件名 img_map {im[id]: im[file_name] for im in data[images]} cat_map {c[id]: i for i, c in enumerate(data[categories])} # 类别从0开始 anns {} for ann in data[annotations]: img_name img_map[ann[image_id]] anns.setdefault(img_name, []).append(ann) out Path(out_dir) out.mkdir(parentsTrue, exist_okTrue) for img_name, ann_list in anns.items(): # 实际工程里要通过 image_id 去查对应宽高 image_meta [im for im in data[images] if im[file_name] img_name][0] h image_meta[height] w image_meta[width] lines [] for a in ann_list: x, y, bw, bh a[bbox] # COCO格式:左上角x,y,宽,高 cx (x bw / 2) / w cy (y bh / 2) / h nw bw / w nh bh / h if nw 0 or nh 0: continue # 过滤崩溃标签 lines.append(f{cat_map[a[category_id]]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}) (out / (img_name.rsplit(., 1)[0] .txt)).write_text(\n.join(lines), encodingutf-8)这段脚本有几个参数需要按项目改一是 image_id 对应的宽高我上面用遍历去查工程里建议先用字典缓存 image_id 到宽高的映射二是类别映射 cat_map如果你的数据集本来就是 YOLO 格式跳过转换直接看下一节。标注里还要注意异物类别不是越多越好。常见数据集里会分硬币、钥匙、发卡、纸屑、其他超过八类之后类间重叠很严重比如纸屑和塑料袋几乎长得一样。我一般建议把长尾类别合并成“其他”给模型少一点负担。另外完整数据集里很可能有一部分图片本身没有异物也就是“负样本”。YOLO允许这些图片的txt文件为空训练时模型会学到正常梳齿板的样子。很多同学为了省事把所有空标签删掉导致模型把正常梳齿板的缝隙阴影误检成异物。正确做法是保留空标签并在划分验证集时确保负样本图片按比例进入验证集。2.3 小目标检测640×640 输入下硬币为什么容易被模型忽略梳齿板异物有个隐蔽难点目标太小。一枚一元硬币在常见的2560×1440摄像头画面里可能占80×80像素缩放到YOLO默认的640×640后只剩下20×20甚至更小经过网络多次降采样到预测层只剩几个像素的响应。YOLOv8默认有三个检测头偏向中大型目标对十几像素的小异物召回率会明显下降。实操里有三条路最常见的是把 imgsz 从640调到960或1280小目标特征保留更多代价是训练和推理变慢第二条是加P2检测头让浅层高分辨率特征参与预测网上大量聊的“yolov8 head改进”其实就是把P2加回来但对毕设来说要改模型源码、重下预训练权重风险偏高第三条是切片推理SAHI把原图切成小块分别检测再合并精度好但部署复杂。我自己的排序是先调大输入尺寸再清点数据集里小目标样本数量都不行才动检测头。如果你接手的数据集没给目标尺寸分布建议先用一个小脚本把所有txt里的宽和高读出来做直方图。经验是如果小于32×32的目标占比超过20%那 imgsz640 基本不能直接满足漏检要求要么放大训练图像要么在部署端对梳齿板区域做ROI裁剪用局部图像做第二路检测。这其实是预警系统里面最容易出效果的一个改动。3. 用完整数据集训练 YOLOv8 梳齿板异物模型环境配置、训练命令与参数调优3.1 环境配置Ultralytics 安装与 CUDA 版本核对先建环境。PyTorch 版本和 CUDA 不匹配是新人最常翻车的地方所以第一步不只是装包而是装完立刻验证。conda create -n escalator python3.9 -y conda activate escalator pip install ultralytics python -c import torch; print(torch.__version__, torch.cuda.is_available())这段命令里 conda 环境名称用 escalator方便识别项目第3行会把 ultralytics、opencv、numpy、matplotlib 等一系列依赖一起装好。最后一行如果输出 True说明 GPU 可用如果输出 False大概率是 torch 装成了 CPU 版需要去 PyTorch 官网按你的 CUDA 版本重新安装。GTX1660Ti 这种 6G 显存的卡跑 yolov8n 很轻松跑 yolov8s 需要把 batch 调小同时打开 AMP 混合精度训练速度能快不少。3.2 数据集目录结构与自动划分脚本YOLO 训练要求数据按 images 和 labels 分开放images 里是原图labels 里是同名 txt。完整数据集解压后一般已经有这个结构也可能需要自己从原始图片加标注重新组织。下面是划分脚本。import random import shutil from pathlib import Path random.seed(42) root Path(raw_data) # 原始图片目录 out Path(datasets/escalator) # 输出目录 for split in (train, val): (out / split / images).mkdir(parentsTrue, exist_okTrue) (out / split / labels).mkdir(parentsTrue, exist_okTrue) images sorted(root.glob(*.jpg)) random.shuffle(images) val_cnt int(len(images) * 0.2) for split, files in [(val, images[:val_cnt]), (train, images[val_cnt:])]: for img_path in files: label_path img_path.with_suffix(.txt) if not label_path.exists(): continue # 负样本可以没有标签这里按需跳过 shutil.copy(img_path, out / split / images / img_path.name) shutil.copy(label_path, out / split / labels / label_path.name)注意两点这里只处理 .jpg如果数据集里还有 .png/.bmp要把 glob 后缀补齐负样本没有 txt 文件是正常现象但上面脚本遇到没有标签的文件会直接跳过导致负样本被排除。更好的做法是把空 txt 文件补上再划入 train/val。建议把划分固定在同一个随机种子这样你自己复现训练时不会因为数据顺序变来变去把结果搞玄学。接着写 dataset.yamlpath: datasets/escalator train: train/images val: val/images names: 0: coin 1: key 2: hairpin 3: paper_cup 4: other这里 names 要和你的标签文件第一列对应。类别名写成英文是避免训练日志在 Windows 控制台乱码界面展示时再映射回中文。数据集里如果标注类别范围不一样一定要改 names否则训练时模型会照着错的索引硬学。3.3 训练命令与关键参数YOLOv8 训练自己的梳齿板数据集时怎么调 imgsz、epochs、batch训练命令不长但参数选择直接影响能不能在一个晚上把模型跑完。yolo detect train dataescalator.yaml modelyolov8n.pt epochs120 imgsz640 batch16 workers4 patience20 device0参数含义按优先级来排imgsz640 是训练输入分辨率小目标多就改成 960但显存和时间会涨batch16 在 6G 显存跑 nano 是安全的显存不够先减到 8不要一次性减太大因为 batch 太小会放大梯度噪声损失曲线抖得厉害workers4 是数据加载线程数Windows 下 workers0 偶尔会报多进程相关错误报错就改成 0但训练会慢一些patience20 表示验证集指标连续20轮不提升就提前停防止模型过拟合到把梳齿当异物。第一次训练建议用官方预训练权重 yolov8n.pt不冷启动。训练过程中不用盯着控制台runs/detect/train 下有一张 results.png里面包含 yolo 的损失函数曲线图和 mAP 曲线能看到训练损失掉头、验证损失是否拐弯。如果 val/box_loss 持续上升但 train_loss 还在降就是过拟合的信号哪怕 mAP 在涨也别选这个权重。3.4 训练产物与验证best.pt 和混淆矩阵怎么看训练结束后runs/detect/train/weights 下面会有 best.pt 和 last.pt。best.pt 是按验证集总指标挑出来的last.pt 是最后一轮。梳齿板异物预警是安全场景漏检比误报严重所以我不直接照搬 best.pt而是先跑一次验证集评估。yolo detect val modelruns/detect/train/weights/best.pt dataescalator.yaml输出里重点看两个指标mAP50 和混淆矩阵里的召回率。mAP50 到 0.85 以上只能说明整体不错但每个类别的召回可能差很多比如“硬币”高、“纸屑”低。Ultralytics 会在 runs/detect/val 下生成 confusion_matrix.png 和 PR 曲线如果发现某个类别召回低于0.6先别急着调模型回去看这个类别的训练样本数是否太少少于200张基本只能靠复制粘贴增强硬凑。最后再拿几个现场真实片段做快速推理确认梳齿板区域出现异物时能连续框出来而不是只在某一帧闪一下。4. 可视化界面与实时预警PyQt5 实时推理、报警联动与置信度阈值设定4.1 界面功能拆解这套系统的“可视化界面”一般长这样左边是实时摄像头画面检测框直接画在梳齿板目标上右边是报警信息列表记录时间、异物类别、置信度和截图下边有启动/停止、置信度滑条、报警消音。对毕设答辩来说界面要做到的不只是好看还要能体现“预警闭环”检测到异物、弹窗提醒、记录留痕。建议保留一张现场截图的功能因为后续验证误报时靠截图复盘比看回放视频高效得多。4.2 用 PyQt5 OpenCV Ultralytics 写一个最小界面线程界面不能把推理放到主线程里。摄像头帧率通常是25fps模型推理即使有GPU也可能只有十几毫秒但窗口拖拽、按钮点击如果阻塞会出现整个界面“假死”的效果。常见做法是开两个QThread一个读视频帧一个跑模型推理中间用队列解耦。下面是一段能跑通的核心结构。import queue from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class PredictThread(QThread): frame_signal pyqtSignal(object) alarm_signal pyqtSignal(list) def __init__(self, model_path, frame_q): super().__init__() self.model YOLO(model_path) self.frame_q frame_q # 另一个线程往这里放帧 self.running True def run(self): while self.running: try: frame self.frame_q.get(timeout0.5) except queue.Empty: continue results self.model.predict(frame, imgsz640, conf0.5, verboseFalse) self.frame_signal.emit(frame) # 主线程显示画面 if results and results[0].boxes is not None: boxes results[0].boxes.data.cpu().numpy() self.alarm_signal.emit(boxes) # 报警判断在界面层做信号 frame_signal 每次把原始帧发回主线程用于显示alarm_signal 把检测框数据发回去。由于 PyQt 信号是跨线程的界面刷新会在主线程完成不会跟推理抢 GIL。这里的 queue 必须用有界队列否则摄像头读取的速度大于模型推理速度时内存里积压的全是老帧报警延迟会越来越大。我一般用 queue.Queue(maxsize2)读帧线程发现队列满了就丢旧帧保证系统始终处理最新画面。4.3 报警抑制连续帧确认、阈值滑动条与报警锁存目标检测单帧超过置信度不代表真的卡滞因为同一枚硬币在连续几帧里会反复出现也可能某一帧反光造成误检。报警逻辑要加三道闸置信度阈值、连续帧确认、报警冷却。ALARM_CONF 0.5 # 现场可调通常 0.3~0.6 REQUIRED_FRAMES 3 # 连续几帧命中才报警 COOLDOWN_SEC 10 # 报警冷却时间 class AlarmJudge: def __init__(self): self.hit_count 0 self.last_alarm_time 0 def update(self, boxes, now): hit any(b[4] ALARM_CONF for b in boxes) self.hit_count self.hit_count 1 if hit else 0 if (self.hit_count REQUIRED_FRAMES and now - self.last_alarm_time COOLDOWN_SEC): self.last_alarm_time now self.hit_count 0 return True return False参数上ALARM_CONF 调低会导致发夹阴影、纸巾折角频繁触发调高会导致小目标漏报。更稳的做法是把 ROI 限定在梳齿板矩形区域扶梯外面的人、行李箱都不参与判断REQUIRED_FRAMES 设成3到5帧可以滤掉单帧闪烁。界面上的置信度滑条可以直接改 ALARM_CONF方便现场不重新训练就适配不同商场的光照。报警锁存的意义是一旦触发界面弹窗直到人工点击“确认”或冷却时间结束避免声光报警反复启停把现场人员搞疲劳。4.4 报警联动串口、蜂鸣器与现场截图报警信号除了弹窗还要能驱动现场设备。常见做法是走串口发一条指令给继电器模块继电器的常开触点点亮声光报警器。这里只需要一个最小串口发送函数毕设演示足够。import serial def trigger_alarm(com/dev/ttyUSB0, port9600): try: with serial.Serial(com, port, timeout1) as ser: ser.write(bALARM_ON\n) except Exception as e: print(串口发送失败, e) # 报警失败也不能让主程序崩掉现场截图在报警触发时保存一张 JPEG路径按时间戳命名。这样报警记录表里可以直接存截图路径答辩时翻起来非常直观。还要注意如果只做了声光报警但没人看就白做了所以界面上最好有一个“消音”按钮和一条“待处理”状态提示值守人员必须手动确认。5. 简单部署的五个坑从 PyTorch 版本到 ONNX 导出的排查记录5.1 现象装完 ultralytics 之后一导入就报 OSError 或 DLL load failed这个报错在 Windows 上最常见原因基本是 torch 和 torchvision 版本不匹配或者 NVIDIA 驱动太老Python 导入 torch 时找不到对应的 CUDA 运行库。解决方法是固定一套版本比如重新安装与 CUDA 对应的 torch。示例pip install torch2.3.1 torchvision0.18 --index-url https://download.pytorch.org/whl/cu121装完再跑一次python -c import torch; print(torch.cuda.is_available())确认 True 再装 ultralytics。不要先装一堆包再回来排查那样会把问题混在一起很难定位。5.2 现象训练时 GPU 占用率只有 20%显存占满但速度很慢这个现象很误导人看起来显卡在跑其实训练卡在数据读取上。原因是 batch 太小、workers0、没有开 cache显卡每处理完一个小批次就要等 CPU 把下一批图读进来。解决方式是 batch 调到 8 或 16workers 给到 4并在显存充足时加cacheTrue。GTX1660Ti 这类卡跑 yolov8n打开 AMP 混合精度也可以明显加速。如果调完占用率还是上不去检查一下是不是训练目录放在机械硬盘换成 SSD 会立竿见影。5.3 现象模型把梳齿板缝隙阴影、金属反光当异物这说明模型没有真正学好“正常梳齿板”长什么样原因是数据集里负样本太少或者负样本没有进入训练。很多同学拿到完整数据集后只关注有异物的图片把空标签图片全删了模型没有见过没有异物的场景推理时自然会把背景纹理当成目标。解决办法是把空 txt 文件保留并保证负样本占训练集的 15%~30%如果已有负样本还误检就专门收集一些反光严重的难样本重新训练。5.4 现象报警画面里硬币明明能看到但检测框就是不出小目标漏检要区分是训练阶段的问题还是推理阶段的问题。先在训练日志里看验证集上“硬币”这一类别的召回率如果本来就低说明训练样本或输入分辨率不够如果训练时召回高但部署到现场视频后漏检多半是摄像头安装距离太远、ROI区域太大。解决方式通常是 imgsz 调大到 960或者在界面代码里对梳齿板区域先裁剪放大再做检测用局部图跑模型。不要直接靠调低置信度去捞小目标那样会把误报也捞进来。5.5 现象pt 模型在电脑上跑得好转成 ONNX 后推理结果全是空或坐标乱了ONNX 导出失败的原因集中在 opset 版本和动态轴设置。opset 太高会导致某些算子不兼容动态轴设置错误会让输出 shape 对不上。我常用的导出命令是yolo export modelbest.pt formatonnx opset12 imgsz640导出后先用 onnxruntime 跑一张测试图对比 PyTorch 的输出框。如果 onnx 输出全空先检查预处理是否一致ultralytics 在导出时会内置归一化你自己写预处理时不要重复做 resized 和归一化。还有一点ONNX 里默认不包含 NMS如果你要部署到 C 或 RKNN得自己在后处理里写非极大值抑制或者用带 NMS 的集成代码。这一条在“简单部署”里最容易卡一整晚。6. 把模型部署到 RK3588 边缘盒子NPU 量化与实时性验证6.1 从 PyTorch 到 RKNN转换脚本与量化校准如果现场不想在 PC 上跑常见做法是部署到 RK3588 这类带 NPU 的边缘盒子上。RK3588 不支持直接跑 PyTorch需要先导出 ONNX再用 rknn-toolkit2 转成 RKNN 模型。yolo export modelbest.pt formatonnx opset12 imgsz640然后用 rknn-toolkit2 的 convert 脚本关键是量化校准数据集要选现场光照下的图片不要拿训练集随便抽几十张。校准图片里要有正常梳齿板也要有少量异物否则 RKNN 的 int8 量化会把小目标的特征直接压掉。转换完成后建议在板子上用 Python 版 RKNN API 做一次逐帧对比把 RKNN 输出和 ONNX 输出的置信度打印出来能发现明显的量化掉点。6.2 实测标准帧率、报警延迟、误报率怎么算部署后别只看检测框要按预警系统的指标验收。指标测试方法合格线推理帧率RK3588 上连续跑10分钟25 FPS 以上640输入报警延迟从异物进入 ROI 到界面弹窗小于1秒误报率录2小时正常客流回放不超过3次漏检率人为放置硬币/钥匙各50次尽量为0我自己第一次把 YOLOv8 转到 RK3588 时偷懒没用校准集直接开了 int8 量化结果硬币漏检了一半。后来把早晚高峰各录了30分钟视频挑出包含不同光照的200帧做校准精度才回到可接受范围。现在我的习惯是改完任何一个阈值或重新量化都用两段回放视频过一遍一段正常客流一段刻意放异物两边结果都能过了再交付。这个步骤才是“简单部署即可运行”背后真正要学的功课。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。