简介基于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。默认是 80COCO你要改成自己的类别数比如 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 txtimport 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.0Mosaic 数据增强。车辆目标相对大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 损失曲线怎么判断收敛和过拟合训练日志里有三个 lossbox_loss、obj_loss、cls_loss另外有precision、recall、mAP0.5、mAP0.5:0.95。我关注的重点box_loss前 20 个 epoch 必须明显下降否则检查 lr 和数据路径。obj_loss是“这个框里有没有物体”的置信度损失。如果 obj_loss 降不动但 mAP 在涨说明检测框位置对了但置信度偏低后处理阈值要调低。mAP0.5到 0.9 以上后mAP0.5:0.95才是区分模型质量的指标。潮汐系统对位置精度有一定要求因为中心点决定车道归属。如果mAP0.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 秒采样一次也可以稳定统计车流。如果换到树莓派 4BCPU 推理 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(clipLimit2.0, tileGridSize(8, 8)) l clahe.apply(l) lab cv2.merge((l, a, b)) return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)参数说明clipLimit2.0控制对比度放大上限太大容易把车灯区域放大成白斑tileGridSize(8,8)把画面分成 64 个小块分别做直方图均衡避免整帧均衡把暗部噪声也放大。这个函数在树莓派 CPU 上约 8ms可以接受。第二种是双模态方案可见光加红外。红外相机对车灯不敏感夜间检测稳定但价格上探而且红外图像没颜色信息训练时要专门做红外版本的模型。除非你要写高质量论文否则不推荐。第三种是模型层面的夜间适配做法是把训练数据做合成夜间化降低真实夜间数据采集成本。用一个简单函数把白天图压暗、加噪、提亮局部高光生成伪夜间样本混入训练集。这个方案我在实践中效果不错配合 CLAHE 部署端增强夜间 mAP 能恢复到白天的 85% 左右。最后提一句验证方法在部署完系统后不要只看 mAP要按时间段统计误检率和车道归属准确率。我的习惯是拿一个下午的实际视频抽 500 帧人工标注车辆位置和所在车道和系统输出对比算出一个“车道归属准确率”。这个指标比 mAP 更贴近潮汐监测的真正业务目标——数对车不如判对道重要。这一套做完你手头的东西就不是 demo而是一个能拿去答辩、也能放进真实路口的系统了。希望帮到你。本文还有配套的精品资源点击获取