简介:针对电梯监控场景下电动车与自行车的自动识别需求,项目基于电梯内部视角数据集对YOLO预训练模型进行微调,并同时提供基于检测与基于跟踪两种技术路线:前者对每帧检测到目标实例输出标注图像,后者在检测结果上进一步去重,适用于毕业设计、课程设计、工程实训、学科竞赛等场景。资源包共134个文件,约16.96MB,主体为34个Python脚本与34个YAML配置文件,涵盖检测与跟踪核心逻辑、模型及数据配置;另有JPG/PNG样例图像、Dockerfile部署文件、Markdown说明文档、训练结果CSV与教程Notebook等,便于环境搭建与结果复现。目前已有65人学习下载,项目经严格测试可正常运行,包含完整源码与工程文件,下载后按README指引即可复现,也可在现有框架上扩展新功能,或借鉴其设计思路完成课程报告与项目答辩。整体目录结构清晰,按功能模块划分,便于快速定位训练、推理与部署代码。
1. 电梯监控里的电动车与自行车识别:先想清楚你要检测什么
把“电梯监控视角内的电动车以及自行车识别”做成一个能交差的毕设或竞赛项目,核心任务只有一个:目标检测。你要做的不是分类、不是分割,而是在电梯这个固定机位、固定俯仰角、固定光照变化的画面里,实时框出“电动车”和“自行车”这两类目标,并给上层联动逻辑提供可靠信号。它看起来像是普通路测检测的简化版,但真正动手后你会发现,电梯场景的坑全藏在视角和反光里。面向的落地场景很具体:电动车禁入电梯的智能预警、自行车进电梯的统计与告警、以及课程项目里最常要求的“能跑通、能演示、能讲清楚原理”。本文会按数据、训练、调参、部署四个环节,把一个最小可用方案完整拆给你。
2. 为什么普通路测模型在电梯里翻车:场景约束与模型选型
2.1 电梯成像与路测数据集的三个根本差异
电梯摄像头一般装在轿厢顶部角落,斜向下看,门在画面一侧或底部。这个视角下,人和车是“从头顶往脚下看”,而绝大多数公开数据集是平视路测视角。同样一辆电动车,路测数据集里能看到完整的侧面轮廓、车轮、把手和后视镜;电梯视角里看到的是车座、脚踏板、车篮和车把的俯视投影,车轮高度被压缩,车身比例完全变形。更麻烦的是,电梯轿厢是不锈钢镜面,白天会形成大面积高光反光,晚上红外补光模式下,金属表面会产生过曝和重影。从公开数据集里随便挑一个 YOLO 预训练权重直接去测电梯视频,最常见的翻车结果是:平视能检出来的电动车,在电梯俯视画面里置信度跌到 0.2 以下,或者把倒影里的车也算一个目标。
这就是第一个需要想清楚的决策:不要迷信预训练模型在 COCO 上的 mAP,电梯场景是一个强领域偏移场景,必须用目标场景的数据做微调,而且数据收集策略要围绕“俯视 + 镜面反光 + 红外模式”三个维度展开。数据量不需要很大,但分布必须贴。我见过不少课程项目用 VOC 或 COCO 里的自行车数据硬训,最后在答辩演示时对着电梯实拍视频漏检率超过 40%,问题不在模型,在数据与场景不匹配。
2.2 模型选型:YOLO 系列足够,关键在于尺寸选择
目标检测模型的可选范围很广,Faster R-CNN、SSD、YOLO、DETR 都能做。但电梯场景有一个硬约束:推理端往往是普通 PC 或 Jetson、RK3588 这类边缘盒子,帧率要求倒不高,5~10 FPS 就够用,但延迟要可控,不能出现“车已经进门了,检测结果才出来”的情况。因此我一般直接选 YOLOv8 系列,理由有三个:训练生态成熟,单卡能跑,导出 ONNX 后部署路径短;小模型 n/s 档在这个任务上已经够用;社区踩坑多,遇到问题容易检索到答案。
具体选 n 还是 s,取决于你的训练数据和推理设备。如果目标在画面里占比较大(电梯视角下整车宽度通常占画面 1/5 以上),YOLOv8n 就够了,推理快、显存小;如果你还想顺带检测“人推车”“车载人”这种复合状态,建议上 s,给模型更多特征容量。至于 YOLO11、YOLOv9 这些更新版本,在电梯這種静态场景下收益不明显,不值得为了新版本额外折腾依赖兼容。我的建议是:先用 YOLOv8s 跑通全流程,最后根据实测帧率再决定要不要降到 n。
2.3 评价指标:mAP 只是参考,漏检率才是硬指标
课程答辩和竞赛评审喜欢看 mAP50、mAP50-95,但真实电梯联动业务里,漏检一辆电动车远比误报一次严重。漏检意味着电动车已经进了电梯,系统没发现,上层无法阻止关门;而误报可以通过时序逻辑过滤(详见第 5 章)。所以你要建立两套评价视角:模型训练阶段看 mAP50,确认模型没有欠拟合;场景验收阶段统计“逐帧漏检率”和“逐帧误报率”,分别计算。
我建议在验证集里单独划出三类难例:夜间红外帧、镜面反光帧、人推车进梯帧,这三类的召回率必须单独看。训练时的 loss 曲线只能说明模型拟合情况,不能说明场景适配情况;真正有效的做法是训练完立即跑一段真实电梯录像,把检测结果叠加到视频上人眼扫一遍,统计哪类画面在漏。这一步是纯手工活,但能帮你省掉后面部署阶段大量的返工时间。
3. 准备俯视数据集:采集、标注与增强的可复现步骤
3.1 数据来源与数量底线
电梯场景数据有三个来源,按优先级排序:自己录真实电梯视频提取帧;找同城朋友帮忙在不同小区采集,覆盖不同轿厢尺寸、不同摄像头安装角度;最后才是用公开路测数据补底。真实电梯视频是金标准,建议至少覆盖 3 部不同电梯,每部录 30~60 分钟,覆盖早中晚和夜间红外模式。视频不需要全程标注,抽帧时均匀取样,每隔 5~10 秒取一帧,避免连续帧高度相似导致的数据冗余。
数量底线我一般这样控制:电动车和自行车各至少 800 个标注实例(不是 800 张图,是 800 个框),加上负样本(空电梯、人拎东西、婴儿车)500~1000 张。如果你时间紧,每类 300 个框也能训出可演示的效果,但夜间和反光场景必须单独保证数量。公开路测数据只能作为预训练来源,不要直接混入微调训练集,因为视角差异会让模型学偏。
3.2 标注规范:贴边、遮挡与类别边界
标注格式用 YOLO 的 txt 格式,每张图一个同名 txt,每行是“类别 中心x 中心y 宽 高”,坐标归一化到 0~1。工具用常见的 LabelImg 或 X-AnyLabeling 即可。这里有三条规则必须遵守,否则后面训练一定出问题。
第一,框要贴紧目标外接矩形,能少留背景就少留背景。俯视视角下,电动车车把和后视镜会向外突出,很多人标注时习惯框到主体车身,把后视镜切掉,结果模型学到的特征里没有“外凸”这个关键几何信息,导致斜放车辆漏检。第二,遮挡目标只标可见部分,不要脑补完整轮廓。人推车进电梯时,车头经常被人挡住,这时只框能看到的车座和车轮;如果硬标完整车,标签边界会带大量行人像素,模型会把“人”也学进“电动车”特征。第三,类别边界要定义清楚。电动车(两轮电瓶车)和自行车在俯视视角下确实难分辨,我的做法是在标注阶段分三类:e-bike、bicycle、person。person 不是必检目标,但保留这个类别可以让模型学会区分“人与车”,后面做联动逻辑时也有用。如果实在分不清,可以把前两类合并成 vehicle,业务目标变成“识别进电梯的两轮载具”。
3.3 用代码做数据增强与样本合成
真实数据不够时,别急着上生成模型,先用传统图像增强和 Copy-Paste 合成把样本量撑起来。下面这段代码演示如何把已标注的电动车目标裁剪出来,随机粘贴到一段电梯背景视频帧上,生成新样本。本质是复制粘贴增强,适合电梯这种背景相对固定的场景。
import cv2 import numpy as np import random def paste_object(bg_path, obj_img, obj_box, out_path): # bg_path: 空电梯背景图路径 # obj_img: 从原图裁剪出的目标图像 # obj_box: YOLO格式 [cls, cx, cy, w, h],用于换算粘贴位置 bg = cv2.imread(bg_path) H, W = bg.shape[:2] # 把YOLO归一化坐标换算成像素 ow, oh = obj_box[3] * W, obj_box[2] * H scale = random.uniform(0.8, 1.2) # 随机缩放模拟远近 ow, oh = int(ow * scale), int(oh * scale) obj = cv2.resize(obj_img, (ow, oh)) # 粘贴在画面下半部,模拟车进电梯后的位置 x = random.randint(int(W * 0.2), int(W * 0.8)) y = random.randint(int(H * 0.4), int(H * 0.9)) if y + oh > H: y = H - oh if x + ow > W: x = W - ow # 简单alpha融合,不要求精细边缘 roi = bg[y:y+oh, x:x+ow] bg[y:y+oh, x:x+ow] = cv2.addWeighted(roi, 0.3, obj, 0.7, 0) # 生成对应YOLO标签 cx, cy = (x + ow / 2) / W, (y + oh / 2) / H nw, nh = ow / W, oh / H with open(out_path.replace(".jpg", ".txt"), "w") as f: f.write(f"0 {cx:.4f} {cy:.4f} {nw:.4f} {nh:.4f}\n") cv2.imwrite(out_path, bg)这段代码的逻辑是:从标注好的原始图片里按标签裁剪出目标,再以随机缩放和随机位置粘贴到空电梯背景上。注意几个参数:scale 控制在 0.8~1.2,是因为电梯视角下目标大小相对稳定,缩放过大反而制造出不符合场景的样本;y 限定在画面下半部,是因为车从门外推入时,实际轨迹集中在摄像头画面的中下区域。合成样本要控制比例,不要让合成样本超过总数量的 30%,否则模型会对真实光照纹理拟合不足。
除了 Copy-Paste,推荐在训练管线里直接开 Mosaic 增强和 HSV 扰动。Mosaic 把四张图拼成一张,能显著提升模型对目标尺度和遮挡的鲁棒性;HSV 扰动里,重点把饱和度扰动范围调大一些,因为夜间红外模式下画面会严重去饱和,提前让模型适应低饱和输入。
4. 训练与调参:能跑通的最小配置和四组关键参数
4.1 最小训练脚本与工程目录规范
训练基于 Ultralytics YOLO,先安装依赖,再按下面的目录结构组织数据,这是最常见的做法,也便于后面多人协作时同步数据格式。
elevator_yolo/ ├── dataset/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ ├── elevator.yaml └── train.py注意 labels 要和 images 按子目录对应,图片是images/train/a.jpg,标签就是labels/train/a.txt。数据集按 8:2 划分训练验证,划分时保证同一个视频的帧不要同时出现在两边,否则验证集被“剧透”,指标虚高。
数据集配置文件 elevator.yaml 是训练入口之一,内容如下:
path: /data/elevator_yolo/dataset train: images/train val: images/val nc: 3 names: 0: e-bike 1: bicycle 2: personpath 建议写绝对路径,避免相对路径在切换工作目录时引发“数据集找不到”这类低级问题。nc 是类别数量,这里按三类来定,如果你不需要 person 类,就改成 2,并把 names 对应删掉。
然后是最小训练脚本:
from ultralytics import YOLO model = YOLO("yolov8s.pt") # 加载COCO预训练权重 results = model.train( data="elevator.yaml", epochs=100, imgsz=640, batch=16, device="0", # 没有GPU就写 "cpu" patience=15, lr0=0.001, # 小数据集用低初始学习率 augment=True, )关键点:从yolov8s.pt预训练权重开始训练,而不是从yolov8s.yaml随机初始化,这一点对少数据场景非常重要。预训练权重已经学会通用纹理和边缘特征,电梯场景微调只需要适配视角分布,收敛速度快得多。batch 设为 16 是 8GB 显存的常见值,如果你显存不够出现“CUDA out of memory”,优先把 batch 降到 8,而不是换更小的模型。imgsz 保持 640,电梯场景目标不算小,加大到 1280 对精度提升有限,但推理耗时几乎翻倍,不划算。
4.2 四组必调参数:学习率、置信度阈值、IoU 阈值与早停
第一组是初始学习率 lr0。少数据场景我最开始也用 0.01,结果训练到第 20 个 epoch 时 mAP50 在 0.6 附近震荡,下不去;把 lr0 降到 0.001 后,验证集损失明显往下走。原因是数据量少时,大学习率容易让 loss 在小范围内来回弹,微调阶段更适合更小步长。
第二组是置信度阈值 conf。训练脚本里不会直接写 conf,它是推理阶段的参数。建议在验证阶段先把 conf 调到 0.25 看召回,再慢慢往上提到 0.4 左右。电梯场景里,漏检比误报严重,所以 conf 宁低勿高。如果你发现误报太多,优先用第 5 章的时序规则去过滤,而不是粗暴提高 conf,否则夜间低置信度帧会全部丢掉。
第三组是 NMS 的 IoU 阈值 iou。默认值 0.7 一般不用动,但有一个特殊场景要改:电梯镜面反光导致同一个车在画面里出现一实一虚两个框,NMS 去不掉实虚框。这时候可以把 iou 降到 0.5,增强对重叠框的抑制。代价是密集场景下可能误删并排真实目标,但电梯里极少有两辆车并排的情况,可以接受。
第四组是早停 patience。我习惯设 15,即验证集 loss 连续 15 个 epoch 不改善就停止。小数据集上 100 epoch 通常在 40~60 个 epoch 就收敛,早停能帮你省时间。注意 patience 不要设太大,否则模型会在过拟合阶段反复震荡,白耗算力。
4.3 训练日志怎么读:损失分开看,不要只看一列
Ultralytics 训练时终端会实时打印 box_loss、cls_loss、dfl_loss 和 mAP50。新手容易犯的错是只看 mAP50有没有涨,忽略损失项的组成。box_loss 偏高说明框的位置不够准,常见原因是标注框贴边程度不一致,有些紧有些松,模型找不到稳定的回归目标;cls_loss 降不下去说明类别特征没分开,在电梯场景几乎都是电动车和自行车互相混淆。
另一个实用技巧是训练结束后去runs/detect/train/weights/目录看权重文件。best.pt是验证集 mAP 最优的权重,last.pt是最后一轮权重。如果两者差异很小,说明训练过程稳定,直接部署 best.pt 即可;如果差异很大,说明训练不稳定,考虑调低 lr0 或检查数据集里是不是有标签错乱。
5. 电梯电动车识别的常见踩坑与排查记录
5.1 白天检测正常,夜间漏检率陡增
现象:同一套权重,白天实拍视频能稳定检出,但晚上红外模式下,电动车置信度跌破阈值,出现大范围漏检。
原因:训练集里没有覆盖红外模式样本,或者只有少量红外样本被数据增强稀释了。电梯夜间会切红外,画面变成灰度、亮度不均、过曝区域大,模型在白天学到的颜色纹理特征(如车牌颜色、车身贴纸)全部失效。
解决:单独收集夜间红外视频,按时间抽帧 500 张以上并入训练集,并在训练时加大 HSV 里饱和度和亮度扰动范围。我习惯把 S 通道扰动上限调高到 1.5,这意味着模型必须学会不看颜色也能识别目标。如果夜间样本实在难采,退而求其次做法是用代码把所有白天图转成灰度并增强对比度,模拟红外效果,但效果远不如真红外样本。
5.2 电动车与自行车类别互相误判
现象:验证集里电动车被识别成 bicycle,或者反过来,准确率只有 70% 左右,业务上无法接受。
原因:电梯俯视视角下,两者的差异不再集中在车身颜色和侧面轮廓,而在轮径、轴距、车身宽度。从正上方看,电动车明显更宽,脚踏板位置更靠后,车座更宽大;自行车车座窄,车轮细。模型默认从平视特征里学,自然抓不住这些俯视判据。
解决:重新检查标注规范,把标签里每辆车的框贴到最外沿,让模型能看到完整的轮距和车把宽度。如果还是分不清,直接用类别重映射把 e-bike 和 bicycle 合并成 vehicle,并把业务目标改成“任何进电梯的两轮载具都要告警”。很多实际项目最后都走这条路,因为电梯管理方真正关心的是“有车进来”,而不是“什么车”。
5.3 斜放车辆的检测框只框住一半,漏检和错框并存
现象:车斜着推进电梯时,检测框只包含车座到车篮之间的一半车身,后轮被切在框外,置信度不足 0.5。
原因:电梯视角下斜放车辆是一个与坐标轴成夹角的矩形,而 YOLO 的水平框无法精确定位倾斜目标。训练时倾斜样本少,模型只能学習“直立车身”的框形,遇到斜置车辆就退化成局部检测。
解决:最直接的办法是扩大训练数据里斜置样本的比例,把标注框仍然按水平矩形贴紧整个车身的斜向外接矩形,也就是允许框包含更多背景。这个框并不完美,但至少让模型学到“斜置时整体形状仍是一个水平略宽的矩形”。如果项目周期长、追求更严谨,可以用 YOLOv8-OBB 旋转框检测,把标注框改成带角度参数的形式,但数据标注成本会明显上升,个人建议课程项目先用水平框方案,把夹角限制在 45 度内的数据覆盖好。
5.4 镜面倒影造成同一辆车检出两个框
现象:电梯门关闭后,门的不锈钢镜面里出现车身倒影,检测器在尸体位置和倒影位置各出一个框,触发两次告警。
原因:NMS 只抑制两个框 IoU 高的重叠框,而实体框和倒影框相距半个车身,IoU 可能只有 0.2~0.3,NMS 不会合并它们。
解决:别指望模型层面解决这个问题。实测最有效的方案是在后处理里加位置约束:电梯轿厢内摄像头画面里,真实车辆到达区域有明确边界,倒影通常出现在门一侧的镜像区域。写死一个掩码区域,只统计落在真实活动区域内的框;或者利用时序信息,倒影框会在电梯门打开瞬间消失,连续 N 帧都出现的框才认定为真实目标。两个方案可以叠加,误报能压到接近零。
5.5 推理延迟过高,电梯门关上了才触发告警
现象:Jetson Nano 或 CPU 上跑 YOLOv8s,每帧推理耗时 300~500ms,加上预处理和跟踪逻辑,从车进梯到触发信号要 2~3 秒。
原因:每帧都做全尺寸推理、没有做跳帧、也没有对 ROI 做裁剪。电梯场景里摄像头固定,目标只在画面中下部分流动,顶部区域大量时间是无意义的背景。
解决:三个优化叠加。第一,把模型换成 YOLOv8n,电梯目标较大,n 的精度损失可接受;第二,推理时把图像先裁剪到预先标定的活动 ROI,比如只保留画面下半部 60%,再送入模型,目标变小、推理变快;第三,控制推理节奏,每 2 帧抽 1 帧做检测,中间帧用上一帧结果直接沿用。实测在 CPU 上能把单帧推理压到 80ms 以内,满足联动需求。
6. 落地部署:从模型导出到电梯联动逻辑
6.1 ONNX 导出与边缘设备推理
训练完成后第一步是把 PyTorch 权重导出成 ONNX,便于脱离训练框架在推理端运行。一条命令即可:
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") model.export(format="onnx", imgsz=640, half=True)half=True 表示导出 FP16 权重,在支持 FP16 的设备上推理速度接近翻倍,但如果你目标设备是纯 CPU,建议改为 half=False,换取更好的兼容性。导出完成后用 ONNXRuntime 做简单推理验证,输入输出 shape 与官方文档一致再封装服务,不要直接在 PyTorch 环境里跑线上逻辑,那样部署环境太重。
边缘设备推理的常见流程是:拉 RTSP 流 -> OpenCV 读帧 -> 缩放到 640 -> letterbox 填充 -> ONNXRuntime 推理 -> 后处理解析框 -> 叠加到原始画面。我这里不把完整代码贴长,只提醒一件事:ONNX 输出的原始张量是(1, 4+nc, 8400)形状,8400 是三个尺度特征图的总锚点数,解析时必须先把维度转成(8400, 4+nc)再继续取置信度和 NMS,很多第一次部署的人在这一步直接晕掉。
6.2 给检测结果加三段式联动逻辑
模型输出的检测框不能直接驱动电梯控制器,否则一次倒影误报就会让电梯门卡住。我的做法是三道规则串联:
第一,单帧确认。置信度大于等于 0.4 且框的面积占整帧比例大于等于 3%,才算一次“候选命中”。第二,时序确认。连续 5 帧(约 0.5~1 秒)都出现候选命中,才触发预告警。第三,停留确认。目标框中心点位置在连续 20 帧内漂移小于 50 像素,判定为“车辆已停稳在电梯内”,此时输出禁止关门信号。这三个条件一层比一层严格,能过滤掉绝大部分的瞬时误报和路过误报。
6.3 一周后的数据回流与阈值再校准
部署不是终点。我现在的习惯是跑一周“影子模式”——只记录检测结果和抓拍,不实际联动电梯。一周后把误报和漏报的截图找出来,分成三类:光线变化、遮挡极端、类别混淆。如果某一类占比明显偏高,就用这些截图去扩充训练集,重新微调 20~30 个 epoch;如果整体误报率已经很低,只需要微调 conf 阈值,不需要重训。这样迭代两到三周,误报率一般能控制在每天 1 次以下。
最后补一句:做这种识别项目,真正花时间的从来不是模型,而是“让模型知道自己在什么环境里工作”。我每一版方案交付前,都会把模型跑在目标电梯的真实录像上,一帧一帧看,看到自己心里有底为止。这个习惯救了我很多次,也希望帮到你。
本文还有配套的精品资源,点击获取