第三章 车载传感器数据处理:从原始信号到融合基础

各位工程师朋友,今天我们来聊聊车载传感器数据处理。说实话,这是整个自动驾驶感知系统的「地基」。地基不牢,上层算法再牛也白搭。我在多个量产项目中踩过坑,今天把这些经验掰开揉碎讲给你听。

3.1 摄像头数据预处理:让AI「看」得更清楚

摄像头是自动驾驶的「眼睛」。但原始图像数据,AI模型是没法直接用的。为什么?因为摄像头输出的RAW格式数据,包含大量噪声和畸变。

我个人习惯把预处理分成三步走:

  1. 去马赛克(Demosaic):把Bayer阵列还原成RGB
  2. 白平衡校正:消除不同光源下的色偏
  3. 畸变校正:修正镜头带来的桶形/枕形畸变

嗯,这里要注意——畸变校正的参数是跟镜头绑定的。我遇到过项目,换了镜头供应商忘了更新校正参数,结果车道线识别全歪了。所以,每次换镜头,必须重新标定。

关键点:预处理不是「越复杂越好」。在Ollama推理场景下,我们通常只做必要的校正,然后直接送入模型。过多的滤波反而会丢失细节。

代码示例(简化版):

import cv2
import numpy as np

# 读取原始图像
img_raw = cv2.imread('camera_raw.bayer', cv2.IMREAD_UNCHANGED)

# 去马赛克(使用OpenCV的demosaicing)
img_rgb = cv2.cvtColor(img_raw, cv2.COLOR_BayerBG2BGR)

# 白平衡校正(灰度世界假设)
def white_balance(img):
    avg_b = np.mean(img[:,:,0])
    avg_g = np.mean(img[:,:,1])
    avg_r = np.mean(img[:,:,2])
    scale_b = avg_g / avg_b
    scale_r = avg_g / avg_r
    img[:,:,0] = np.clip(img[:,:,0] * scale_b, 0, 255)
    img[:,:,2] = np.clip(img[:,:,2] * scale_r, 0, 255)
    return img

img_wb = white_balance(img_rgb)

# 畸变校正(需要相机内参和畸变系数)
camera_matrix = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])
dist_coeffs = np.array([k1, k2, p1, p2, k3])
img_undistorted = cv2.undistort(img_wb, camera_matrix, dist_coeffs)

我的经验:在Ollama中,建议把预处理写成独立的C++模块,通过Python绑定调用。这样推理时不会因为Python的GIL锁拖慢速度。

3.2 激光雷达点云处理:从稀疏点到结构化数据

激光雷达输出的是点云——一堆(x, y, z, intensity)的四维点。但AI模型通常需要规则化的输入,比如体素网格或鸟瞰图。

我常用的处理流程:

  • 体素滤波:降采样,减少计算量
  • 地面分割:去掉地面点,减少干扰
  • 聚类:把点云分成一个个物体
  • 特征提取:计算每个聚类的形状、大小、方向

曾经有个项目,点云数据量太大,Ollama推理延迟飙到200ms。后来发现是体素滤波的格子设得太小。你想想看,64线激光雷达每秒30万点,每个点都处理,CPU直接冒烟。后来我把体素大小从0.1m调到0.2m,延迟降到50ms,精度只掉了2%。

避坑指南:我曾经在雨雪天气测试,发现点云中大量噪声点。后来加了「离群点移除」步骤,效果立竿见影。记住:激光雷达不是万能的,天气不好时一定要做滤波。

点云处理核心代码:

import open3d as o3d
import numpy as np

# 读取点云
pcd = o3d.io.read_point_cloud('lidar.pcd')

# 体素降采样
voxel_size = 0.2  # 单位:米
pcd_down = pcd.voxel_down_sample(voxel_size)

# 地面分割(使用RANSAC)
plane_model, inliers = pcd_down.segment_plane(distance_threshold=0.2,
                                              ransac_n=3,
                                              num_iterations=100)
outliers = pcd_down.select_by_index(inliers, invert=True)

# 欧式聚类
labels = np.array(outliers.cluster_dbscan(eps=0.5, min_points=10))

3.3 毫米波雷达信号处理:多普勒效应的妙用

毫米波雷达跟摄像头、激光雷达不一样。它直接输出目标列表——每个目标有距离、速度、角度。但原始信号是时域/频域数据,需要经过FFT和CFAR检测才能提取目标。

我个人觉得,毫米波雷达最大的价值在于「速度测量」。摄像头和激光雷达都测不准速度,但雷达可以。为什么呢?因为多普勒效应直接给出径向速度。

处理流程:

  1. 距离FFT:把时域信号转成距离-幅度谱
  2. 多普勒FFT:提取速度信息
  3. CFAR检测:自适应阈值,找出真实目标
  4. 角度估计:通过多天线相位差计算角度

注意:毫米波雷达的「角度分辨率」通常不高(比如10度左右)。所以雷达检测到的目标,位置精度不如激光雷达。但速度精度极高(误差<0.1m/s)。

简化代码示例:

import numpy as np
from scipy.fft import fft

# 假设接收到的中频信号(128个chirp,每个chirp 256个采样点)
signal = np.random.randn(128, 256)  # 模拟数据

# 距离FFT(对每个chirp做FFT)
range_fft = fft(signal, axis=1)

# 多普勒FFT(对每个距离bin做FFT)
doppler_fft = fft(range_fft, axis=0)

# CFAR检测(简化版:固定阈值)
threshold = 3 * np.std(doppler_fft)
detections = np.where(np.abs(doppler_fft) > threshold)

我的建议:在Ollama中,毫米波雷达数据通常不需要做复杂的预处理。直接拿雷达输出的目标列表,跟其他传感器做融合就行。别在雷达预处理上花太多时间,它的数据格式已经很友好了。

3.4 多传感器融合基础:1+1 > 2

好了,现在三个传感器都处理完了。但单独用哪个都不完美:

传感器优点缺点
摄像头纹理丰富,能识别车道线、交通标志受光照影响大,测距不准
激光雷达3D位置精确,不受光照影响雨雪天性能下降,成本高
毫米波雷达速度精确,全天候工作角度分辨率低,无法识别物体类型

所以,必须融合。我常用的融合策略是「松耦合」——先各自检测,再在目标级别做关联和融合。

核心步骤:

  • 时间同步:把不同频率的数据对齐到同一时间戳
  • 空间对齐:把所有传感器坐标转换到车体坐标系
  • 数据关联:判断不同传感器检测到的是不是同一个目标
  • 状态估计:用卡尔曼滤波融合位置、速度、类别

曾经踩过的坑:时间同步没做好,摄像头检测到行人时,激光雷达已经扫到下一帧了。结果融合出来的目标位置差了2米。后来我加了「插值对齐」,问题解决。

下面我用SVG画一张融合流程图,帮你理清思路:

多传感器融合处理流程 摄像头 激光雷达 毫米波雷达 畸变校正 · 白平衡 体素滤波 · 地面分割 FFT · CFAR检测 目标检测 · 分类 聚类 · 特征提取 目标列表生成 多传感器融合 融合目标列表

融合的最终目标,是输出一个「统一的目标列表」。每个目标包含:位置(x, y, z)、速度(vx, vy)、类别(车/人/自行车)、置信度。这个列表直接喂给Ollama做推理。

我的经验:在Ollama中,融合后的数据最好用Protocol Buffers序列化。这样跨模块传输时,解析速度快,而且类型安全。别用JSON,解析太慢了。

好了,这一章的内容就到这里。传感器数据处理是基础中的基础,但也是最容易出bug的地方。希望我的这些经验能帮你少走弯路。


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