用Python+OpenCV+YOLOv5构建智能交通监控原型:从车道统计到超速预警

周末早晨被小区门口此起彼伏的喇叭声吵醒时,我突然意识到——为什么不用手头的树莓派和旧笔记本摄像头做个交通监控原型?这个想法最终催生了一个能自动统计车道车流、识别超速车辆的迷你系统。整个过程就像搭积木一样有趣:用YOLOv5识别车辆、DeepSort跟踪轨迹、OpenCV计算速度,最后通过蜂鸣器实现超速警报。下面分享这个项目的完整实现路径,所有代码都已开源。

1. 环境搭建与工具选型

工欲善其事,必先利其器。这套系统的核心是几个相互配合的技术组件:

# 基础环境配置(基于Python 3.8)
conda create -n traffic python=3.8
conda activate traffic
pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html
pip install opencv-python numpy pandas scipy

技术栈对比表

组件选型理由替代方案
YOLOv5实时性最佳(60FPS+)Faster R-CNN, SSD
DeepSort多目标跟踪精度高SORT, FairMOT
OpenCV成熟的视觉处理库PIL, scikit-image
PyQt5轻量级GUI开发Tkinter, Kivy

在硬件选择上,我测试了三种配置:

  • 树莓派4B+USB摄像头:勉强能跑3FPS,适合演示但实用性低
  • 旧游戏本+内置摄像头:达到15FPS,满足基本实时性
  • NVIDIA Jetson Nano:最佳性价比,轻松实现30FPS

提示:建议从本地视频文件测试开始,再过渡到实时摄像头输入,可以大幅降低调试难度。

2. 车辆检测与跟踪实战

YOLOv5的预训练模型虽然能检测车辆,但在实际道路场景中表现如何?我在小区门口采集了200张包含轿车、卡车、公交车的图片,用LabelImg进行标注后,对模型进行了微调。

# 训练自定义数据集(建议至少1000张图)
python train.py --img 640 --batch 16 --epochs 50 --data vehicle.yaml --weights yolov5s.pt

车辆跟踪的关键在于DeepSort的参数调优。经过多次实验,我发现这些参数组合效果最佳:

# deepsort配置示例
cfg = {
    "max_dist": 0.2,  # 特征距离阈值
    "min_confidence": 0.3,  # 检测置信度
    "nms_max_overlap": 0.5,  # NMS重叠率
    "max_iou_distance": 0.7,  # IoU距离
    "max_age": 30,  # 目标保留帧数
    "n_init": 3  # 初始确认帧数
}

常见跟踪失效场景及解决方案

  1. 车辆遮挡:通过kalman滤波器预测轨迹
  2. 光照变化:在HSV色彩空间计算直方图特征
  3. 尺度变化:使用多尺度检测(YOLOv5的Pyramid结构)
  4. ID切换:调整max_dist和max_age参数

3. 车道划分与流量统计

要实现分车道统计,首先需要在视频中定义虚拟检测线。我用OpenCV的透视变换将倾斜的道路图像转换为鸟瞰图:

def get_warp_matrix(img, src_points):
    dst_points = np.float32([[0, 0], [img.shape[1], 0], 
                            [img.shape[1], img.shape[0]], [0, img.shape[0]]])
    M = cv2.getPerspectiveTransform(src_points, dst_points)
    return M

# 在原始图像选取道路区域四个顶点
src = np.float32([[580, 460], [710, 460], [1100, 720], [200, 720]])
warp_matrix = get_warp_matrix(frame, src)

流量统计的核心逻辑是检测车辆与虚拟线的相交事件。我设计了一个高效的碰撞检测算法:

def check_crossing(track, line):
    x1, y1, x2, y2 = track.current_bbox
    prev_pos = track.prev_position
    curr_pos = ((x1+x2)/2, (y1+y2)/2)
    
    # 线段相交检测
    return intersect(prev_pos, curr_pos, line[0], line[1])

统计结果可视化方案对比

方案优点缺点
控制台打印实现简单不直观
CSV日志便于分析实时性差
PyQt5图表交互性强开发复杂
OpenCV叠加实时直观样式简单

最终我选择用OpenCV直接在视频上叠加动态柱状图,效果虽然朴素但信息传达非常直接。

4. 车辆测速与超速预警

测速需要先确定现实世界的距离与图像像素的映射关系。我在道路上放置了已知长度的参照物(2米长的木板),通过像素测量得到比例系数:

比例系数 = 实际长度(米) / 图像长度(像素)

速度计算的核心代码如下:

def calculate_speed(pixel_dist, fps, scale):
    # pixel_dist: 两帧间移动的像素距离
    # fps: 视频帧率
    # scale: 像素/米比例
    return (pixel_dist * fps) / scale  # 单位:米/秒

为了让超速警示更明显,我通过Python的winsound模块实现了声音报警,同时用GPIO控制LED闪烁:

def speed_alert(speed):
    if speed > SPEED_LIMIT:
        # Windows声音报警
        winsound.Beep(2000, 1000)  
        
        # 树莓派GPIO控制
        GPIO.output(LED_PIN, GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(LED_PIN, GPIO.LOW)

测速精度优化技巧

  • 使用移动平均滤波平滑速度曲线
  • 在道路平直区段进行测速(透视变换后)
  • 对同一车辆连续跟踪5帧以上再计算速度
  • 定期用GPS测速仪校准系统参数

5. 系统集成与性能优化

将各个模块组装成完整系统时,最大的挑战是如何保证实时性。通过性能分析发现,YOLOv5的检测阶段占用了70%以上的计算资源。我采用了这些优化手段:

多线程处理架构

from threading import Thread
from queue import Queue

class VideoStream:
    def __init__(self, src):
        self.stream = cv2.VideoCapture(src)
        self.stopped = False
        
    def start(self):
        Thread(target=self.update, args=()).start()
        return self
        
    def update(self):
        while True:
            if self.stopped:
                return
            self.grabbed, self.frame = self.stream.read()
            
    def read(self):
        return self.frame

关键性能指标对比

优化措施原FPS优化后FPS内存占用(MB)
原始版本8.2-1200
多线程12.7+55%1500
半精度推理15.3+20%900
图像降采样18.1+18%600
跟踪跳帧22.4+24%600

最终系统在Jetson Nano上达到了25FPS的处理速度,足够实时分析720P的视频流。整个项目最耗时的部分其实是数据收集和标注——2000张标注图片花了我整整三个周末。但看到系统准确识别出邻居家超速的快递车时,那种成就感绝对值得这些付出。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐