简介:一套基于YOLOv8的智慧社区电梯电动车禁入识别系统源码包,面向计算机视觉方向的学生和开发者,尤其适合毕业设计、课程设计等场景。系统围绕电梯场景下的电动车目标检测展开,采用YOLOv8模型训练与推理,搭配可视化界面与视频检测模块,可帮助使用者从数据准备、模型训练到结果评估完整走通一条检测项目链路。压缩包共8个文件,包含3个Python脚本(分别对应可视化界面、视频检测、模型训练)、3个模型权重文件(含预训练与训练所得)及2个txt说明文档,整体仅15.91MB,结构简洁、部署门槛低。项目代码已经过运行验证,训练后可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图,为答辩汇报提供直观数据支撑。当前已有42人学习浏览,适合初学者上手复现,也可在现有代码基础上修改扩展,用于其他目标检测任务。
1. 电梯口那台旧电脑,也能跑起的电动车禁入识别
小区电梯口贴着「禁止电动车入内」,保安盯监控根本盯不过来;真正把车堵在楼道里充电的,往往是推车进电梯的那一批人。《基于YOLOv8的智慧社区电梯电动车禁入识别系统》要做的,就是让摄像头自己认出一辆电动车,联动语音告警甚至阻止关门。这个方案不是多高深的算法研究,而是一条完整链路:YOLOv8负责检测,数据集负责让模型认识「电梯里的两轮车」,可视化界面负责让人看得见结果,部署教程负责把模型放到真实环境跑起来。它最打动人的是上手门槛低——一台消费级显卡就能训练,CPU 也能推理,特别适合要交毕设的本科生、想练目标检测全流程的初学者,以及物业智能化改造前的技术验证。
2. 电梯场景的电动车识别:难点不在模型,在数据
2.1 从「能检测」到「认得准」:电梯监控的三种真实干扰
马路场景的车辆检测,模型看到的基本是完整车身。电梯场景完全不是一回事。人推车进电梯时,车身被人体挡住一半是常态,后面再跟进来一个人,车尾也被挡住,检测器面对的经常是「半个电动车」。这是第一种干扰,也是最常见的误检来源——车身中段被完全遮挡时,模型会把人的腿部特征和车轮特征混在一起。
第二种干扰来自安装角度。电梯摄像头通常装在轿厢顶部,广角俯视,车头朝里、朝外、侧放三种姿态下,电动车在画面里的形状差异非常大。广角边缘的畸变还会让车把手和车筐变形,车轮可能被拉成椭圆。很多公开数据集里的电动车图片是平视角度拍的,模型在俯视角度下直接掉点,这就是「训练集看着挺好,一上真实监控就翻车」的主要原因。
第三种干扰是光照。电梯内顶灯加不锈钢门反光,车身高光区域会直接吃掉车轮和车架轮廓,镜面反射还会把周围环境映进车身区域。夜间低照度下,监控画面噪点变大,小目标更难识别。这些干扰叠加在一起,决定了「完整数据集」不应该只是网上拼凑的图片包,而必须包含与电梯摄像头视角、遮挡、光照分布一致的样本。
动手前的数据体检:从目标小区实际监控导出 10 段视频,每段抽 20 帧,按下面三个维度过一遍:
| 观察项 | 判断标准 | 不达标时的动作 |
|---|---|---|
| 目标完整度 | 车身可见比例是否超过 50% | 补充遮挡样本,而不是盲目加数据量 |
| 最小目标宽度 | 画面中车身最窄处是否大于 40 像素 | 提高采集分辨率或降低检测距离 |
| 光照分布 | 高光、暗光、夜间样本是否都有 | 分时段采集,夜间单独补充 |
这个体检做 15 分钟,比先标注 1000 张图再发现方向错了要划算得多。
2.2 YOLOv8 的选型理由:精度、速度与复现成本的平衡点
目标检测模型可选范围很广,但毕设和课设项目有一个共性约束:时间有限,显卡一般,还要能在答辩现场讲清楚。YOLOv8 是当前这个约束下的最佳平衡点。
相比 YOLOv5,YOLOv8 把 C3 结构换成了 C2f,梯度流更丰富,对小目标和遮挡目标的特征提取更充分;检测头从 anchor-based 换成 anchor-free,省去了调 anchor 参数的环节,默认参数就能跑出不错的结果;损失函数采用 DFL 加 CIoU,框回归更精确,特别适合电动车这种长宽比不规则的物体。很多同学纠结 YOLOv8 和 YOLOv7 怎么选,实际落地中 v8 的部署生态更活跃,RK3588、Jetson 这类边缘设备上都有现成案例,遇到问题能搜到解决方案,这对单兵作战的学生项目来说比那一点精度差异更重要。
| 对比维度 | YOLOv5 | YOLOv7 | YOLOv8 |
|---|---|---|---|
| 网络结构 | C3 | E-ELAN | C2f |
| 检测头 | anchor-based | anchor-based | anchor-free |
| 默认参数稳定性 | 需要调 anchor | 需要调 anchor | 开箱即用 |
| 部署教程丰富度 | 丰富 | 一般 | 最活跃 |
| 对遮挡/小目标 | 一般 | 较好 | 较好 |
具体选哪个尺寸,看训练和推理的硬件。YOLOv8n 体积最小,适合边缘设备实时推理;YOLOv8s 精度更高,显存占用和速度都在消费级显卡可接受范围。我的建议是:先用 n 跑通全流程,再用 s 做最终版本,两个模型的训练代码完全相同,只是换一个权重文件的事。YOLOv8 的网络结构图和训练自己的数据集这两个话题,网上教程非常多,这也从侧面说明选 v8 能把踩坑成本降到最低。
3. 把环境先跑通:YOLOv8 推理链路的最小闭环
3.1 环境配置:显卡不是必需品,但显存决定训练深度
不少同学一开始就卡在环境上。YOLOv8 的环境配置其实很简单,核心就一个ultralytics包。我自己习惯先用 conda 建独立环境,避免和系统 Python 相互污染:
conda create -n ebike python=3.10 -y conda activate ebike pip install ultralytics python -c "from ultralytics import YOLO; print(YOLO('yolov8n.pt'))"最后一行会首次加载预训练权重,如果输出一段模型结构信息而不是报错,说明安装成功。ultralytics会自动装好对应版本的 PyTorch,不需要手动安装 CUDA 工具链。这里有个常被忽略的点:默认装的是 CPU 版 PyTorch,训练会很慢。确认 GPU 是否可用,用一行命令:
python -c "import torch; print(torch.cuda.is_available())"输出True说明 GPU 可用,输出False说明装成了 CPU 版。解决办法是到 PyTorch 官网按自己的 CUDA 版本重新安装 torch,然后再装ultralytics。环境配置这一步,卡住的人多半是没确认 PyTorch 和 CUDA 的对应关系,而不是ultralytics本身的问题。如果你用的是 GTX 1660 Ti 这类 6GB 显存的显卡,把训练参数里的 batch 设为 8 到 16 之间完全跑得动,不要被网上「大显存才配玩 YOLO」的说法吓退。
3.2 用预训练权重做第一次推理:跑通的最小验证
环境装好后,先用 COCO 预训练权重跑一张图,验证整个推理链路。这一步很重要,它能提前暴露路径、依赖、中文乱码等一堆后续会反复出现的问题:
from ultralytics import YOLO model = YOLO("yolov8n.pt") results = model.predict( source="elevator_frame.jpg", # 电梯实拍图或任意图片 conf=0.25, # 置信度阈值,低于该值不显示 iou=0.45, # NMS 的 IoU 阈值,控制重叠框合并 imgsz=640, # 推理分辨率,越大越慢但小目标越准 save=True, # 保存带标注的结果图 project="runs/detect", # 输出根目录 name="first_try", # 本次输出子目录 ) for r in results: for box in r.boxes: cls_id = int(box.cls[0]) conf = float(box.conf[0]) print(f"class={model.names[cls_id]}, conf={conf:.2f}")跑通的标志是runs/detect/first_try下生成一张带框的图片。conf和iou这两个参数在后续调试中会频繁用到:conf调低能减少漏报但增加误报,iou调高会让密集重叠的框被合并。预训练模型只认识 COCO 的 80 类物体,其中bicycle和motorbike和电动车有一定相似性,所以第一次推理可能会把电动车识别成自行车——这是正常的,恰恰说明模型迁移学习有基础。source参数不止支持图片,直接传入视频文件路径或摄像头 ID 也能跑,这就是后面可视化界面的原型。
4. 凑一套能训的电动车数据集:公开盘点、自采与 VOC 转 YOLO
4.1 公开数据集盘点:哪些能用,哪些是坑
标题里写了「完整数据集」,但说实话,网上能直接下载的公开数据集几乎没有专门针对「电梯内电动车」的。很多同学第一反应是找 CCPD 数据集,但 CCPD 是车牌检测数据集,里面的「车」是车牌而不是电动车;HRSC2016 是遥感船只数据集,CrowdHuman 是密集行人数据集,和电梯场景差得很远。我见过有人拿这些数据集硬训,结果模型学会了检测车牌和船,完全不认识电动车。
| 数据集 | 实际内容 | 对电梯电动车项目的价值 |
|---|---|---|
| COCO | 80 类日常物体,含 bicycle | 做预训练权重,迁移学习起点 |
| CCPD | 中国车牌 | 不适用,类别完全不同 |
| CrowdHuman | 密集行人,大量遮挡 | 遮挡样本的标注思路可参考 |
| HRSC2016 | 遥感船只 | 不适用 |
| 自采数据 | 模拟电梯监控角度 | 核心,决定检测效果上限 |
结论很直接:这个项目的数据集主体应该是自采的。COCO 的 bicycle 和 motorbike 图片可以作为辅助迁移数据,但最终训练集必须包含大量俯视、遮挡、电梯光照环境下的电动车图片。数据量不用贪多,质量合格的 500 张图配合数据增强,效果比网上下载 5000 张角度不符的图好得多。数据集的「完整」不是说数量大,而是覆盖了电梯场景的主要干扰形态。
4.2 自采数据:手机就够了,但角度要模拟电梯摄像头
没有真实电梯监控权限时,用手机就能完成采集。关键是把手机摆在正确的位置——电梯轿厢顶部角落,模拟 2.2 米左右高度的俯视视角,打开广角模式。拍摄时覆盖这些形态:人推车进电梯、车头朝里、车头朝外、车身侧放、前后遮挡、夜间和白天各一半。拍摄原则是「模拟真实安装位置」,而不是像拍商品图那样把车完整摆正。
采集到的图片不需要全部进训练集。把模糊的、目标占比过小的、和现有样本高度重复的图删掉,剩下 300 到 500 张做初始训练集就够了。考虑到隐私,尽量避开可清晰识别的人脸区域,这也是工程落地的常识。原始图片统一重命名为纯数字文件名,避免后面脚本处理时遇到中文路径问题。
4.3 标注与格式转换:LabelImg 手把手,以及边界框的四个边界坑
标注工具用 LabelImg,导出格式选 PascalVOC。标注原则有一条必须执行:框住电动车车身,不要把人框进去。人推车进电梯时,人和车是粘连的,框住车身意味着主动舍弃被遮挡的部分,让模型学习「车身特征」,而不是「人体特征」。
标注完成后拿到的是 XML 文件,YOLOv8 训练需要的是 TXT 格式。转换脚本不长,但边界情况很值得注意:
import xml.etree.ElementTree as ET from pathlib import Path CLASSES = ["e-bike"] # 类别名必须和标注时完全一致 def convert_voc_to_yolo(xml_path, out_dir, classes): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") width = int(size.find("width").text) height = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in classes: continue cls_id = classes.index(name) box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) x_center = (x1 + x2) / 2 / width y_center = (y1 + y2) / 2 / height box_w = (x2 - x1) / width box_h = (y2 - y1) / height lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") out_txt = Path(out_dir) / (Path(xml_path).stem + ".txt") out_txt.write_text("\n".join(lines)) # 用法:遍历 annotations 目录,把结果写到 labels/train代码逻辑是按 XML 里的size节点拿到图片宽高,把绝对像素坐标归一化到 0 到 1 之间。这里最容易踩的坑有四个:
第一,类别 ID 从 0 开始,CLASSES.index(name)得到的就是训练时用的类别编号,如果 yaml 里的类别顺序和这里不一致,模型会学到错误的对应关系。第二,归一化必须用 XML 里的图片原始宽高,不能用缩放后的显示尺寸,否则框全部偏移。第三,某些标注工具允许框超出图片边界,导致归一化后坐标大于 1 或小于 0,训练时会出现 loss 为 NaN,转换时要做一步clip把坐标限制在 [0, 1] 内。第四,一张图如果没有可转换的目标,不要生成空 TXT 文件,YOLOv8 读到空标签会报警告,虽然不致命但会干扰判断。
转换完成后整理目录结构,YOLOv8 的 data yaml 直接引用即可:
dataset/ images/ train/ val/ labels/ train/ val/ e_bike.yaml5. 训练调参与避坑:让 mAP 从生涩到能看
5.1 训练脚本参数拆解:epochs、imgsz、batch 与预训练权重
数据集准备好之后,训练本身不复杂,但参数设置会影响最终效果和训练时长。先写数据集配置文件e_bike.yaml:
path: ./dataset train: images/train val: images/val nc: 1 names: ["e-bike"]然后用预训练权重做迁移学习:
from ultralytics import YOLO model = YOLO("yolov8n.pt") # 加载 COCO 预训练权重 model.train( data="e_bike.yaml", epochs=80, imgsz=640, batch=16, # GTX 1660 Ti 6G 建议 8~16,显存溢出就减半 device=0, # CPU 训练改 device="cpu",但会慢很多 patience=15, # 验证集损失 15 轮不下降就早停 lr0=0.01, # 初始学习率 lrf=0.01, # 最终学习率 = lr0 * lrf amp=True, # 混合精度训练,省显存 workers=4, # 数据加载线程数 )几个参数按实际硬件调整:
| 参数 | 作用 | 调参建议 |
|---|---|---|
epochs | 训练轮数 | 数据量小于 500 张时,80 轮足够 |
imgsz | 训练分辨率 | 640 是速度和精度平衡点,小目标多可试 960 |
batch | 每批样本数 | 显存溢出就减半,不要硬撑 |
patience | 早停耐心值 | 15 到 20 比较稳,防止过拟合 |
amp | 混合精度 | 6GB 显存建议开启,几乎不损失精度 |
batch是显存不够时第一个要动的参数。6GB 显存跑imgsz=640, batch=16是安全的,如果报 CUDA OOM,先降 batch,再考虑关amp。千万不要为了凑 batch 把imgsz降到 320,那样模型学到的是模糊特征,部署到真实监控上会漏检。训练日志会实时打印 box_loss、cls_loss、mAP50 等指标,即使不看曲线图,也能从训练过程中初步判断是否收敛。
5.2 从损失曲线到验证集:判断训练有没有跑偏
训练结束后,runs/detect/train目录下会自动生成results.png和results.csv。results.png已经画好了损失曲线和 mAP 曲线,但有些人觉得图太小看不清趋势,用脚本自己画更直观:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") df = df.rename(columns=lambda x: x.strip()) plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(df["epoch"], df["train/box_loss"], label="train_box_loss") plt.plot(df["epoch"], df["val/box_loss"], label="val_box_loss") plt.xlabel("epoch") plt.ylabel("loss") plt.legend() plt.subplot(1, 2, 2) plt.plot(df["epoch"], df["metrics/mAP50(B)"], label="mAP50") plt.xlabel("epoch") plt.ylabel("mAP50") plt.legend() plt.savefig("train_curve.png")看图时抓住三个判断点:第一,训练集和验证集的 box_loss 是否都在下降,如果训练集下降但验证集开始上升,说明过拟合了,早停参数会在 15 轮后自动停止,不用手动干预。第二,mAP50 的趋势是否还在上升,如果在最后几轮还在明显上升,可以增加 epochs 继续训练。第三,训练过程中如果 loss 出现大幅抖动,通常是学习率过高或标注数据有误,需要停下来检查标签文件。
搞清楚这些细节之后,你的项目验收时就能准确回答「为什么不在 50 轮就停止」这类问题,这比拿着模型傻跑 300 轮要有说服力得多。
5.3 避坑记录:五条血泪经验与排查步骤
训练阶段是整个项目最玄学的部分,问题往往不是模型结构,而是数据和环境。整理几条我实际踩过的坑:
现象 1:CUDA OOM,训练到一半直接中断。原因:batch 太大或 amp 被关闭,6GB 显存撑不住 640 分辨率。解决:batch 减半,开启amp=True;如果还不行,把workers降为 2,减少数据加载时的显存峰值。
现象 2:训练 loss 为 NaN。原因:标签文件里坐标越界,或类别 ID 大于nc。解决:写脚本扫描所有 TXT,检查每个cls_id < nc、坐标值在 [0, 1] 内,越界的数据重新标注或删掉。
现象 3:train loss 正常下降,但 val loss 越来越高。原因:训练集和验证集分布不一致,比如验证集里夜间样本占比过高,或者数据量太少模型记住了训练集。解决:重新划分数据集,保证 train/val 的时段和姿态分布一致;增加数据增强。
现象 4:训练正常,但推理检测不到视频里的小目标。原因:推理imgsz用的 640,目标本身只有 30 像素宽,特征太小。解决:推理时把imgsz提到 960,检测率会明显提升,代价是单帧耗时增加,实时性要求高的场景需要权衡。
现象 5:训练中断了,不想从头再来。原因:断电、显存被其他程序占用等意外。解决:训练过程中会自动保存last.pt和best.pt,用model = YOLO("runs/detect/train/weights/last.pt")加载后,在train()中加resume=True继续训练,不用从头开始。
6. 可视化界面与部署验收:别让模型死在 .py 里
6.1 PyQt 界面核心循环:图像、视频与实时摄像头
模型训练好以后,要让它变成「能演示的系统」,可视化界面是必选项。用 PyQt 写检测界面时,核心是把模型推理放进独立线程,避免阻塞界面刷新:
from PyQt5.QtCore import pyqtSignal, QThread from PyQt5.QtGui import QImage class DetectThread(QThread): frame_ready = pyqtSignal(QImage) def __init__(self): super().__init__() self.model = YOLO("best.pt") self.running = True def run(self): cap = cv2.VideoCapture(0) # 0 为摄像头,也可以是视频文件路径 while self.running: ok, frame = cap.read() if not ok: break results = self.model.predict(frame, conf=0.4, imgsz=640, verbose=False) # 在 frame 上画检测框、显示告警状态 self.frame_ready.emit(QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888))QThread负责循环读帧和推理,通过frame_ready信号把结果帧传回主线程更新界面。注意conf参数在界面演示阶段可以调低到 0.3,提高召回率,避免演示时漏检太尴尬;真实部署时再调回 0.4 以上,减少误报。
6.2 部署三步走与验收指标:别急着上 RK3588
部署路径我建议按三步走:先在训练用的电脑上跑通「摄像头 + 界面」的完整链路,这是第一关;然后把模型导出为 ONNX,用 onnxruntime 替代 ultralytics 做推理,单帧速度会明显提升;最后才考虑 RK3588、Jetson Orin 这类边缘设备,移植时重点重新测conf阈值,边缘设备的算力差异会改变最佳置信度设置。不要一上来就在嵌入式设备上折腾,环境问题会淹没模型问题。
验收时用一张表量化项目是否合格,这是答辩和方案汇报里最有说服力的部分:
| 指标 | 合格线 | 测试方式 |
|---|---|---|
| 单帧推理耗时 | 本机 ≤ 30ms | 统计 1000 帧平均耗时 |
| 误报次数 | 每天 ≤ 3 次 | 用无车录像加速回放 |
| 漏报次数 | 真实推车基本不漏 | 分别测白天、夜间、遮挡场景 |
| 连续运行稳定性 | 8 小时不崩溃 | 长时间运行记录日志 |
这三个指标里,误报和漏报是跷跷板:conf调高,漏报变多;conf调低,误报变多。我现在的习惯是先拿一段真实录像反复调conf,直到漏报为零、误报可接受,再固定参数跑一晚上验证,而不是训练完就直接接摄像头。把这一步做扎实,项目交付才有底气和说服力,希望帮到你。
本文还有配套的精品资源,点击获取