11、仪表盘与抬头显示(HUD):Cluster 应用开发,InstrumentClusterService 与 HUD 接口
各位同学,今天我们来聊聊车载系统里最“显眼”的两个模块——仪表盘(Cluster)和抬头显示(HUD)。
说实话,这两个东西在 Android Automotive OS 里属于“高优先级”模块。为什么?因为驾驶员开车时,视线离开路面超过两秒,事故风险就翻倍。所以仪表盘和 HUD 的设计,核心就一句话:让信息一目了然,让交互零干扰。
我在做某款量产车的 Cluster 开发时,遇到过一个问题:仪表盘上的导航箭头比实际路口晚了 200 毫秒。结果测试车在园区里连续错过三个转弯……嗯,从那以后我对时序同步就特别敏感。
11.1 Cluster 应用开发的核心思路
Cluster 应用,说白了就是跑在仪表盘屏幕上的那个“数字仪表”。它显示车速、转速、续航、报警灯、导航提示等。在 Android Automotive OS 里,它不是一个普通 App,而是一个系统级特权应用。
为什么是特权应用?你想想看,普通 App 能随便抢占前台吗?不能。但仪表盘必须常驻显示,不能被其他应用覆盖。所以 Cluster 应用通常被配置为 system|persistent,并且拥有 SYSTEM_ALERT_WINDOW 权限。
关键点:Cluster 应用不能依赖 Activity 生命周期来管理显示。它应该使用 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 或者直接绑定到 Display 上。
我个人习惯的做法是:在 AndroidManifest.xml 中声明 android:persistent="true",然后在 SystemServer 启动时通过 InstrumentClusterService 来管理它的生命周期。
11.2 InstrumentClusterService 是什么?
InstrumentClusterService 是 Android Automotive OS 中专门管理仪表盘显示的系统服务。它运行在 system_server 进程中,负责:
- 注册 Cluster 应用
- 管理 Cluster 显示窗口
- 分发车辆信号(车速、转速、电量等)
- 处理 HUD 投射请求
它的工作流程大致是这样的:
看到这个图了吗?InstrumentClusterService 是中间那个“枢纽”。它从车辆信号源拿到数据,然后分发给 Cluster 应用和 HUD。Cluster 应用负责渲染,HUD 负责投射。
11.3 HUD 接口的设计与实现
HUD(Head-Up Display)就是把信息投射到前挡风玻璃上。在 Android Automotive OS 里,HUD 接口是通过 CarHudManager 暴露给应用的。
我记得第一次调 HUD 接口时,发现投射的图像总是歪的。查了半天,原来是 HUD_TRANSFORM_MATRIX 没配置对。HUD 的投影需要根据挡风玻璃的曲率和驾驶员的眼位做校准,这个矩阵就是干这个的。
小技巧:HUD 的亮度要跟随环境光自动调节。你可以通过 CarSensorManager 监听 SENSOR_TYPE_LIGHT 来实现。白天亮一点,晚上暗一点,不然晚上开车会被 HUD 晃瞎眼。
HUD 接口的核心方法如下:
// 注册 HUD 显示区域
CarHudManager hudManager = (CarHudManager) car.getCarManager(Car.HUD_SERVICE);
HudDisplay display = hudManager.getHudDisplay(HudDisplay.DISPLAY_TYPE_WINDOWSHIELD);
// 设置显示内容
HudLayer layer = display.createLayer(HudLayer.LAYER_TYPE_NAVIGATION);
layer.setContent(hudNavigationImage);
layer.setAlpha(0.8f);
layer.setTransformMatrix(calibrationMatrix);
// 提交显示
display.commitLayer(layer);
这里要注意,HudLayer 是有层级顺序的。导航箭头应该在顶层,车速信息在中间层,背景在底层。层级乱了,信息就重叠了。
警告:HUD 的刷新率不要超过 30fps。我见过有人把 HUD 刷到 60fps,结果挡风玻璃上的图像一直在抖,驾驶员反馈说“像看老电影”。HUD 的物理响应时间有限,刷太快反而适得其反。
11.4 Cluster 与 HUD 的数据同步
Cluster 和 HUD 显示的数据必须同步。比如车速,Cluster 上显示 60km/h,HUD 上就不能显示 58km/h。这个同步是通过 CarPropertyManager 的 PropertyId 来保证的。
| 属性 | PropertyId | 数据来源 | 更新频率 |
|---|---|---|---|
| 车速 | VEHICLE_SPEED | CAN 总线 | 100ms |
| 发动机转速 | ENGINE_RPM | ECU | 100ms |
| 续航里程 | FUEL_LEVEL | BMS | 1000ms |
| 导航箭头 | NAVIGATION_TURN | 导航 App | 事件触发 |
我在项目中遇到过一个问题:车速在 Cluster 上显示正常,但 HUD 上总是延迟 300ms。后来发现是 HUD 的渲染管线里多了一层 BufferQueue。解决办法是让 HUD 直接订阅 CarPropertyManager 的 onPropertyChanged 回调,而不是通过 Cluster 中转。
11.5 避坑指南
做 Cluster 和 HUD 开发,有几个坑我替你们踩过了:
- 不要在主线程做渲染。 Cluster 的渲染线程要独立,否则 UI 卡顿会直接影响驾驶员判断。
- HUD 的坐标系和屏幕坐标系不一样。 HUD 是投影到无穷远的,所以坐标变换要小心。我曾经把 HUD 上的导航箭头画到了挡风玻璃外面……
- 报警灯的优先级要明确。 电池故障灯和胎压报警灯同时亮时,哪个在前?这个要在
InstrumentClusterService里定义好优先级列表。 - 测试时一定要用实车或高保真模拟器。 普通模拟器没有 CAN 信号,你测不出时序问题。
嗯,今天就先聊到这里。Cluster 和 HUD 的内容其实很深,后面我们还会讲到多屏互动和窗口管理。但今天这些基础,足够你上手写一个简单的仪表盘应用了。