20、语音交互:车载语音助手集成、语音识别服务、TTS 引擎定制

语音交互,说白了就是给车装上「耳朵」和「嘴巴」。

在 Android Automotive OS 里做语音,跟手机上的体验完全不同。手机是你对着它说话,车机是你坐在驾驶位上,可能隔着一米远,周围还有胎噪、风噪、空调声。我刚开始做车载语音时,第一个坑就是——为什么我喊破喉咙,车机都没反应?

嗯,这里要注意:车载语音的核心不是「能不能识别」,而是「在嘈杂环境下能不能稳定识别」。

20.1 车载语音助手的整体架构

先看一张我画的架构图,帮你快速建立全局认知:

车载语音交互系统架构 驾驶员 / 乘客 语音指令 唤醒词检测(Hotword) 语音识别(ASR) 语义理解(NLP) 动作执行 / TTS 反馈

整个流程其实就四步:唤醒 → 识别 → 理解 → 执行。但每一步在车机上都藏着不少坑。

20.2 唤醒词检测(Hotword)

唤醒词,就是你说「你好,小驰」或者「Hey Google」那一下。车机平时处于低功耗监听状态,只对特定唤醒词响应。

我个人习惯用 Snowboy 或者 Porcupine 这类离线唤醒引擎。为什么不用在线?你想想看,如果车在地下车库没信号,唤醒都做不到,那语音助手就是个摆设。

关键点:唤醒词检测必须在本地完成,延迟要控制在 200ms 以内。超过这个时间,用户会感觉「这车反应好慢」。

我在项目中遇到过一个问题:某款车的空调噪音比较大,唤醒率从 95% 直接掉到 70%。后来怎么解决的?我们在麦克风阵列上做了波束成形(Beamforming),只聚焦驾驶员方向的声音。嗯,效果立竿见影。

20.3 语音识别服务(ASR)集成

ASR(Automatic Speech Recognition)是整个语音交互的「耳朵」。Android Automotive OS 里,你可以用两种方式集成:

  • Google 的 SpeechRecognizer API:开箱即用,但依赖网络。适合在线场景。
  • 第三方离线引擎:比如科大讯飞、百度、思必驰的车载 SDK。适合离线场景。

我建议你 混合使用:有网时用在线引擎,准确率高;没网时切到离线引擎,保证基本功能可用。

来看一段典型的集成代码:

// 创建语音识别 Intent
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
        RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "zh-CN");
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);

// 启动识别
startActivityForResult(intent, REQUEST_CODE_SPEECH);

这段代码看起来简单,但实际车机上有个大坑:麦克风权限。Android Automotive OS 对麦克风的访问控制比手机严格得多。我曾经因为没在 manifest 里声明 RECORD_AUDIO 权限,结果识别结果永远是空的。排查了整整一下午。

注意:在 Android Automotive OS 上,除了 RECORD_AUDIO 权限,还需要 android.car.permission.CAR_AUDIO_FOCUS 权限,否则语音识别会被系统静音。

20.4 语义理解(NLP)与意图映射

ASR 把语音转成文字,NLP 负责理解文字的意思。比如用户说「我有点冷」,NLP 要能映射到「调高空调温度」这个动作。

在车机上,我推荐用 基于规则的意图解析 加上 轻量级 NLU 模型。为什么?因为车载场景是有限的——导航、音乐、空调、电话、车窗,就这几个领域。没必要上大模型,成本高、延迟大。

举个例子:

// 简单的意图匹配
String text = "导航到天安门";
if (text.contains("导航到") || text.contains("去")) {
    String destination = extractDestination(text);
    startNavigation(destination);
} else if (text.contains("空调") || text.contains("冷") || text.contains("热")) {
    adjustClimate(text);
} else {
    fallbackToOnlineNLU(text);
}

你看,先做本地规则匹配,匹配不上再走在线 NLU。这样 80% 的指令可以在 100ms 内响应,用户体验会好很多。

20.5 TTS 引擎定制

TTS(Text To Speech)是语音助手的「嘴巴」。Android 自带的 TextToSpeech API 够用,但车机上需要定制几个地方:

  • 语速:开车时用户注意力有限,语速要比手机快一点,但也不能太快。我一般设到 1.1x 到 1.2x。
  • 音色:车载场景建议用中性、沉稳的声音。太尖锐或太萌的音色会让驾驶员分心。
  • 打断机制:TTS 正在播报时,用户说「闭嘴」,系统要能立即停止。

来看一段 TTS 定制的代码:

TextToSpeech tts = new TextToSpeech(context, status -> {
    if (status == TextToSpeech.SUCCESS) {
        // 设置中文
        tts.setLanguage(Locale.CHINESE);
        // 设置语速(1.0 为正常)
        tts.setSpeechRate(1.15f);
        // 设置音调
        tts.setPitch(1.0f);
    }
});

// 播报
tts.speak("前方 500 米有测速摄像头",
        TextToSpeech.QUEUE_FLUSH, null, "speed_camera");

小技巧:QUEUE_FLUSH 模式可以打断当前播报,立即播报新内容。适合导航场景中频繁更新路况的情况。

我记得有一次,用户反馈 TTS 播报时音乐声音没有降低,导致完全听不清。后来我们在 TTS 播报前申请了 音频焦点(Audio Focus),播报时音乐自动降低音量,播报完再恢复。这个体验细节,用户可能说不出来,但能感觉到「这车很聪明」。

20.6 避坑指南

做车载语音,我踩过的坑比走过的路还多。这里列几个最常见的:

  1. 麦克风阵列布局:麦克风不要放在出风口附近,空调风声会直接灌进去。我见过一个项目,麦克风装在空调出风口旁边,结果识别率惨不忍睹。
  2. 回声消除(AEC):TTS 播报时,麦克风会同时采集到播报的声音。如果不做回声消除,ASR 会把 TTS 的内容也识别进去,形成「自说自话」的死循环。
  3. 多语言支持:出口车型要考虑多语言。我建议用 Locale 动态切换,不要硬编码语言包。
  4. 隐私合规:语音数据不能随意上传。国内要求数据本地化,欧盟要求 GDPR 合规。最好在设置里提供「语音数据不上传」的开关。

20.7 性能与体验指标

最后,给你一张我常用的性能指标表,做语音交互时对照着优化:

指标 目标值 说明
唤醒延迟 < 200ms 从说出唤醒词到系统响应
识别延迟 < 500ms 从说话结束到文字结果返回
TTS 首帧延迟 < 300ms 从收到文本到第一个音频帧播放
离线识别率 > 90% 在 60dB 噪声环境下
在线识别率 > 95% 在安静环境下

这些指标不是拍脑袋定的。我当年做第一个车载语音项目时,唤醒延迟 400ms,被客户骂得狗血淋头。后来优化到 150ms,客户说「嗯,还行」。你看,差 250ms,体验就是天壤之别。

语音交互在车机上不是「能用就行」,而是「好不好用」。用户可能不会天天用语音,但一旦用了,体验不好就会留下「这车语音很蠢」的印象。做得好,它就是加分项;做不好,就是减分项。你自己选吧。


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