单目视觉自动泊车:畸变校正与车位检测测距的实现

发布时间:2026/9/13 5:24:34
单目视觉自动泊车:畸变校正与车位检测测距的实现 简介这是一份面向高校毕业设计与课程开发的基于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.m2.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^2k1、k2、k3 是径向系数切向项是 2p1xy p2(r^2 2*x^2) 这类交叉项p1、p2 是切向系数。MATLAB 用 calibrateCamera 得到 cameraParameters 对象里面封装了这五个系数和内参矩阵 Kfx、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.mOutputView 的取舍与灰度化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 的单应矩阵 Hs * [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.mHough 变换提取车位线拿到干净的二值图后下一步是找车位线。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.my1y2(子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三个环节独立定位不用整条链路一起抓。本文还有配套的精品资源点击获取