☰
公交车站违停自动检测:视频AI与车牌识别取证系统实战
2026/10/3 9:10:06 网站建设 项目流程

公共道路资源被违章车辆占用,尤其是公交车站这种“即停即走”区域,一直是城市交通治理的痛点。最近网传一段视频:网约车司机把车停在公交车站,和公交车驾驶员发生争执,全程情绪激动,最后被现场视频完整记录。这类事件多到不新鲜,真正值得关注的是背后的技术问题——如果每个公交站都靠驾驶员自己下车去理论,问题永远解决不完。这次我们来看一套可行的解法:基于视频 AI 和车牌识别的公交车站占用检测与取证系统,专门用于识别“非公交车辆违规停靠公交站”这一场景,并自动生成证据链。

这套项目思路的核心不是让公交车驾驶员当场和对方争执,而是通过摄像头 + 本地推理服务,实时识别进入公交站区域的车辆类型、停留时长、是否属于公交车,一旦满足“非公交车 + 停留超时”两个条件,就自动保存视频片段、抓拍关键帧、记录时间戳和车牌号,形成一个可追溯的取证包。整个过程不依赖人工盯着屏幕,也不需要驾驶员在冲突现场冒险沟通。

本文会按本地部署实操的路线展开:先说这套系统能做什么、硬件门槛大概在哪,再给你一套可以跑通的环境准备和启动流程,然后按“车辆检测 -> 区域判定 -> 停留计时 -> 车牌识别 -> 证据生成”的顺序做功能验证。最后我会单独讲清楚批量任务和 API 接口怎么接,以及实际部署中常见的坑——端口冲突、显存不足、车牌识别不准、视频卡顿这些都怎么排查。

如果你正在做智慧交通、安防摄像头、视频结构化、违规取证相关的项目,或者你只是想知道“公交站被占用到底能不能自动检测”,这篇文章可以直接收藏。

1. 核心能力速览

这套系统的完整名字可以叫“公交车站违章占用自动检测与视频取证系统”。不同团队的开源实现结构可能略有差异,但核心流程基本一致:视频流输入 -> 车辆检测 -> 车辆分类 -> 区域停留判断 -> 车牌识别 -> 证据保存。

能力项说明
项目类型视频结构化分析 + 违章检测 + 自动取证
主要功能公交站区域车辆检测、车型分类、停留超时判定、车牌识别、视频片段回传
输入形式RTSP 摄像头流、本地视频文件、图片批量检测
输出形式标注视频、抓拍图片、车牌文本、JSON 结构化记录
硬件门槛可 CPU 推理,但多路视频建议使用 NVIDIA GPU;显存占用以视频路数和模型大小为准
推荐模型目标检测模型(如 YOLO 系)+ 车牌 OCR 模型,具体以项目实际集成为准
启动方式命令行启动或 Docker Compose 启动,可单独启动检测服务与 Web 服务
是否支持 API支持,可提供 HTTP 接口提交视频/图片并获取检测结果
是否支持批量任务支持,可批量处理历史视频文件,也可对多路 RTSP 流做并行分析
适合场景公交站台监控、公交专用道治理、停车场违停取证、历史视频翻查

这里要说明一点:不同 GitHub 开源项目的检测器版本、接口路径和模型权重差异很大,下面所有代码示例给的是通用模板,路径和端口需要按你实际拉取的项目替换。

2. 适用场景与使用边界

这套系统最大的价值在于把“人盯监控”变成“算法盯监控”。具体能解决以下问题:

  • 公交站被网约车、出租车、私家车长时间占用时,系统自动记录占用起止时间,不需要公交驾驶员下车交涉。
  • 多路公交站同时监控时,人工看不过来,算法可以 7x24 小时不间断跑。
  • 事件发生后,平台能直接导出“抓拍图 + 视频片段 + 车牌号 + 时间戳”四件套,提供给管理方复核。
  • 对历史监控视频做批量回溯,可以统计某个公交站的高频占用时段和车牌,为现场执法和站点优化提供数据支持。

不过也要说清楚边界。纯视觉方案存在几个天然限制:

  • 摄像头视野固定,遮挡严重时检测不到车。
  • 夜间、逆光、雨天对目标检测和车牌识别都有影响,需要挑选合适的摄像头安装位置或加补光。
  • 系统只能证明“车停了”,不能直接证明“驾驶员在斗气”或“车内是否有人”,情绪冲突类细节还需要结合现场录音或人工复核。
  • 车辆分类(公交车/网约车/私家车)依赖模型训练数据,极端情况下可能把一类车误判成另一类。

合规使用方面要特别强调:凡是涉及道路监控、车牌、行人面部信息,必须在合法授权范围内使用,只能在授权的测试环境、自有测试素材或已获得拍摄许可的路口/站台部署。不能把系统用于未经同意的个人车辆跟踪、扒取车牌库、公共或私域流量下的任意监控。发布和商用前,需要确认符合当地关于公共区域视频采集和隐私保护的规定。人脸信息默认不进存储,车牌数据访问要有权限控制。

3. 环境准备与前置条件

在动手部署之前,把环境先梳理清楚。一套最小可运行的配置包括下面几部分。

3.1 操作系统与基础软件

  • 操作系统:Ubuntu 20.04 / 22.04 为最稳妥,Windows 也可以跑,但 Docker 和 CUDA 环境配置会麻烦一些。
  • Python 版本:3.8 到 3.10 之间,具体以项目 requirements.txt 为准。
  • Docker:如果采用镜像启动,需要安装 Docker 和 Docker Compose。

3.2 显卡与推理加速

如果只处理单路摄像头或少量历史视频,CPU 推理也可以,但速度会比较慢。对于多路 RTSP 流实时分析,推荐:

  • NVIDIA GPU,显存 6G 起步,具体占用取决于目标检测模型的分辨率和批量大小。
  • 需要安装 NVIDIA 驱动、CUDA toolkit、cuDNN。
  • 如果使用 Docker 启动,需要额外配置nvidia-container-toolkit。

3.3 摄像头或测试视频

准备阶段不需要真实现场摄像头,用两段测试视频就够:

  • 一段包含公交车停在公交站区域内的画面。
  • 一段包含社会车辆(网约车/私家车)在公交站临停或长停的画面。

如果手头没有现场视频,也可以先用公开的交通监控样例视频验证流程。

3.4 磁盘与目录规划

视频证据会占磁盘。规划目录时建议把输入、输出、模型分开放:

project/ ├── models/ # 检测模型与 OCR 模型权重 ├── inputs/ # 测试视频、历史视频 ├── outputs/ # 抓拍图、标注视频、JSON 结果 └── logs/ # 服务日志与任务日志

4. 安装部署与启动方式

下面给出两种启动方式:命令行启动和 Docker 启动。无论用哪种,先把项目代码拉下来,然后安装依赖。

4.1 克隆项目与安装依赖

# 以通用项目路径为例,实际仓库地址需要替换 git clone https://github.com/example/bus-station-monitor.git cd bus-station-monitor # 创建虚拟环境 python -m venv venv source venv/bin/activate # 安装依赖 pip install -r requirements.txt

依赖安装如果很慢,可以换国内 PyPI 镜像:

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

4.2 命令行启动检测服务

很多视频分析项目会提供一个 CLI 入口,作用是读取一个视频文件或 RTSP 流,输出检测结果。通用命令模板如下:

python detect.py \ --source ./inputs/test_car.mp4 \ --output ./outputs/ \ --model ./models/yolov8s_vehicle.pt \ --ocr-model ./models/plate_ocr.pth \ --zone-polygon "[(100,200),(500,200),(500,600),(100,600)]" \ --max-stay-seconds 60

参数含义:

参数作用
--source输入视频路径或 RTSP 地址
--output结果输出目录
--model车辆检测模型权重路径
--ocr-model车牌识别模型权重路径
--zone-polygon公交站区域多边形坐标,需要根据实际画面标定
--max-stay-seconds超过该秒数判定为长时间占用

这里面的zone-polygon是最关键的一个参数,它的坐标对应画面里公交站台的实际位置。你需要在画面上手动框一个区域,把公交站允许即停即走的范围圈出来。区域标定越准,误报越少。

4.3 Docker 启动整套服务

如果项目提供了docker-compose.yml,可以一条命令启动检测服务和 Web 管理端:

docker compose up -d

启动后访问 Web 管理端,默认端口可能是 8080 或 7860,具体看项目里的docker-compose.yml配置。如果页面打不开,先检查容器状态:

docker compose ps

日志查看方式:

docker compose logs -f

4.4 端口冲突处理

启动服务时如果出现端口被占用,先找占用进程:

sudo lsof -i :8080

然后换端口启动,或者直接修改 docker-compose 中的端口映射:

services: app: ports: - "8081:8080"

5. 功能测试与效果验证

部署启动之后,按下面顺序做功能测试。每项测试都给出测试目的、输入素材、操作步骤和判断标准,方便直接对照检查。

5.1 测试一:车辆检测是否正常

  • 测试目的:确认检测服务能识别画面里的车辆。
  • 输入素材:一段公交站区域有车辆经过的普通视频。
  • 操作步骤:运行检测命令,输出目录里生成带标注框的视频。
python detect.py \ --source ./inputs/station_sample01.mp4 \ --output ./outputs/test1/ \ --model ./models/yolov8s_vehicle.pt
  • 判断标准:输出视频中,车辆身上出现目标框,框的类别标签正确,公交车、小轿车、货车分类基本准确。
  • 常见失败:画面里车没框出来,优先调低检测置信度阈值,或者换更大的模型权重。

5.2 测试二:公交站区域判定与停留计时

  • 测试目的:确认“进入区域 -> 离开区域 -> 计算停留时长”这条链路能跑通。
  • 输入素材:一辆社会车辆停在公交站区域内约 90 秒后离开。
  • 操作步骤:在配置中设置--max-stay-seconds 60,跑完整段视频。
  • 预期结果:车辆进入区域后开始计时,超过 60 秒触发“占用”事件,输出 JSON 中包含:
{ "event_id": "evt_20250712_093001", "plate": "京A12345", "vehicle_type": "sedan", "enter_time": "2025-07-12 09:30:01", "leave_time": "2025-07-12 09:31:31", "stay_seconds": 90, "violation": true }
  • 判断标准:JSON 中stay_seconds与视频实际停车时长基本一致,误差在 1 到 2 秒内。
  • 常见失败:停留时间误差大,通常是目标跟踪丢失导致。需要调小跟踪算法的丢失帧阈值,或者多路摄像头画面之间做轨迹衔接。

5.3 测试三:公交车白名单豁免

  • 测试目的:确认公交车停在站点不会被误判为违章。
  • 输入素材:真实公交车进站上下客,停留约 40 秒后离开。
  • 操作步骤:保持相同配置,运行视频。
  • 预期结果:公交车在区域内停留不生成报警,或者生成一条“合法停靠”记录且不打违规标签。
  • 判断标准:vehicle_type识别为bus,事件中violation为false。
  • 常见失败:公交车被识别成普通客车,说明车型分类模型训练数据不足,需要补充样本微调。

5.4 测试四:车牌识别与夜间效果

  • 测试目的:确认车牌 OCR 在白天和夜间都能出结果。
  • 输入素材:白天车牌清晰的视频一段,以及夜间或逆光视频一段。
  • 操作步骤:单独跑车牌识别模块,用两张抓拍图片做输入。
  • 预期结果:白天车牌输出正确,夜间只要车牌区域有补光,也能输出大部分字符。
import requests # 单张图片车牌识别接口示例 url = "http://127.0.0.1:8000/api/v1/plate/recognize" files = {"image": open("./outputs/plate_shot.jpg", "rb")} response = requests.post(url, files=files, timeout=30) print(response.json())
  • 判断标准:字符识别准确率白天不低于 95%,夜间不低于 80%,具体数值以实际模型为准。
  • 常见失败:夜间识别不出来,优先检查摄像头红外补光,其次考虑对车牌区域做超分辨率或亮度增强预处理。

5.5 测试五:证据链完整性

  • 测试目的:确认一个完整的取证包是否包含抓拍图、视频片段、车牌、时间戳。
  • 输入素材:一次完整的违章占用事件。
  • 操作步骤:事件触发后查看输出目录。
  • 预期结果:
outputs/evt_20250712_093001/ ├── snapshot.jpg ├── clip.mp4 ├── plate.jpg ├── event.json └── raw_frames/
  • 判断标准:五个文件都在,event.json能正常解析,clip.mp4覆盖了进站到出站的完整过程,snapshot.jpg能看清车辆外观和车牌区域。
  • 常见失败:视频片段被截断,通常是存储写入速度跟不上推理速度,需要降低保存视频的分辨率或帧率。

6. 接口 API 与批量任务

服务跑通后,真正进入业务系统的是 API 和批量处理能力。这里分两块讲。

6.1 HTTP API 调用

检测服务通常提供两类接口:文件上传分析和 RTSP 实时分析。

文件上传接口通用调用模板:

import requests url = "http://127.0.0.1:8000/api/v1/video/analyze" payload = { "zone_polygon": [[100, 200], [500, 200], [500, 600], [100, 600]], "max_stay_seconds": 60, "save_evidence": True } with open("./inputs/test_car.mp4", "rb") as f: files = {"video": ("./inputs/test_car.mp4", f, "video/mp4")} response = requests.post(url, data=payload, files=files, timeout=300) print(response.json())

返回结果结构示例:

{ "task_id": "20250712_A001", "events": [ { "plate": "京A12345", "vehicle_type": "sedan", "stay_seconds": 90, "violation": true, "evidence_dir": "outputs/evt_20250712_093001/" } ], "processed_frames": 1200, "elapsed_seconds": 32.5 }

RTSP 流接入模板:

import requests url = "http://127.0.0.1:8000/api/v1/stream/start" payload = { "stream_url": "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101", "zone_polygon": [[100, 200], [500, 200], [500, 600], [100, 600]], "max_stay_seconds": 60 } response = requests.post(url, json=payload, timeout=30) print(response.json())

需要注意,RTSP 的 URL 里如果带密码,不要在生产环境明文传到日志里。建议通过环境变量或密钥管理服务注入。

6.2 批量处理历史视频

批量任务的思路很简单:把一批视频文件放到固定目录,通过脚本遍历调用检测服务,结果按视频名归档。

import os import glob import requests import json input_dir = "./inputs/history/" output_dir = "./outputs/history/" api_url = "http://127.0.0.1:8000/api/v1/video/analyze" video_list = glob.glob(os.path.join(input_dir, "*.mp4")) for video_path in video_list: video_name = os.path.basename(video_path).replace(".mp4", "") with open(video_path, "rb") as f: files = {"video": (video_path, f, "video/mp4")} try: resp = requests.post(api_url, files=files, timeout=600) result = resp.json() except Exception as e: print(f"{video_name} 处理失败: {e}") continue # 每个视频结果单独保存 out_path = os.path.join(output_dir, f"{video_name}.json") with open(out_path, "w", encoding="utf-8") as fp: json.dump(result, fp, ensure_ascii=False, indent=2) print(f"{video_name} 完成,耗时 {result.get('elapsed_seconds')} 秒")

批量处理要注意两点。第一,任务要加失败重试机制,单条视频解析失败不能中断整个任务队列。第二,输出要和输入一一对应,建议每条视频单独生成一个结果目录,不要把所有事件的图片视频混在一起。

6.3 批量任务队列设计

如果处理几十上百路视频,直接 for 循环不现实,建议用消息队列做任务分发。最简单的方案是 Redis + 简单 Worker:

任务生产者:扫描视频目录,把视频路径写入 Redis 队列 任务消费者:从 Redis 取任务,调用检测服务,写结果

伪代码:

import redis import json r = redis.Redis(host="127.0.0.1", port=6379, db=0) # 生产者 def push_task(video_path, zone_polygon): task = {"video_path": video_path, "zone_polygon": zone_polygon} r.lpush("analysis_tasks", json.dumps(task)) # 消费者 def pop_task(): task_json = r.rpop("analysis_tasks") if task_json: return json.loads(task_json) return None

当检测服务不稳定时,队列方案天然支持失败重试:任务执行失败后,重新放回队列末尾,并记录重试次数。

7. 资源占用与性能观察

这部分直接决定你能同时接几路摄像头。先说观察方法,再说影响因素。

7.1 怎么看资源占用

Linux 下用nvidia-smi看显存和 GPU 利用率:

nvidia-smi

实时刷新:

nvidia-smi -l 2

CPU 和内存占用:

top # 或者 htop

7.2 影响性能的主要因素

因素影响
输入分辨率分辨率越高,检测越慢。建议先用 1280x720 做目标检测,车牌区域单独用 ROI 放大
画面帧率公交站场景不需要每秒都检测,抽帧到 5 FPS 或 10 FPS 足够
检测模型大小YOLOv8n 和 YOLOv8x 推理耗时能差 5 到 10 倍
车牌 OCR 频率不需要每帧都做 OCR,只在车辆进站和出站两个时机各识别一次
视频路数每增加一路 RTSP 流,GPU 占用线性增加
证据保存策略同时保存 MP4 和逐帧 JPEG 会大量消耗磁盘 IO,按需选择

7.3 降低资源占用的实践

  • 检测帧率与视频保存帧率分开。检测可以用 5 FPS,保存的证据片段用 25 FPS。
  • 车辆未进入公交站区域时,不启动车牌识别模块。
  • 如果 CPU 推理,优先选择量化模型,例如 TensorRT 或 ONNX 的 FP16 版本。
  • 多路摄像头共用同一个目标检测服务,不要每路都单独启动一个模型实例。

7.4 进程残留与端口占用

检测服务异常退出后,进程可能还占着显存或端口。排查方式:

ps aux | grep python kill -9 <pid>

如果是在 Docker 里,容器退出后 GPU 显存没释放,重启容器前先确认没有残留容器:

docker ps -a

8. 常见问题与排查方法

问题现象可能原因排查方式解决方案
依赖安装失败Python 版本不匹配或缺少编译工具查看错误日志,确认 PIL/numpy/onnxruntime 等是否编译失败创建 Python 3.9 虚拟环境重新安装;使用国内镜像源
模型文件缺失权重未下载或路径配置错误检查 models 目录和启动参数按 README 下载对应权重,或调整--model路径
CUDA 不可用驱动版本过低,或 PyTorch 的 CUDA 版本不匹配python -c "import torch; print(torch.cuda.is_available())"安装匹配的 CUDA 版本,或回退 CPU 推理
显存不足检测分辨率太大,批量值过高nvidia-smi查看显存占用降低输入分辨率、批量值设为 1、压缩模型
启动后页面打不开端口被占用或服务未启动检查服务日志和端口状态更换端口或重启服务
API 调用超时单段视频推理时间过长检查视频时长和分辨率加大请求超时时间,或改用异步任务接口
批量任务卡住单条视频损坏或模型推理死锁查看任务日志定位卡住的任务增加任务级超时和失败跳过
输出视频没有标注框检测置信度阈值过高查看结果 JSON 是否为空调低置信度阈值
车牌识别结果乱码图像中有遮挡或字体模糊查看抓拍图车牌区域提高抓拍分辨率,调整摄像头角度
公交车被误判违章车型分类模型不准查看事件 JSON 的 vehicle_type补充公交车样本,重新微调分类模型

9. 最佳实践与使用建议

从项目能被真正用到生产环境的角度,下面这些建议很关键。

9.1 第一次运行先小参数测试

不要上来就接 10 路摄像头。先用 1 路视频流、低分辨率、单模型跑通整个链路,确认事件能产生、证据能落盘、接口能返回结果,再逐步加路数。

9.2 保留一套最小可运行配置

把一组合适的参数固化下来,比如区域多边形坐标、置信度阈值、停留时长阈值、抽帧间隔,写进一个配置文件。这样遇到问题可以快速复位。

9.3 区域标定是误报率的关键

公交站区域画太大,路边临时停的车会被误报;画太小,真正停在站台里的车可能不在区域内。标定之后用 30 分钟以上的不同时段视频做回放验证。

9.4 模型、素材、结果分目录管理

模型的权重文件、输入素材、输出证据不能混在同一个目录。建议按本文第 3 节的目录结构,长期运行下来找问题会快很多。

9.5 批量任务要加日志和失败重试

跑历史视频批量回溯时,没有日志等于白跑。每条视频处理结束后至少输出一行结果摘要,包含视频名、耗时、事件数、是否成功。

2025-07-12 10:33:01 [INFO] station_001.mp4 完成,耗时 45.2s,事件 2 条 2025-07-12 10:33:47 [ERROR] station_002.mp4 失败,OCR 服务超时

9.6 接口服务要限制访问范围

API 服务不要直接暴露在公网。如果必须提供外部访问,至少加一层 Token 或 API Key 校验,并限制来源 IP。车牌和视频证据信息敏感,建议走内网访问,日志里不要明文打印车牌之外的隐私字段。

9.7 涉及人脸、车牌、版权素材时必须确认授权

视频巡检测试永远使用自有素材或已授权的测试数据,不要拿着系统去扫公共摄像头、抓取他人车辆信息或采集未授权的人脸数据。发布和商用之前,确认系统符合相关隐私保护要求,并向管理方报备部署点位和采集范围。

10. 总结与下一步

回到最初那个网约车司机和公交车驾驶员争论的案例。单次争执解决不了公交站被反复占用的问题,真正有效的方案是把“人工争论”变成“自动记录”。这套基于视频检测和车牌识别的公交站占用取证系统,最值得尝试的点在于:只要把区域画好、停留时长阈值设好,它能自动完成从车辆进站到证据打包的全过程,一整天不需要人盯。

最先应该验证的功能是第 5.2 节里的“区域判定 + 停留计时”。这一步通没通,直接决定后续证据输出是否可信。最容易踩的坑有两个:一是区域多边形标得不准,二是停留计时误差太大。前者用真实画面反复调坐标,后者从跟踪丢失上找原因。

后续可以扩展的方向很明确:多路摄像头跨镜跟踪、夜间低照度增强、占用时段统计分析、公交站违停高发时段预测、与现有交通管理平台对接。如果再把检测模型换成支持更多车型分类的版本,还能覆盖更多违章场景。

这个项目的路线,基本就是当前智慧交通里“AI 巡检 + 自动取证”的标准套路。拿一段 10 分钟的历史视频先跑一遍,你就能直观感受到这套系统的价值。建议收藏备用,需要做公交站占用检测或类似违停取证项目时,按这篇文章的流程走一遍就行。

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

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

立即咨询