23、摄像头系统:Camera HAL 与车载摄像头(环视、DMS),视频流处理
车载摄像头,说白了就是汽车的「眼睛」。环视让你看清周围,DMS盯着驾驶员有没有走神。但要把这些画面流畅地送到屏幕上,背后是一整套复杂的流水线。
我个人习惯把摄像头系统分成三层:硬件驱动层、HAL抽象层、应用框架层。今天咱们重点聊HAL层,因为它是连接硬件和系统的桥梁,也是坑最多的地方。
23.1 车载摄像头的特殊之处
手机摄像头和车载摄像头,虽然都叫Camera,但完全是两码事。我在项目中遇到过最头疼的问题——车载摄像头对实时性的要求极其苛刻。
举个例子:环视系统要求4路摄像头画面拼接后延迟不超过100ms。一旦超了,驾驶员看到的就是「过去」的画面,倒车时很容易出事。
| 对比项 | 手机摄像头 | 车载摄像头 |
|---|---|---|
| 帧率要求 | 30fps 够用 | 环视30fps,DMS 15-30fps |
| 延迟容忍 | 200-300ms 可接受 | 环视<100ms,DMS<150ms |
| 工作温度 | 0~40°C | -40~85°C |
| 安全等级 | 无要求 | ASIL-B 或更高 |
核心要点:车载摄像头系统必须满足功能安全(ISO 26262)要求。HAL层需要支持故障检测、超时监控、数据完整性校验。
23.2 Camera HAL 架构解析
Android Automotive 的 Camera HAL 基于 HIDL(Treble架构) 或 AIDL 实现。我个人更推荐新项目直接用AIDL,因为HIDL已经被Google标记为废弃了。
HAL层主要干三件事:
- 设备管理:打开/关闭摄像头,配置分辨率、帧率、曝光参数
- 数据流控制:启动/停止视频流,处理buffer队列
- 元数据传递:把摄像头特性(如视场角、畸变参数)传给上层
来看一个典型的HAL接口定义:
// ICameraDevice.hal
interface ICameraDevice {
// 打开摄像头,返回一个流通道
open(StreamConfig config) generates (IStreamChannel channel);
// 获取摄像头特性
getCameraCharacteristics() generates (CameraMetadata metadata);
// 关闭摄像头
close();
};
interface IStreamChannel {
// 开始流传输
startStream();
// 停止流传输
stopStream();
// 注册buffer回调
setBufferCallback(IBufferCallback cb);
};
我的经验:车载摄像头通常有多个物理接口(FPD-Link、GMSL、MIPI CSI-2)。HAL层最好抽象出一个「物理端口」的概念,方便适配不同硬件。
23.3 环视系统:4路画面的同步难题
环视系统需要4个广角摄像头同时工作。你想想看,如果4路画面不同步,拼接出来的俯视图就会出现「鬼影」——车已经动了,某个角度的画面还停留在上一帧。
我曾经在调试环视系统时,发现左摄像头总是比右摄像头慢2帧。查了半天,原来是左摄像头的I2C时钟配置错了,导致曝光时间偏长。
解决同步问题的常用方法:
- 硬件同步(推荐):所有摄像头共用同一个帧同步信号(VSYNC)
- 软件时间戳:在HAL层给每帧打上硬件时间戳,上层根据时间戳对齐
- Buffer队列同步:使用同步栅栏(Sync Fence)确保4路buffer同时就绪
环视系统的典型数据流:
// 伪代码:环视数据流处理
void processSurroundView() {
// 1. 从4个摄像头获取原始帧
Buffer buf_left = cameraLeft->getFrame();
Buffer buf_right = cameraRight->getFrame();
Buffer buf_front = cameraFront->getFrame();
Buffer buf_rear = cameraRear->getFrame();
// 2. 等待所有buffer就绪(同步点)
syncFence->waitAll({buf_left, buf_right, buf_front, buf_rear});
// 3. 畸变校正 + 透视变换
buf_left = undistort(buf_left, calibData.left);
buf_right = undistort(buf_right, calibData.right);
// ... 其他摄像头同理
// 4. 拼接成俯视图
Image stitched = stitch({buf_left, buf_right, buf_front, buf_rear});
// 5. 送显示
display->show(stitched);
}
23.4 DMS:驾驶员监控的特殊要求
DMS(Driver Monitoring System)和环视完全不同。它不需要广角,但需要红外补光和高灵敏度,因为要在夜间也能看清驾驶员的脸。
嗯,这里要注意:DMS摄像头通常安装在方向盘后方或A柱上,视角要覆盖驾驶员面部。HAL层需要支持ROI(感兴趣区域)裁剪,因为上层算法只关心人脸区域,不需要整帧画面。
DMS视频流处理的关键点:
- 低延迟编码:如果DMS需要上传云端分析,HAL层要支持硬件编码(如H.264)
- 隐私保护:DMS画面不能随意存储,HAL层最好提供「安全buffer」机制,防止应用层非法读取
- 红外LED控制:通过GPIO或I2C控制红外补光灯,HAL层要暴露这个接口
避坑指南:我曾经遇到过DMS摄像头在强光下画面过曝的问题。原因是HAL层没有做自动曝光(AE)的快速收敛。车载场景光照变化剧烈(进出隧道),AE算法必须能在1-2帧内调整到位。
23.5 视频流处理的性能优化
车载系统资源有限,不能像手机那样随便开4K 60fps。我建议遵循以下原则:
| 场景 | 推荐分辨率 | 推荐帧率 | 编码格式 |
|---|---|---|---|
| 环视 | 1280x720 x4 | 30fps | 原始YUV(不编码) |
| DMS | 640x480 | 15-30fps | H.264(如需上传) |
| 行车记录 | 1920x1080 | 30fps | H.265 |
性能优化的几个实战技巧:
- Buffer复用:不要每帧都申请新buffer,用环形缓冲区(Ring Buffer)循环使用
- 零拷贝:通过ION/DMA-BUF让摄像头直接写数据到GPU/显示内存,绕过CPU
- 多线程流水线:采集、处理、显示分别用不同线程,避免互相阻塞
下面这张图展示了整个视频流处理的核心逻辑:
我的建议:调试摄像头系统时,先在HAL层加一个「环回测试」模式——让摄像头采集的画面直接显示,不经过任何处理。这样可以快速判断问题是出在硬件、驱动还是上层算法。
最后说一句,车载摄像头系统最考验人的不是技术有多深,而是对细节的把控。一个像素的偏移、一帧的延迟、一个I2C时序的抖动,都可能导致整个系统不可用。嗯,慢慢调,别急。
公众号:蓝海资料掘金营,微信deep3321