22、语音交互:Google Assistant 集成,自定义语音引擎(VHAL 语音控制)

语音交互在车载系统里,说实话,已经不只是“锦上添花”的功能了。用户坐进车里,第一件事可能就是喊“Hey Google,导航去公司”。如果这个链路没打通,整个座舱体验会大打折扣。

今天我们来聊聊 Android Automotive OS 里的语音交互。重点有两个:一是怎么把 Google Assistant 集成进来,二是如果车厂想用自己的语音引擎,怎么通过 VHAL 做语音控制。

Google Assistant 的集成方式

Google Assistant 在 AAOS 里不是随便一个 App,它是系统级服务。集成它,说白了就是走 Google 的 GMS 认证流程。

我当年在项目里第一次接 Assistant,以为加个 APK 就行。结果发现,事情没那么简单。

核心要点:Google Assistant 需要 Automotive GMS 包,并且要配置好对应的权限和 Service。

集成步骤大致如下:

  1. 获取 GMS 认证——车厂需要和 Google 签协议,拿到 Automotive GMS 包。
  2. 配置 Assistant Service——在 AndroidManifest.xml 里声明 Assistant 服务。
  3. 绑定麦克风输入——语音数据要从车载麦克风走 Audio HAL 传到 Assistant。
  4. 处理响应——Assistant 返回的结果,要么播报,要么执行动作。

这里有个坑。我记得有一次,麦克风数据一直传不过去。查了半天,发现是 Audio Policy 配置里没把麦克风设备设为“语音识别”的默认源。嗯,这种细节很容易漏掉。

自定义语音引擎:为什么需要?

有些车厂不想用 Google Assistant。原因很多:数据隐私、本地化需求、或者单纯想打造自己的品牌语音助手。

这时候就需要自定义语音引擎。说白了,就是自己写一个语音识别 + 语义理解的 Service,然后把它注册成系统默认的 Assistant。

怎么做?核心就两步:

  • 实现一个 VoiceInteractionService 的子类。
  • 在 Manifest 里声明 BIND_VOICE_INTERACTION 权限。

代码大概长这样:

public class MyCarVoiceService extends VoiceInteractionService {
    @Override
    public void onReady() {
        super.onReady();
        // 初始化语音引擎
        initVoiceEngine();
    }

    private void initVoiceEngine() {
        // 加载本地语音模型
        // 或者连接云端语音服务
    }
}

然后在 Manifest 里:

<service
    android:name=".MyCarVoiceService"
    android:permission="android.permission.BIND_VOICE_INTERACTION">
    <intent-filter>
        <action android:name="android.service.voice.VoiceInteractionService" />
    </intent-filter>
</service>

提示:注册完 Service 后,还要在 Settings 里把默认 Assistant 改成你的 Service。否则系统不会调用它。

VHAL 语音控制:让语音真正“开车”

语音控制不只是“打开音乐”这种 App 层面的操作。真正有价值的是控制车辆硬件——比如调空调、开窗户、调座椅。

这些操作怎么通过语音实现?答案就是 VHAL。

VHAL(Vehicle Hardware Abstraction Layer)是 AAOS 里访问车辆硬件的标准接口。语音引擎识别到用户的意图后,通过 VHAL 下发指令,车就能动起来。

举个例子,用户说“把空调调到 24 度”。流程是这样的:

  1. 语音引擎识别出“空调”和“24度”。
  2. 引擎调用 VHAL 的 setProperty 方法,设置 HVAC 温度属性。
  3. VHAL 把指令传给 MCU,空调开始工作。

代码示例:

public void setHvacTemperature(int zone, float temp) {
    VehiclePropValue prop = new VehiclePropValue();
    prop.prop = VehicleProperty.HVAC_TEMPERATURE_SET;
    prop.areaId = zone;
    prop.value.floatValue = temp;
    prop.status = VehiclePropertyStatus.AVAILABLE;

    try {
        mVehicle.setProperty(prop);
    } catch (RemoteException e) {
        Log.e("VoiceControl", "设置空调温度失败", e);
    }
}

警告:VHAL 的 setProperty 是异步的。不要在主线程里调用,也不要做同步等待。我曾经见过一个项目,因为同步等待 VHAL 返回,导致 UI 线程卡死,整个系统都冻住了。

语音控制的权限与安全

语音控制车辆硬件,涉及安全问题。你不能让语音随便控制刹车、油门吧?

AAOS 里,VHAL 的每个属性都有访问权限控制。语音引擎需要申请对应的权限,才能在 VHAL 里操作。

常见的权限分级:

权限级别 示例操作 是否需要用户确认
低风险 调音量、切歌 不需要
中风险 调空调、开窗户 建议确认
高风险 开车门、启动引擎 必须确认

我建议,中风险以上的操作,语音执行前最好弹个确认框。或者要求用户说“确认”才能执行。否则误触发会很麻烦。

SVG 流程图:语音控制 VHAL 的完整链路

用户语音输入 语音引擎识别 意图解析 VHAL 指令下发 权限检查 车辆硬件执行 拒绝/确认流程 语音反馈播报 语音处理 VHAL 控制 硬件执行 安全确认 反馈播报

这张图展示了从用户说出指令到车辆硬件执行的完整链路。你想想看,中间任何一个环节出问题,语音控制就废了。所以每个节点都要做好异常处理。

实际项目中的避坑指南

我在几个项目里踩过不少坑,挑几个典型的说说:

  • 麦克风唤醒延迟——用户喊了“Hey Google”,但系统要等两三秒才响应。原因是麦克风没做低功耗唤醒。后来我们改成了 DSP 硬件唤醒,延迟降到 0.5 秒以内。
  • VHAL 属性冲突——语音引擎和 HMI 同时操作同一个 VHAL 属性,导致状态不一致。解决办法是加一个锁机制,同一时间只允许一个模块写属性。
  • 多音区处理——车里四个座位,每个座位的人都能喊语音。但空调温度只能调一个区的。我们后来做了“焦点座位”策略,谁先唤醒,语音就优先处理谁的命令。

小技巧:调试语音控制 VHAL 时,可以用 dumpsys vehicle_hal 查看当前所有属性的值。这样能快速定位是语音引擎没发指令,还是 VHAL 没执行。

总结一下

语音交互在 AAOS 里,核心就是两件事:集成 Google Assistant 或者自建语音引擎,然后通过 VHAL 控制车辆硬件。

集成 Assistant 走 GMS 认证流程,自建引擎则要实现 VoiceInteractionService。VHAL 语音控制要注意权限分级和安全确认,别让语音随便动刹车油门。

嗯,语音这块其实还有很多细节,比如多语言支持、离线识别、降噪处理。但今天先把主链路讲清楚,剩下的后面再聊。


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