4、感知系统(一):摄像头标定与图像预处理、目标检测(YOLO系列、Transformer)在车端的部署优化

各位同学,欢迎来到感知系统的第一讲。说实话,感知是整个自动驾驶系统里最“卷”的模块,没有之一。摄像头又是感知的绝对主力,今天我们就来聊聊摄像头从上车到真正能用的那些关键步骤。

4.1 摄像头标定:让摄像头“认识”这个世界

摄像头装上车,它看到的是一堆像素点。怎么把这些像素点映射到真实世界的三维坐标?这就是标定要解决的问题。我个人习惯把标定分成两部分:内参标定和外参标定。

4.1.1 内参标定

内参,说白了就是摄像头自己的“脾气”。焦距、主点、畸变系数,这些参数决定了光线怎么穿过镜头落到传感器上。

我在项目中遇到过最头疼的事——同一批次的摄像头,内参居然有差异。你想想看,如果每个摄像头都用自己的“出厂默认参数”,那融合出来的结果肯定一塌糊涂。

常用的标定方法是张正友标定法。用棋盘格,拍个十几张不同角度的照片,然后解算。代码实现其实不复杂,OpenCV 里直接有现成的函数。

import cv2
import numpy as np

# 准备棋盘格角点坐标
objp = np.zeros((6*9, 3), np.float32)
objp[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2)

# 存储所有图像的点
objpoints = []  # 世界坐标系中的点
imgpoints = []  # 图像坐标系中的点

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
    if ret:
        objpoints.append(objp)
        imgpoints.append(corners)

# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, gray.shape[::-1], None, None
)
我的经验:标定板一定要平整。我曾经用一张有点褶皱的打印纸做标定,结果畸变参数偏差了 15% 以上。建议用亚克力板或者玻璃板贴棋盘格。

4.1.2 外参标定

外参解决的是“摄像头装在车上的什么位置、什么角度”。旋转矩阵 R 和平移向量 T,把摄像头坐标系和车身坐标系对齐。

外参标定有两种主流方式:

  • 离线标定:用标定间,车停到固定位置,拍标定板。精度高,但费时费力。
  • 在线标定:利用车道线、静止物体等特征,实时估计外参。车开起来就能自动校准。

嗯,这里要注意。量产车上,外参是会漂移的。颠簸、碰撞、甚至温度变化,都可能导致摄像头松动。所以在线标定几乎是必备功能。

避坑指南:我曾经遇到一个项目,外参标定后一切正常,但跑了 2000 公里后,车道线投影偏差了 30 厘米。查了半天,发现是摄像头支架的螺丝松了。从那以后,我要求所有外参标定结果必须加上置信度评估,一旦置信度下降就触发重新标定。

4.2 图像预处理:给算法“喂”好数据

摄像头采集的原始图像,直接扔给神经网络?不行。原始图像有噪声、光照变化、畸变,这些都会严重影响检测效果。

4.2.1 去畸变

广角摄像头拍出来的图像,边缘有明显的桶形畸变。如果不处理,远处的行人可能被“拉弯”。去畸变就是根据内参中的畸变系数,把图像“拉直”。

# 去畸变
undistorted = cv2.undistort(img, mtx, dist, None, newcameramtx)

这里有个性能问题。去畸变需要对每个像素做映射计算,在车端芯片上可能成为瓶颈。我建议用查表法——提前算好映射表,运行时直接查表。

4.2.2 图像增强

车端摄像头面临的环境太复杂了。逆光、隧道、夜间、雨雾……每个场景都需要不同的处理策略。

场景 问题 常用方法
逆光 目标区域过暗 自适应直方图均衡化(CLAHE)
夜间 信噪比低 多帧融合、去噪
雨雾 对比度下降 去雾算法(暗通道先验)
隧道 亮度突变 自动曝光控制 + 增益调整
核心原则:预处理不能过度。过度增强会引入伪影,反而降低检测精度。我一般会先跑一个轻量级的场景分类器,根据场景选择不同的预处理策略。

4.3 目标检测:从 YOLO 到 Transformer

目标检测是感知系统的核心任务。车端需要检测车辆、行人、骑行者、交通标志、车道线……而且必须在毫秒级完成。

4.3.1 YOLO 系列在车端的部署

YOLO 系列,尤其是 YOLOv5 和 YOLOv8,是车端部署的常青树。为什么?

  • 速度快:单阶段检测,一次前向推理直接输出结果。
  • 精度够用:在车端场景下,mAP 能达到 70-80%。
  • 生态好:有成熟的 ONNX 导出和 TensorRT 部署方案。

部署优化的关键点:

  1. 模型量化:FP32 转 INT8,推理速度提升 2-3 倍,精度损失控制在 1% 以内。
  2. 算子融合:Conv + BN + ReLU 融合成一个算子,减少内存访问。
  3. 输入尺寸优化:640x640 是 YOLO 的默认输入,但车端可以用 480x480,速度更快。
# TensorRT 部署示例(伪代码)
import tensorrt as trt

# 构建引擎
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network()
parser = trt.OnnxParser(network, TRT_LOGGER)
parser.parse_from_file("yolov8n.onnx")

# 配置量化
config = builder.create_builder_config()
config.set_flag(trt.BuilderFlag.INT8)
config.int8_calibrator = MyCalibrator()  # 自定义校准器

# 构建引擎
engine = builder.build_engine(network, config)
我的建议:量化校准数据集一定要用真实的车端场景数据。用 COCO 数据集做校准,到了实际路测,精度可能掉 3-5 个点。我吃过这个亏。

4.3.2 Transformer 在车端的落地

DETR 和它的变体(如 Deformable DETR)带来了端到端检测的新思路。不需要 NMS,不需要 anchor,直接输出检测结果。

但 Transformer 在车端部署有两个硬伤:

  • 计算量大:自注意力机制的计算复杂度是 O(n²),对高分辨率图像不友好。
  • 推理延迟高:在 Orin 上,DETR 的推理延迟可能是 YOLOv8 的 3-5 倍。

怎么优化?

我个人比较看好 Deformable DETR 的方案。它用可变形注意力代替全局注意力,只关注关键位置的特征。计算量大幅下降,精度反而有提升。

另外,混合架构也是一个方向。比如用 CNN 做 backbone 提取特征,用 Transformer 做 head 进行检测。这样既保留了 CNN 的高效特征提取能力,又利用了 Transformer 的全局建模能力。

量产现状:截至 2024 年,Transformer 在车端还没有大规模量产。YOLO 系列仍然是主流。但我判断,未来 2-3 年,随着芯片算力的提升和模型压缩技术的进步,Transformer 会逐步进入量产。

4.4 车端部署的工程化思考

算法再好,部署不到车端也是白搭。这里分享几个工程化的经验。

4.4.1 芯片适配

不同芯片的算子支持不一样。Orin 对 Transformer 的支持比 TDA4 好很多。选芯片的时候,一定要先跑一遍模型,看看哪些算子不支持,哪些需要手动实现。

4.4.2 流水线设计

图像采集、预处理、推理、后处理,这些步骤可以做成流水线。用多线程 + 环形缓冲区,让每个阶段并行执行。我见过一个项目,流水线优化后,整体吞吐量提升了 40%。

4.4.3 故障处理

摄像头被泥巴糊住了怎么办?算法检测结果置信度持续偏低怎么办?量产系统必须有故障检测和降级机制。比如检测到摄像头遮挡,就切换到雷达为主、摄像头为辅的模式。

避坑指南:我曾经遇到一个 case,摄像头标定参数在 OTA 升级后丢失了。原因是标定参数存在掉电易失的存储区域。从那以后,我要求所有标定参数必须做三备份:Flash、E2PROM、云端。

好了,这一讲的内容就到这里。下一讲我们会深入聊聊激光雷达和毫米波雷达的感知方案,以及多传感器融合的那些坑。各位同学,有什么问题可以随时交流。


公众号:蓝海资料掘金营,微信deep3321