怎么打造车载语音交互:Google Voice Interaction 给你答案

在某些场景下进行图形交互显得有些困难、甚至危险,比如驾驶汽车。那么在这些场景下可以适当加入语音交互,在解放手眼的同时可以增强安全、避免分心。

概述

语音交互并不是一个新事物,很早就有了。比如 Apple 设备的 Siri、Amazon 的 Alxea、Google 的 Google Assistant 等等。

它们大多是系统的内置服务,由热词唤醒或按键触发,之后只通过语音指令即可完成完整的交互。可这些交互场景往往覆盖了系统服务或系统 App,而对第三方 App 的支持有限或者鲜少针对第三方 App 完成完整的语音交互逻辑。

第三方 App 除了被动等待系统语音服务的调度,当然可以选择主动支持。可是完全依靠自己实现的话,需要考虑监听、识别、理解、分析、调度等诸多复杂逻辑和流程,耗时耗力、可能还入不敷出。

那有没有简单办法来快速切入、试试水呢?

在 Android 生态当中,我们可以选择 Voice Interaction 来完成。Voice Interaction,简称 VI,是 Android 平台特有的语音交互 API,第三方 App 可以通过它来接入系统的语音服务。

这些服务称作 Voice Interaction App,简称 VIA。Android 设备一般都会内置一个或多个 VIA 服务,比如 Pixel 设备默认内置了 Google Assistant、Samsung 设备默认的 Bixby

当第三方 App 接入它们之后,可以便捷地实现一些语音交互功能。比如在删除某项数据的时候,App 可以调度这些服务发起语音提示,并等待用户发出确认或取消的语音指令,其识别之后自动将结果返回回来,App 接棒完成后续的处理。

后面将着重演示如何使用 VI API 在 Pixel 模拟器上调度 Google Assistant 完成几个语音交互的示例。

Confirmation Request

Android 的 Activity 组件提供了发起和停止 VI 调用的方法:startLocalVoiceInteraction() 和 stopLocalVoiceInteraction()。

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     fun onButtonClick(view: View?) {
         when (view?.id) {
             R.id.btn_confirm->{
                 val bundle = Bundle().apply {
                     putString("name", "Test Voice Interaction")
                 }
                 startLocalVoiceInteraction(bundle)
             }
         }
     }
 }

调用被发起后 Activity 的 onLocalVoiceInteractionStarted() 会被回调,在这里 App 可以获取到向 VIA 请求的入口即 VoiceInteractor

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     override fun onLocalVoiceInteractionStarted() {
         val request = testConfirmation()
         voiceInteractor.submitRequest(request)
     }
 }

接着可以创建 Request 实例,并使用得到的 VoiceInteractor 向系统发出去。

Request 的类型有很多,比如适用于上面提到的确认交互场景的 ConfirmationRequest。而且为便于用户准确理解,Request 还可以指定友好的提示说明,用 Prompt 实例构建。

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     private fun testConfirmation(): VoiceInteractor.Request {
         val prompt = VoiceInteractor.Prompt(resources.getString(R.string.vi_confirmation_prompt))
 
         return object : VoiceInteractor.ConfirmationRequest(prompt, null) { ... }
     }
 }

系统收到 Request 后会按照提示调用 TTS 进行朗读,并等待用户的后续语音指令,当用户发出不同指令或指令超时的时候,Request 的相应回调将被系统触发:

  • YES:onConfirmationResult() 被回调并且 confirmed 参数为 true
  • NO:onConfirmationResult() 被回调但 confirmed 参数为 false
  • 超时:onCancel() 被回调

这里演示当点击删除 Button 之后,App 通过 VIA 发出询问用户是否要删除该首歌曲的语音提示。用户发出 Yes 之后弹出 Toast 的同时将该首歌曲的 TextView 隐藏。

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     private fun testConfirmation(): VoiceInteractor.Request {
         val prompt = VoiceInteractor.Prompt(resources.getString(R.string.vi_confirmation_prompt))
 
         return object : VoiceInteractor.ConfirmationRequest(prompt, null) {
             override fun onConfirmationResult(confirmed: Boolean, result: Bundle?) {
                 val stringId =
                     if (confirmed) R.string.vi_confirmation_confirmed else R.string.vi_confirmation_cancelled
 
                 Toast.makeText(
                     this@VoiceInteractionActivity,
                     stringId,
                     Toast.LENGTH_SHORT
                 ).show()
 
                 if (confirmed)
                     confirmTv?.visibility = View.INVISIBLE
 
                 stopLocalVoiceInteraction()
             }
 
             override fun onCancel() {
                 Toast.makeText(
                     this@VoiceInteractionActivity,
                     R.string.vi_confirmation_timeout,
                     Toast.LENGTH_SHORT
                 ).show()
 
                 stopLocalVoiceInteraction()
             }
         }
     }
 }

一开始发现点击 Button 之后没有任何反应:虽然日志上显示 onLocalVoiceInteractionStarted() 能回调,但既没有收到系统的语音提示,发出 YES 或者 NO 也没有收到 Request 的回调。

经过调查发现模拟器的音量和 Microphone 没有打开。

怎么打造车载语音交互:Google Voice Interaction 给你答案_第1张图片

重试之后可以听到系统发出 “Are you sure you want to delete this song?” 的语音提示了,但我发出的指令仍然没有反馈。

在模拟器上打开了 Online Test Mic 发现发出的语音模拟器是能收到的,即麦克风没有问题。那么必然是识别那块除了问题。重新取了日志,果然发现了问题:ASR 识别连接发生了错误,虽然我已经连上了网。

 06-21 22:41:51.307  1506  8756 W ErrorReporter: reportError [type: 211, code: 65561, bug: 0]: errorCode: 65561, engine: 2
 06-21 22:41:51.307  1506  8756 I NetworkRecognitionRnr: Using pair HTTP connection
 06-21 22:41:51.311  1506  7017 I PairHttpConnection: [Upload] Connected
 06-21 22:41:51.317  1506  1990 W CronetNetworkRqstWrppr: Upload request without a content type.
 06-21 22:41:51.324  1506  1972 I S3RecognizerInfoBuilder: S3PreambleType 0

一顿折腾之后,模拟器能够科学上网了,再试果然成功了。

怎么打造车载语音交互:Google Voice Interaction 给你答案_第2张图片

录屏可以看到点击了 “Delete that song” Button 之后,Google Assistant 弹出了 UI 说明,GIF 无法展示,事实上还播放了对应的语音提示。

在此之后,当发出了 “Yes” 的 Voice 之后,被它成功地识别了,并回调了我们的 Delete 逻辑,最终隐藏了目标歌曲。

Pick Option Request

除了借助 VI 帮忙做 YES 或 NO 的判断题,还可以通过 PickOptionRequest 让 VI 帮忙做选择题。发起和回调的处理差不多,区别在于 Request 的部分,需要传入选项 Array

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     private fun testPickup(): VoiceInteractor.Request {
         val prompt = VoiceInteractor.Prompt(resources.getString(R.string.vi_pick_prompt))
 
         val optionList = arrayOf(
             VoiceInteractor.PickOptionRequest.Option(optionsArray[0], 0),
             VoiceInteractor.PickOptionRequest.Option(optionsArray[1],1),
             VoiceInteractor.PickOptionRequest.Option(optionsArray[2], 2)
         )
 
         return object : VoiceInteractor.PickOptionRequest(prompt, optionList, null) { ... }
     }
 }

这里模拟一个场景,当驾驶员搜索或者打开歌单出现一堆歌曲的时候,App 可以设计如下流程进行语音选择:

  1. App 将界面内歌曲列表传递给 VIA 让其播报出来,通过语音提示驾驶员
  2. 当驾驶员听到满意的歌名之后,将其念出来
  3. VIA 将自动识别并匹配上其索引,最后回传给 App
  4. 进而 App 可以依据索引直接选择对应歌曲进行播放

另外要注意,选择后有其特有的回调即 onPickOptionResult()。

 class VoiceInteractionActivity: AppCompatActivity() {
     ...
     private fun testPickup(): VoiceInteractor.Request {
         ...
         return object : VoiceInteractor.PickOptionRequest(prompt, optionList, null) {
             override fun onPickOptionResult(
                 finished: Boolean,
                 selections: Array?,
                 result: Bundle?
             ) {
                 if (finished && selections?.size == 1) {
                     val index = selections[0].index
 
                     Toast.makeText(
                         this@VoiceInteractionActivity,
                         "${resources.getString(R.string.vi_pick_selected_prefix)} ${optionList[index].label}",
                         Toast.LENGTH_SHORT
                     ).show()
 
                     var selectedItem: View? = when (index) {
                         0 -> optionTv1
                         1 -> optionTv2
                         2 -> optionTv3
                         else -> { null }
                     }
 
                     selectedItem?.isPressed = true
                 }
 
                 stopLocalVoiceInteraction()
             }
 
             override fun onCancel() {
                 Toast.makeText(
                     this@VoiceInteractionActivity,
                     R.string.vi_confirmation_timeout,
                     Toast.LENGTH_SHORT
                 ).show()
 
                 stopLocalVoiceInteraction()
             }
         }
 }

怎么打造车载语音交互:Google Voice Interaction 给你答案_第3张图片

可以看到点击 “Choose a song” Button 之后,Google Assistant 弹出了 “Which song do you want?” 的 UI 提示,以及同等的语音提示。

当发出了 “dances with wolves” 的 Speech 之后,它不仅听到了还进行了模糊识别(谁叫自己英语发音不标准呢 )并成功回调了 Select 目标 Item 的逻辑。

其他 Request

除了用于确认的 ConfirmationRequest、用于选择的 PickOptionRequest,还有其他 Request:

  • Command Request,用于向 VIA 发送预设的 Command String(比如控制导航、媒体、车辆、通信等特殊 Command),可在 onCommandResult() 里回调,命令执行与否在 isCompleted 参数中体现
  • Complete Voice Request,用于通知 VIA 已经成功通过 Voice Interaction 完成交互逻辑,在 onCompleteResult() 回调里可以关闭 Activity
  • Abort Voice Request,用于通知 VIA 无法通过 Voice Interaction 完成交互,在收到 onAbortResult() 回调里可以开启传统的 UI 操作 Activity 以继续完成交互。

VI Flow

怎么打造车载语音交互:Google Voice Interaction 给你答案_第4张图片

如同 AccessibilityService,VIA 的核心服务 VoiceInteractionService 依赖 SystemService 的调度,该服务名为 VoiceInteractionManagerService

在 VIA 设置为 Default Digital Assistant App 之后或重启之后,VoiceInteractionManagerService 会绑定 VIA 的 VoiceInteractionService 并进行 ASR、NLU、NLG、TTS 等服务或 Engine 的初始化,同时开启对 Hotword 的探测。

当 Client App 通过 VI 发出 Request 后,VoiceInteractionManagerService 会绑定 VoiceInteractinoSessionService 并开启一个 VoiceInteractionSession 进行处理。

该 Session 收到具体的的 Request,在展示 UI 的同时会依据传入的 Prompt 文本调用 TTS 进行朗读。之后调用 MediaRecorder 进行录音,并将数据交由 ASR 和 NLU 进入语音识别和语义分析。

当识别到的结果和目标意图符合或模糊匹配上的话,将会回调 Request 的相应 Callback。

注意点

在使用 VI API 实战的时候需要留意如下几点:

  1. 确保麦克风打开
  2. 确保扬声器音量足够大
  3. 确保网络正常,可以下载必要的语音包的
  4. 尽量科学上网,否则可能无法识别语音(虽然我觉得基础指令的解析本可以在本地完成)
  5. 确保设备中存在 VIA 并且设置为默认的 Digital Assistant App(如果设备中没有,可以考虑下载、安装 Google Assistant & Google,并设置为默认 App)

如果在实战过程中发现一些问题,可以查看如下日志以帮助分析失败的原因:

 adb logcat -s GoogleTTSServiceImpl -s VoiceDataDownloader -s VoiceDataManager -s VoiceGenerator -s TextToSpeech -s GoogleTTSService -s GoogleTTSServiceImpl

结语

和语音助手一样,Voice Interaction API 也早就出现了,准确的是在 Android 6 推出的,可是鲜少有朋友了解或使用过。

VI 这套 API 可以免去自行集成 ASR、NLU、NLG、TTS 这些复杂模块的步骤,而且随着 AOSP 的版本升级未来还可以便捷地支持更多功能、无需自行扩展架构。

如果为了体验或者给 App 提供基础的语音交互功能,不妨从接入 VoiceInteraction 开始!当然作为 VI 的实现方 VIA 才是语音交互的精髓,后续将从原理、实战进行更完整地探讨。

参考资料

Android车载开发与设备调试学习手册

Automotive

1.Android Automotive 13 版本详情
2.Android Automotive 12 QPR3 版本详情
3.Android Automotive 12L 版本详情
4.Android Automotive 12L 版本详情
5.Android Automotive 12 版本详情
6.Android Automotive 11 版本详情
7.什么是 Android Automotive?

怎么打造车载语音交互:Google Voice Interaction 给你答案_第5张图片

Android 虚拟设备

1.概览
2.用作开发平台的 Android 虚拟设备
3.构建自己的云模拟器

怎么打造车载语音交互:Google Voice Interaction 给你答案_第6张图片

模拟器 USB 透传模式集成指南

1.已测试的加密狗
2.原生 USB 支持
3.蓝牙支持
4.Wi-Fi 支持

怎么打造车载语音交互:Google Voice Interaction 给你答案_第7张图片

使用 Pixel 手机作为开发平台

1.注意事项
2.前提条件
3.代码同步及 build
4.设置设备以刷写 build
5.刷写 build

怎么打造车载语音交互:Google Voice Interaction 给你答案_第8张图片

有需要的可以 直接点击此处参考↓↓↓ 学习!!!

测试工具和基础架构

1.系统性能工具
2.界面测试和框架
3.在 Android Automotive OS (AAOS) 中模拟网络
4.Complete Automotive Tests in a Box
5.在 AAOS 设备上启用模糊测试工具
6.Spectatio:Automotive 测试框架
7.车载音频
8.音频焦点
9.音频路由
10.音量管理
11.创建播放器的替代方案
12.电台调谐器
13.蓝牙
14.按应用选择网络 (PANS)
15.屏幕和输入源概览
16.监控状态
17.按键输入
18.仪表板
19.有关防止驾驶员分心的准则
20.汽车用户体验限制 21.增强型视觉系统 (EVS)
22.Android Automotive 中的闪存磨损管理
23.位置信息绕过许可名单政策
24.电源管理
25.时间
26.启用 USB Gadget HAL API
27.用户与账号
28.SWITCH_USER
29.USER_IDENTIFICATION_ASSOCIATION
30.车载设备硬件抽象层
31.车载相机 HAL

怎么打造车载语音交互:Google Voice Interaction 给你答案_第9张图片

语音

1.语音助理点读功能
2.语音交互简介
3.集成流程
4.应用开发
5.执行命令
6.测试和调试

怎么打造车载语音交互:Google Voice Interaction 给你答案_第10张图片

人机接口

1.HMI 概览
2.AOSP 主机集成指南

怎么打造车载语音交互:Google Voice Interaction 给你答案_第11张图片

汽车设置结果

1.概览
2.添加汽车设置
3.重新排列汽车设置
4.汽车设置中的“防分心优化”
5.汽车设置搜索索引
6.双窗格自定义
7.偏好设置字幕

怎么打造车载语音交互:Google Voice Interaction 给你答案_第12张图片

自定义状态栏系统图标

1.特定图标的叠加
2.单个图标
3.配置图标

怎么打造车载语音交互:Google Voice Interaction 给你答案_第13张图片

通知

1.概览
2.设置通知
3.浮动通知

怎么打造车载语音交互:Google Voice Interaction 给你答案_第14张图片

车载设备界面库集成指南

1.车载设备界面库概述
2.将车载设备界面库集成到应用中
3.自定义应用
4.车载设备界面插件
5.包含 car-ui-lib 的 Google 软件包列表
6.添加自定义字体
7.自定义车载设备界面偏好设置
8.使用运行时资源叠加层来自定义工具栏
9.CarUiListItem
10.自定义 CarUiRecyclerView
11.排查运行时资源叠加层问题
12.车载设备界面库版本说明

怎么打造车载语音交互:Google Voice Interaction 给你答案_第15张图片

拨号键

1.概览
2.技术详情
3.自定义准则
4.全局可配置值
5.工具栏
6.调试和测试

怎么打造车载语音交互:Google Voice Interaction 给你答案_第16张图片

车载媒体

1.自定义 Media
2.系统组件和用户流

怎么打造车载语音交互:Google Voice Interaction 给你答案_第17张图片

适用于 Automotive 的设备管理

1.个人设备
2.企业设备

旋控器

1.概述
2.开发应用
3.不使用车载设备界面库开发应用
4.适用于 OEM 的集成指南

系统界面

1.实现系统界面
2.SystemUIOverlayWindow 管理系统
3.SystemUIOverlayWindow Codelab

有需要的可以 直接点击此处参考↓↓↓ 学习!!!

虚拟化

1.概述
2.架构
3.工具

怎么打造车载语音交互:Google Voice Interaction 给你答案_第18张图片

你可能感兴趣的:(Android,车载,音视频,交互,android,移动开发,车载,音频)