简介:这是一份面向高校毕业设计与课程开发的基于Matlab单目视觉自动泊车系统源码文档包,适合自动化、车辆工程、计算机视觉方向学生参考,解决停车位检测、视觉测距与自动泊车控制等问题。项目贯穿图像畸变校正、地面3D坐标计算、停车位检测、LabVIEW MyRIO上位机开发四大部分,同时给出Matlab与LabVIEW两套检测实现;压缩包共77个文件,大小10.54MB,包括Matlab源文件、LabVIEW程序vi、实验图像与误差分析xlsx表格,主程序与子VI层级清楚,便于按模块学习。源码经过测试,附带readme说明与正逆结合示意图、反向校正程序、二值化处理及测距误差表,可直接运行或扩展;检测误差与测距误差xlsx数据可供算法精度分析,适合二次开发。目前已有42人学习浏览,对毕业论文撰写、课程设计答辩或实际泊车预研均有较高参考意义。
1. 单目视觉自动泊车:一颗摄像头如何撑起车位检测与测距
把车开进地下车库,只靠一颗前视摄像头,既要拉直畸变、又要算出车位线到车身的真实距离,这是单目自动泊车系统最核心的一对矛盾。这套基于 MATLAB 与 LabVIEW MyRIO 的项目源码,把整条链路拆成四个可独立验证的模块:镜头畸变校正(Rectify.m)、地面坐标与测距(ceju.m)、车位线检测(Line.m)以及 MyRIO 上的电机舵机控制 VI。它适合毕业设计和课程设计直接复现,也适合做过标定的工程师移植到自己的平台。源码里最值得读的不是某个函数,而是两份误差 Excel 与反向校正程序 backword.m 构成的“检测—评估—修正”闭环,这正是很多入门项目缺失的部分。
2. 镜头畸变校正与二值化预处理:从 Rectify.m 到 im2bw_xg.m
2.1 畸变模型与 para.mat 里的标定参数
普通车载摄像头的镜头视场角大,边缘畸变明显。畸变分两类:径向畸变(桶形或枕形)和切向畸变(镜片装配偏心引起)。径向畸变的数学形式是归一化坐标下的多项式:
x_undist = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) y_undist = y * (1 + k1*r^2 + k2*r^4 + k3*r^6)其中 r^2 = x^2 + y^2,k1、k2、k3 是径向系数;切向项是 2p1xy + p2(r^2 + 2*x^2) 这类交叉项,p1、p2 是切向系数。MATLAB 用 calibrateCamera 得到 cameraParameters 对象,里面封装了这五个系数和内参矩阵 K(fx、fy、cx、cy)。这套项目把标定结果统一存在 para.mat 里。
% 读取标定参数 calib = load('para.mat'); camParam = calib.camParam; % cameraParameters 对象 K = camParam.IntrinsicMatrix'; % 3x3 内参, MATLAB 里按转置存储 radial = camParam.RadialDistortion; % [k1, k2, k3] tangential = camParam.TangentialDistortion;% [p1, p2]说明:IntrinsicMatrix 在 MATLAB 里以转置形式存储,做 K*x 这类列向量左乘前必须转置回来。radial 向量可能只有两个元素(标定时只选了 k1、k2),取值前用 numel 判断一下,否则取 k3 会越界。这是从 para.mat 往下走最容易踩的第一个坑。
2.2 Rectify.m:OutputView 的取舍与灰度化
Rectify.m 把原始帧送进 undistortImage,去畸变后再交给后面的检测流程。关键在于 OutputView 参数。
% Rectify.m - 图像畸变校正 function I_rect = Rectify(I_raw, camParam) % I_raw : 摄像头原始帧 % camParam : cameraParameters 对象 if size(I_raw, 3) == 3 I_gray = rgb2gray(I_raw); % 车位线检测用灰度即可 else I_gray = I_raw; end I_rect = undistortImage(I_gray, camParam, 'OutputView', 'same'); end两种输出模式的差异:
| OutputView | 输出尺寸 | 适用场景 | 对坐标映射的影响 |
|---|---|---|---|
| 'same' | 与输入一致,边缘像素重采样 | 检测 + 测距链路 | 无影响,H 可直接复用 |
| 'full' | 更大,四周出现黑边 | 纯可视化、展示校正效果 | 需重算内参与单应矩阵 |
用 'same' 的理由很实际:后面 ceju.m 里的单应矩阵 H 是在校正前的图像坐标系下标定的,输出尺寸一旦改变,H 就得重算;而 'full' 输出的更大图像只适合直接给人看。项目里的 x1_r.jpg 就是校正后的样例图,拿它和 x1.jpg 逐像素对比,边缘车位线的弧度就能看出校正效果。rect_xg.m 是 Rectify.m 的迭代版,区别在会把结果写盘成图片文件,方便离线反复调参。
2.3 im2bw_xg.m:自适应阈值与形态学后处理
车位线检测的前提是拿到干净的二值图。im2bw_xg.m 从名字看是 im2bw 的修改版,改动核心在于支持三种阈值策略。
% im2bw_xg.m - 改进二值化函数 function BW = im2bw_xg(I_gray, method, param) % method: 'otsu' 全局阈值 / 'adaptive' 自适应 / 'fixed' 固定阈值 % param : adaptive 时是 Sensitivity(0~1), fixed 时是灰度阈值 switch method case 'otsu' level = graythresh(I_gray); BW = imbinarize(I_gray, level); case 'adaptive' BW = ~imbinarize(I_gray, 'adaptive', ... 'ForegroundPolarity', 'bright', ... 'Sensitivity', param); case 'fixed' BW = I_gray > param; end BW = bwareaopen(BW, 50); % 删掉面积小于 50 像素的孤立噪点 BW = imclose(BW, strel('rectangle', [5 5])); % 接上断开的线 end说明:adaptive 模式下 Sensitivity 越大越容易把阴影判成前景,从 0.4 起步试。bwareaopen 的 50 像素阈值要按图像分辨率调,1280x720 的图放到 100 更合适。imclose 的矩形结构元用来接上光照不均造成的断线,5x5 起步,太大会把相邻车位线粘连。地库照明不均匀时 otsu 会频繁出错,adaptive 是正解,但代价是计算量上来了,在 MyRIO 上实时跑需要降分辨率或只在 ROI 内二值化,这个到第 4 章展开。
3. 地面 3D 坐标计算与测距:ceju.m / yuzhi.m / ponit.m 的配合
3.1 地面平面假设与单应矩阵
单目相机没有双目视差,单像素不含深度。自动泊车能测距的前提是:车位线和障碍物都在停车场地平面上。射影几何里,一个平面到另一个平面的映射是 3x3 的单应矩阵 H:
s * [X; Y; 1] = H * [u; v; 1]H 有 8 个自由度,4 组不共线的对应点可解,实际标定会采集 8~12 组点做最小二乘,抑制单个点的标定误差。MATLAB 里 fitgeotrans 可以直接估计 projective 变换。项目里除了 para.mat,还有一个 pw.mat,从命名看就是 plane-to-world 的单应矩阵。
% 估计地面单应矩阵(标定板角点为例) tform = fitgeotrans(imagePts, groundPts, 'projective'); H = tform.T'; % 3x3 单应矩阵, 注意转置fitgeotrans 返回的 T 是“行向量作用于行坐标”的形式,转置成 H 后才能用于 H*[u;v;1] 这种列向量左乘。这个转置弄反,测距会整体偏移,且偏移随距离增大而放大。
3.2 ceju.m:齐次坐标映射与距离输出
ceju.m 是测距主程序,输入校正后图像上的目标点像素坐标,输出该点到摄像头地面投影点的距离。
% ceju.m - 地面距离计算 function [dist_mm, pts_ground] = ceju(pts_pixel, H) % pts_pixel : Nx2 像素坐标(校正后图像) % H : 3x3 单应矩阵(像素->地面, 单位 mm) N = size(pts_pixel, 1); p = [pts_pixel, ones(N, 1)]'; % 3xN 齐次坐标 g = H * p; % 映射到地面齐次坐标 g = g ./ g(3, :); % 除以 w, 恢复欧氏坐标 pts_ground = g(1:2, :)'; % Nx2 地面坐标 dist_mm = sqrt(sum(pts_ground.^2, 2)); % 到地面原点的欧氏距离 end除以 w 这步不能省,projective 变换不是线性缩放,w 随像素位置变化。dist_mm 的单位由 groundPts 的标定单位决定,标定时填毫米输出就是毫米。要测车位线到车头的距离,用 pts_ground 做向量差而不是直接减距离标量,因为车位线和车头通常不在同一条直线上。
3.3 yuzhi.m、ponit.m 与误差链
测距基于特征点,特征点来自 ponit.m:在二值图上跑 regionprops,把满足面积阈值的目标质心当作车位角点。yuzhi.m 负责提供动态阈值,它只在 ROI 内统计灰度分布,避免全图亮度不均导致阈值漂移。项目里 my1.m 就是把 Rectify → yuzhi → ponit → ceju 串起来的主脚本,按顺序调用就能跑通整条测距链路。
% yuzhi.m - ROI 内动态阈值 function [th, meanVal] = yuzhi(I_gray, roi) if nargin < 2 roi = [1, 1, size(I_gray, 2), size(I_gray, 1)]; end sub = I_gray(roi(2):roi(2)+roi(3)-1, ... roi(1):roi(1)+roi(4)-1); th = graythresh(sub); meanVal = mean(sub(:)); end% ponit.m - 特征点提取 function [pts, areas] = ponit(BW, minArea) s = regionprops(BW, 'Centroid', 'Area'); areas = [s.Area]; idx = find(areas >= minArea); pts = reshape([s(idx).Centroid], 2, [])'; endponit.m 隐含一个假设:区域质心近似等于车位角点。透视畸变下矩形车位在图像里是梯形,质心会偏向长边。更稳的做法是检测角点或拿 Hough 直线求交点,这就用到第 4 章的 Line.m。整条测距链路的误差逐级累计:
| 误差来源 | 影响方式 | 缓解手段 |
|---|---|---|
| 阈值分割偏差 | 质心偏移 | 缩小 ROI、用 adaptive |
| 单应矩阵标定误差 | 随距离放大 | 增加标定点数量 |
| 特征点定位误差 | 亚像素抖动 | 角点检测替代质心 |
yuzhi.m 的 ROI 越小越贴合局部光照,但太小会丢掉车位线,需要反复试。项目里 测距误差.xlsx 记录的就是这条链路在不同距离下的真实偏差。
4. 停车位检测与 LabVIEW MyRIO 上位机联动
4.1 Line.m:Hough 变换提取车位线
拿到干净的二值图后,下一步是找车位线。Line.m 走经典 Hough 变换:每个前景像素投票到 (θ, ρ) 参数空间,再找累积器峰值。
% Line.m - Hough 直线检测 function lines = Line(BW, minLen, fillGap) if nargin < 3 minLen = 40; % 最短直线长度(像素) fillGap = 20; % 同一线上断点最大间隔 end [H, T, R] = hough(BW, 'ThetaResolution', 1, 'RhoResolution', 1); P = houghpeaks(H, 30, 'Threshold', 0.3 * max(H(:))); lines = houghlines(BW, T, R, P, 'MinLength', minLen, 'FillGap', fillGap); endThetaResolution 设 1 度对车位线足够,0.5 度会让累积器大一半且更容易产生断裂峰。MinLength 在透视效果明显时不能设太大,远处车位线在图像里只有二三十像素长,40 是折中值。FillGap 把同一条线上因二值化断裂的线段重连,20 像素以内都合。检测结果可以直接叠到校正图上验证,项目中带的 IMG_1100.png、IMG_1108.png 是实拍地库场景图,可以直接用来测 Line.m 在不同光照下的表现:
lines = Line(BW, 40, 20); for k = 1:length(lines) xy = [lines(k).point1; lines(k).point2]; plot(xy(:,1), xy(:,2), 'LineWidth', 2, 'Color', 'green'); end4.2 check.m:车位合法性判定
多条直线围出的四边形不一定是车位。check.m 做几何约束检查:最小外接矩形的宽、长要大于车宽和车长的 1.2 倍,长边与车身方向夹角小于 10 度。1.2 倍不是拍脑袋——车位标准宽度是车宽加 0.6~0.8 米,1.2 倍给检测误差留了余量,又不会把相邻两个车位合并。
% check.m - 车位合法性检测 function [ok, box] = check(corners, carW, carL, maxAngle) % corners: 4x2 顶点坐标(由直线交点计算) box = minAreaRect(corners); % 最小外接矩形 w = box(3); l = box(4); ang = abs(atan2(box(2,2)-box(1,2), box(2,1)-box(1,1))); ok = (w > 1.2*carW) && (l > 1.2*carL) && (ang < maxAngle); end检测结果会和人工标注对比,统计漏检率、误检率,记录在 检测误差.xlsx。这份数据是毕设答辩里最有力的支撑材料,把“检测准不准”从主观感受变成量化指标。
4.3 MyRIO 子 VI 架构与数据通路
LabVIEW 部分是实车半边:MATLAB 离线验证算法,MyRIO 实时执行。从源码里的 VI 列表能看出层次:main.vi 是顶层状态机,下面挂二值化(子VI)、y1y2(子VI)、电机(子VI)、舵机(子VI),array.vi 做缓冲,Image To EDVR同元素地址操作.vi 负责把图像数据转成 EDVR 引用以便在实时循环间传递。
| VI 文件名 | 职责 | 对应 MATLAB 端 |
|---|---|---|
| main.vi | 状态机主循环 | 项目入口 |
| 二值化(子VI).vi | 灰度转二值 + 形态学 | im2bw_xg.m |
| y1y2(子VI).vi | 输出两条边界线的位置参数 | Line.m 的直线参数 |
| 电机(子VI).vi | 驱动电机 PWM 输出 | 无 |
| 舵机(子VI).vi | 转向舵机 PWM 控制 | 无 |
| array.vi | 循环间数组数据传递 | 无 |
y1y2 这个名字指的是直线方程 y = y1 和 y = y2,也就是车位横向边界线的两条纵坐标。MyRIO 端不需要完整图像处理,只需要上位机把两条边界线的位置发过去,就能规划泊车路径。这种“重计算在 PC、实时控制在嵌入式”的划分是工程上最稳妥的做法,也避免了在 MyRIO 上跑完整视觉管线导致的帧率不足。
接线层面,y1y2(子VI) 之间用 functional global variable 或 queue 传数组;电机、舵机各自用并行 timed loop 输出 50Hz PWM,脉宽 1.0~2.0ms 对应舵机 -45°~+45°。这个参数必须和实测舵机行程对齐,不能照抄,否则打满舵会顶机械限位。Line Profile.vi 是调试工具,用来观察车位线附近的灰度剖面,判断二值化阈值是否合适。
提示:MyRIO 的 FPGA 模式下可以用更高频率生成 PWM,但舵机和电机是低频执行器,50Hz 定时循环足够,FPGA 资源留给图像采集和编码更划算。
5. 反向校正与误差回灌:把两份 Excel 变成调参依据
5.1 从测距误差数据反推偏差来源
测距误差.xlsx 记录不同距离下的测量值与真实值,读进来算两个指标:RMSE 反映整体抖动,平均偏差反映系统性偏移。
T = readtable('测距误差.xlsx'); err = T.测量距离 - T.真实距离; fprintf('RMSE=%.1fmm bias=%.1fmm\n', sqrt(mean(err.^2)), mean(err));平均偏差明显非零,优先怀疑 H 标定时地面点的世界坐标量错;RMSE 大而偏差接近零,则去调 yuzhi.m 的 ROI 大小和 bwareaopen 的噪点阈值。
5.2 backword.m:把检测结果叠回原始图
校正图上检测到的角点要叠到原始图上显示或下发到执行机构,必须先做逆变换。undistortImage 没有现成的逆函数,backword.m 用固定点迭代逼近:
% backword.m - 校正坐标逆映射回原始畸变图 function pts_raw = backword(pts_rect, camParam) K = camParam.IntrinsicMatrix'; fx = camParam.FocalLength(1); fy = camParam.FocalLength(2); cx = camParam.PrincipalPoint(1); cy = camParam.PrincipalPoint(2); xn = (pts_rect - [cx cy]) ./ [fx fy]; % 像素 -> 归一化 xk = xn; k = camParam.RadialDistortion; for i = 1:30 r2 = sum(xk.^2, 2); radial = 1 + k(1)*r2 + k(2)*r2.^2; xd = xk .* radial; % 正向畸变模型 xk = xn + (xk - xd); % 残差反馈修正 end pts_raw = xk .* [fx fy] + [cx cy]; % 归一化 -> 像素 end每轮先对当前估计值作用一次正向径向畸变,再用残差修正估计,30 轮可收敛到 0.01 像素以内。只补偿了径向项 k1、k2,切向系数 p1、p2 在多数车载摄像头场景下贡献小于 0.1 像素,省略影响可忽略。后处理把检测框叠回原始图时必须走这一步,否则图像边缘的框和线明显不贴合。
5.3 换场地只需重做三步
换场地后畸变系数一般可以复用(前提是镜头焦距没动),真正要重做的是三步:用 pit.bmp 对准地面四个已知间距的标记点重新估计 H;把 yuzhi.m 的 ROI 框到新车位区域;再用 check.m 的车宽车长裕量适配新车型。调试顺序按静态图、视频流、实车闭环走:先用 x1.jpg 确认检测框,再切实时流看抖动,最后挂 MyRIO。曲线不直查畸变,阈值乱跳查 ROI,测距漂移查 H,三个环节独立定位,不用整条链路一起抓。
本文还有配套的精品资源,点击获取