语音交互深度集成:Google Assistant集成、Siri集成、自定义唤醒词

语音交互,说白了就是车载系统的“第二双手”。

我做了这么多年车载开发,最深的体会就是:屏幕再大,也不如一句话来得快。尤其在驾驶场景下,眼睛离开路面一秒都危险。所以,Google Assistant和Siri的深度集成,几乎是现代车载信息娱乐系统的标配。

这一章,我们就来聊聊怎么把这两个“大管家”请进你的车机,顺便再搞一个自定义唤醒词,让用户喊“你好小蓝”就能唤醒系统。

18.1 Google Assistant 集成:从唤醒到对话

Google Assistant的集成,其实分两层:浅层集成和深层集成。

浅层集成,就是简单的“语音转文字”+“文字转语音”。深层集成,则是把Assistant的对话能力嵌入到你的应用里,让它能控制导航、音乐、空调等车载功能。

18.1.1 核心API与权限

你需要引入Google Mobile Services(GMS)的语音服务包。嗯,这里要注意,国内有些车机没有GMS,那就得走其他方案了。

// 在build.gradle中添加依赖
implementation 'com.google.android.gms:play-services-assistant:2.0.0'
implementation 'com.google.android.gms:play-services-voice:1.0.0'

// AndroidManifest.xml中声明权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />

18.1.2 启动Assistant会话

我个人习惯用Intent的方式启动Assistant。这样最简单,也最稳定。

// 启动Google Assistant
private void launchGoogleAssistant() {
    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    } else {
        // 降级方案:打开语音搜索
        Intent searchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        startActivity(searchIntent);
    }
}

我在项目中遇到过一个问题:有些车机厂商把Google Assistant给阉割了。这时候,你就得准备一个降级方案,比如直接用系统自带的语音识别。

18.1.3 深度集成:自定义Action

如果你想让Assistant能控制你的App,比如“导航到公司”,那就得注册Deep Link。

<!-- AndroidManifest.xml -->
<activity android:name=".NavigationActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data
            android:scheme="mycar"
            android:host="navigate" />
    </intent-filter>
</activity>

然后在Assistant的Actions Console里配置好对应的意图。这样,用户说“导航到公司”,Assistant就会把参数传给你的App。

小提示: 记得在Deep Link里处理参数解析。我曾经踩过一个坑:用户说“导航到天安门”,结果传过来的地址是“天安门广场”,导致地图搜索失败。后来我加了一层模糊匹配,才搞定。

18.2 Siri集成:CarPlay的语音入口

Siri的集成,主要是在CarPlay场景下。你想想看,用户连上CarPlay,第一件事就是喊“嘿Siri”。

SiriKit是苹果给开发者留的接口。通过它,你可以让Siri控制你的App里的功能。

18.2.1 配置Siri权限

// Info.plist中配置
<key>NSSiriUsageDescription</key>
<string>我们需要使用Siri来控制导航和音乐</string>

// Capabilities中开启Siri
// 注意:需要开发者账号

18.2.2 实现Siri意图

假设你要让Siri能控制你的导航App,需要实现一个Intents Extension。

// 创建Intents Extension Target
// 在IntentHandler中处理请求

class IntentHandler: INExtension, INStartAudioCallIntentHandling {
    
    func handle(intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Void) {
        // 处理语音通话请求
        let response = INStartAudioCallIntentResponse(code: .success, userActivity: nil)
        completion(response)
    }
    
    func resolveContacts(for intent: INStartAudioCallIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
        // 解析联系人
        guard let contacts = intent.contacts else {
            completion([INPersonResolutionResult.needsValue()])
            return
        }
        completion(contacts.map { INPersonResolutionResult.success(with: $0) })
    }
}

我记得有一次,用户反馈说Siri识别不了“导航到加油站”。查了半天,发现是Intents的词汇表没配好。后来我在App的Info.plist里加了一组自定义词汇,问题就解决了。

注意: SiriKit的意图类型是有限的。不是所有功能都能暴露给Siri。比如,你想让Siri控制空调温度,那就得走捷径(Shortcuts)方案了。

18.3 自定义唤醒词:打造专属语音入口

Google Assistant和Siri虽然好用,但毕竟是别人的东西。很多车厂希望有自己的唤醒词,比如“你好,小鹏”、“嗨,蔚来”。

自定义唤醒词,说白了就是做一个本地语音唤醒引擎。它不需要联网,实时监听麦克风,检测到特定关键词就触发动作。

18.3.1 技术选型

方案 优点 缺点 适用场景
Snowboy 轻量、离线、跨平台 准确率一般、已停止维护 原型验证
Porcupine 准确率高、支持自定义词 商业使用需付费 量产项目
Kaldi + 自训练 完全可控、准确率最高 开发成本高、资源消耗大 高端车型
厂商SDK(如科大讯飞) 本地化好、中文识别强 依赖第三方 国内车机

我个人比较推荐Porcupine。它轻量、准确,而且支持自定义唤醒词。你只需要录制几遍你的唤醒词,它就能生成一个模型文件。

18.3.2 Porcupine集成示例

// 1. 添加依赖
implementation 'ai.picovoice:porcupine-android:2.1.0'

// 2. 初始化唤醒词引擎
private Porcupine porcupine;
private PorcupineManager porcupineManager;

private void initWakeWord() {
    try {
        // 使用预置的唤醒词
        porcupine = new Porcupine.Builder()
                .setAccessKey("你的AccessKey")
                .setKeyword(Porcupine.BUILT_IN_KEYWORDS.PICOVOICE)
                .build();
        
        // 或者使用自定义唤醒词
        porcupine = new Porcupine.Builder()
                .setAccessKey("你的AccessKey")
                .setKeywordPath("path/to/custom_keyword.ppn")
                .build();
        
        // 启动监听
        porcupineManager = new PorcupineManager(porcupine, wakeWordCallback);
        porcupineManager.start();
    } catch (PorcupineException e) {
        e.printStackTrace();
    }
}

// 3. 唤醒回调
private PorcupineManagerCallback wakeWordCallback = new PorcupineManagerCallback() {
    @Override
    public void onWakeWordDetection(int keywordIndex) {
        // 检测到唤醒词,触发语音交互
        runOnUiThread(() -> {
            // 播放提示音
            // 启动语音识别
            // 或者直接打开Assistant
            launchGoogleAssistant();
        });
    }
};

18.3.3 避坑指南:唤醒词的那些坑

我曾经在一个项目里,把唤醒词设成了“你好小蓝”。结果测试的时候,发现它经常被“你好小兰”误唤醒。后来分析了一下,发现是“蓝”和“兰”的发音太接近了。

所以,选唤醒词有几个原则:

  • 音节要清晰:避免“吃”、“是”这种轻音字
  • 长度要适中:2-4个音节最好。太短容易误唤醒,太长用户嫌麻烦
  • 避免常见词:比如“你好”、“导航”这种高频词,很容易误触发
  • 测试环境要真实:在车内、开空调、放音乐的情况下测试,看看误唤醒率
核心要点: 自定义唤醒词不是“录一遍就能用”的。你需要采集至少50个人的声音,覆盖不同性别、年龄、口音,才能训练出一个靠谱的模型。

18.4 语音交互的完整流程

说了这么多,我们来画一张图,看看语音交互的完整链路是什么样的。

语音交互完整流程 1. 唤醒词检测 本地引擎实时监听 2. 语音识别 ASR 转文字 3. 语义理解 NLP 解析意图 4. 动作执行 导航/音乐/空调 5. 语音反馈 TTS 播报结果 6. 等待下次唤醒 继续监听麦克风 整个流程在 500ms 内完成,才能给用户“秒响应”的体验

你看,从唤醒到反馈,整个链路其实挺长的。任何一个环节慢了,用户都会觉得“这车机好蠢”。

18.5 实战经验:多语音源共存

一个现实的问题:车机上同时有Google Assistant、Siri、自定义唤醒词,它们会不会打架?

会的。我遇到过最典型的情况是:用户喊“嘿Siri”,结果Google Assistant也跳出来了。

解决方案其实不复杂:

  • 优先级管理:自定义唤醒词 > Siri > Google Assistant。先响应自家的,再响应外部的
  • 互斥机制:当一个语音引擎被激活时,其他引擎暂时休眠
  • 场景判断:如果检测到CarPlay连接,优先使用Siri;如果是Android Auto,优先使用Google Assistant
我的习惯: 在系统设置里加一个“语音助手选择”的开关,让用户自己决定用哪个。毕竟,用户才是车的主人。

18.6 性能与功耗的平衡

语音唤醒引擎是要一直跑在后台的。它要实时监听麦克风,这就意味着CPU不能休眠。

功耗问题,在车载场景下其实还好,因为车机有车载电源。但性能问题要注意:

  • 唤醒引擎最好跑在独立的DSP或者NPU上,不要占用主CPU
  • 如果硬件不支持,那就得优化算法,比如降低采样率、减少特征维度
  • 设置合理的超时机制:如果10秒内没有语音输入,自动进入低功耗模式

嗯,说到低功耗模式,我记得有一次测试,发现车机在熄火后还在监听麦克风,导致电瓶亏电。后来加了一个“电源状态监听”,熄火后自动关闭唤醒引擎,问题才解决。

语音交互的深度集成,说白了就是让车机“听得懂人话”。Google Assistant和Siri是两条大腿,抱好了能省不少事。自定义唤醒词则是你的特色,让用户记住你的品牌。

下一章,我们会聊聊更高级的话题:多模态交互——语音+手势+触控,怎么让它们协同工作。不过那是后话了,先把今天的代码跑通再说。


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