从分类到拟合:解析Ultra Fast车道线检测模型的“快”与“准”

张开发
2026/4/11 9:35:28 15 分钟阅读

分享文章

从分类到拟合:解析Ultra Fast车道线检测模型的“快”与“准”
1. 为什么车道线检测需要又快又准想象一下你正在高速公路上以120km/h的速度行驶每秒钟车辆就会前进33米。如果车道线检测模型处理一帧图像需要100毫秒那么在这段时间里车辆已经移动了3.3米——这足以让车辆偏离车道。这就是Ultra Fast车道线检测模型诞生的背景实时性就是安全性。传统车道线检测方法主要分为两类基于回归的方法直接预测车道线的坐标点就像用笔画线一样连续描点基于分割的方法对每个像素点进行分类判断是否属于车道线类似涂色游戏但实测下来回归方法在CULane数据集上准确率只有70%左右而分类方法能达到90%以上。这就像考试时选择题比填空题更容易拿高分一样分类任务有明确的选项约束不容易跑偏。2. 模型如何用分类替代回归2.1 网格化分类的妙招作者提出了一个反直觉的设计把连续的车道线位置预测变成离散的网格分类问题。具体操作是在图像高度方向预设18个关键行如288像素高度下取[121,131,...,287]在宽度方向将每行划分为200个格子让模型预测每个关键行中车道线落在哪个格子这就好比把预测某人身高的回归问题转化为判断他属于1.6-1.7m还是1.7-1.8m的分类问题。虽然损失了些精度但换来三个好处计算量大幅降低从全图像素处理到只需处理18×200个格子输出维度固定便于硬件优化准确率反而更高约束在有限选项内# 网格化处理的核心代码dataset.py def _grid_pts(self, pts, num_cols, w): col_sample np.linspace(0, w - 1, num_cols) # 将宽度划分为num_cols个格子 for i in range(num_lanes): # 遍历每条车道线 pti pts[i, :, 1] # 获取纵坐标 to_pts[:, i] np.asarray( [int(pt // (col_sample[1]-col_sample[0])) if pt!-1 else num_cols for pt in pti]) # 判断属于哪个格子2.2 结构化先验的引入模型还巧妙利用了车道线的空间特性纵向连续性相邻行的车道点不会突然跳跃横向平滑性车道线曲率变化是渐进的通过两种特殊设计的损失函数实现相邻行分类一致性损失ParsingRelationLoss强迫模型对相邻行的预测分布相似二阶差分约束损失ParsingRelationDis用车道线曲率的二阶导数趋近零来保证平滑# 连续性损失实现loss.py class ParsingRelationLoss(nn.Module): def forward(self, logits): # logits形状[B,201,18,4] for i in range(0, h-1): loss_all.append(logits[:,:,i,:] - logits[:,:,i1,:]) # 相邻行预测差异 return smooth_l1_loss(torch.cat(loss_all), torch.zeros_like(loss))3. 训练阶段的秘密武器3.1 辅助分割的师徒模式模型在训练时采用双任务学习主任务网格化分类徒弟辅助任务像素级分割师傅这个设计的精妙之处在于训练时分割任务提供更精细的监督信号就像师傅手把手教徒弟推理时丢弃分割分支徒弟出师独立工作速度不受影响分割标签只用生成不用标注通过车道线延伸算法自动生成实测表明这种模式比单纯分类训练准确率提升约5%而推理速度保持不变。3.2 车道线延伸的数据增强原始标注的车道线往往只覆盖道路中央部分如下图左。作者开发了自动延伸算法对已有车道点进行线性拟合将拟合线延伸到图像边界过滤掉不合理的延伸如超出图像范围# 车道线延伸代码片段dataset.py valid_idx all_idx_cp[i, valid, :] # 获取有效车道点 p np.polyfit(valid_idx_half[:,0], valid_idx_half[:,1], deg1) # 线性拟合 fitted np.polyval(p, all_idx_cp[i, pos:, 0]) # 延伸预测 all_idx_cp[i, pos:, 1] fitted # 更新延伸后的点4. 模型架构的极简主义4.1 骨干网络的选择论文使用了ResNet作为backbone但做了三点精简只保留前三个阶段的特征图C3/C4/C5输出通道数压缩为原版的1/4使用深度可分离卷积替代常规卷积这样的设计使得在Jetson Nano上也能达到150FPS的推理速度内存占用仅800MB。4.2 双分支设计网络的两个分支各司其职分类分支输入骨干网络输出的特征图处理全局平均池化 两层全连接输出18×4个201维分类向量分割分支仅训练时输入多尺度特征图1/8,1/16,1/32分辨率处理转置卷积上采样 特征融合输出与原图同分辨率的分割图# 网络前向代码model.py def forward(self, x): x2, x3, fea self.model(x) # 骨干网络 if self.use_aux: # 训练时 x2 self.aux_header2(x2) # 1/8分辨率处理 x3 F.interpolate(x3, scale_factor2) # 1/16→1/8 x4 F.interpolate(self.aux_header4(fea), scale_factor4) # 1/32→1/8 aux_seg self.aux_combine(torch.cat([x2,x3,x4], dim1)) fea self.pool(fea).view(-1, 1800) # 分类分支处理 group_cls self.cls(fea).view(-1, *self.cls_dim) return (group_cls, aux_seg) if self.use_aux else group_cls5. 实战部署建议在Jetson Nano上部署时我踩过三个坑值得分享TensorRT加速FP16模式比FP32快2倍但要注意最后一层Softmax的精度损失内存优化设置torch.backends.cudnn.benchmarkTrue可提升20%速度温度控制持续高负载时最好加散热风扇否则会触发降频对于实际应用建议调整两个参数行锚点数量18个适合高速场景城市道路可增至36个网格划分密度200格在1280×720分辨率下够用4K视频建议400格车道线检测看似简单但要同时做到快和准就需要在算法设计上像Ultra Fast模型这样另辟蹊径。这种将回归问题转化为分类问题的思路在其他视觉任务如人脸关键点检测中也有借鉴价值。

更多文章