
简介面向计算机视觉与深度学习方向毕设、课设场景的完整项目资源内容聚焦电梯内电瓶车闯入识别这一具体安防需求基于YOLOv8目标检测框架实现。项目代码经过完整测试可直接运行适合需要快速落地项目的在校学生或初学者使用。包内共8个文件包括3个Python脚本可视化界面、视频检测、模型训练、3个模型权重文件及2个txt说明文档整体压缩包约15.91MB结构紧凑下载后按README指引即可完成部署。当前已有42人学习该资源。除基础检测功能外项目可自动产出核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图覆盖模型训练评估全流程。源码、数据集、可视化界面与部署说明配套完整既能支撑毕设答辩的指标展示也便于在此基础上二次修改延伸功能可直接作为毕设、课设或大作业的完整方案。1. 电梯内电瓶车闯入报警为什么我劝你直接把YOLOv8当首选而不是自己造轮子小区物业最头疼的事里电瓶车进电梯绝对排前三。轿厢空间小、行人进出频繁、摄像头视角还带广角畸变想靠传统图像处理做“车形识别”根本不现实。而YOLOv8这种单阶段目标检测器用一份标注好的电瓶车数据集跑几百个epoch就能在电梯这种固定场景里把精度做到能用级别而且推理速度在CPU上都能到30ms级更别提接显卡或NPU了。所以我接到这类毕设或课设需求时第一反应永远是把YOLOv8作为检测核心外面套一个报警联动逻辑和可视化界面数据、训练、界面、部署一条龙学生拿到手里简单部署就能跑这才叫“靠谱”。这套方案适合谁想做毕设的本科生、要交课程设计的高年级学生以及真正需要在小区或写字楼试点“电瓶车禁入电梯”的硬件集成商。它不要求你会写神经网络但要求你能照着文档把环境配通、把命令跑顺。接下来我按“数据→训练→界面→部署”的顺序把这套东西拆给你看。2. 从数据集到标注文件让YOLOv8认得出“电瓶车”而不是“电动车”或“自行车”2.1 数据集的常见组织方式与类别约定不管标题里附带的数据集长什么样你要先搞明白Ultralytics YOLOv8期望的数据集结构。它不像早期YOLO那样需要你手动在txt里填归一化坐标现代ultralytics框架会读取一个data.yaml里面写清楚train/val路径和类别名。常见做法是文件系统这么摆dataset/ ├── images/ │ ├── train/ # 训练图像约80% │ └── val/ # 验证图像约20% ├── labels/ │ ├── train/ # 与图像同名的txt标注 │ └── val/ └── data.yamldata.yaml的内容很简单但类别顺序千万别乱训练时你写几推理时就是几path: ./dataset # 数据集根目录用相对路径更省心 train: images/train val: images/val names: 0: e_bike # 只管电瓶车这一类避免和自行车混淆我一般会把类别名写成e_bike而不是electric_bicycle因为标注工具里短名字更好写而且模型学的是视觉特征不是语义标签名字不影响精度。2.2 把VOC格式转成YOLO格式转换脚本与四个边界坑很多公开数据集下载下来是VOC格式——JPEGImages里放图Annotations里放XML。YOLOv8不认XML你得先转。转换核心就是读XML里的bndbox坐标换算成归一化的x_center、y_center、width、height。下面这个脚本我调过很多次直接可用import os import xml.etree.ElementTree as ET import cv2 # 只需要改这三个路径和类别映射 xml_dir Annotations img_dir JPEGImages out_label_dir labels CLASS_MAP {bicycle: 0, e_bike: 0} # 如果你想把自行车也算进来映射到同一类 def convert_voc_to_yolo(xml_file, img_file, out_txt): tree ET.parse(xml_file) root tree.getroot() img cv2.imread(img_file) h_img, w_img img.shape[:2] # 关键坑1必须用真实图像尺寸不能信XML里的size节点 lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASS_MAP: continue # 跳过无关类别 cat_id CLASS_MAP[name] bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 关键坑2坐标要夹紧到图像范围内有些标注会出边 x1 max(0, min(x1, w_img - 1)) x2 max(0, min(x2, w_img - 1)) y1 max(0, min(y1, h_img - 1)) y2 max(0, min(y2, h_img - 1)) if x2 - x1 2 or y2 - y1 2: continue # 过滤掉过小框 x_center (x1 x2) / 2 / w_img y_center (y1 y2) / 2 / h_img w_norm (x2 - x1) / w_img h_norm (y2 - y1) / h_img lines.append(f{cat_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines)) if __name__ __main__: os.makedirs(out_label_dir, exist_okTrue) for filename in os.listdir(xml_dir): if not filename.endswith(.xml): continue stem os.path.splitext(filename)[0] xml_path os.path.join(xml_dir, filename) img_path os.path.join(img_dir, stem .jpg) out_path os.path.join(out_label_dir, stem .txt) if not os.path.exists(img_path): print(f警告: 找不到图像 {img_path}跳过) continue convert_voc_to_yolo(xml_path, img_path, out_path) print(转换完成)这段代码的逻辑不复杂但你最容易翻车的地方是XML里标的坐标是基于原始图像还是在原图上做过缩放。我踩过的坑是某些数据集标注时图被resize过但XML没同步所以转换后最好随机抽三张图用下面这段代码把标注画回图上目检import cv2 label_path labels/0003.txt img_path JPEGImages/0003.jpg img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f.readlines(): cat, xc, yc, bw, bh line.split() xc, yc, bw, bh float(xc) * w, float(yc) * h, float(bw) * w, float(bh) * h x1, y1 int(xc - bw/2), int(yc - bh/2) x2, y2 int(xc bw/2), int(yc bh/2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_vis.jpg, img)如果框的位置明显偏了那问题多半出在图像读取的通道顺序或坐标类型上如果只是个别框偏多半是XML标注本身有问题这种脏数据我一般直接删不要硬留着拖累训练。2.3 数据增强电梯场景的针对性配置电梯场景的特殊性在于视角几乎是固定的、光照变化有规律开门亮、关门暗、反光强烈。所以增强策略要克制不能像通用检测那样乱加旋转。我在ultralytics配置文件里常用的是轻微HSV扰动和随机平移而关闭上下翻转因为电瓶车在电梯里不会倒立。# augment参数写在训练命令中即可这里列出关键项 hsv_h: 0.015 # 色相轻微扰动电梯里白炽灯和日光灯有色差 hsv_s: 0.5 # 饱和度增强让车身颜色区分度更高 hsv_v: 0.4 # 明度扰动模拟电梯内灯光变化 translate: 0.1 # 平移10%适应电瓶车在轿厢不同位置 flipud: 0.0 # 关键禁止上下翻转否则模型会混乱 fliplr: 0.5 # 左右翻转可以开电瓶车侧面对称性较好fliplr开0.5是安全的因为电瓶车本身左右对称特征明显但flipud一旦开启模型就会把“轮胎在下方”这个强先验学反了推理时可能出现误检。另外我建议你别用mosaic1.0默认值电梯里的小目标多而密集马赛克拼接会产生很多截断样本设成0.5就够了。3. 用YOLOv8训练自己的数据集从环境到损失曲线判读3.1 环境搭建与训练启动训练前的环境配置是新手最容易“原地爆炸”的阶段。我的建议是用conda建一个干净的Python 3.10环境然后装ultralytics和torch。不要图省事用pip全局装因为毕设机器上通常还跑着TensorFlow或PaddleOCR版本冲突会让你浪费一整天。命令如下conda create -n yolo python3.10 -y conda activate yolo # 先装CPU版torch再换GPU版避免CUDA版本不匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics装完之后验证一张图能推理用官方预训练权重跑一次yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg这一步走通说明环境没毛病。然后启动训练的命令很简洁核心参数就几个yolo train datadataset/data.yaml modelyolov8s.pt epochs100 imgsz640 batch8 device0这里选yolov8s而不是yolov8n是因为电梯里电瓶车的特征是“小目标遮挡近距离大目标”混合体n模型参数量太少对车身局部的纹理特征学习不充分v8s在精度和速度之间最平衡GTX 1660 Ti这种显卡跑起来batch8也不吃力。如果你显存只有6Gbatch降到4但学习率也要相应调低否则loss震荡这个后面讲。3.2 训练参数逐项拆解哪些动了会影响结果表格估一下关键参数数值来自我多次调参的血泪经验不是权威标准但能帮你少折腾参数我的建议值作用与翻车点modelyolov8s.pt从预训练权重开始迁移学习收敛极快epochs100~150不要过早停看验证集mAP是否还在上升imgsz640电梯原始视频往往是1080p但训练用640可加速batch81660Ti/ 163080显存不够时降batch不要硬撑optimizerAdamWSGD配自动学习率也可以但AdamW对新手更稳lr00.01batch8batch减半时lr0也减半否则loss震荡patience30连续30个epoch验证损失不降就早停cos_lrTrue余弦退火在epochs100时收益明显这表里最容易坑人的是lr0和batch的联动关系。我见过不少人拿默认lr00.01配batch4结果前20个epoch的box_loss完全不降还以为数据出了问题。实际是batch太小导致梯度估计噪声大而学习率没有对应缩小。比如batch4时我会直接改成lr00.005并在命令行加weight_decay0.0005。3.3 损失函数曲线图怎么判读别等训练完才发现问题YOLOv8训练时终端会打印box_loss、cls_loss、dfl_loss三个指标但更直观的是画出曲线图。用ultralytics的回调或直接读runs/detect/train/results.csv我习惯训练后手动画一次import pandas as pd import matplotlib.pyplot as plt # 读训练日志 df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(10, 5)) 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.title(box_loss curve) plt.savefig(loss_curve.png, dpi150)判读曲线是有套路的。前面30个epochtrain loss下降、val loss也下降这是健康状态如果train loss下降但val loss在第40个epoch之后开始回头向上说明过拟合了解决办法是加数据增强强度或提前早停。如果train loss和val loss从第10个epoch开始一起“横盘”那不是模型不行是学习率太低把lr0翻五倍再来一轮。还有一类常见情况是val loss掉到某个值后不再动但val的mAP50却在涨。这不是bugYOLOv8的box_loss用的是CIoU变体和最终评测的IoU阈值不完全同频只要mAP在涨就让它继续跑。我一般让训练跑到150个epoch再用早停机制截断这样既不会浪费算力也能保证模型看到足够的样本变化。4. 可视化界面与报警联动从检测框到“有人管”的最后一公里4.1 用PySide6做一个电梯监控操作台训练好的模型只是个引擎毕设需要让人看得见检测效果所以一个带实时视频预览和报警记录的界面必不可少。我用PySide6做桌面端用OpenCV读电梯监控流的RTSP地址推理用YOLOv8的predict接口报警逻辑单独写一个线程。界面布局简化思路是左右两栏左边是实时检测画面右边是报警日志表和电梯状态指示。核心代码就这一段其他布局代码不展开import sys import cv2 import threading from PySide6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout from ultralytics import YOLOv8 class ElevatorGUI(QWidget): def __init__(self, weight_path, rtsp_url): super().__init__() self.model YOLOv8(weight_path) self.cap cv2.VideoCapture(rtsp_url) self.label QLabel() layout QVBoxLayout() layout.addWidget(self.label) self.setLayout(layout) self.running True self.alert_count 0 threading.Thread(targetself.video_loop, daemonTrue).start() def video_loop(self): while self.running and self.cap.isOpened(): ret, frame self.cap.read() if not ret: continue results self.model(frame, imgsz640, conf0.45, verboseFalse) boxes results[0].boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) cv2.putText(frame, fe_bike {conf:.2f}, (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) self.alert_count 1 # 报警联动这里可以接蜂鸣器、继电器或推送给物业 self.label.setPixmap(self.cvmat_to_pixmap(frame))注意self.alert_count没有做去重电梯里电瓶车停留时间较长每一帧都会触发一次计数这在真实部署时会刷爆日志。常见做法是加一个冷却时间例如同一个检测框在5秒内只报警一次用一个简单的IOU记忆列表或者时间戳字典就能实现。4.2 报警联动逻辑不只是画框要真的“喊人”检测框画出来只是第一步项目标题里强调了“报警”两个字所以联动逻辑必须完整。我给这个系统定的联动顺序是检测到电瓶车 → 语音播报“电瓶车禁止入内” → 电梯门保持开启 → 抓拍当前帧保存到本地 → 通过HTTP请求推送给物业值班室。联动实现中比较坑的是语音播报不能卡住视频线程所以要走异步队列import queue import time import requests alert_queue queue.Queue() def alert_worker(): while True: item alert_queue.get() img_path, timestamp item # 推送抓拍图到物业后台这里用你的后端API地址 with open(img_path, rb) as f: try: requests.post(http://your-server/api/alert, files{image: f}, data{timestamp: timestamp}, timeout2) except Exception as e: print(f推送失败: {e}) # 语音播报用系统自带say/espeak都可以Linux下可以用pyttsx3 alert_queue.task_done() # 在检测线程里满足冷却条件时 queue_img falerts/{int(time.time())}.jpg cv2.imwrite(queue_img, frame) alert_queue.put((queue_img, time.strftime(%Y-%m-%d %H:%M:%S)))这里把网络请求放在独立worker线程里避免RTSP解码帧被网络阻塞拖到掉帧。实测RTSP流如果每帧都要等HTTP响应画面会从25fps掉到10fps以下。用队列就是黑匣子两侧解耦检测只管写推送只管发。5. 实战避坑指南训练到部署最常见的五个翻车点5.1 推理置信度阈值设太高导致漏报现象 部署后电梯里明明有电瓶车系统就是不报警只在人推着车经过时才偶尔闪一下框。原因 训练时用默认conf0.25测试觉得准但真实电梯场景里YOLOv8推理结果的置信度普遍比验证集低10~15个百分点。因为验证集里是正对画面的标准角度实际摄像头是俯视广角车身占比小。解决 把推理置信度从0.25降到0.15~0.18同时配合类别判断。比如先模糊判断置信度在0.2以下的检测框如果连续10帧持续出现同一个区域也触发预警或者干脆把conf设0.15宁可误报也不漏报误报用联动冷却时间消化。5.2 dataset.yaml路径写绝对路径换机器就废现象 在自己电脑上训练好好的把整个项目拷到别的电脑测试时一运行就报Dataset not found。原因 data.yaml里path字段写了绝对路径如/home/xxx/dataset换机器路径变了。解决 用相对路径。path字段写.然后保证你在项目根目录下运行训练命令。这也是我习惯把所有数据和代码放在同一个工程目录里的原因移动时整体拷贝不破路径。5.3 电梯关门瞬间误检为电瓶车现象 系统在电梯门关闭过程中频繁报警但轿厢里只有人没有车。原因 电梯门关闭时门的边缘轮廓和反光条纹在特定角度下很像电瓶车的车把或车身条纹。我在验证时用标准测试图都会过滤这种情况但现场测试就露馅了。解决 除了降低误检更可靠的做法是加一个“时间维度的确认机制”。YOLOv8单帧检测容易受噪声影响所以我会在报警逻辑里要求连续3~5帧都检测到同一位置的框才触发报警。这个用简单的前后帧框重叠度计算就能实现不依赖跟踪器。牺牲的是报警延迟1秒左右但对电梯场景完全可接受。5.4 CPU推理速度不够掉帧严重现象 用yolov8s.pt在CPU推理一帧要120~150ms画面卡顿严重无法实时报警。原因 目标检测的实时性需要速度与精度折中。很多毕设想在没独显的笔记本上跑直接拿s模型硬扛。解决 换yolov8n.pt权重并导出成ONNX用OpenVINO或ONNXRuntime推理CPU上能跑到30~40ms。或者用imgsz480推理虽然小幅降低精度但画面流畅度明显改善。更激进的做法是每3帧取1帧做检测中间帧用上一帧的结果框叠加。5.5 训练和推理的输入尺寸不一致导致框漂移现象 训练时用640推理时界面里OpenCV的窗口显示正常但保存的报警截图里框偏移明显。原因 YOLOv8推理时会将图像letterbox到输入尺寸推理后坐标会映射回原图。如果你在可视化时把result.plot()的结果与原始帧混用而OpenCV显示时做了缩放就会出现框和物体对不上的错觉。这类问题多半出在代码里混用了两种坐标系的图像。解决 统一用YOLOv8返回的boxes.xyxy在原始帧上自己画框不要用result.plot()再叠加到另一个画布上去。后者的封装把框画在letterbox图上你转回原始尺寸时容易少算padding偏移。6. 进阶部署要点把模型搬上边缘设备和嵌入式摄像头如果你只想交毕设到第4步为止已经完整了。但如果你在思考“这个方案能不能真落地到小区”那最终会遇到一个问题——物业的电梯监控主机往往是一台海思芯片的NVR或者RK3588开发板跑不了PySide6那套界面。我自己的经验是做一个HTTP推流Web端预览的轻量方案更实用。具体做法是把检测逻辑写成一个后台服务RTSP拉流、推理、报警推送全部是Python脚本视频预览画面用WebRTC推流到物业的浏览器。但这一步对算法改动不小。如果你只是单人开发推荐先验证一个更朴素的做法把模型导出成ONNX计算量和推理速度都直观可见。导出命令就一句话yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640 dynamicTrue导出后可以用onnxruntime做CPU推理实测速度比原生PyTorch快一倍以上而且能直接跑在带NPU的RK3588开发板上。RK3588的推理部署有专门工具链你需要把ONNX转成RKNN格式转换时注意算子的兼容性YOLOv8的DFL头在RKNN旧版本里会报不支持的算子通常要升级到rknn-toolkit2的最新版才顺畅。我的教训是别在算子报错时试图改模型结构优先升级工具链版本。验证部署成功有个土办法拿手机上拍的一张电梯内电瓶车照片通过接口上传到推理服务返回的JSON里带上检测框的坐标和置信度再用你写的小网页把框画出来。如果这个闭环走通那说明模型文件、推理框架、网络协议都通了剩下的只是把摄像头接到RTSP地址上而已。现场调试时我还有个习惯让报警事件带一张抓拍图写进本地SQLite每周抽看一次确认误报率和漏报率的变化趋势。真出现问题回溯抓拍图能省掉大半排查时间这比对着终端日志猜黑匣子靠谱得多。希望这些思路能帮你的毕设少走几步弯路也祝你调试顺利。本文还有配套的精品资源点击获取