☰
基于YOLOv8的商场扶梯逆行行为预警系统:原理、部署与调优
2026/10/1 19:19:31 网站建设 项目流程

简介:这份资源是面向计算机、人工智能、自动化等专业学生与教师的YOLOv8实战项目包,聚焦商场扶梯场景下的逆行行为预警,可用于毕业设计、课程设计或大作业。项目包含训练与推理源码、可视化界面、完整数据集及部署说明,部署流程简单,运行后能生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图,便于答辩展示与结果分析。压缩包共8个文件,以3个py脚本、3个pt权重和2个txt说明为主,整体约15.91MB,涵盖模型训练、视频检测与界面交互等模块。已有37人学习下载。读者可直接获得一套可复现的检测方案,并在此基础上修改扩展功能,适合作为项目初期立项演示或进阶学习参考。

1. 扶梯逆行预警这件事,为什么值得用 YOLOv8 认真做一遍

商场扶梯的逆行行为,是那种「平时不出事、出事就是大事」的典型场景。传统做法靠安保盯监控,一个监控室十几路画面,人眼根本盯不过来,等发现有人逆着扶梯往下走,往往人已经摔了。我最早接触这个需求,是帮一个做智慧商场的团队做行为识别,他们一开始想用红外对射加逻辑判断,结果发现扶梯上人一多,遮挡、并排、抱小孩这些情况全乱套,误报率高到安保直接关掉报警。后来换成基于视觉的方案,用 YOLOv8 做人体检测加轨迹方向判断,才把误报压下来。

这套《基于 YOLOv8 的商场扶梯逆行行为预警系统》本质上就是干这件事:用 YOLOv8 检测扶梯区域内的人,跟踪每个人的运动轨迹,判断方向和扶梯运行方向是否相反,一旦逆行就触发预警。它包含源码、可视化界面、完整数据集和部署教程,简单部署即可运行,适合毕设或课程设计。为什么选 YOLOv8 而不是更早的 YOLOv5?因为 v8 的 anchor-free 头对小目标和密集人群更友好,扶梯场景里人挤人、半身遮挡是常态,这一点很关键。下面我按「先立住原理、再动手复现、最后讲坑」的顺序,把整套方案拆开讲清楚。

2. 逆行预警的技术骨架:检测、跟踪、方向判定怎么串起来

2.1 为什么是「检测 + 跟踪 + 方向」三段式,而不是端到端行为分类

很多人第一反应是直接上一个行为识别模型,比如 SlowFast 或者 ST-GCN,输入一段视频直接输出「逆行/正常」。我试过,翻车了。原因有三个:第一,行为识别模型需要大量标注好的视频片段,而扶梯逆行是低频事件,你很难凑够正样本;第二,端到端模型的可解释性差,安保看到报警不知道为什么报,没法信任;第三,训练成本高,毕设或课程设计的机器根本跑不动。

所以更稳的路线是拆成三段:YOLOv8 负责逐帧检测人体框,ByteTrack 或 BoT-SORT 负责把帧间的人框关联成轨迹,方向判定模块负责拿轨迹的位移向量和扶梯方向做比较。这样做的好处是每一段都可以单独调试、单独替换。检测不准就换模型或调置信度,跟踪丢 ID 就调跟踪器的匹配阈值,方向误判就改判定逻辑。整条链路是透明的,出问题能定位。

具体到方向判定,核心逻辑是:对每条轨迹,取最近 N 帧的中心点坐标,拟合出运动方向向量,再和预设的扶梯运行方向向量做夹角计算。夹角超过阈值(比如 90 度)就判定为逆行。这里有个细节,扶梯上正常乘客是站着不动的,人是被扶梯带着走的,所以轨迹方向其实反映的是扶梯方向。逆行的人是主动往下走,速度会比扶梯快,方向也相反。所以判定时不能只看方向,还要看速度——如果一个人静止站在扶梯上,他的轨迹位移方向就是扶梯方向,这是正常的。

2.2 数据集怎么准备:标注规范与扶梯场景的特殊处理

标题里说包含完整数据集,但你要拿自己的商场画面跑,还是得会自己标。扶梯场景的数据集有几个特殊点:一是视角固定,基本都是斜上方俯视,人体框会有透视变形;二是人群密集,框和框之间重叠严重;三是光照变化大,商场中庭的玻璃顶棚会让画面忽明忽暗。

标注用 LabelImg 或 Labelme 都行,YOLOv8 要的是 YOLO 格式的 txt。每张图对应一个 txt,每行是class_id x_center y_center width height,全部归一化到 0-1。扶梯场景我一般只标一个类person,因为逆行判定只关心人,不需要区分性别年龄。标注时有个血泪经验:被扶梯扶手挡住一半的人也要标,哪怕只露出上半身,因为跟踪器需要连续的检测框才能维持 ID,漏标一帧就可能导致轨迹断裂。

数据增强方面,YOLOv8 内置了 mosaic、mixup、随机缩放这些。扶梯场景我建议额外加一点亮度扰动和运动模糊,模拟商场灯光变化和摄像头轻微抖动。但不要用水平翻转,因为扶梯方向是固定的,翻转后方向就反了,会让模型学到错误的左右关系。

2.3 从标注到训练:一份能直接跑的 YOLOv8 配置

数据集目录结构按 YOLOv8 的要求组织:

dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml

data.yaml内容:

path: ./dataset train: images/train val: images/val nc: 1 names: ['person']

训练命令用官方 CLI 就行,不用自己写训练循环:

yolo detect train \ model=yolov8n.pt \ data=dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/escalator \ name=exp1

参数说明:model选yolov8n.pt是因为扶梯场景类别单一,n 版本足够,而且推理快,适合实时预警;imgsz=640是精度和速度的平衡点,如果你用 RK3588 这类边缘板部署,可以降到 416;batch=16看显存,GTX1660Ti 跑 640 大概能到 16,显存不够就减半;epochs=100配合早停,一般 60-80 轮就收敛了。训练完看runs/escalator/exp1/results.png里的损失曲线,如果 val 损失还在降但 train 已经平了,说明还能再训;如果 val 开始翘头,就是过拟合,加 dropout 或减 epochs。

2.4 跟踪与方向判定:把检测框变成「谁在往哪走」

检测只给你每帧的框,跟踪才给你 ID 和轨迹。YOLOv8 官方支持 ByteTrack 和 BoT-SORT,直接调:

from ultralytics import YOLO model = YOLO('runs/escalator/exp1/weights/best.pt') results = model.track( source='escalator.mp4', tracker='bytetrack.yaml', conf=0.4, iou=0.5, persist=True, stream=True )

conf=0.4是置信度阈值,扶梯场景人密集,调太低会有一堆误检,调太高会漏掉被遮挡的人,0.4 是我试下来比较稳的值;iou=0.5是 NMS 的 IoU 阈值,人多的时候可以降到 0.45 减少框重叠;persist=True保证视频流里 ID 连续。

拿到轨迹后,方向判定我一般这么写:

import numpy as np from collections import defaultdict, deque track_history = defaultdict(lambda: deque(maxlen=30)) ESCALATOR_DIR = np.array([0, -1]) # 假设扶梯向上,画面 y 轴向下为正 def check_reverse(track_id, center): track_history[track_id].append(center) if len(track_history[track_id]) < 15: return False pts = np.array(track_history[track_id]) vec = pts[-1] - pts[0] norm = np.linalg.norm(vec) if norm < 20: # 位移太小,可能是静止站立 return False vec = vec / norm cos_angle = np.dot(vec, ESCALATOR_DIR) return cos_angle < -0.3 # 夹角大于约 107 度判定逆行

逻辑说明:track_history存每条轨迹最近 30 帧的中心点;ESCALATOR_DIR是扶梯运行方向的单位向量,需要根据你的摄像头视角标定;norm < 20是过滤静止的人,因为站着不动的人位移接近零,方向向量没意义;cos_angle < -0.3是方向阈值,对应夹角约 107 度,留了一定容差,避免斜着走的人误报。这个阈值要根据实际画面调,太松会漏报,太紧会误报。

3. 可视化界面与部署:让安保看得懂、跑得起来

3.1 可视化界面要展示什么:报警框、轨迹线、状态面板

毕设和课程设计里,可视化界面是加分项,但很多人做成「为了好看而好看」,堆一堆图表反而看不清重点。扶梯逆行预警的界面,核心就三样:实时画面叠加检测框和轨迹线、逆行报警高亮、状态统计面板。

检测框用绿色画正常人,红色画逆行的人,框上标 ID 和速度。轨迹线用半透明的线把最近 30 帧的中心点连起来,这样安保能直观看到「这个人是在往上走还是往下走」。报警时画面闪红边,同时状态面板里逆行计数加一,记录时间戳。

用 PyQt5 或 Gradio 都能做。Gradio 更简单,适合快速搭:

import gradio as gr import cv2 import numpy as np def process_frame(frame, model, tracker): results = model.track(frame, persist=True, conf=0.4, tracker='bytetrack.yaml') annotated = results[0].plot() # 这里插入方向判定逻辑,逆行的人框改红色 return annotated def run_demo(video_path): cap = cv2.VideoCapture(video_path) model = YOLO('best.pt') while cap.isOpened(): ret, frame = cap.read() if not ret: break yield process_frame(frame, model, None) gr.Interface( fn=run_demo, inputs=gr.Video(label='上传扶梯监控视频'), outputs=gr.Image(label='实时预警画面'), title='扶梯逆行行为预警系统' ).launch()

逻辑说明:model.track带persist=True保证视频流里 ID 不跳;results[0].plot()是 Ultralytics 自带的画框方法,返回带框的 numpy 数组;方向判定逻辑插在plot之后,把逆行的框颜色覆盖成红色。Gradio 的yield让它变成流式输出,看起来像实时视频。

3.2 部署到边缘设备:RK3588 和 CPU 版本的取舍

标题说「简单部署即可运行」,但实际落地时你得选部署平台。如果只是毕设演示,笔记本 CPU 跑就够了,yolov8n在 i5 上大概 15-20 FPS,够用。如果要真装到商场,就得考虑 RK3588 这类边缘计算盒,功耗低、能 24 小时跑。

RK3588 部署 YOLOv8 的流程是:PyTorch 模型 → ONNX → RKNN。转换命令:

# 先导出 ONNX yolo export model=best.pt format=onnx imgsz=640 # 再用 rknn-toolkit2 转 RKNN python -m rknn.api.rknn_converter \ --onnx best.onnx \ --output best.rknn \ --target rk3588 \ --quantize \ --dataset dataset/images/val

参数说明:--quantize开启量化,RK3588 的 NPU 对 int8 支持最好,量化后速度能翻倍,但精度会掉 1-2 个点,扶梯场景类别单一,掉这点精度可以接受;--dataset指定量化校准集,用验证集的前 100 张图就行,不用太多。转完后用rknn-toolkit2的推理接口加载,和 ONNX 的用法差不多。

CPU 版本部署就简单了,直接pip install ultralytics,然后model.predict就行。Ubuntu 20.04 上装环境,注意 Python 版本别太高,3.8-3.10 最稳,3.11 以上有些依赖会编译失败。

3.3 报警联动:从「看到」到「通知到人」

预警系统光在界面上闪红没用,得让安保收到通知。常见做法是接一个 webhook,逆行触发时 POST 一条消息到企业微信或钉钉的机器人。代码很简单:

import requests import json def send_alert(track_id, timestamp, image_path): webhook = '你的机器人webhook地址' payload = { 'msgtype': 'text', 'text': { 'content': f'扶梯逆行预警!ID: {track_id},时间: {timestamp}' } } requests.post(webhook, data=json.dumps(payload))

注意别每帧都发,会刷屏。我一般加一个冷却时间,同一个 ID 30 秒内只报一次,不同 ID 之间也做去重。另外报警图片存本地,消息里带路径,安保点开能看截图。

4. 避坑与排查:那些让我熬夜的翻车现场

4.1 轨迹 ID 频繁跳变,同一个人被当成好几个人

现象:画面里一个人正常走,ID 从 1 跳到 5 又跳到 12,方向判定模块拿到一堆碎片轨迹,误报逆行。

原因:ByteTrack 的匹配阈值太严,或者检测框抖动太大,帧间 IoU 不够,跟踪器认为是新目标。扶梯场景里人被扶手挡住、弯腰、抱小孩,框的形状变化剧烈,很容易丢匹配。

解决:把tracker换成botsort.yaml,BoT-SORT 有 ReID 特征,对形变更鲁棒;同时把检测的conf从 0.4 降到 0.35,让更多框进入跟踪器;再不行就调track_high_thresh和track_low_thresh,在bytetrack.yaml里改,把低阈值调低,让弱检测也能参与匹配。

4.2 逆行判定在扶梯口误报,人刚上扶梯就被报警

现象:人从平地踏上扶梯的瞬间,轨迹方向是水平的,和扶梯方向夹角接近 90 度,被判定为逆行。

原因:扶梯口是方向过渡区,人的运动方向从水平变成沿扶梯方向,中间有个夹角。判定模块没做区域过滤,把过渡区也算进去了。

解决:在画面里画一个多边形 ROI,只对扶梯中段区域做方向判定,扶梯口上下各留一段缓冲区不判。ROI 用cv2.polylines画,判定前先cv2.pointPolygonTest检查中心点是否在 ROI 内。这个 ROI 每个摄像头都要单独标,因为安装角度不同。

4.3 夜间或逆光时检测框大面积丢失

现象:商场中庭玻璃顶棚,下午逆光时画面过曝,YOLOv8 检测不到人,预警直接失效。

原因:训练集里逆光样本太少,模型没见过这种极端光照。另外摄像头自动曝光在逆光时会压暗主体,人变成剪影。

解决:训练时加亮度扰动增强,hsv_v参数调到 0.6;如果已经部署了,在推理前加一个 CLAHE 自适应直方图均衡,把暗部提亮。代码:

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) frame_lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) frame_lab[:, :, 0] = clahe.apply(frame_lab[:, :, 0]) frame = cv2.cvtColor(frame_lab, cv2.COLOR_LAB2BGR)

clipLimit=2.0控制对比度增强幅度,太高会放大噪声;tileGridSize=(8,8)是分块大小,画面大就调大。

4.4 模型在本地跑得好,部署到 RK3588 后精度暴跌

现象:PyTorch 模型 mAP 0.85,转 RKNN 量化后掉到 0.6,漏检严重。

原因:量化校准集选得不好,或者量化时用了对称量化,而 YOLOv8 的激活值分布不对称。

解决:校准集要用有代表性的图,别只用几张,至少 100 张,覆盖不同光照和人群密度;量化时用quantized_dtype='asymmetric_quantized-8'非对称量化;如果还不行,就只量化卷积层,检测头保持 fp16,精度能回来不少。

4.5 多路视频同时跑,帧率掉到个位数

现象:一个盒子接 4 路摄像头,每路都跑 YOLOv8,GPU/NPU 跑满,帧率从 25 掉到 5。

原因:没做批处理,每路单独推理,NPU 利用率低。另外视频解码也占资源。

解决:把 4 路画面拼成一张大图,一次推理,再拆开结果。RK3588 的 NPU 支持 batch,拼图后 batch=4,吞吐能翻倍。解码用硬件解码,ffmpeg的h264_rkmpp或 OpenCV 的cv2.CAP_GSTREAMER。如果还不行,就降分辨率,640 降到 416,速度能提 40%。

5. 把预警做成「可验证」的系统:几个我常用的调优技巧

5.1 用混淆矩阵反推阈值,而不是拍脑袋

方向判定的cos_angle阈值和位移norm阈值,很多人凭感觉设,结果要么误报要么漏报。我一般会标一段测试视频,人工标出所有逆行片段,然后跑一遍系统,统计不同阈值下的 TP、FP、FN,画 ROC 曲线,选 F1 最高的点。这个流程用 sklearn 几行就能做:

from sklearn.metrics import precision_recall_curve, f1_score import numpy as np # y_true: 人工标注,1 逆行 0 正常 # y_scores: 系统输出的 cos_angle 值 precision, recall, thresholds = precision_recall_curve(y_true, -y_scores) f1 = 2 * precision * recall / (precision + recall + 1e-6) best_thresh = thresholds[np.argmax(f1)] print(f'最佳阈值: {-best_thresh:.3f}, F1: {max(f1):.3f}')

注意y_scores取负,因为 cos_angle 越小越可能是逆行,而 sklearn 默认分数越大越正。跑完你会得到一个有数据支撑的阈值,比拍脑袋靠谱得多。

5.2 用「轨迹平滑」压掉抖动误报

检测框逐帧抖动会让方向向量乱跳,偶尔触发误报。我一般对轨迹中心点做一次滑动平均或卡尔曼滤波,再算方向。滑动平均最简单:

def smooth_track(pts, window=5): if len(pts) < window: return pts kernel = np.ones(window) / window x = np.convolve(pts[:, 0], kernel, mode='valid') y = np.convolve(pts[:, 1], kernel, mode='valid') return np.stack([x, y], axis=1)

window=5是平滑窗口,太大方向会滞后,太小压不住抖动,5 帧在 25 FPS 下是 0.2 秒,够用。平滑后再算方向向量,误报能降一半。

5.3 一个我坚持的习惯:每次改完参数,先跑固定测试集

调参最怕「改完感觉好了,其实只是换了个视频」。我习惯留一段 5 分钟的固定测试视频,包含正常上行、正常下行、逆行、静止站立、人群密集五种情况,每次改完参数先跑这段,看各项指标有没有退化。这个习惯帮我避免了好几次「调好一个场景、搞坏另一个场景」的翻车。

这套方案从检测到跟踪到方向判定,链路是完整的,源码、界面、数据集、部署教程都齐了,拿来跑通不难。难的是把它调到能在真实商场里稳定运行,那需要你对着实际画面反复调 ROI、阈值和平滑参数。我踩过的坑上面都写了,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询