尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于YOLOv8的公交站台违规占用自动检测与告警方案

基于YOLOv8的公交站台违规占用自动检测与告警方案 之前在公交站台监控项目中经常遇到这样一个场景出租车、网约车为了上下客直接停在公交站台区域把公交车进站通道堵住。遇到着急的乘客双方驾驶员很容易在早高峰对峙起来路怒冲突往往就是这么产生的。更麻烦的是这类现场通常没有民警在场事后的视频取证全靠人工一帧一帧翻监控效率非常低。本文围绕“公交站台被社会车辆长时间占用”这个真实痛点整理一套基于目标检测的自动识别与告警方案包含环境搭建、核心判定逻辑、完整可运行代码和工程落地建议适合正在做智能交通、安防监控、视频分析项目的开发者参考。1. 背景公交站台被占用靠人眼盯不现实1.1 一个常见的城市交通矛盾场景很多城市公交站台都设置在道路右侧站台边缘划了黄色网格线或“公交专用”字样。但实际情况是网约车、出租车在接单时往往直接把车停在站台内等客有的甚至长时间占着位置不走。公交车进不了站只能在机动车道中间上下客既危险又容易造成后方拥堵。乘客和司机互相催促几句火气一上来“路怒”冲突就发生了。这类问题单靠交警巡逻很难全覆盖因为违停时间可能集中在早晚高峰而警力有限。更常见的是公交车驾驶员通过行车记录仪或手机拍摄把视频发到举报平台再由人工审核。人工审核的问题在于视频可能几十秒甚至几分钟审核员需要反复拖动进度条确认车辆是否长时间占道、是否影响公交车运行效率很低漏判率高。1.2 人工巡检抓拍的局限性把“曝光”从人工行为变成自动行为是智能视频监控最直接的切入点。如果能做到视频画面中一旦有车辆进入公交站台区域并且持续停留超过指定时间系统自动截图、保存证据、推送告警那人工只需要复核少量告警效率会高很多。这里有几个技术点需要拆开看第一怎么判断画面里有车第二怎么判断车在公交站台区域内第三怎么判断车辆是“长时间占用”而不是“短暂经过”。这三个问题分别对应目标检测、区域判定、时长累计是整套系统最核心的三个环节。1.3 目标检测方案能解决什么目标检测技术现在已经非常成熟基于 YOLO 系列模型开发者不需要自己训练模型也能完成车辆识别。YOLOv8 作为当前常用的目标检测框架支持车辆、行人、交通标志等 80 类常见物体识别直接使用官方预训练权重即可检测出 car、bus、truck、motorcycle 等车辆类别。检测到车辆只是第一步真正决定“是否违停”的是业务规则。同一辆车可能只是在站台前慢慢路过也可能停在站台等客五分钟。前者不算违停后者需要告警。所以还要引入两个规则车辆中心点是否落在公交站台矩形区域内以及车辆在区域内的连续停留时间是否超过阈值。只有两个条件同时满足才触发告警。2. 技术方案与识别原理2.1 目标检测模型YOLOv8YOLOv8 是 Ultralytics 公司维护的目标检测库相比早期 YOLO 版本它在训练和推理接口上更统一调用方式也很简单。一个YOLO(yolov8n.pt)就能加载模型输入图片或视频帧输出检测框、类别、置信度。在公交站台场景中我们通常只关心车辆类不关心行人、猫狗等无关目标。COCO 数据集中车辆相关类别包括2car小汽车3motorcycle摩托车5bus公交车7truck卡车通过类别白名单过滤可以避免行人误入区域导致误报也能去掉一些明显无关的检测结果。2.2 公交站台区域判定目标检测模型返回的是检测框坐标格式是(x1, y1, x2, y2)分别代表左上角和右下角在画面中的像素坐标。要判断车辆是否在公交站台内最简单的办法是取检测框的中心点坐标然后判断中心点是否落在预先标注的公交站台矩形区域内。为什么用中心点而不是整个检测框因为检测框本身可能比车辆实际轮廓大尤其在车辆倾斜停放时框边缘可能压到站台边界。用中心点可以更稳定地表达“车辆主体在区域内”这个语义。公交站台区域可以预先在画面中标注通常用两个点确定一个矩形左上角(x1, y1)和右下角(x2, y2)。标注一次之后只要摄像头位置不变这个区域就是固定的。2.3 两级判定从“车辆出现”到“违停成立”整套判定逻辑可以拆成两级。第一级是感知层。模型检测到画面中有车辆并且该车辆中心点进入公交站台区域说明“有车辆出现在不该出现的区域”。第二级是业务层。车辆进入区域后开始计时如果连续停留在区域内超过设定的阈值比如 10 秒或 30 秒就判定为“长时间占用”触发告警。如果车辆只是路过几秒内就离开区域计时器清零不产生告警。为什么要设时间阈值因为公交车进出站也需要在站台临时停靠网约车上下客可能只停几秒钟如果每一次检测到车辆就告警那误报会非常多。时间阈值可以过滤掉“短暂经过”和“正常上下客”的情况只保留真正的长时间占道行为。3. 环境准备与项目结构3.1 环境要求本文示例以 Python 环境为主建议使用 Python 3.9 及以上版本。核心依赖只有两个ultralytics提供 YOLOv8 模型加载与推理能力。opencv-python负责视频读取、画面绘制和图像保存。操作系统不限Windows、Linux、macOS 都可以运行。视频文件可以是行车记录仪导出的 MP4也可以是监控摄像头保存的视频片段。如果是实时摄像头流OpenCV 的VideoCapture也支持 RTSP 地址只需把视频路径换成 RTSP URL 即可。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。3.2 安装依赖创建虚拟环境并安装依赖python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate pip install ultralytics opencv-python首次运行时会自动下载yolov8n.pt预训练权重文件大小约 6 MB。如果你的网络环境不方便自动下载也可以手动下载后放到项目目录再把模型路径指向本地文件。3.3 项目目录设计示例项目结构如下bus_station_monitor/ ├── alerts/ # 告警截图输出目录 ├── test_bus_station.mp4 # 测试视频 ├── station_monitor.py # 主脚本 └── config.yaml # 推荐使用的配置文件alerts目录用于存放告警截图每次触发告警会自动生成一张带时间戳的 JPG 图片。station_monitor.py是主脚本负责视频读取、目标检测、区域判定和告警输出。config.yaml是工程化建议中的配置项后续可以按需拆分。4. 核心功能拆解与代码实现4.1 视频读取与抽帧OpenCV 的VideoCapture可以逐帧读取视频文件。每一帧是一张 BGR 格式的图像直接传给 YOLOv8 模型进行检测。需要注意的是视频帧率影响时长判定。通常使用cap.get(cv2.CAP_PROP_FPS)读取视频帧率如果读取不到或返回 0则默认按 25 帧每秒处理。帧率用于后续计算视频时间进度但本文示例的时长判定直接使用系统时间也就是墙钟时间这样更贴近“真实停车时长”的语义。4.2 车辆目标检测使用model.predict(frame, imgsz640, conf0.45, verboseFalse)对当前帧执行检测。imgsz是输入模型的图像尺寸640是 YOLOv8 常用的输入尺寸。conf是置信度阈值低于该值的结果会被过滤掉。检测结果results[0].boxes中包含所有检测框。对每一个检测框取出类别 IDbox.cls[0]和坐标box.xyxy[0]先判断类别是否在车辆白名单中再计算中心点最后判断中心点是否落在公交站台区域内。4.3 区域进入判断区域进入判断使用矩形包含关系。预先定义公交站台区域STATION_ZONE (1100, 520, 1720, 800)这组数字表示区域左上角 x 坐标 1100、y 坐标 520右下角 x 坐标 1720、y 坐标 800。这个坐标需要根据实际视频画面手动标注不同摄像头画面完全不一样。判断逻辑很简单def in_station_zone(cx, cy, zone): x1, y1, x2, y2 zone return x1 cx x2 and y1 cy y2如果车辆中心点满足这个条件就认为车辆进入了公交站台区域。4.4 持续占用计时与告警持续占用计时使用一个变量zone_occupied_since记录第一次检测到车辆进入区域的时间点。当车辆进入区域且zone_occupied_since为空说明这是车辆首次进入记录当前时间。当车辆持续在区域内且已经超过阈值触发告警。触发后重新记录当前时间避免对同一次停车重复告警。当车辆离开区域zone_occupied_since置空计时器清零。这种简化逻辑没有考虑多辆车的跟踪问题但在单目标、单区域场景下已经足够。真实项目中如果站台同时有多辆车建议引入目标跟踪器区分不同车辆的身份。4.5 证据保存触发告警时把当前帧保存为 JPG 截图。截图命名使用时间戳保证唯一性snapshot_name falert_{time.strftime(%Y%m%d_%H%M%S)}.jpg同时在画面上绘制公交站台矩形框和车辆检测框便于人工复核时快速定位问题。5. 完整实战案例5.1 主脚本代码以下代码文件路径为bus_station_monitor/station_monitor.py可以直接复制运行。 公交站台违停自动识别演示脚本 依赖: pip install ultralytics opencv-python import time from pathlib import Path import cv2 from ultralytics import YOLO # -------------------- 配置区域 -------------------- VIDEO_PATH test_bus_station.mp4 # 输入视频文件 MODEL_PATH yolov8n.pt # 目标检测模型 STATION_ZONE (1100, 520, 1720, 800) # 公交站台区域 (x1, y1, x2, y2) HOLD_SECONDS 10 # 占用多少秒后判定为违停 CONF_THRESHOLD 0.45 # 检测置信度阈值 OUTPUT_DIR Path(alerts) # 告警截图保存目录 # ------------------------------------------------- # COCO 数据集中车载相关类别: 2car, 3motorcycle, 5bus, 7truck VEHICLE_CLASSES {2, 3, 5, 7} VEHICLE_NAMES {2: car, 3: motorcycle, 5: bus, 7: truck} OUTPUT_DIR.mkdir(exist_okTrue) def load_model(model_path: str): return YOLO(model_path) def in_station_zone(cx: float, cy: float, zone: tuple) - bool: x1, y1, x2, y2 zone return x1 cx x2 and y1 cy y2 def draw_zone(frame, zone, color(0, 255, 255), thickness2): x1, y1, x2, y2 zone cv2.rectangle(frame, (x1, y1), (x2, y2), color, thickness) return frame def detect_zone_vehicle(frame, model): results model.predict(frame, imgsz640, confCONF_THRESHOLD, verboseFalse) boxes results[0].boxes for box in boxes: cls_id int(box.cls[0]) if cls_id not in VEHICLE_CLASSES: continue x1, y1, x2, y2 [round(v, 1) for v in box.xyxy[0].tolist()] cx (x1 x2) / 2 cy (y1 y2) / 2 if in_station_zone(cx, cy, STATION_ZONE): return { cls: VEHICLE_NAMES.get(cls_id, vehicle), box: (x1, y1, x2, y2), center: (cx, cy), } return None def main(): cap cv2.VideoCapture(VIDEO_PATH) if not cap.isOpened(): print(f无法打开视频文件: {VIDEO_PATH}) return fps cap.get(cv2.CAP_PROP_FPS) if fps 0: fps 25.0 model load_model(MODEL_PATH) zone_occupied_since None frame_count 0 while True: ret, frame cap.read() if not ret: break frame_count 1 frame draw_zone(frame, STATION_ZONE) vehicle detect_zone_vehicle(frame, model) now time.time() if vehicle is not None: x1, y1, x2, y2 vehicle[box] cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) cv2.putText(frame, vehicle[cls], (int(x1), int(y1) - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) if zone_occupied_since is None: zone_occupied_since now occupied_duration now - zone_occupied_since if occupied_duration HOLD_SECONDS: snapshot_name falert_{time.strftime(%Y%m%d_%H%M%S)}.jpg snapshot_path OUTPUT_DIR / snapshot_name cv2.imwrite(str(snapshot_path), frame) print(f[告警] 检测到车辆长时间占用公交站台已保存截图: {snapshot_path}) # 截屏后重新计时避免同一次停车连续重复告警 zone_occupied_since now else: zone_occupied_since None cv2.putText(frame, fframe: {frame_count}, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2) cv2.imshow(bus station monitor, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() if __name__ __main__: main()代码说明load_model函数加载 YOLOv8 预训练模型模型可以是官方默认权重也可以是你自己微调过的权重。detect_zone_vehicle是核心函数它遍历当前帧所有检测框判断是否有车辆中心点位于公交站台区域内。主循环中zone_occupied_since表示车辆第一次进入区域的时刻。只有当车辆持续在区域内超过HOLD_SECONDS秒时才保存告警截图。cv2.imshow会弹出预览窗口调试时很方便如果你在服务器上运行可以注释掉预览相关代码。5.2 运行命令把测试视频放到项目目录后执行python station_monitor.py如果一切正常会弹出视频预览窗口。当有车辆在公交站台区域内停留超过 10 秒终端会输出告警信息并在alerts目录生成截图。5.3 预期输出正常运行时终端输出类似[告警] 检测到车辆长时间占用公交站台已保存截图: alerts/alert_20250101_120000.jpg告警截图包含黄色矩形公交站台区域和红色矩形目标车辆检测框同时车辆类别会显示在检测框上方例如car。5.4 真实项目扩展方向以上是最小可用版本。实际项目中建议扩展以下几点将config.yaml中的参数读入脚本避免修改代码。接入实时视频流例如 RTSP 摄像头地址。告警消息推送到钉钉、企业微信或短信网关。把告警截图和车辆信息写入数据库便于后续查询统计。引入目标跟踪器准确区分“同一辆车停了很久”和“多辆车轮流短暂停车”两种完全不同的情况。6. 常见问题与排查6.1 问题排查表问题现象常见原因解决思路启动后没有检测框模型路径或视频路径错误检查文件路径先用独立脚本测试模型加载车辆没有进入区域公交站台区域坐标标注偏差逐帧查看画面调整区域坐标行人进入区域却误报置信度阈值过低或类别过滤不完整提高conf确认只保留车辆类别画面卡顿明显每帧调用predict推理耗时高跳帧检测、使用流式推理或换用更小模型停车时间判定不准未做多目标跟踪多辆车同时在场时逻辑混乱引入 ByteTrack 或改用model.track()夜间检测效果差光线暗模型对低光场景鲁棒性不足增加补光、使用图像增强、换用更大的模型告警重复触发截屏后计时器设计不合理加入冷却时间或维护“已告警”状态6.2 区域坐标不准怎么办区域坐标是影响判定准确率的关键。调试时可以先打开视频暂停在车辆真正停在站台内的那一帧用鼠标读取车辆所在位置的像素坐标再填到STATION_ZONE中。也可以在代码里临时把区域矩形放大一些跑一遍视频观察车辆轨迹逐步缩小区域范围。6.3 推理速度太慢怎么处理如果视频分辨率很高比如 4K 画面建议先缩放再送入模型或者设置imgsz640让 YOLOv8 内部自动缩放。还可以每隔一帧检测一次比如if frame_count % 2 0才执行检测减少一半推理开销。更高效的方式是使用model.predict(sourcevideo, streamTrue)或者把模型导出为 TensorRT、OpenVINO 格式部署在边缘设备上。7. 最佳实践与工程建议7.1 配置外置化不要把视频路径、区域坐标、时间阈值写死在代码里。推荐用config.yaml统一管理video_path: test_bus_station.mp4 model_path: yolov8n.pt station_zone: [1100, 520, 1720, 800] hold_seconds: 10 conf_threshold: 0.45 alert_cooldown_seconds: 60 output_dir: alerts camera_id: bus_station_01这样换摄像头、调阈值时只需要改配置文件不需要重新部署代码。7.2 告警去重与冷却机制当一辆车违停 5 分钟时如果每 10 秒触发一次告警那会产生大量重复消息。常见做法是维护一个已告警车辆 ID 集合或者设置告警冷却时间。比如触发一次告警后同一个区域在 60 秒内不再重复告警直到车辆离开后重新进入才恢复。7.3 完整证据链告警截图建议包含以下信息摄像头编号、时间戳、区域框、车辆检测框、车辆类别。如果是合规取证系统还需要保留原始视频片段保存违规时间段前后的录像方便人工复核。7.4 多目标跟踪简单版脚本只能判断“区域有没有车”不能判断“哪辆车停了多久”。如果站台内先后停了两辆车第一辆停 5 秒走了第二辆停 20 秒简单版逻辑在车辆切换瞬间可能会把计时清零导致漏报。解决方法是引入 ByteTrack 或 DeepSORT 跟踪器给每个车辆分配 ID记录每个 ID 的进入时间和离开时间。Ultralytics 也提供了model.track()可以在检测的同时完成跟踪比自行实现跟踪逻辑方便很多。7.5 合规与隐私边界智能监控系统一定要考虑合规性。本文提到的识别、截图、告警只能用于公共区域交通秩序治理等合法场景必须有合法授权不能用于偷拍、侵犯隐私等行为。部署监控点位前要确认相关法规和管理要求保存的视频和图片数据要控制访问权限设置保留期限不重复采集与业务无关的信息。涉及摄像头布局、数据留存策略时建议咨询法务或相关主管部门。7.6 模型与硬件选型如果摄像头角度固定、画面变化不大用yolov8n足够推理速度快。如果画面复杂、遮挡严重可以升级到yolov8s或yolov8m。边缘设备部署时优先把模型导出为 ONNX、TensorRT 或 OpenVINO 格式推理速度会明显提升。7.7 异常处理与日志监控进程需要长期运行建议加上日志记录比如每次告警记录一条日志包含时间、摄像头编号、车辆类别、截图路径。如果视频流断开要有重连机制。模型推理报错时要能捕获异常并继续处理下一帧避免进程崩溃。8. 总结本文围绕公交站台被网约车、出租车长时间占用的场景给出了一套从目标检测到区域判定、从持续计时到告警截图的完整实现。你可以先用离线视频把脚本跑通观察告警效果再逐步替换成实时视频流接入告警推送和数据库存储。真正难的不是模型调用而是业务规则和工程细节。区域坐标怎么标、时间阈值设多少、多辆车怎么区分、告警怎么去重这些都会影响系统实际效果。如果你也在做类似场景建议先拿一段真实监控视频从本文的最简版本开始调参再逐步加入跟踪和推送能力。后续还可以把方案扩展到消防通道占用、斑马线违停、应急车道占用等场景核心思路都是相通的。
返回列表