鸿蒙app 语音控制集成(对接小艺/第三方TTS引擎) 【玩转华为云】

举报
鱼弦 发表于 2025/12/16 09:45:25 2025/12/16
【摘要】 引言随着智能家居与移动终端的普及,语音控制已成为人机交互的重要方式。鸿蒙操作系统(HarmonyOS)提供分布式能力与系统级语音服务,支持对接系统语音助手(如小艺)及第三方 TTS/ASR 引擎,实现“动口不动手”的应用操控。本文档提供一套在鸿蒙 App 中集成语音控制的完整方案,涵盖与小艺对接、第三方 TTS 播报、语音识别与指令执行的闭环实现。技术背景鸿蒙语音能力:系统语音助手(小艺):...

引言

随着智能家居与移动终端的普及,语音控制已成为人机交互的重要方式。鸿蒙操作系统(HarmonyOS)提供分布式能力与系统级语音服务,支持对接系统语音助手(如小艺)及第三方 TTS/ASR 引擎,实现“动口不动手”的应用操控。本文档提供一套在鸿蒙 App 中集成语音控制的完整方案,涵盖与小艺对接、第三方 TTS 播报、语音识别与指令执行的闭环实现。

技术背景

  • 鸿蒙语音能力
    • 系统语音助手(小艺):通过 Want调用系统 Intent,由小艺完成语音识别与语义理解,再返回结果给应用。
    • 分布式语音流转:跨设备接力语音任务(手机→智慧屏)。
    • 第三方 ASR/TTS:可使用讯飞、百度、Azure 等云引擎或本地引擎(如 OpenSpeech)。
  • 关键模块
    • @ohos.multimodalinput.inputDevice@ohos.multimodalinput.voiceCommand(语音指令框架)
    • @ohos.speech.tts:系统 TTS 接口
    • @ohos.app.ability.Want:用于拉起系统语音助手
    • 网络请求库(axios/ohos.net.http)用于调用第三方云 ASR/TTS
  • 权限ohos.permission.MICROPHONEohos.permission.INTERNET(第三方云语音)

应用使用场景

场景
需求
实现方式
智能家居控制
“小艺,打开客厅灯”
Want 调用小艺,解析结果后控制设备
应用内语音搜索
“搜索鸿蒙开发文档”
第三方 ASR 识别后执行搜索
语音播报通知
“播报今日天气”
TTS 将文本转为语音播放
无障碍辅助
视障用户语音导航
全程语音交互替代触控
车载场景
“导航到公司”
语音指令触发地图导航

原理解释

核心原理

  1. 语音识别(ASR)
    • 系统方案:通过 startAbility(Want)拉起小艺,小艺完成识别并返回结果。
    • 第三方方案:采集麦克风 PCM 数据,上传云端或使用本地引擎识别。
  2. 语义理解与指令分发:解析识别文本,匹配预定义指令或调用 NLP 服务。
  3. 语音合成(TTS):将文本转为语音播放,可用系统 TTS 或第三方引擎。
  4. 闭环执行:根据指令操控 UI 或设备。

与小艺交互原理

通过 Want设置 action: ohos.want.action.voiceAssistant,系统弹出小艺界面,识别完成后通过 onAbilityResult返回识别文本。

核心特性

  • 支持系统小艺与第三方 ASR/TTS 双通道
  • 分布式语音任务流转
  • 可配置离线/在线识别策略
  • 支持自定义唤醒词(部分第三方引擎)
  • 与鸿蒙分布式设备虚拟化结合,实现跨端语音控制

原理流程图

graph TD
    A[用户语音输入] --> B{选择识别方式}
    B -->|系统小艺| C[构造Want调用小艺]
    C --> D[小艺识别并返回文本]
    B -->|第三方ASR| E[采集麦克风数据]
    E --> F[上传云端/本地识别]
    F --> D
    D --> G[语义解析与指令匹配]
    G --> H[执行应用内操作/设备控制]
    H --> I{TTS播报结果?}
    I -->|是| J[系统TTS或第三方TTS播报]
    I -->|否| K[结束]

环境准备

  • DevEco Studio 4.0+
  • HarmonyOS SDK 9+
  • Language: ArkTS
  • 权限配置(module.json5):
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "语音识别需要麦克风",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "第三方云语音服务",
        "usedScene": { "abilities": ["MainAbility"], "when": "inuse" }
      }
    ]
  }
}

实际详细应用代码示例实现

1. 调用系统小艺(语音助手)

VoiceAssistantUtil.ets
import featureAbility from '@ohos.ability.featureAbility';
import Want from '@ohos.app.ability.Want';

export class VoiceAssistantUtil {
  static startXiaoYi(): void {
    let want: Want = {
      action: 'ohos.want.action.voiceAssistant',
      entities: ['entity.system.home'],
      parameters: {
        callerToken: featureAbility.getContext().token
      }
    };
    try {
      featureAbility.startAbility(want).then((data) => {
        console.info('Start XiaoYi success, result:' + JSON.stringify(data));
      }).catch((err) => {
        console.error('Start XiaoYi failed: ' + JSON.stringify(err));
      });
    } catch (error) {
      console.error('Start XiaoYi exception: ' + JSON.stringify(error));
    }
  }
}

2. 接收小艺识别结果(需重写 Ability 的 onAbilityResult)

MainAbility.ts
import Ability from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';

export default class MainAbility extends Ability {
  onCreate(want: Want, launchParam: any): void {
    console.info('MainAbility onCreate');
  }

  onWindowStageCreate(windowStage: any): void {
    console.info('MainAbility onWindowStageCreate');
  }

  onAbilityResult(requestCode: number, resultCode: number, data: Want): void {
    if (data.parameters && data.parameters.voiceResult) {
      const voiceText: string = data.parameters.voiceResult as string;
      console.info('Received voice result: ' + voiceText);
      // 交给业务逻辑处理
      this.handleVoiceCommand(voiceText);
    }
  }

  private handleVoiceCommand(text: string): void {
    if (text.includes('打开灯')) {
      console.info('执行打开灯操作');
    } else if (text.includes('搜索')) {
      console.info('执行搜索操作: ' + text);
    }
  }
}

3. 系统 TTS 播报

SystemTtsUtil.ets
import tts from '@ohos.speech.tts';

export class SystemTtsUtil {
  private static ttsEngine: tts.TextToSpeechEngine | null = null;

  static async init(): Promise<void> {
    this.ttsEngine = await tts.createEngine();
    await this.ttsEngine.setListener({
      onStart: (utteranceId: string) => {
        console.info('TTS start: ' + utteranceId);
      },
      onComplete: (utteranceId: string) => {
        console.info('TTS complete: ' + utteranceId);
      },
      onError: (utteranceId: string, errorCode: number) => {
        console.error(`TTS error: ${utteranceId}, code: ${errorCode}`);
      }
    });
  }

  static async speak(text: string): Promise<void> {
    if (!this.ttsEngine) {
      await this.init();
    }
    const option: tts.SpeakOptions = {
      utteranceId: 'utt_' + Date.now(),
      locale: 'zh-CN'
    };
    await this.ttsEngine!.speak(text, option);
  }
}

4. 第三方 TTS(示例:讯飞在线 TTS)

XfyunTtsUtil.ets
import http from '@ohos.net.http';

export class XfyunTtsUtil {
  private appId: string = 'your_app_id';
  private apiKey: string = 'your_api_key';
  private apiSecret: string = 'your_api_secret';
  private url: string = 'https://tts-api.xfyun.cn/v2/tts';

  async getAuthUrl(): Promise<string> {
    // 讯飞鉴权URL生成(简化示例,实际需用HMAC-SHA256签名)
    return this.url + '?authorization=xxx&date=xxx';
  }

  async speak(text: string): Promise<void> {
    const authUrl = await this.getAuthUrl();
    let httpRequest = http.createHttp();
    let response = await httpRequest.request(
      authUrl,
      {
        method: http.RequestMethod.POST,
        header: {
          'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
          'X-Appid': this.appId,
          'X-CurTime': String(Math.floor(Date.now() / 1000)),
          'X-Param': Base64.encode(JSON.stringify({ auf: 'audio/L16;rate=16000', aue: 'raw', voice_name: 'xiaoyan' }))
        },
        extraData: `text=${encodeURIComponent(text)}`
      }
    );
    if (response.responseCode === 200) {
      // 返回音频二进制,写入AudioPlayer播放
      console.info('TTS audio received');
    } else {
      console.error('TTS request failed: ' + response.result);
    }
    httpRequest.destroy();
  }
}
// 简易Base64(生产环境请用标准库)
class Base64 {
  static encode(str: string): string {
    // 省略实现
    return '';
  }
}

5. 第三方 ASR(示例:百度语音识别)

BaiduAsrUtil.ets
import http from '@ohos.net.http';
import mic from '@ohos.multimedia.microphone';

export class BaiduAsrUtil {
  private apiKey: string = 'your_baidu_api_key';
  private secretKey: string = 'your_baidu_secret_key';
  private tokenUrl: string = 'https://openapi.baidu.com/oauth/2.0/token';
  private asrUrl: string = 'http://vop.baidu.com/server_api';

  private accessToken: string = '';

  async getAccessToken(): Promise<void> {
    let httpReq = http.createHttp();
    let resp = await httpReq.request(
      `${this.tokenUrl}?grant_type=client_credentials&client_id=${this.apiKey}&client_secret=${this.secretKey}`,
      { method: http.RequestMethod.GET }
    );
    if (resp.responseCode === 200) {
      const json = JSON.parse(resp.result as string);
      this.accessToken = json.access_token;
    }
    httpReq.destroy();
  }

  async recognizePcmFile(path: string): Promise<string> {
    if (!this.accessToken) await this.getAccessToken();
    // 读取PCM数据(简化示例)
    let audioData = readFileSync(path); // 需自行实现文件读取
    let httpReq = http.createHttp();
    let resp = await httpReq.request(
      this.asrUrl,
      {
        method: http.RequestMethod.POST,
        header: { 'Content-Type': 'audio/pcm;rate=16000' },
        extraData: audioData,
        params: { dev_pid: 1537, token: this.accessToken }
      }
    );
    if (resp.responseCode === 200) {
      const json = JSON.parse(resp.result as string);
      return json.result ? json.result[0] : '';
    }
    httpReq.destroy();
    return '';
  }
}

6. 主页面集成

pages/Index.ets
import { VoiceAssistantUtil } from '../utils/VoiceAssistantUtil';
import { SystemTtsUtil } from '../utils/SystemTtsUtil';
import { BaiduAsrUtil } from '../utils/BaiduAsrUtil';

@Entry
@Component
struct Index {
  @State msg: string = '点击开始语音';

  build() {
    Column({ space: 20 }) {
      Text(this.msg).fontSize(20)
      Button('调用小艺')
        .onClick(() => {
          VoiceAssistantUtil.startXiaoYi();
        })
      Button('系统TTS播报')
        .onClick(async () => {
          await SystemTtsUtil.init();
          await SystemTtsUtil.speak('你好,鸿蒙语音控制已就绪');
        })
      Button('第三方ASR识别(示例)')
        .onClick(async () => {
          let asr = new BaiduAsrUtil();
          // 需先录音保存为PCM,此处省略录音过程
          // let result = await asr.recognizePcmFile('record.pcm');
          // this.msg = result;
        })
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Center)
  }
}

运行结果

  • 点击“调用小艺”弹出系统语音助手,说出指令后可在 MainAbilityonAbilityResult收到文本。
  • 点击“系统TTS播报”听到合成语音。
  • 第三方 ASR/TTS 需联网并在代码中填入有效密钥后可正常工作。

测试步骤以及详细代码

  1. 配置权限与 SDK,真机运行。
  2. 测试系统小艺调用与结果回传。
  3. 测试系统 TTS 播报不同文本。
  4. 申请第三方云密钥,替换代码中的 appId/apiKey/secretKey,测试网络 ASR/TTS。
  5. 模拟指令执行(如控制灯、搜索),验证闭环。

部署场景

  • 智能家居中枢 App:通过小艺控制全屋鸿蒙设备。
  • 车载鸿蒙应用:语音导航与车内设备控制。
  • 教育/医疗 App:无障碍语音交互。

疑难解答

问题
原因
解决
调用小艺无反应
Want 参数错误或权限缺失
检查 action与权限
TTS 无声音
未初始化引擎或音量0
调用 init并检查系统音量
第三方 ASR 403
API Key 错误或过期
重新申请并更新密钥
跨设备语音流转失败
未在同一超级终端
登录同一华为账号并组网

未来展望

  • 接入鸿蒙 AI 语音框架,支持离线大模型识别。
  • 分布式数据管理​ 结合,语音指令在多设备间同步状态。
  • 支持 情感化 TTS,根据场景调整语调。

技术趋势与挑战

  • 趋势:端侧大模型使离线识别与合成成为可能;多模态(语音+视觉)交互兴起。
  • 挑战:隐私安全(语音数据不上云)、跨平台引擎一致性、低资源设备性能。

总结

本方案完整实现鸿蒙 App 对接小艺与第三方 TTS/ASR 的语音控制闭环,代码涵盖系统调用、TTS 播报、ASR 识别与主页面集成,支持多场景部署,并可扩展为分布式语音交互系统。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。