27、自动驾驶感知:多传感器融合、点云处理、车道线检测、障碍物识别与跟踪
自动驾驶感知,说白了就是给车装上“眼睛”和“大脑”。
我做了这么多年嵌入式,最深的体会是:单一传感器永远有短板。摄像头怕逆光、怕黑夜;激光雷达怕雨雪、怕灰尘;毫米波雷达分辨率又不够。所以,多传感器融合不是选择题,而是必答题。
这一章,我们就来聊聊在 Jetson 平台上,如何把这几样东西揉到一起,做出靠谱的感知系统。
核心要点:多传感器融合不是简单叠加,而是数据层面的对齐、时间层面的同步、决策层面的互补。
27.1 多传感器融合:为什么必须做?
我见过不少团队,一开始只靠摄像头做感知。结果呢?一到隧道口,车就“瞎”了。为什么?因为摄像头在光照剧烈变化时,动态范围根本跟不上。
激光雷达呢?它不怕暗,但怕大雨。雨滴会反射激光,产生大量噪点。
所以,融合的本质是取长补短。
常见的融合策略
- 前融合(Early Fusion):在原始数据层就合并。比如把激光点云投影到图像上,再一起送进神经网络。好处是信息损失少,坏处是计算量大。
- 后融合(Late Fusion):每个传感器各自做检测,最后把结果合并。比如摄像头检测到行人,激光雷达也检测到行人,然后做匹配。好处是模块解耦,坏处是可能漏掉单传感器没检测到的目标。
- 特征融合(Feature Fusion):在神经网络中间层做融合。这是目前工业界的主流做法,兼顾了精度和效率。
我的经验:在 Jetson Orin 上,我建议用特征融合。因为前融合太吃带宽,后融合又容易丢目标。特征融合刚好在中间,性能与精度平衡得最好。
27.2 点云处理:从原始数据到结构化信息
激光雷达出来的数据,是一堆三维坐标点。每个点还有反射强度信息。但原始点云不能直接用——太稀疏、太乱。
我们需要做几件事:
- 滤波:去掉离群点、地面点。地面点对障碍物检测是干扰。
- 降采样:点云太密了,计算扛不住。用体素滤波器(Voxel Grid Filter)把空间划分成小格子,每个格子只保留一个点。
- 分割:把点云分成不同的物体。常用的有欧几里得聚类、区域生长。
- 特征提取:对每个物体提取形状、大小、朝向等特征。
我推荐用 PointPillars 这个网络。它把点云投影成伪图像,然后用 2D 卷积处理。在 Jetson 上跑起来非常快,精度也够用。
# 点云体素滤波示例(使用 Open3D)
import open3d as o3d
pcd = o3d.io.read_point_cloud("lidar.pcd")
# 体素下采样,每个体素边长 0.2 米
downsampled = pcd.voxel_down_sample(voxel_size=0.2)
# 去除离群点
cl, ind = downsampled.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
filtered = downsampled.select_by_index(ind)
o3d.visualization.draw_geometries([filtered])
注意:体素大小不能乱设。设太大,小物体会丢失;设太小,计算量爆炸。我一般从 0.1 米开始试,根据场景调整。
27.3 车道线检测:视觉的看家本领
车道线检测,说白了就是告诉车“你该走哪条道”。
传统方法用霍夫变换、滑动窗口。但现在的自动驾驶,基本都用深度学习。我常用的网络是 LaneNet 和 Ultra-Fast-Lane-Detection。
LaneNet 的思路很巧妙:它先做语义分割,把每个车道线像素标出来;然后做聚类,把属于同一条线的像素归到一起。这样即使车道线被遮挡,也能推断出完整曲线。
在 Jetson 上部署时,要注意几点:
- 输入分辨率不要太高。640x360 就够,再高帧率就掉下来了。
- 用 TensorRT 做 INT8 量化。我试过,精度损失不到 1%,但速度能快 3 倍。
- 后处理用 CUDA 加速。尤其是曲线拟合,CPU 上跑太慢。
避坑指南:我曾经在一条乡间小路上翻车——因为路沿是黄色的,车道线也是黄色的,模型把路沿当成了车道线。后来我在训练数据里加了大量“非车道线但颜色相似”的负样本,才解决这个问题。
27.4 障碍物识别与跟踪:不只是“看到”
识别出障碍物只是第一步。你还要知道它往哪走、速度多快、会不会撞上。这就是跟踪要做的事。
我常用的跟踪框架是 SORT(Simple Online and Realtime Tracking)和它的升级版 Deep SORT。
SORT 的核心是卡尔曼滤波 + 匈牙利匹配。卡尔曼滤波预测目标下一帧的位置,匈牙利匹配把检测结果和已有轨迹配对。简单、快、够用。
Deep SORT 在 SORT 基础上加了外观特征。用一个小网络提取每个目标的特征向量,匹配时不仅看位置,还看长相。这样即使目标短暂被遮挡,重新出现后也能认出它。
# 卡尔曼滤波预测示例(简化版)
import numpy as np
class KalmanFilter:
def __init__(self):
self.x = np.zeros(4) # [x, y, vx, vy]
self.P = np.eye(4) * 1000 # 初始不确定性很大
def predict(self, dt=0.1):
F = np.array([[1, 0, dt, 0],
[0, 1, 0, dt],
[0, 0, 1, 0],
[0, 0, 0, 1]])
self.x = F @ self.x
self.P = F @ self.P @ F.T
return self.x[:2] # 返回预测位置
关键点:跟踪的难点不在算法本身,而在工程实现。比如:检测帧率不稳定怎么办?目标 ID 频繁跳变怎么办?这些都需要在实际系统中反复调参。
27.5 系统架构:把一切串起来
下面这张图,是我在项目中常用的感知系统架构。它把前面讲的所有模块串在了一起。
你看,整个流程是串行的。传感器数据先做时间同步,然后对齐到统一坐标系,再进入特征融合模块,最后分发给不同的感知任务。
这里有个坑:时间同步。摄像头是 30fps,激光雷达是 10fps,毫米波雷达是 20fps。如果不做同步,你拿到的数据可能是“过去”的。我一般用 PTP(精确时间协议)给所有传感器打时间戳,然后在 Jetson 上做插值对齐。
27.6 在 Jetson 上的部署要点
| 模块 | 推荐方案 | Jetson 优化技巧 |
|---|---|---|
| 点云处理 | PointPillars + TensorRT | INT8 量化,CUDA 加速体素化 |
| 车道线检测 | Ultra-Fast-Lane-Detection | 输入 640x360,FP16 推理 |
| 障碍物检测 | YOLOX + 激光雷达融合 | 多线程流水线,检测与跟踪分离 |
| 目标跟踪 | Deep SORT | 特征提取网络用轻量级 MobileNet |
性能警告:在 Jetson Orin NX 上,如果同时跑点云 + 视觉 + 跟踪,GPU 占用率很容易冲到 90% 以上。我建议用 DLA(深度学习加速器) 分担一部分推理任务,把 GPU 留给后处理和可视化。
嗯,说到可视化,我习惯用 ROS 2 + RViz2 做调试。把点云、检测框、车道线都发布成 topic,在 RViz 里一看就知道哪里有问题。这个习惯帮我省了不少排查时间。
最后说一句:感知系统没有“完美”这一说。你只能在精度、速度、鲁棒性之间做取舍。我的建议是——先让系统跑起来,再一点点优化。别一开始就追求 99% 的精度,那会让你陷入调参的泥潭。