《MATLAB实战训练营:从入门到工业级应用》工程实用篇-🚗 自动驾驶初体验:车道线检测算法实战(MATLAB2016b版)
大家好!今天我要带大家一起探索自动驾驶中一个非常基础但又至关重要的技术——车道线检测。我们将从零开始,一步步用MATLAB2016b实现一个完整的车道线检测系统。准备好你的MATLAB,让我们开始这场有趣的自动驾驶之旅吧!✨
一、准备工作与环境搭建
1.1 为什么选择MATLAB2016b?
MATLAB在图像处理和计算机视觉领域有着强大的功能,而2016b版本稳定且兼容性好,适合教学和实验。当然,如果你有更新的版本也完全没问题!
1.2 需要安装的工具箱
确保你已经安装了以下工具箱(可以通过ver
命令查看):
- Image Processing Toolbox
- Computer Vision System Toolbox
如果没有安装,可以通过MATLAB的"附加功能"菜单进行安装。
1.3 测试数据准备
我们将使用一段高速公路行车视频作为测试数据。你可以使用自己的行车记录仪视频,或者自己从网上下载一个视频:
高速公路驾驶highway_lane
二、车道线检测基础理论
2.1 车道线检测的基本流程
一个典型的车道线检测流程包括以下几个步骤:
- 图像获取 📷
- 预处理(去噪、增强等) 🛠️
- 边缘检测 ✂️
- 感兴趣区域(ROI)提取 🔍
- 霍夫变换检测直线 📐
- 车道线拟合与可视化 🚦
2.2 为什么选择霍夫变换?
霍夫变换是检测图像中几何形状(如直线、圆等)的经典算法。它能够将图像空间中的像素点映射到参数空间,通过累加器找出最可能的几何形状参数。
三、实战开始!一步步实现车道线检测
3.1 读取并显示视频
首先,我们需要读取视频并显示第一帧,看看我们的"战场"是什么样子:
% 创建视频读取对象
videoReader = VideoReader('highway_lane.mp4');% 读取第一帧
frame = readFrame(videoReader);% 显示原始图像
figure('Name', '原始视频帧');
imshow(frame);
title('原始视频帧');
3.2 图像预处理
原始图像包含太多干扰信息,我们需要进行预处理:
% 转换为灰度图像
grayFrame = rgb2gray(frame);% 高斯滤波去噪
filteredFrame = imgaussfilt(grayFrame, 2);% 显示预处理结果
figure('Name', '预处理结果');
subplot(1,2,1); imshow(grayFrame); title('灰度图像');
subplot(1,2,2); imshow(filteredFrame); title('高斯滤波后');
3.3 边缘检测
边缘检测是车道线检测的关键步骤,我们使用Canny算法:
% Canny边缘检测
edgeThreshold = [0.1, 0.2]; % 阈值可以调整
sigma = 1; % 高斯滤波参数
edges = edge(filteredFrame, 'Canny', edgeThreshold, 'both', sigma);% 显示边缘检测结果
figure('Name', '边缘检测结果');
imshow(edges);
title('Canny边缘检测结果');
小技巧:阈值的选择很重要,太低会检测到太多噪声,太高可能会漏掉真实的车道线。可以尝试调整看看效果变化!
3.4 感兴趣区域(ROI)提取
我们不需要分析整个图像,只需要关注车辆前方的道路区域:
% 获取图像尺寸
[rows, cols] = size(edges);% 定义ROI多边形顶点(梯形区域)
roi = [1, rows; cols/2-50, rows/2+50; cols/2+50, rows/2+50; cols, rows];% 创建ROI掩模
roiMask = poly2mask(roi(:,1), roi(:,2), rows, cols);% 应用ROI掩模
roiEdges = edges & roiMask;% 显示ROI提取结果
figure('Name', 'ROI提取');
subplot(1,2,1); imshow(edges); hold on;
plot(roi(:,1), roi(:,2), 'r-', 'LineWidth', 2);
title('原始边缘+ROI区域');
subplot(1,2,2); imshow(roiEdges);
title('ROI内边缘');
3.5 霍夫变换检测直线
现在是重头戏——使用霍夫变换检测直线:
% 霍夫变换参数设置
thetaResolution = 0.5; % 角度分辨率
rhoResolution = 1; % 距离分辨率
threshold = 50; % 累加器阈值
minLineLength = 30; % 最小线段长度
maxLineGap = 20; % 线段间最大间隔% 执行霍夫变换
[H, theta, rho] = hough(roiEdges, 'Theta', -90:thetaResolution:89, 'RhoResolution', rhoResolution);% 检测峰值
peaks = houghpeaks(H, 10, 'Threshold', threshold);% 检测线段
lines = houghlines(roiEdges, theta, rho, peaks, 'FillGap', maxLineGap, 'MinLength', minLineLength);% 显示霍夫变换结果
figure('Name', '霍夫变换检测结果');
imshow(frame); hold on;
for k = 1:length(lines)xy = [lines(k).point1; lines(k).point2];plot(xy(:,1), xy(:,2), 'LineWidth', 2, 'Color', 'green');
end
title('霍夫变换检测到的直线');
3.6 车道线筛选与拟合
不是所有检测到的直线都是车道线,我们需要筛选:
% 筛选左右车道线
leftLines = [];
rightLines = [];for k = 1:length(lines)% 计算线段斜率x1 = lines(k).point1(1);y1 = lines(k).point1(2);x2 = lines(k).point2(1);y2 = lines(k).point2(2);slope = (y2 - y1) / (x2 - x1);% 根据斜率筛选if slope < -0.3 % 左车道线通常有负斜率leftLines = [leftLines; [x1, y1; x2, y2]]; % 往后追加elseif slope > 0.3 % 右车道线通常有正斜率rightLines = [rightLines; [x1, y1; x2, y2]]; % 往后追加end
end% 拟合左右车道线(使用最小二乘法)
if ~isempty(leftLines)leftPoints = [leftLines(1:2,:); leftLines(3:4,:)];leftPoly = polyfit(leftPoints(:,2), leftPoints(:,1), 1);
endif ~isempty(rightLines)rightPoints = [rightLines(1:2,:); rightLines(3:4,:)];rightPoly = polyfit(rightPoints(:,2), rightPoints(:,1), 1);
end% 显示最终结果
figure('Name', '最终车道线检测结果');
imshow(frame); hold on;% 绘制左车道线
if exist('leftPoly', 'var')yLeft = [rows/2+50, rows];xLeft = polyval(leftPoly, yLeft);plot(xLeft, yLeft, 'LineWidth', 4, 'Color', 'red');
end% 绘制右车道线
if exist('rightPoly', 'var')yRight = [rows/2+50, rows];xRight = polyval(rightPoly, yRight);plot(xRight, yRight, 'LineWidth', 4, 'Color', 'blue');
endtitle('最终车道线检测结果');
legend('左车道线', '右车道线');
四、完整视频处理与优化
4.1 封装为函数
让我们把上面的代码封装成一个函数,方便处理视频的每一帧:
function [leftLine, rightLine] = detectLanes(frame)% 转换为灰度图像grayFrame = rgb2gray(frame);% 高斯滤波filteredFrame = imgaussfilt(grayFrame, 2);% 边缘检测edgeThreshold = [0.1, 0.2];sigma = 1;
% edges = edge(filteredFrame, 'Canny', edgeThreshold, 'both', sigma);edges = edge(filteredFrame, 'Canny', edgeThreshold, 'both');% ROI提取[rows, cols] = size(edges);roi = [1, rows; cols/2-50, rows/2+50; cols/2+50, rows/2+50; cols, rows];roiMask = poly2mask(roi(:,1), roi(:,2), rows, cols);roiEdges = edges & roiMask;% 霍夫变换thetaResolution = 0.5;rhoResolution = 1;threshold = 50;minLineLength = 30;maxLineGap = 20;[H, theta, rho] = hough(roiEdges, 'Theta', -90:thetaResolution:89, 'RhoResolution', rhoResolution);peaks = houghpeaks(H, 10, 'Threshold', threshold);lines = houghlines(roiEdges, theta, rho, peaks, 'FillGap', maxLineGap, 'MinLength', minLineLength);% 筛选和拟合车道线leftLines = [];rightLines = [];for k = 1:length(lines)x1 = lines(k).point1(1);y1 = lines(k).point1(2);x2 = lines(k).point2(1);y2 = lines(k).point2(2);slope = (y2 - y1) / (x2 - x1);if slope < -0.3leftLines = [leftLines; [x1, y1; x2, y2]];elseif slope > 0.3rightLines = [rightLines; [x1, y1; x2, y2]];endend% 返回拟合结果leftLine = [];rightLine = [];if ~isempty(leftLines) & length(leftLines)>4leftPoints = [leftLines(1:2,:); leftLines(3:4,:)];leftLine = polyfit(leftPoints(:,2), leftPoints(:,1), 1);endif ~isempty(rightLines) & length(rightLines)>4rightPoints = [rightLines(1:2,:); rightLines(3:4,:)];rightLine = polyfit(rightPoints(:,2), rightPoints(:,1), 1);end
end
4.2 处理整个视频
现在我们可以处理整个视频了:
clc
close all
% 创建视频读取和写入对象
videoReader = VideoReader('highway_lane.mp4');
videoWriter = VideoWriter('lane_detection_result.avi');
open(videoWriter);% 创建显示窗口
figure('Name', '实时车道线检测', 'Position', [100, 100, 800, 600]);while hasFrame(videoReader)% 读取当前帧frame = readFrame(videoReader);% 检测车道线[leftLine, rightLine] = detectLanes(frame);% 显示结果imshow(frame); hold on;% 绘制左车道线if ~isempty(leftLine)yLeft = [size(frame,1)/2+50, size(frame,1)];xLeft = polyval(leftLine, yLeft);plot(xLeft, yLeft, 'LineWidth', 4, 'Color', 'red');end% 绘制右车道线if ~isempty(rightLine)yRight = [size(frame,1)/2+50, size(frame,1)];xRight = polyval(rightLine, yRight);plot(xRight, yRight, 'LineWidth', 4, 'Color', 'blue');endtitle('实时车道线检测');drawnow;% 写入视频frameWithLanes = getframe(gcf);writeVideo(videoWriter, frameWithLanes.cdata);hold off;
end% 关闭视频写入器
close(videoWriter);
disp('视频处理完成');
五、进阶优化与挑战
5.1 优化建议
我们的基础版本已经可以工作了,但还有很大优化空间:
- 动态ROI调整:根据车辆速度调整ROI区域大小
- 颜色信息利用:结合车道线颜色(黄、白)增强检测
- 卡尔曼滤波:平滑车道线检测结果,减少抖动
- 曲线车道检测:使用二次或三次多项式拟合曲线车道
5.2 常见问题与解决方案
问题1:在强光下检测效果差
- 解决方案:使用自适应直方图均衡化(CLAHE)增强对比度
% 替换灰度转换和高斯滤波部分
labFrame = rgb2lab(frame);
L = labFrame(:,:,1)/100;
L = adapthisteq(L);
labFrame(:,:,1) = L*100;
enhancedFrame = lab2rgb(labFrame);
grayFrame = rgb2gray(enhancedFrame);
问题2:检测到非车道线的边缘
- 解决方案:增加后处理步骤,如基于车道线几何约束的筛选
六、总结与展望
恭喜你!🎉 我们已经完成了一个完整的车道线检测系统。虽然它看起来简单,但这是自动驾驶的基础模块之一。通过这个项目,我们学习了:
- 图像预处理技术
- 边缘检测算法
- 霍夫变换原理与应用
- 车道线拟合方法
未来你可以尝试:
- 实现更复杂的车道线检测算法
- 集成到更大的自动驾驶系统中
- 尝试使用深度学习的方法(如LaneNet)
希望这篇教程对你有所帮助!如果有任何问题或建议,欢迎在评论区留言讨论。Happy coding! 💻🚀
附录:完整代码下载
点击这里下载完整MATLAB代码包
参考文献:
- MATLAB官方文档
- 《计算机视觉:算法与应用》Richard Szeliski
- 《自动驾驶中的计算机视觉》系列论文