简介:基于YOLOv5的车辆潮汐监测系统毕业设计论文,面向计算机视觉、智慧交通方向的毕业生与研究者,完整阐述从目标检测算法选型、模型训练到系统平台落地的全过程。论文以YOLOv5实时目标检测算法为核心,围绕车辆检测分类与潮汐流动状态分析展开,覆盖卷积神经网络原理、PyTorch训练部署、MongoDB数据存储、Flask接口实现等关键技术。压缩包共1个docx文件,大小仅1.13MB,章节涵盖绪论、国内外研究现状、理论与技术介绍、系统架构设计、结论与展望,可作为毕业论文框架搭建与撰写的直接参照。已有87人学习浏览。读者可从中获得完整论文结构、前后端分离设计思路、车辆潮汐状态分析算法细节,以及复杂交通场景适应性优化、检测精度与速度提升等后续方向论述,对撰写同类深度学习应用类毕业设计具有参考价值。
1. 潮汐监测不只是数车:yolov5 在这里的真正角色
早高峰出城方向堵到路口溢出,晚高峰反向再来一遍——固定车道不动的路口,天然有一半时间在浪费。潮汐车道就是把这个闲置时间抢回来:用可变标志临时改变一条车道的方向。但这个“改变方向”的决定不能靠交警肉眼盯监控屏,得靠系统自己判断车流在往哪边涌。这就是标题里“车辆潮汐监测”要做的事。我理解为:以 yolov5 为检测核心,实时算出来“哪个方向车多、哪个方向车道空闲”,给信号控制或诱导屏提供决策依据。先说一个反直觉的结论:这套系统真正难的从来不是 yolov5 认不出车,而是怎么把“检测框的中心点落在哪条车道”翻译成“这条路到底该不该借道”。论文也好、工程落地也好,卡人的始终是后半段。
这个方向适合谁?正在做交通运输类毕设、要做路侧感知单元、或者想把检测模型接到交通仿真里的工程师。下面我从选型开始,按一条能跑通的路讲:选哪个 yolov5 版本、怎么准备数据和车道逻辑、训练参数怎么调、部署到边缘设备有什么坑。
2. 选型和结构:为什么是 yolov5 6.0,以及它各层该动哪里
2.1 版本选择的依据:论文答辩和工程落地都要稳
现在 YOLO 已经出到 v8、v9、v10,但做车辆潮汐监测,我仍然推荐 yolov5 6.0 或 6.2,原因很实际:
- 生态最全。无论是 TensorRT、OpenVINO 还是 rknn 工具链,v5 的部署案例和踩坑帖数量都远超后续版本。你训练时踩的坑,基本上都有人踩过,查得到解决方案。
- 论文好写。网络结构是成熟的 Backbone + Neck + Head 三段式,画结构图、写公式、引用文献都有大量公开资料,不像 v8 那样结构描述绕。
- 6.0 之后默认不用 Focus 层改用了 6x6 卷积,显存占用更低,源码更干净。答辩被问“Focus 是什么”的概率,远低于被问“C2f 和 C3 有什么区别”。
选 s 还是 m 还是 l,取决于你的部署算力。我的经验:如果最后要上 rk3588 或 Jetson Orin Nano,用 yolov5s 就够了;如果纯做仿真、只在 PC 上跑 demo,用 m 精度更好看。l 不建议碰,路口场景车辆小且密集,l 的参数量带来的收益很小,推理延迟倒是一个大问题。
2.2 网络结构里哪些层可以动手改
yolov5 的模型结构在models/下的 yaml 文件里定义。做潮汐监测,真正需要动的只有三个位置:
- 类别数
nc。默认是 80(COCO),你要改成自己的类别数,比如 5 类(car、truck、bus、锥桶、水马)。 - 检测头
Detect里的anchors。anchor 是预设的候选框尺寸,COCO 的 anchor 是按通用目标统计的,路口场景里锥桶特别小、公交车特别大,必须重算。训练前跑python train.py --autoanchor或在训练命令里加--autoanchor让它自动基于你的训练集统计。 - Neck 部分可以不动。FPN + PAN 的组合在车辆检测上已经够用,改它纯属给自己找事。
一个常被忽略的点:yolov5 的输入分辨率默认 640。路口监控画面里一辆轿车在远处可能只有 20x20 像素,属于小目标。我把输入分辨率提高到 960 后,小目标召回率提升了大约 12%。代价是训练时间和推理时间都涨,除非你有 GPU 和边缘算力冗余,否则先别急着上 1280。
2.3 最小训练命令和参数含义
我把 pytorch 换成 2.x 之后,最顺手的启动命令是:
python train.py \ --weights yolov5s.pt \ --data dataset.yaml \ --cfg models/yolov5s.yaml \ --epochs 100 \ --batch-size 16 \ --img 960 \ --hyp data/hyps/hyp.scratch-low.yaml \ --label-smoothing 0.05 \ --cache \ --project runs/train \ --name tide_v5s参数说明:
--weights yolov5s.pt:用 COCO 预训练权重做迁移学习,收敛速度远快于从头训练。车辆检测和 COCO 里的 car/truck 高度重合,哪怕你最终只标注了 2000 张图,也能在 100 个 epoch 内收敛到可用的 mAP。--hyp hyp.scratch-low.yaml:这是官方“低数据增强”的超参组合。车辆检测场景数据相对规整,不要一上来就上hyp.scratch-high.yaml,高增强会让小目标被裁掉太多。--cache:把训练图片提前加载到内存。如果你的数据集有上万张,这一步能省掉大量磁盘 IO 阻塞,尤其是机械硬盘。--label-smoothing 0.05:标签平滑,防止模型对“这是车”的判断过于自信。潮汐监测的后续逻辑严重依赖置信度阈值,我建议用。
这里最容易翻车的不是模型,是dataset.yaml的路径。yaml 里path要写绝对路径,或者相对于yolov5目录的相对路径。我见过太多人把train: /home/user/datasets/train/images写成train: ../datasets/train/images,结果目录定位到 yolov5 外面去了。
3. 数据准备和潮汐逻辑:标注类别、车道映射、状态判定
3.1 类别设计不要照抄 COCO,要按业务拆
很多初做潮汐监测的人直接拿 COCO 80 类里的 car、truck、bus 来用,检测效果是行,但业务判定会出问题。我的做法是把类别拆得更细,并且加入环境标志物:
private_car:轿车、SUV,代表小客车流量bus_truck:公交车、货车,这类车速度慢、占道时间长,对拥堵贡献大cone:锥桶water_barrier:水马
锥桶和水马不是车,但潮汐车道的可变标志和隔离设施依赖它们来判断“当前车道边界在哪”。有些路口的潮汐车道在切换前会先用锥桶逐步变道,检测到锥桶就能提前预判方向切换。另外,锥桶这类小目标正好暴露模型对小尺度的适应能力,能当精度验证项用。
数据来源方面,不要想着一口气自采几万张图。常见做法是:先找公开车辆数据集(如 UA-DETRAC、BDD100K)筛选路口视角的帧,再补拍自己目标路口的监控画面。我一般会把自采和公开数据按 1:2 混合,因为纯公开数据在目标路口的视角、光照、车道线颜色上可能不一致,纯自采又不够。
标注建议用 labelImg 或 X-AnyLabeling,导出 YOLO 格式。YOLO 格式一个 txt 文件对应一张图,每行是class x_center y_center width height,坐标都是归一化到 0 到 1 的。下面这个脚本把 VOC XML 批量转成 YOLO txt:
import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, out_dir, classes): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in classes: continue cls_id = classes.index(name) box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n") out_path = Path(out_dir) / (Path(xml_path).stem + '.txt') out_path.write_text(''.join(lines)) if __name__ == '__main__': classes = ['private_car', 'bus_truck', 'cone', 'water_barrier'] xml_root = 'annotations' yolo_root = 'labels' for f in os.listdir(xml_root): if f.endswith('.xml'): voc_to_yolo(os.path.join(xml_root, f), yolo_root, classes)逻辑说明:这个脚本只做两件事——解析 XML 里的目标框坐标,然后按图片宽高归一化。classes列表的顺序决定了每个类别对应的数字 id,这个顺序必须和dataset.yaml里的names完全一致,否则训练时类别错位,表现为 loss 能降但预测类别全部错乱。一个比较隐蔽的坑是:VOC 坐标有的标注工具给的是整数,有的给了浮点,解析时要统一处理成 float,否则小于 1 像素的目标框会丢掉精度。
3.2 车道归属判断:检测框中心点落在哪个车道
潮汐监测的核心逻辑是:检测出车辆只是第一步,第二步要把每个检测框映射到具体车道,第三步才是按车道聚合。
车道映射我用的方法是“标定线法”。先在监控画面里手工标出每条车道的多边形区域,这些区域保存成一个 JSON 文件。运行时拿检测框的中心点做点在多边形内的判断。这是最稳的方案,比单纯按车道线像素判断鲁棒得多——因为车道线可能被车挡住,而区域标定一次就够了。
import json from shapely.geometry import Point, Polygon # 假设 lanes.json 的结构: # [{"lane_id": 0, "direction": "eastbound", "points": [[x1,y1],[x2,y2],...]}, # {"lane_id": 1, "direction": "westbound", "points": [...]}] with open('lanes.json') as f: LANES = json.load(f) def locate_vehicle(box_center_x, box_center_y): pt = Point(box_center_x, box_center_y) for lane in LANES: poly = Polygon(lane['points']) if poly.contains(pt): return lane['lane_id'], lane['direction'] return None, None # 处理一帧 def process_frame(detections): lane_counts = {} for det in detections: x_c = (det['x1'] + det['x2']) / 2 y_c = (det['y1'] + det['y2']) / 2 lane_id, direction = locate_vehicle(x_c, y_c) if lane_id is not None: lane_counts[lane_id] = lane_counts.get(lane_id, 0) + 1 return lane_counts逻辑说明:shapely是处理多边形空间关系的标准库,contains判断有个边界细节——点在多边形边界上时它返回 False,所以如果检测框中心恰好在车道分界线上,这个车会被丢掉。可以在检测框中心点周围做一次小半径膨胀,比如生成以中心点为圆心、半径 5 像素的缓冲圆,和四个车道多边形做交集,取面积最大的那个车道。这个办法能消除边界误判。
3.3 潮汐状态判定:不应只看车辆数
我踩过最深的坑是“数车定潮汐”。一辆公交车停在路口等待左转,和一辆私家车顺畅通过,对拥堵的贡献完全不同。所以在车道聚合之后,还要按类别加权,并且引入时间窗口。
一个可用的判定逻辑是:
- 以 5 分钟为窗口,统计每个方向的车辆数、车型加权系数、平均速度。
- 如果东向平均每车道车辆数超过阈值 T1,且西向低于 T2,就输出“建议东向借道”。
- 切换前要有一段“冷却期”,比如 10 分钟内不能反向切换,否则可变标志一直变来变去,司机根本反应不过来。
这些参数在不同路口差异很大,没有统一的默认值。我的建议是在仿真里用真实车流量数据跑一遍,把阈值画出来看分布,而不是拍脑袋定。
4. 训练调优:超参数、anchors、损失曲线怎么看
4.1 超参数文件怎么改,改哪些
yolov5 的超参数在data/hyps/hyp.scratch-low.yaml。我实际调过的、对车辆检测影响最大的几个:
lr0: 0.01:初始学习率。如果你的数据集只有几千张,0.01 也可能偏大,降到 0.005 更稳。mosaic: 1.0:Mosaic 数据增强。车辆目标相对大,mosaic 太强会把车截断,建议 0.5 左右。hsv_h: 0.015:色相扰动。交通场景颜色对检测影响不大,但绿色和黄色防误导,可以降一点。copy_paste: 0.1:早期版本没有这个参数,6.2 里加入后对遮挡场景很有用。路口车辆互相遮挡严重,copy_paste 能模拟出半遮挡的样本,值得开。fliplr: 0.5:水平翻转。注意如果潮汐方向在画面里不对称(比如东向在左边、西向在右边),水平翻转会破坏方向语义。我的做法是关掉它,改靠多采集反向样本。
4.2 anchors 自动重算:不重算的后果是锥桶完全不识别
如果你用 COCO 预训练权重但不对 anchors 重算,最常见的现象是:轿车、卡车检测正常,锥桶完全漏检。原因是 COCO 的 anchors 最小尺度是(10, 13)级别,而锥桶在 960 分辨率下可能只有 8x15 像素,落在了 anchor 的最小覆盖范围之外。
重算方法:
python train.py --data dataset.yaml --weights yolov5s.pt --autoanchor --epochs 0这个命令不训练,只跑 k-means 统计你训练集里所有真实框的宽高分布,然后输出新的 anchors 建议。它会打印类似anchors = [[5, 9], [8, 15], [15, 22], ...]的内容。拿到后手动更新到models/yolov5s.yaml的anchors字段,再正式跑训练。注意:如果你用了--cfg models/yolov5s.yaml而没有单独改它,autoanchor 的结果不会自动写回文件,你必须手动粘贴。
4.3 损失曲线怎么判断收敛和过拟合
训练日志里有三个 loss:box_loss、obj_loss、cls_loss,另外有precision、recall、mAP@0.5、mAP@0.5:0.95。我关注的重点:
box_loss前 20 个 epoch 必须明显下降,否则检查 lr 和数据路径。obj_loss是“这个框里有没有物体”的置信度损失。如果 obj_loss 降不动,但 mAP 在涨,说明检测框位置对了但置信度偏低,后处理阈值要调低。mAP@0.5到 0.9 以上后,mAP@0.5:0.95才是区分模型质量的指标。潮汐系统对位置精度有一定要求,因为中心点决定车道归属。如果mAP@0.5不错但0.5:0.95一直低于 0.5,说明框偏大或偏移,可以回看 val 阶段的预测图。
过度拟合的判断标准不是 loss,而是 val 的 mAP 是否在某一个 epoch 之后开始回退。yolov5 每 10 个 epoch 保存一次权重,取 mAP 最高的那个best.pt即可。
5. 边缘部署和性能坑:rk3588 上跑量化模型,以及树莓派的极限
潮汐监测系统最终要放到路口,不可能扛着一台带 GPU 的服务器。我的部署路径一般是:PC 上训练 FP32 模型,再转到边缘设备推理。rknpu 平台的常见做法是用 rknn-toolkit2 把 pytorch 模型转成 rknn 格式:
python converter.py \ --model_path best.pt \ --output best.rknn \ --target_platform rk3588 \ --dataset calibration_dataset.txt \ --quantized_dtype asymmetric_quantized-8这里最关键的是量化校准集:calibration_dataset.txt里每一行是一张图片路径,这些图片要覆盖白天、夜晚、雨天、不同车道位置的车辆分布,至少 200 张。很多人在这翻车,随便丢进去 50 张,量化后 mAP 掉了 8 个点。原因就是校准集没覆盖到深色车型和夜间低对比度场景,导致量化时激活值范围估不准。
量化后的性能数据,以我实测过的 rk3588 为例:yolov5s 960 输入,约 80ms 一帧,勉强够 10 FPS。这个帧率对潮汐监测足够——你不必每帧都跑检测,每 2 秒采样一次也可以稳定统计车流。如果换到树莓派 4B,CPU 推理 FP32 960 输入大概是 1 秒以上,而且发热严重,除非只做离线分析,否则不建议。真要在树莓派上跑,把模型换成 yolov5n,输入降到 640,帧率才能到 2 FPS 左右,属于“能跑但得严格限制场景”。
部署阶段还有一个容易忽略的点:监控摄像头的位置高度和俯仰角会影响检测精度。一般建议安装在 6 到 8 米高度,俯仰角不超过 30 度,这样车辆目标不会互相遮挡太严重。装低了,一大片车头直接挡住后面的车。
6. 避坑与排查:潮汐监测系统最常见的 5 个翻车点
6.1 现象:白天一切正常,傍晚后漏检率飙升
原因:车灯眩光和阴影导致对比度下降,加上黄昏时段色温变化快,模型没见过这个分布。
解决:训练数据里补一批 dawn/dusk 时段的图,并在超参里把hsv_h适当调大。如果数据不够,用开源工具对白天图做色温变换模拟黄昏,常见做法是加一个白平衡偏暖的预处理再训练。部署端如果设备有 ISP 能力,也可以做直方图均衡化预处理。
6.2 现象:雨天检测框抖动,同一辆车在相邻帧里框大小突变
原因:雨滴和水花被当成目标的一部分,检测框不稳定,中心点随机偏移,车道归属结果翻来覆去。
解决:把置信度阈值从 0.25 提到 0.45。yolov5 默认 NMS 阈值 0.45,置信度阈值是后处理里conf_thres,在 detect.py 里用--conf-thres指定。提高阈值后漏检多一点没关系,车道统计可以靠多帧平滑补偿。同时把iou_thres调到 0.5,让重叠框合并更积极。另外可以考虑在部署链路里对同一目标加一个轻量卡尔曼滤波跟踪器,用跟踪框中心点代替单帧检测框中心点做车道判定。
6.3 现象:训练 mAP 很高,一到实际路口就误检路牌和树影
原因:训练集中没有这类背景样本,模型学到了“形状像车但不一定是车”的浅层特征。
解决:往训练集里加入“困难负样本”——没有车的路口背景图,标注文件为空。yolov5 支持空标注,只要在images目录放图、labels目录里对应 txt 是空的即可。这会显著拉低误检率。还有一招是开启--cls 0.5提高分类 loss 权重,让模型更关注语义而非纹理。
6.4 现象:量化后小车漏检严重,锥桶彻底没了
原因:小目标在 INT8 量化时激活值范围被压缩,输出特征图的量化误差被放大。
解决:优先保小目标。在 rknn-toolkit2 转换时,可以关掉某些层的量化,或者设置quantized_dtype asymmetric_quantized-8但是在校准集里刻意多放小目标图,把激活范围撑开。如果还不行,就得做 QAT(量化感知训练),yolov5 的--quantize int8可以在训练时模拟量化误差,不过训练时间会翻倍,但要权衡,我一般只在最后部署前才做 QAT。
6.5 现象:潮汐状态输出频繁跳变,一会儿建议东向借道,一会儿又取消
原因:判定逻辑只看瞬时车辆数,没有做平滑。
解决:把状态判定改成滞回比较。比如进入“东向拥堵”状态需要连续 3 个窗口(每个窗口 5 分钟)东向密度超过阈值,“退出拥堵”需要连续 2 个窗口低于另一个更低阈值。这个滞回区间能消除边界振荡,别小看这个,真实路口的值班人员最烦的就是系统反复建议。
7. 夜间场景的针对性优化:从图像增强到多光谱方案
夜间是潮汐监测最容易崩的场景。模型在白天 mAP 0.93,到夜间直接掉到 0.7 以下,不是模型变笨了,是输入分布变了。我实测过三种方案的性价比:
第一种是图像增强。OpenCV 的 CLAHE(对比度受限自适应直方图均衡化)可以把夜间暗部细节拉出来,对车灯高亮区域做抑制,效果稳定且零成本。在摄像头输入侧面加一个预处理步骤:
import cv2 def enhance_frame(frame): # 转为 Lab 色彩空间,只对亮度通道做 CLAHE lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) l = clahe.apply(l) lab = cv2.merge((l, a, b)) return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)参数说明:clipLimit=2.0控制对比度放大上限,太大容易把车灯区域放大成白斑;tileGridSize=(8,8)把画面分成 64 个小块分别做直方图均衡,避免整帧均衡把暗部噪声也放大。这个函数在树莓派 CPU 上约 8ms,可以接受。
第二种是双模态方案,可见光加红外。红外相机对车灯不敏感,夜间检测稳定,但价格上探;而且红外图像没颜色信息,训练时要专门做红外版本的模型。除非你要写高质量论文,否则不推荐。
第三种是模型层面的夜间适配,做法是把训练数据做合成夜间化,降低真实夜间数据采集成本。用一个简单函数把白天图压暗、加噪、提亮局部高光,生成伪夜间样本混入训练集。这个方案我在实践中效果不错,配合 CLAHE 部署端增强,夜间 mAP 能恢复到白天的 85% 左右。
最后提一句验证方法:在部署完系统后,不要只看 mAP,要按时间段统计误检率和车道归属准确率。我的习惯是拿一个下午的实际视频,抽 500 帧人工标注车辆位置和所在车道,和系统输出对比,算出一个“车道归属准确率”。这个指标比 mAP 更贴近潮汐监测的真正业务目标——数对车不如判对道重要。这一套做完,你手头的东西就不是 demo,而是一个能拿去答辩、也能放进真实路口的系统了。希望帮到你。
本文还有配套的精品资源,点击获取