4、车道线检测技术:基于视觉的车道线检测(边缘检测、霍夫变换)、基于激光雷达的车道线检测
车道线检测,说白了就是给自动驾驶汽车画“跑道”。没有它,车辆连自己在哪条道上都搞不清。我做了这么多年高精地图,最深的体会就是:车道线检测做不好,后面的规划控制全是空中楼阁。
今天咱们聊聊两种主流方案:视觉方案和激光雷达方案。这两种我都踩过坑,也积累了不少经验,分享给你。
4.1 基于视觉的车道线检测
视觉方案成本低、信息丰富,是量产车的首选。但它的短板也很明显——怕光照、怕雨雪、怕遮挡。嗯,这里要注意,视觉方案的核心流程其实就三步:预处理、特征提取、模型拟合。
4.1.1 边缘检测:找到车道线的“边界”
边缘检测是视觉车道线检测的第一步。为什么要做边缘检测?因为车道线在图像中就是一条亮线,和路面有显著的灰度差异。找到这个差异,就能定位车道线。
我个人习惯用Canny边缘检测。它比Sobel、Laplacian这些老方法稳定得多。Canny的核心思想是:先平滑去噪,再计算梯度,最后做非极大值抑制和双阈值处理。
关键参数:
- 低阈值:控制边缘连接的灵敏度。设得太低,噪声会被当成边缘;设得太高,真正的边缘会断裂。
- 高阈值:控制强边缘的保留。一般设为低阈值的2-3倍。
我在项目中遇到过一个问题:城市道路的沥青路面纹理复杂,Canny会检测出大量伪边缘。后来我加了一个预处理步骤——先做高斯滤波,再用形态学闭运算填充路面纹理。效果立竿见影。
import cv2
import numpy as np
def detect_edges(image):
# 1. 高斯滤波去噪
blurred = cv2.GaussianBlur(image, (5, 5), 1.5)
# 2. Canny边缘检测
low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blurred, low_threshold, high_threshold)
return edges
避坑指南:我曾经在隧道场景中吃过亏。隧道内光照均匀,但车道线磨损严重,Canny检测出的边缘断断续续。我的解决办法是:先做直方图均衡化增强对比度,再降低Canny的低阈值到30。这样虽然会引入一些噪声,但车道线的完整性保住了。
4.1.2 霍夫变换:从“点”到“线”的魔法
边缘检测得到的是像素点,但我们需要的是直线方程。霍夫变换就是干这个的——把图像空间中的点映射到参数空间,通过投票找到最可能的直线。
霍夫变换的原理其实不复杂。你想想看,一条直线可以用极坐标表示为:ρ = x·cosθ + y·sinθ。每个边缘点都会在(ρ, θ)空间画出一条正弦曲线。多条曲线的交点,就是一条直线。
实际应用中,我常用概率霍夫变换(Probabilistic Hough Transform)。它比标准霍夫变换快得多,而且能直接输出线段的端点坐标。
def detect_lane_lines(edges):
# 概率霍夫变换
lines = cv2.HoughLinesP(
edges,
rho=1, # 距离分辨率(像素)
theta=np.pi/180, # 角度分辨率(弧度)
threshold=50, # 投票阈值
minLineLength=40, # 最小线段长度
maxLineGap=100 # 最大线段间隙
)
return lines
注意:霍夫变换的参数调优是个体力活。我建议你先用一张典型场景的图片,手动调整threshold和minLineLength,直到检测结果满意。然后把这个参数作为基准,再针对特殊场景做微调。
4.1.3 车道线拟合与跟踪
霍夫变换检测出的线段往往是零散的。我们需要把它们合并成完整的车道线。常用的方法有两种:
- 滑动窗口法:从图像底部开始,用固定宽度的窗口向上滑动,统计窗口内的像素点,拟合二次曲线。
- RANSAC拟合:随机采样点,拟合直线,计算内点比例,迭代找到最优模型。
我个人更推荐滑动窗口法。为什么?因为车道线在图像中通常是曲线(尤其是弯道),二次曲线比直线更贴合实际。RANSAC更适合直线检测。
def sliding_window_fit(binary_warped):
# 直方图统计,找到车道线起始位置
histogram = np.sum(binary_warped[binary_warped.shape[0]//2:, :], axis=0)
# 找到左右车道线的起始x坐标
midpoint = histogram.shape[0] // 2
leftx_base = np.argmax(histogram[:midpoint])
rightx_base = np.argmax(histogram[midpoint:]) + midpoint
# 滑动窗口参数
nwindows = 9
window_height = binary_warped.shape[0] // nwindows
margin = 100
# ... 窗口滑动和像素点收集 ...
# 用二次多项式拟合
left_fit = np.polyfit(lefty, leftx, 2)
right_fit = np.polyfit(righty, rightx, 2)
return left_fit, right_fit
经验之谈:我在高速公路上做过测试,滑动窗口法在直道和缓弯上表现很好。但遇到急弯(比如匝道),窗口宽度要适当增大,否则车道线会跑出窗口。我一般把margin设为150像素,能覆盖大多数弯道场景。
4.2 基于激光雷达的车道线检测
激光雷达方案不受光照影响,夜间、逆光都能稳定工作。但它的缺点也很明显:点云稀疏,车道线这种“薄”物体很难直接检测。
说白了,激光雷达看到的车道线,就是路面上一条反射强度不同的“线”。我们需要利用反射强度信息来提取它。
4.2.1 反射强度阈值分割
车道线通常用高反光材料(如热熔涂料)绘制,反射强度远高于普通路面。我们可以设定一个强度阈值,把高反射点提取出来。
def extract_lane_points_from_lidar(point_cloud):
# point_cloud: N x 4 矩阵 (x, y, z, intensity)
intensity_threshold = 0.3 # 根据传感器标定
# 提取高反射点
lane_points = point_cloud[point_cloud[:, 3] > intensity_threshold]
return lane_points
注意:反射强度阈值不是固定的。不同厂家的激光雷达(如Velodyne、Hesai、RoboSense)的强度值范围不同。我建议你在采集数据时,先做一次强度标定——找一段标准车道线,统计其强度分布,再设定阈值。
4.2.2 基于高度滤波的路面分割
提取高反射点之前,必须先剔除地面以上的物体(如路沿、护栏、车辆)。否则这些物体的高反射点会干扰车道线检测。
我常用的方法是:基于高度进行路面分割。假设车辆在平坦路面上行驶,激光雷达安装高度已知,那么路面点的高度应该在一个小范围内波动。
def ground_segmentation(point_cloud, lidar_height=1.8):
# 假设路面高度在 -0.2m 到 0.2m 之间
ground_mask = (point_cloud[:, 2] > -0.2) & (point_cloud[:, 2] < 0.2)
ground_points = point_cloud[ground_mask]
return ground_points
避坑指南:我曾经在坡道路段翻过车。车辆上坡时,激光雷达的俯仰角变化,导致路面高度偏移。我的解决办法是:用IMU数据补偿激光雷达的姿态,或者用RANSAC平面拟合动态估计路面高度。
4.2.3 点云聚类与车道线拟合
提取出高反射路面点后,还需要把它们聚类成独立的车道线。常用的聚类算法有:
- DBSCAN:基于密度的聚类,能自动识别噪声点。适合车道线这种形状规则的簇。
- 欧式聚类:基于距离的聚类,简单快速。但需要手动设定聚类半径。
我个人习惯用DBSCAN。它不需要预先指定簇的数量,而且能很好地处理车道线断裂的情况。
from sklearn.cluster import DBSCAN
def cluster_lane_points(lane_points):
# DBSCAN聚类
clustering = DBSCAN(eps=0.3, min_samples=10).fit(lane_points[:, :2])
labels = clustering.labels_
# 提取每个簇的点
clusters = {}
for i, label in enumerate(labels):
if label not in clusters:
clusters[label] = []
clusters[label].append(lane_points[i])
return clusters
聚类完成后,对每个簇做直线或曲线拟合。由于激光雷达点云在远距离处很稀疏,我一般只拟合直线,或者用低阶多项式(一次或二次)。
4.3 视觉与激光雷达的融合策略
两种方案各有优劣。在实际工程中,我建议做融合。怎么融合?我分享一个我常用的策略:
| 场景 | 主传感器 | 辅助传感器 | 说明 |
|---|---|---|---|
| 白天、光照良好 | 视觉 | 激光雷达 | 视觉检测为主,激光雷达做校验 |
| 夜间、逆光 | 激光雷达 | 视觉 | 激光雷达为主,视觉辅助 |
| 雨雪、雾天 | 激光雷达 | 视觉(关闭) | 视觉基本失效,全靠激光雷达 |
| 车道线磨损 | 视觉 + 激光雷达 | — | 两者独立检测,结果做加权融合 |
核心思路:不要试图用一个传感器解决所有问题。视觉和激光雷达是互补的。视觉提供纹理信息,激光雷达提供几何信息。两者结合,才能在各种场景下稳定输出车道线。
4.4 本章知识体系
下面这张图总结了车道线检测的核心逻辑。你可以把它当作一个快速参考。
这张图把视觉和激光雷达两条技术路线并列展示。你可以看到,两者最终都汇聚到“车道线参数输出”。在实际工程中,我建议你根据场景动态选择主传感器,而不是固定用一种方案。
最后说一句:车道线检测没有银弹。视觉方案便宜但脆弱,激光雷达稳定但昂贵。作为工程师,我们的任务不是争论哪个更好,而是根据项目需求、成本预算、运行场景,做出最合理的技术选型。嗯,这就是我这些年最大的体会。