HarmonyOS开发:语音控制——语音助手集成

举报
Jack20 发表于 2026/06/26 15:58:46 2026/06/26
【摘要】 HarmonyOS开发:语音控制——语音助手集成📌 核心要点:语音控制是智能家居最自然的交互方式,SpeechRecognizer做语音识别,自定义指令解析器把"把客厅灯调暗一点"翻译成设备控制指令,语音反馈让用户确认操作结果。 背景与动机你躺在沙发上,不想动。灯太亮了,你想调暗一点。掏手机→解锁→打开App→找到客厅灯→拖滑块——5步操作,你已经不想调了。如果只需要说一句"把客厅灯调暗...

HarmonyOS开发:语音控制——语音助手集成

📌 核心要点:语音控制是智能家居最自然的交互方式,SpeechRecognizer做语音识别,自定义指令解析器把"把客厅灯调暗一点"翻译成设备控制指令,语音反馈让用户确认操作结果。

背景与动机

你躺在沙发上,不想动。灯太亮了,你想调暗一点。掏手机→解锁→打开App→找到客厅灯→拖滑块——5步操作,你已经不想调了。

如果只需要说一句"把客厅灯调暗一点"呢?

语音控制是智能家居最自然的交互方式。不用动手,不用找手机,张嘴就能控制。尤其是双手被占用的时候——做饭、抱孩子、搬东西——语音控制简直是刚需。

但语音控制做起来没那么简单。“把客厅灯调暗一点”——你怎么知道"客厅灯"是哪个设备?"调暗一点"是调到多少?用户说"开灯"但家里有5个灯,开哪个?语音识别错了怎么办?

这些问题不解决,语音控制就是"听不懂、猜不对、用不了"的摆设。

核心原理

语音控制流程

从用户说话到设备执行,经历四个阶段:语音识别 → 指令解析 → 设备控制 → 语音反馈。

flowchart TD
    A[用户说话] --> B[语音唤醒词检测]
    B --> C[录音采集]
    C --> D[SpeechRecognizer语音识别]
    
    D --> E[识别文本]
    E --> F[指令解析器]
    
    F --> G{解析结果}
    G -->|明确指令| H[提取设备+属性+]
    G -->|模糊指令| I[多候选匹配]
    G -->|无法解析| J[语音反馈:我没听懂]
    
    H --> K{设备是否存在?}
    K -->|存在| L[下发控制指令]
    K -->|不存在| M[语音反馈:找不到该设备]
    
    I --> N[语音反馈:您是要A还是B?]
    N --> O[用户确认]
    O --> L
    
    L --> P{控制是否成功?}
    P -->|成功| Q[语音反馈:已为您开灯]
    P -->|失败| R[语音反馈:控制失败]
    
    classDef voice fill:#1565C0,color:#fff,stroke:#0D47A1
    classDef parse fill:#2E7D32,color:#fff,stroke:#1B5E20
    classDef control fill:#E65100,color:#fff,stroke:#BF360C
    classDef feedback fill:#6A1B9A,color:#fff,stroke:#4A148C
    classDef error fill:#C62828,color:#fff,stroke:#B71C1C

    class A,B,C,D,E,voice
    class F,G,H,I,parse
    class K,L,control
    class Q,R,N,feedback
    class J,M,error

指令解析

指令解析是语音控制的核心。把自然语言翻译成设备控制指令,需要三个信息:

信息 说明 示例
设备 控制哪个设备 “客厅灯”→ light-001
属性 控制什么属性 “亮度”→ brightness
设成什么值 “调暗一点”→ 50%

指令解析的关键挑战是模糊表达。用户不会说"设置light-001的brightness为50",而是说"把客厅灯调暗一点"。"调暗一点"是多少?30%?50%?这需要上下文——如果当前亮度是80%,"调暗一点"可能是50%;如果当前亮度是30%,"调暗一点"可能是20%。

语音反馈

语音控制必须有语音反馈。用户说了"开灯",灯亮了,但用户没听到确认——他不确定是语音控制成功了还是别人手动开的灯。

语音反馈的原则:简短、明确、及时。

  • 成功:“已为您开灯”(不说"好的,我已经帮您把客厅主灯打开了",太啰嗦)
  • 失败:“客厅灯控制失败”(直接说原因)
  • 歧义:“您是要开客厅灯还是卧室灯?”(让用户选择)

代码实战

基础用法:语音识别

用鸿蒙的@kit.AISpeechKit进行语音识别,把语音转成文本。

// services/VoiceRecognizer.ets
// 语音识别服务

import { speechRecognizer } from '@kit.AISpeechKit'
import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit'

// 识别结果
interface RecognizeResult {
  text: string           // 识别文本
  confidence: number     // 置信度 0-1
  isFinal: boolean       // 是否是最终结果
}

// 识别回调
interface RecognizeCallbacks {
  onResult?: (result: RecognizeResult) => void
  onError?: (error: string) => void
  onStart?: () => void
  onEnd?: () => void
}

class VoiceRecognizer {
  private asrEngine?: speechRecognizer.SpeechRecognitionEngine
  private isListening: boolean = false

  // 初始化语音识别引擎
  async init(callbacks: RecognizeCallbacks): Promise<boolean> {
    try {
      // 检查麦克风权限
      const hasPermission = await this.checkMicrophonePermission()
      if (!hasPermission) {
        callbacks.onError?.('没有麦克风权限')
        return false
      }

      // 创建语音识别引擎
      const extraParams: Record<string, Object> = {
        'locate': 'CN',           // 中国区域
        'recognizerMode': 'short'  // 短语音识别模式
      }

      const initParams: speechRecognizer.SpeechRecognitionParameter = {
        language: 'zh-CN',        // 中文
        extraParams: extraParams
      }

      this.asrEngine = speechRecognizer.createEngine(initParams)

      // 设置识别回调
      const listener: speechRecognizer.RecognitionListener = {
        // 识别结果回调
        onResult: (result: speechRecognizer.SpeechRecognitionResult) => {
          const isFinal = result.isFinal
          const text = result.result?.[0]?.value || ''
          const confidence = result.result?.[0]?.confidence || 0

          callbacks.onResult?.({ text, confidence, isFinal })
        },
        // 识别开始
        onStart: () => {
          this.isListening = true
          callbacks.onStart?.()
        },
        // 识别结束
        onComplete: () => {
          this.isListening = false
          callbacks.onEnd?.()
        },
        // 识别错误
        onError: (error: speechRecognizer.SpeechRecognitionError) => {
          this.isListening = false
          callbacks.onError?.(`识别错误: ${error.errorCode}`)
        }
      }

      this.asrEngine.setListener(listener)
      return true

    } catch (err) {
      callbacks.onError?.(`初始化失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 开始语音识别
  startListening(): boolean {
    if (!this.asrEngine || this.isListening) return false

    try {
      const recognizerParams: speechRecognizer.SpeechRecognitionParameter = {
        language: 'zh-CN',
        audioInfo: {
          audioType: 'pcm',
          sampleRate: 16000,
          soundChannel: 1,
          sampleBit: 16
        }
      }

      this.asrEngine.startListening(recognizerParams)
      return true
    } catch (err) {
      console.error(`启动识别失败: ${JSON.stringify(err)}`)
      return false
    }
  }

  // 停止识别
  stopListening(): void {
    if (this.asrEngine && this.isListening) {
      this.asrEngine.finish()
    }
  }

  // 取消识别
  cancelListening(): void {
    if (this.asrEngine && this.isListening) {
      this.asrEngine.cancel()
      this.isListening = false
    }
  }

  // 检查麦克风权限
  private async checkMicrophonePermission(): Promise<boolean> {
    try {
      const atManager = abilityAccessCtrl.createAtManager()
      const bundleName = bundleManager.getBundleInfoForSelfSync(
        bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
      ).name
      const result = await atManager.checkAccessToken(bundleName, 'ohos.permission.MICROPHONE')
      return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
    } catch (err) {
      return false
    }
  }

  // 是否正在监听
  getIsListening(): boolean {
    return this.isListening
  }
}

export default new VoiceRecognizer()

进阶用法:指令解析器

把识别出的文本解析成设备控制指令,处理模糊表达和歧义。

// services/VoiceCommandParser.ets
// 语音指令解析器 - 自然语言→设备控制指令

// 解析后的指令
interface ParsedCommand {
  success: boolean
  deviceId?: string       // 目标设备ID
  deviceName?: string     // 设备名称(用于反馈)
  property?: string       // 控制属性
  value?: Object          // 目标值
  ambiguity?: {           // 歧义信息
    type: 'device' | 'value'
    candidates: Array<{ deviceId: string, name: string }>
  }
  feedback?: string       // 语音反馈文本
}

// 设备别名映射
interface DeviceAlias {
  deviceId: string
  name: string
  aliases: string[]       // 别名列表
  properties: Array<{
    key: string
    name: string
    aliases: string[]
    type: 'switch' | 'slider' | 'enum'
    options?: string[]    // enum类型的选项别名
  }>
}

class VoiceCommandParser {
  // 设备别名表
  private deviceAliases: DeviceAlias[] = []

  // 初始化设备别名
  setDeviceAliases(aliases: DeviceAlias[]): void {
    this.deviceAliases = aliases
  }

  // 解析语音指令
  parse(text: string, deviceStates: Map<string, Record<string, Object>>): ParsedCommand {
    // 预处理:去标点、转小写
    const normalized = text.replace(/[,。!?、\s]/g, ' ').trim()

    // 第一步:识别设备
    const deviceMatch = this.matchDevice(normalized)
    if (!deviceMatch) {
      return {
        success: false,
        feedback: '抱歉,我没有找到对应的设备'
      }
    }

    // 第二步:识别操作
    const actionMatch = this.matchAction(normalized, deviceMatch)
    if (!actionMatch) {
      return {
        success: false,
        feedback: `抱歉,我不理解对${deviceMatch.name}的操作`
      }
    }

    // 第三步:解析目标值
    const valueMatch = this.matchValue(normalized, actionMatch, deviceStates.get(deviceMatch.deviceId))

    // 检查歧义
    if (deviceMatch.ambiguous) {
      return {
        success: false,
        ambiguity: {
          type: 'device',
          candidates: deviceMatch.candidates || []
        },
        feedback: `您是要控制${deviceMatch.candidates?.map(c => c.name).join('还是')}`
      }
    }

    return {
      success: true,
      deviceId: deviceMatch.deviceId,
      deviceName: deviceMatch.name,
      property: actionMatch.key,
      value: valueMatch.value,
      feedback: `已为您${this.getFeedbackText(actionMatch, valueMatch.value, deviceMatch.name)}`
    }
  }

  // 匹配设备
  private matchDevice(text: string): {
    deviceId: string
    name: string
    ambiguous?: boolean
    candidates?: Array<{ deviceId: string, name: string }>
  } | null {
    const matches: Array<{ deviceId: string, name: string, score: number }> = []

    for (const device of this.deviceAliases) {
      // 精确匹配名称
      if (text.includes(device.name)) {
        matches.push({ deviceId: device.deviceId, name: device.name, score: 10 })
      }

      // 匹配别名
      for (const alias of device.aliases) {
        if (text.includes(alias)) {
          matches.push({ deviceId: device.deviceId, name: device.name, score: 5 })
          break
        }
      }
    }

    if (matches.length === 0) return null

    // 按分数排序
    matches.sort((a, b) => b.score - a.score)

    // 检查歧义(多个设备匹配且分数相同)
    const topScore = matches[0].score
    const topMatches = matches.filter(m => m.score === topScore)

    if (topMatches.length > 1) {
      return {
        deviceId: topMatches[0].deviceId,
        name: topMatches[0].name,
        ambiguous: true,
        candidates: topMatches.map(m => ({ deviceId: m.deviceId, name: m.name }))
      }
    }

    return { deviceId: topMatches[0].deviceId, name: topMatches[0].name }
  }

  // 匹配操作
  private matchAction(text: string, device: { deviceId: string }): {
    key: string
    name: string
    type: string
  } | null {
    const deviceAlias = this.deviceAliases.find(d => d.deviceId === device.deviceId)
    if (!deviceAlias) return null

    // 开关操作
    if (text.includes('开') || text.includes('打开')) {
      const switchProp = deviceAlias.properties.find(p => p.key === 'power')
      if (switchProp) {
        return { key: 'power', name: '开关', type: 'switch' }
      }
    }

    if (text.includes('关') || text.includes('关闭')) {
      const switchProp = deviceAlias.properties.find(p => p.key === 'power')
      if (switchProp) {
        return { key: 'power', name: '开关', type: 'switch' }
      }
    }

    // 属性匹配
    for (const prop of deviceAlias.properties) {
      if (prop.aliases.some(alias => text.includes(alias))) {
        return { key: prop.key, name: prop.name, type: prop.type }
      }
    }

    return null
  }

  // 匹配值
  private matchValue(
    text: string,
    action: { key: string, type: string },
    currentState?: Record<string, Object>
  ): { value: Object } {
    // 开关值
    if (action.key === 'power') {
      const isOn = text.includes('开') || text.includes('打开')
      return { value: isOn }
    }

    // 数值类属性
    if (action.type === 'slider') {
      // 精确数值:"调到50%"
      const numMatch = text.match(/(\d+)/)
      if (numMatch) {
        return { value: parseInt(numMatch[1]) }
      }

      // 模糊表达:"调暗一点""调亮一点"
      const currentValue = (currentState?.[action.key] as number) || 50
      if (text.includes('暗') || text.includes('低') || text.includes('小')) {
        return { value: Math.max(0, currentValue - 20) }
      }
      if (text.includes('亮') || text.includes('高') || text.includes('大')) {
        return { value: Math.min(100, currentValue + 20) }
      }
      if (text.includes('最')) {
        if (text.includes('暗') || text.includes('低')) return { value: 0 }
        if (text.includes('亮') || text.includes('高')) return { value: 100 }
      }

      // 默认值
      return { value: currentValue }
    }

    // 枚举类属性
    if (action.type === 'enum') {
      // 根据文本匹配枚举选项
      const deviceAlias = this.deviceAliases.find(d =>
        d.properties.some(p => p.key === action.key)
      )
      const prop = deviceAlias?.properties.find(p => p.key === action.key)
      if (prop?.options) {
        for (const option of prop.options) {
          if (text.includes(option)) {
            return { value: option }
          }
        }
      }
    }

    return { value: true }
  }

  // 生成反馈文本
  private getFeedbackText(action: { key: string, name: string }, value: Object, deviceName: string): string {
    if (action.key === 'power') {
      return value ? `打开${deviceName}` : `关闭${deviceName}`
    }
    return `${deviceName}${action.name}调整为${value}`
  }
}

export default new VoiceCommandParser()
export type { ParsedCommand, DeviceAlias }

完整示例:语音控制页面

整合语音识别、指令解析、设备控制、语音反馈,实现完整的语音控制体验。

// pages/VoiceControlPage.ets
// 语音控制页面

import VoiceRecognizer from '../services/VoiceRecognizer'
import VoiceCommandParser, { ParsedCommand, DeviceAlias } from '../services/VoiceCommandParser'
import DeviceControlManager from '../services/DeviceControlManager'
import { textToSpeech } from '@kit.AISpeechKit'

@Entry
@Component
struct VoiceControlPage {
  @State isListening: boolean = false
  @State recognizedText: string = ''
  @State commandHistory: Array<{ text: string, result: string, time: string }> = []
  @State pulseScale: number = 1.0

  private commandParser = VoiceCommandParser
  private ttsEngine?: textToSpeech.TextToSpeechEngine

  aboutToAppear() {
    this.initVoiceRecognizer()
    this.initCommandParser()
    this.initTts()
  }

  // 初始化语音识别
  async initVoiceRecognizer() {
    await VoiceRecognizer.init({
      onResult: (result) => {
        this.recognizedText = result.text
        if (result.isFinal && result.text) {
          this.handleCommand(result.text)
        }
      },
      onError: (error) => {
        this.isListening = false
        this.speak('语音识别出错,请重试')
      },
      onStart: () => {
        this.isListening = true
      },
      onEnd: () => {
        this.isListening = false
      }
    })
  }

  // 初始化指令解析器
  initCommandParser() {
    const aliases: DeviceAlias[] = [
      {
        deviceId: 'light-001',
        name: '客厅灯',
        aliases: ['客厅的灯', '客厅主灯', '大厅灯'],
        properties: [
          { key: 'power', name: '开关', aliases: [], type: 'switch' },
          { key: 'brightness', name: '亮度', aliases: ['亮度', '光', '灯光'], type: 'slider' }
        ]
      },
      {
        deviceId: 'ac-001',
        name: '空调',
        aliases: ['卧室空调', '冷气'],
        properties: [
          { key: 'power', name: '开关', aliases: [], type: 'switch' },
          { key: 'temperature', name: '温度', aliases: ['温度', '度'], type: 'slider' },
          {
            key: 'mode', name: '模式', aliases: ['模式'], type: 'enum',
            options: ['制冷', '制热', '自动', '除湿']
          }
        ]
      },
      {
        deviceId: 'curtain-001',
        name: '窗帘',
        aliases: ['客厅窗帘', '遮光帘'],
        properties: [
          { key: 'position', name: '开合度', aliases: ['开合', '打开'], type: 'slider' }
        ]
      }
    ]

    this.commandParser.setDeviceAliases(aliases)
  }

  // 初始化语音合成
  async initTts() {
    try {
      const initParams: textToSpeech.CreateEngineParams = {
        language: 'zh-CN',
        person: 0,
        online: 1
      }
      this.ttsEngine = textToSpeech.createEngine(initParams)
    } catch (err) {
      console.error(`TTS初始化失败: ${JSON.stringify(err)}`)
    }
  }

  // 语音合成说话
  speak(text: string) {
    if (this.ttsEngine) {
      const speakParams: textToSpeech.SpeakParams = {
        requestId: `tts_${Date.now()}`,
        extraParams: { speed: 1.0, pitch: 1.0, volume: 2 }
      }
      this.ttsEngine.speak(text, speakParams)
    }
  }

  // 处理语音指令
  handleCommand(text: string) {
    // 获取设备状态
    const deviceStates = new Map<string, Record<string, Object>>()
    deviceStates.set('light-001', DeviceControlManager.getDeviceState('light-001') || {})
    deviceStates.set('ac-001', DeviceControlManager.getDeviceState('ac-001') || {})

    // 解析指令
    const result = this.commandParser.parse(text, deviceStates)

    // 记录历史
    const now = new Date()
    const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`

    if (result.success && result.deviceId && result.property) {
      // 执行控制
      DeviceControlManager.controlDevice(result.deviceId, result.property, result.value!)

      this.commandHistory.unshift({
        text: text,
        result: result.feedback || '执行成功',
        time: timeStr
      })

      // 语音反馈
      this.speak(result.feedback || '已执行')
    } else if (result.ambiguity) {
      // 歧义处理
      this.commandHistory.unshift({
        text: text,
        result: result.feedback || '存在歧义',
        time: timeStr
      })

      this.speak(result.feedback || '请明确您要控制的设备')
    } else {
      // 解析失败
      this.commandHistory.unshift({
        text: text,
        result: result.feedback || '未能识别',
        time: timeStr
      })

      this.speak(result.feedback || '抱歉,我没有理解您的指令')
    }
  }

  // 开始/停止语音识别
  toggleListening() {
    if (this.isListening) {
      VoiceRecognizer.stopListening()
    } else {
      this.recognizedText = ''
      VoiceRecognizer.startListening()
    }
  }

  build() {
    Navigation() {
      Column() {
        // 语音识别状态区
        Column() {
          // 声波动画
          Stack() {
            Circle()
              .width(120)
              .height(120)
              .fill('#F0F7FF')

            Circle()
              .width(this.isListening ? 140 : 120)
              .height(this.isListening ? 140 : 120)
              .fill('#007DFF')
              .opacity(this.isListening ? 0.2 : 0)

            Image($r('sys.media.ohos_ic_public_voice'))
              .width(48)
              .height(48)
              .fillColor(this.isListening ? '#007DFF' : '#999999')
          }
          .width(160)
          .height(160)
          .onClick(() => this.toggleListening())

          // 状态文本
          Text(this.isListening ? '正在聆听...' : '点击开始语音控制')
            .fontSize(16)
            .fontColor(this.isListening ? '#007DFF' : '#999999')
            .margin({ top: 16 })

          // 识别文本
          if (this.recognizedText) {
            Text(`"${this.recognizedText}"`)
              .fontSize(18)
              .fontColor('#333333')
              .fontWeight(FontWeight.Medium)
              .margin({ top: 12 })
              .padding({ left: 24, right: 24 })
          }
        }
        .width('100%')
        .padding({ top: 40, bottom: 24 })
        .alignItems(HorizontalAlign.Center)

        Divider().color('#F0F0F0')

        // 快捷指令提示
        Column() {
          Text('试试说:').fontSize(14).fontColor('#999999').margin({ bottom: 8 })
          Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
            ForEach([
              '打开客厅灯', '把空调调到26度',
              '关闭窗帘', '空调开制冷模式',
              '把灯调暗一点', '打开所有灯'
            ], (hint: string) => {
              Text(hint)
                .fontSize(13)
                .fontColor('#007DFF')
                .backgroundColor('#F0F7FF')
                .borderRadius(16)
                .padding({ left: 12, right: 12, top: 6, bottom: 6 })
                .margin({ right: 8, bottom: 8 })
                .onClick(() => {
                  this.handleCommand(hint)
                  this.recognizedText = hint
                })
            })
          }
        }
        .width('100%')
        .padding(16)
        .backgroundColor('#FFFFFF')

        Divider().color('#F0F0F0')

        // 指令历史
        Column() {
          Text('指令历史')
            .fontSize(14)
            .fontColor('#999999')
            .margin({ bottom: 8 })

          List({ space: 8 }) {
            ForEach(this.commandHistory, (item: { text: string, result: string, time: string }) => {
              ListItem() {
                Row() {
                  Column() {
                    Text(`"${item.text}"`).fontSize(14).fontColor('#333333')
                    Text(item.result).fontSize(12).fontColor('#4CAF50').margin({ top: 2 })
                  }
                  .alignItems(HorizontalAlign.Start)
                  .layoutWeight(1)

                  Text(item.time).fontSize(12).fontColor('#CCCCCC')
                }
                .width('100%')
                .padding(12)
                .backgroundColor('#F8F8F8')
                .borderRadius(8)
              }
            })
          }
          .width('100%')
          .layoutWeight(1)
        }
        .width('100%')
        .padding(16)
        .layoutWeight(1)
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#FFFFFF')
    }
    .title('语音控制')
    .titleMode(NavigationTitleMode.Mini)
  }
}

踩坑与注意事项

1. 语音识别的准确率

语音识别不是100%准确的。环境噪音、方言、口音、语速都会影响识别结果。“把客厅灯打开"可能被识别成"把客厅灯打开”——没问题,但也可能被识别成"把客厅灯打看"——这就没法解析了。

解法: 指令解析器做模糊匹配,不需要文本完全一致。用关键词匹配而不是全文匹配——只要文本中包含"客厅"“灯”"开"这些关键词,就能解析。同时利用识别结果的置信度,置信度低于0.5时提示用户再说一遍。

2. 麦克风权限

语音识别需要麦克风权限。HarmonyOS对权限管控严格,没有权限调用API会直接报错。更麻烦的是,用户可能拒绝权限,然后不知道为什么语音控制不工作。

解法: 在语音控制页面首次打开时请求权限。如果用户拒绝,显示说明文字"语音控制需要麦克风权限,请在设置中开启",并提供跳转到系统设置的按钮。

3. 语音唤醒词

理想情况下,用户不需要打开App,直接说"小智小智,开灯"就能控制。但语音唤醒词检测需要持续监听麦克风,非常耗电,而且HarmonyOS对后台麦克风的使用有严格限制。

解法: App内语音控制不需要唤醒词,用户点击按钮后直接说话。如果要做全局语音唤醒,需要接入鸿蒙的语音助手框架(@ohos.ai.intelligentVoice),让系统代理语音唤醒检测。

4. 多设备同名

“开灯”——家里有5个灯,开哪个?如果只匹配到1个灯,直接开;如果匹配到多个,必须让用户确认。

解法: 指令解析器返回歧义信息,语音反馈让用户选择。"您是要开客厅灯、卧室灯还是厨房灯?"用户回答"客厅"后,再执行。

5. 语音合成的延迟

语音反馈用TTS(Text-to-Speech),但TTS有延迟——从调用speak到声音出来,可能需要0.5-1秒。用户说完"开灯",灯亮了,但1秒后才听到"已为您开灯",体验割裂。

解法: 控制指令和语音反馈并行执行,不要串行。灯先亮,反馈后说。反馈文本尽量短——"已开灯"比"已为您打开客厅灯"好。

HarmonyOS 6适配说明

HarmonyOS 6对语音能力做了几项增强:

  1. 语音识别引擎优化speechRecognizer新增continuous模式,支持长语音连续识别,不需要用户反复点击按钮。适合长时间语音交互的场景。

  2. 离线语音识别:新增离线识别能力,不需要网络也能识别基本指令。适合网络不稳定的环境(如地下室、电梯)。

  3. 语音唤醒框架:新增@ohos.ai.intelligentVoice语音唤醒框架,支持自定义唤醒词。App注册唤醒词后,系统在锁屏状态下也能检测到唤醒词并启动App。

  4. 语音意图理解:新增语音意图理解API,直接输出结构化的意图结果(设备+操作+值),不需要自己写指令解析器。

适配代码示例:

// HarmonyOS 6 语音唤醒
import { intelligentVoice } from '@kit.AISpeechKit'

// 注册语音唤醒词
const wakeupConfig: intelligentVoice.WakeupConfig = {
  wakeupPhrases: [
    { phrase: '小智小智', id: 1 },
    { phrase: '打开智能家居', id: 2 }
  ],
  sensitivity: 0.5  // 灵敏度
}

const wakeupEngine = intelligentVoice.createWakeupEngine(wakeupConfig)

wakeupEngine.on('wakeup', (result: intelligentVoice.WakeupResult) => {
  // 检测到唤醒词,启动语音识别
  VoiceRecognizer.startListening()
})

// HarmonyOS 6 语音意图理解
import { nlu } from '@kit.AISpeechKit'

const intent = await nlu Understander.processText({
  text: '把客厅灯调暗一点',
  category: 'smarthome'  // 指定领域为智能家居
})

// intent输出结构化结果
// { device: '客厅灯', action: 'brightness', value: 'decrease' }

总结

语音控制是智能家居最自然的交互方式。从语音识别到指令解析到设备控制到语音反馈,四个环节缺一不可。

指令解析是核心难点。自然语言千变万化,“开灯”“打开灯”“把灯开了”"灯开"都是同一个意思,你的解析器得都能理解。模糊表达(“调暗一点”)和歧义(“开灯”→哪个灯?)是最常见的坑。

维度 评价
学习难度 ⭐⭐⭐⭐ 语音识别API不难,但指令解析和歧义处理需要大量规则和测试
使用频率 ⭐⭐⭐⭐ 语音控制是智能家居的加分项,使用频率取决于用户习惯
重要程度 ⭐⭐⭐⭐ 语音控制是智能家居差异化的关键,没有语音控制就少了灵魂

一句话:语音控制做得好,用户用上就回不去;做得差,用户试一次就放弃。 关键是"听懂"比"听见"更重要。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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