基于YOLO的车辆目标检测MATLAB仿真实现与调参指南
2026/9/14 13:27:07 网站建设 项目流程

简介:面向高校本硕博及科研人员的基于YOLO网络的行驶车辆目标检测Matlab仿真方案,可用于车辆检测算法编程学习、课题复现与教学演示。资源完整覆盖数据准备、网络构建、模型训练和检测输出等关键环节,包含338张车辆图像样本、4个Matlab脚本、4个mat数据文件及说明文档,运行主程序Runme_.m即可观察检测效果,结合操作录像能直观理解YOLO网络结构、特征提取与边界框回归过程。压缩包共349个文件,约213.79MB,其中mp4和avi双版本操作视频提供了从环境配置到调试排错的完整演示,可避免初学者在路径设置、函数调用等环节反复踩坑。目前已有1881人学习使用,资源目录结构清晰,既适合刚接触YOLO的本科生快速上手,也方便研究生在高阶实验中直接扩展,是一份实用性与教学性兼备的车辆目标检测素材。

1. 一份 YOLO 车辆检测的 MATLAB 工程,先别急着双击 Runme_.m

我拿到过不少标着"基于 YOLO 的行驶车辆目标检测 MATLAB 仿真"的压缩包,里面通常是一段Runme_.m、一个操作录像0023.avi,以及image006.jpgimage041.jpg一类的样本帧。多数人第一反应是双击主程序看效果,但真正值得花时间的反而是后两个文件:操作录像能告诉你工程路径依赖,样本帧决定了 anchor 和输入尺寸该怎么配。这篇文章把 YOLO 目标检测流程从原理、数据准备、训练参数到运行排错拆开讲,适合需要交课程设计、复现论文,或想在纯 MATLAB 环境里把车辆检测跑通并改成自己数据的读者。文中代码以 MATLAB R2021a 为基准,低版本需要先确认工具箱函数是否支持。

2. YOLO 目标检测核心:网格、anchor 与损失函数在车辆场景下的取舍

YOLO 从第一代到现在已经跨了很多版本,但车辆检测这类中尺度目标任务里真正要理解的不是网络层数,而是三个机制:网格回归、锚点框和损失函数。这三个点直接决定你后面改参数时是瞎调还是有的放矢。

2.1 一次回归的网格机制

YOLO 把输入图划分成 S x S 的网格,每个网格负责预测中心落在该网格内的目标。和 Faster R-CNN 式的两阶段检测不同,YOLO 用一次前向传播直接输出边界框坐标、置信度和类别概率,所以"目标检测流程"被压缩成了一个回归问题。

以车辆为例,假设输入图是 416x416,网格数是 13x13,那么每个网格的感受野覆盖约 32x32 像素。一辆在画面里占 120x90 像素的轿车,中心点落在某个网格内,该网格需要预测出(x, y, w, h)四个量,其中 x、y 是相对网格左上角的偏移,w、h 是相对整张图宽高的比例。这里有个容易忽略的点:网格只能决定"谁来负责这个目标",而目标大小是否合适,要看 anchor 与真实框的 IoU。

2.2 anchor 先验框选多大,取决于车辆在图像中的占比

anchor 是 YOLO 给每个网格预设的一组宽高模板。对行驶车辆这种水平方向占主体、比例集中在 0.8:1 到 2.5:1 的目标,先验框选得像正方形会让训练初期损失很高。

我一般会先统计数据集里所有真实框的宽高分布,然后用 k-means 在归一化宽高上聚出 6 个中心作为 anchor。MATLAB 的训练接口允许直接传 anchor 矩阵,例如:

% 输入尺寸 416x416,anchor 为宽高像素值 anchorBoxes = [ 20 32; 45 60; 80 90; 120 150; 180 220; 280 300];

每行对应一个先验框,宽高单位是像素,顺序没有硬性要求。车辆目标在画面边缘或远处时,小 anchor 会优先负责小目标;近处的大车由大 anchor 负责。如果训练样本里目标普遍在 100x100 以上,却给了一堆 20x32 的 anchor,网络训练时大部分负样本会集中在小 anchor 上,导致召回率偏高但定位精度下降。

2.3 损失函数拆开看

YOLO 损失函数分为位置、置信度、分类三块,但不同版本处理方式差异很大。YOLOv2 用的是简单的均方误差,YOLOv3 把分类损失改成二值交叉熵,YOLOv4 进一步引入了 CIoU 和焦点损失的思想。下表是一个常见对比:

版本位置回归置信度损失分类损失适用场景
YOLOv2MSEMSEsoftmax目标尺度相对统一
YOLOv3MSEBCEBCE多尺度,小目标明显改善
YOLOv4CIoUBCE + focalBCE密集、遮挡场景更好

在 MATLAB 里运行的是 YOLOv4 检测器时,损失函数变化主要体现在宽高回归不再直接用 w、h 的平方误差,而是计算 CIoU。下面这段是用于理解损失构成的简化代码,不是可以直接替换进训练循环的实现:

function loss = vehicleYoloLoss(pred, target, objectMask) % pred: 网络输出的边界框参数 % target: 真实框参数 % objectMask: 1 表示该网格负责正样本 xyLoss = sum(sum((pred.xy - target.xy).^2, 3) .* objectMask); whLoss = sum(sum((sqrt(abs(pred.wh)) - sqrt(abs(target.wh))).^2, 3) .* objectMask); confLoss = sum(sum((pred.conf - target.conf).^2, 3) .* objectMask); classLoss = sum(sum(crossentropy(pred.class, target.class), 3) .* objectMask); loss = xyLoss + whLoss + confLoss + classLoss; end

objectMask是关键:只有真实目标中心点所在的网格才会反向传播位置和分类梯度,其余网格只参与置信度损失计算。这样做的目的是让网络把大部分表达能力留给真实目标区域。sqrt开根号处理是因为宽高跨度大,直接算差值会让小目标的值被大目标淹没。实际 YOLOv4 使用 CIoU,把重叠面积、中心距离和长宽比统一进一个指标,训练曲线更稳定,但概念上仍是这四个部分。

3. 数据准备与标注格式:把 image016.jpg 变成可训练的 boxLabelDatastore

进入 MATLAB 实现前,数据准备排在第一位。YOLO 训练不认"文件夹里有图片就行",它需要图片和对应的边界框标签成对出现。这个工程里能看到image006.jpgimage016.jpgimage033.jpg等文件,但没有单独列出标签文件,常见做法是把标签放在每个图片同名.txt里,或者集中在一个 labels 目录。先从目录加载开始。

3.1 当前文件夹与工程目录组织

运行要求里专门强调"matlab 左侧当前文件夹窗口必须是当前工程所在路径",这一点不是废话。MATLAB 的函数搜索路径和文件读取路径并不总是一致,如果你的工作目录切到了别的文件夹,Runme_.m里的相对路径会立刻失效。代码开头可以用一段防御性判断兜底:

if ~isfile('Runme_.m') error('当前文件夹不是工程根目录,请在 MATLAB 主界面的当前文件夹窗口切换过来'); end

判定依据是当前目录下是否存在Runme_.m,这比检查某个图片文件更可靠。实际操作时,打开 MATLAB 后直接进到工程压缩包解压出的目录,再运行Runme_.m。操作录像操作录像0023.avi里演示的也是这个动作。

3.2 把 YOLO 格式标签转成 boxLabelDatastore 需要的 table

MATLAB 的检测器训练需要boxLabelDatastore,它要求输入是一个两列表格:第一列是图片路径,第二列是边界框元胞数组。YOLO 的 txt 标签通常保存的是归一化后的[class_id, x_center, y_center, width, height],需要转换一下。

下面是一个通用转换函数,假设图片在images/下,标签在labels/下,文件同名:

function trainingData = loadVehicleData(imageDir, labelDir) imds = imageDatastore(imageDir); labelFiles = string(imds.Files); labelFiles = replace(labelFiles, imageDir, labelDir); labelFiles = replace(labelFiles, '.jpg', '.txt'); numImages = numel(imds.Files); boxesByImage = cell(numImages, 1); labelsByImage = cell(numImages, 1); for i = 1:numImages fid = fopen(labelFiles(i), 'r'); raw = textscan(fid, '%f%f%f%f%f'); fclose(fid); classIDs = raw{1}; xc = raw{2}; yc = raw{3}; w = raw{4}; h = raw{5}; % 归一化坐标转像素坐标 I = readimage(imds, i); imgW = size(I, 2); imgH = size(I, 1); boxes = zeros(numel(xc), 4); for j = 1:numel(xc) x1 = (xc(j) - w(j) / 2) * imgW; y1 = (yc(j) - h(j) / 2) * imgH; boxes(j, :) = [x1, y1, w(j) * imgW, h(j) * imgH]; end boxesByImage{i} = boxes; labelsByImage{i} = repmat("vehicle", size(classIDs)); end trainingData = table(imds.Files, boxesByImage, ... 'VariableNames', {'imageFilename', 'vehicle'}); end

这段代码里textscan按空格读取 YOLO 五行数据,x_centery_center是相对整张图的归一化值,所以要乘以图片宽高得到像素坐标。MATLAB 的边界框格式是[x, y, width, height],注意不是 xmin、ymin、xmax、ymax。转换后每一行对应一张图,boxesByImage是一个 Nx4 矩阵,N 是该图的目标数。如果某个图片没有目标,这一行必须是空数组[],否则训练时报错。

3.3 数据增强与训练集划分

车辆检测最容易遇到的增强手段是随机水平翻转、色彩抖动和随机缩放。MATLAB R2021a 里可以用transform对 combined datastore 做增强,但要注意边界框要跟着图片一起变换。翻转后,x 坐标变成imgWidth - x - width。常见的写法是自定义一个增强函数,再传给transform

训练集与验证集的划分我一般按 8:2 做:

rng(2024); numImages = height(trainingData); idx = randperm(numImages); trainIdx = idx(1:floor(numImages * 0.8)); valIdx = idx(floor(numImages * 0.8) + 1:end); trainData = trainingData(trainIdx, :); valData = trainingData(valIdx, :);

划分时用randperm固定随机种子,保证复现性。对于这项工程的图片数量,如果只有十来张,训练时数据量不够,模型很容易过拟合到具体图片,后面会讲到用数据增强和早停来缓解。

4. Runme_.m 主流程拆解:YOLOv4 训练与测试代码怎么改

这一步是整个工程的主干。运行前先确认已经安装了 Computer Vision Toolbox、Deep Learning Toolbox、Image Processing Toolbox,以及可选的 Parallel Computing Toolbox。MATLAB R2021a 中的 YOLO 目标检测器接口是yolov4ObjectDetector,不要自己从零搭 darknet,这个函数已经把网络结构和 COCO 预训练权重封装好了。

4.1 主程序与子函数的边界

工程目录里除了Runme_.m还有其他子函数文件,运行要求写明"不要直接运行子函数文件"。原因是子函数依赖主程序里已经赋值的变量,单独运行会出现Undefined function or variable。主程序的逻辑是:加载图片、构造数据、设置 anchor、训练、测试。子函数只负责其中一小步。

4.2 训练参数与 anchor 的设定

Runme_.m中,训练部分核心代码如下:

% 输入尺寸:416x416,车辆目标多为中尺度,不推荐偏小 inputSize = [416 416]; % 根据车辆宽高统计得到的 anchor anchorBoxes = [ 20 32; 45 60; 80 90; 120 150; 180 220; 280 300]; % 使用 COCO 预训练权重,类别改为 vehicle baseDetector = yolov4ObjectDetector('tiny-yolov4-coco', 'vehicle', ... anchorBoxes, inputSize); % 训练选项 options = trainingOptions('sgdm', ... 'InitialLearnRate', 0.001, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropFactor', 0.1, ... 'LearnRateDropPeriod', 10, ... 'MiniBatchSize', 8, ... 'MaxEpochs', 30, ... 'Shuffle', 'every-epoch', ... 'VerboseFrequency', 20, ... 'Verbose', true, ... 'Plots', 'training-progress', ... 'ExecutionEnvironment', 'cpu'); % 训练 detector = trainYOLOv4ObjectDetector(baseDetector, trainData, options);

说明几个参数。InitialLearnRate是 0.001 在城市道路、白天光照这类样本上比较稳;如果损失曲线反复震荡,改 0.0005。LearnRateDropPeriod设为 10,意思是每 10 轮学习率降到原来的 0.1,这样后期参数更新步长变小。MiniBatchSize取决于显存或内存,CPU 训练时 8 是安全值,批量再大内存可能吃紧。ExecutionEnvironment设为'cpu'时,训练速度会比较慢,但在没有 NVIDIA GPU 的普通笔记本上能跑通;如果机器有显卡并装了 CUDA,可以把'cpu'改成'gpu',训练时间能缩短一个数量级。

4.3 测试阶段与检测可视化

训练完成后,Runme_.m会读取测试图片并画框。这里注意detect返回的 bboxes 是 Nx4 矩阵,每行[x, y, width, height],和第三章转换格式保持一致。示例代码:

testImage = imread('image033.jpg'); [bboxes, scores, labels] = detect(detector, testImage, 'Threshold', 0.5); if ~isempty(bboxes) annotatedImage = insertObjectAnnotation(testImage, 'Rectangle', ... bboxes, cellstr(labels), 'FontSize', 12, 'LineWidth', 2); else annotatedImage = testImage; end imshow(annotatedImage); title(sprintf('检测到 %d 辆车', size(bboxes, 1)));

Threshold是置信度阈值,0.5 表示置信度高于一半的框才保留。想看到更多候选框就降到 0.3,但这时误检也会增多。insertObjectAnnotationscores可以以第 5 个参数传入,显示出每个框的置信度数字,方便判断检测器是"没找到"还是"找到了但置信度低"。

检测阶段还有一个容易踩的地方:detect函数默认会把输入图片 resize 到训练时设置的inputSize,所以你直接传入一张 1920x1080 的图也能跑,但检测框坐标会自动映射回原图尺寸,不需要自己写坐标变换。车辆密集时,YOLO 会输出大量重叠框,selectStrongestBbox可以在检测后做 NMS 合并:

[bboxes, scores] = selectStrongestBbox(bboxes, scores, ... 'RatioType', 'Min', 'OverlapThreshold', 0.5);

OverlapThreshold越大,允许保留的重叠框越多;对贴得很近的车辆,0.4 到 0.5 之间表现比较合理。太小会漏掉并排的车,太大则会出现一个目标两个框。

5. 运行验证与进阶调参:从损失不降到小目标漏检

真正把Runme_.m跑起来只是第一步,后面验证效果和调参才是重点。这里给出我在车辆检测任务里常用的检查顺序和几个坑。

5.1 运行前检查与常见报错

现象原因处理方式
Undefined function 'yolov4ObjectDetector'版本低于 R2021a 或工具箱缺失安装 Deep Learning Toolbox 和 Computer Vision Toolbox
Invalid training databoxLabelDatastore 中有空标签检查boxesByImage是否为空 cell
损失停在负数或 NaN学习率过大或输入包含 NaN 像素调低InitialLearnRate,确认图片读取正常
训练不收敛,损失不下降anchor 完全脱离目标尺寸重新统计真实框宽高,聚类出 anchor
检测结果全为 0 个框置信度阈值太高、目标过小降低Threshold到 0.3,检查输入尺寸

第一种报错最常见。MATLAB 的版本号在网上经常被讨论,但工程本身只需要matlab2021a 或者更高版本。运行前在命令行输入ver('deep')可以查看 Deep Learning Toolbox 版本,如果显示不存在,说明安装不完整。

5.2 验证检测精度的量化方法

光看一两张图不够,工程里提供了多帧图片,可以把所有测试图片跑一遍并计算平均精度。常见做法是:

detectionResults = detect(detector, valData, 'Threshold', 0.2); [ap, recall, precision] = evaluateDetectionPrecision(detectionResults, valData); fprintf('车辆检测平均精度 AP = %.3f\n', ap);

valData必须是和训练数据相同格式的 table,也就是第三章构造的那种两列表。Threshold在评估时通常设置得较低,因为 NMS 之后只保留最优框,低阈值可以在召回率层面看到模型真实能力。AP 在 0.6 以上算可用,低于 0.4 时不要急着加网络深度,先回去看 anchor 和训练样本数量。

5.3 小目标漏检怎么调

行驶车辆目标检测的小目标通常指远处车道上的车辆,在 416x416 输入下可能只有 25x25 像素左右。遇到这类漏检,优先把输入尺寸从 416 提到 608,比如设置inputSize = [608 608]。这会增加计算量,但小目标的分辨率直接翻倍,AP 往往有可观提升。

如果目标还是偶尔丢失,第二个有效手段是调整 anchorBoxes 的最小一行,把最小的 anchor 改成更贴近小车的尺寸。比如把第一行从20 32改成10 18,让网络在小候选框上有更多回归能力。我通常在训练 20 轮后跑一次验证集,统计漏检目标的真实框宽高,和当前最小的 anchor 对比,差距超过 1.5 倍就需要更新。

第三个手段是检查图片标注是否准确。如果标注框比目标实际轮廓大了一圈,CIoU 损失里长宽比惩罚项会让网络预测出偏大的框,小目标之间又容易互相覆盖。对image006.jpg这类远处小车的样本,标注框收紧到车体轮廓,比换网络更有效。改动完标注后,删除保存的 checkpoint,重新从第四章第 4 行的trainYOLOv4ObjectDetector开始训练即可。

本文还有配套的精品资源,点击获取

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

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

立即咨询