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 显示 CarPropertyManager 车速/转速/续航 通过 CarPropertyManager 订阅车辆属性变化 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。这个同步是通过 CarPropertyManagerPropertyId 来保证的。

属性 PropertyId 数据来源 更新频率
车速 VEHICLE_SPEED CAN 总线 100ms
发动机转速 ENGINE_RPM ECU 100ms
续航里程 FUEL_LEVEL BMS 1000ms
导航箭头 NAVIGATION_TURN 导航 App 事件触发

我在项目中遇到过一个问题:车速在 Cluster 上显示正常,但 HUD 上总是延迟 300ms。后来发现是 HUD 的渲染管线里多了一层 BufferQueue。解决办法是让 HUD 直接订阅 CarPropertyManageronPropertyChanged 回调,而不是通过 Cluster 中转。

11.5 避坑指南

做 Cluster 和 HUD 开发,有几个坑我替你们踩过了:

  • 不要在主线程做渲染。 Cluster 的渲染线程要独立,否则 UI 卡顿会直接影响驾驶员判断。
  • HUD 的坐标系和屏幕坐标系不一样。 HUD 是投影到无穷远的,所以坐标变换要小心。我曾经把 HUD 上的导航箭头画到了挡风玻璃外面……
  • 报警灯的优先级要明确。 电池故障灯和胎压报警灯同时亮时,哪个在前?这个要在 InstrumentClusterService 里定义好优先级列表。
  • 测试时一定要用实车或高保真模拟器。 普通模拟器没有 CAN 信号,你测不出时序问题。

嗯,今天就先聊到这里。Cluster 和 HUD 的内容其实很深,后面我们还会讲到多屏互动和窗口管理。但今天这些基础,足够你上手写一个简单的仪表盘应用了。


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