HarmonyOS APP开发:语音唤醒与低功耗监听

举报
Jack20 发表于 2026/06/21 13:51:50 2026/06/21
【摘要】 HarmonyOS APP开发:语音唤醒与低功耗监听核心要点:掌握HarmonyOS语音唤醒(Voice Wakeup)引擎配置、唤醒词注册、低功耗常驻监听、后台Agent机制,以及功耗优化策略。 一、背景与动机“小艺小艺,打开客厅的灯。”这句话你一定不陌生。当设备处于待机状态,屏幕是黑的,CPU在低频运行,但它依然能听到你的唤醒词——这就是语音唤醒(Voice Wakeup)的魔力。语音...

HarmonyOS APP开发:语音唤醒与低功耗监听

核心要点:掌握HarmonyOS语音唤醒(Voice Wakeup)引擎配置、唤醒词注册、低功耗常驻监听、后台Agent机制,以及功耗优化策略。


一、背景与动机

“小艺小艺,打开客厅的灯。”

这句话你一定不陌生。当设备处于待机状态,屏幕是黑的,CPU在低频运行,但它依然能听到你的唤醒词——这就是语音唤醒(Voice Wakeup)的魔力。

语音唤醒和语音识别(ASR)的区别在于:

维度 语音唤醒 语音识别
目的 检测特定关键词 将语音转为文字
功耗 极低(毫瓦级) 较高
模型大小 KB级 MB-GB级
运行方式 常驻后台 按需启动
误唤醒率 要求极低(<1次/24h) 无此指标

语音唤醒是语音交互的"守门人"——只有它先听到唤醒词,后续的ASR、NLU才会被激活。它的核心挑战是:在极低功耗下保持高灵敏度,同时控制误唤醒率

HarmonyOS的语音唤醒能力有几个关键特性:

  • 端侧常驻:唤醒模型运行在DSP/NPU上,不占用主CPU
  • 自定义唤醒词:支持开发者注册自定义唤醒词
  • 低功耗Agent:后台持续监听,功耗控制在5mA以内
  • 多唤醒词并发:支持同时监听多个唤醒词

二、核心原理

2.1 语音唤醒技术架构

语音唤醒的核心流程:音频采集 → 前端信号处理 → 特征提取 → 唤醒词检测 → 唤醒事件回调

flowchart TD
    A[麦克风持续采集] --> B[前端信号处理]
    B --> C[降噪与回声消除]
    C --> D[特征提取 MFCC]
    D --> E[唤醒词模型推理]
    E --> F{置信度判断}
    F -->|置信度 ≥ 阈值| G[触发唤醒事件]
    F -->|置信度 < 阈值| H[继续监听]
    G --> I[启动ASR引擎]
    I --> J[进入语音交互模式]
    
    K[低功耗DSP/NPU] -.-> E
    
    classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
    classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
    classDef error fill:#EF5350,stroke:#C62828,color:#FFF
    classDef info fill:#81C784,stroke:#388E3C,color:#000
    classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
    
    class A,B,C primary
    class D,E purple
    class F warning
    class G,I,J info
    class H,K primary

2.2 低功耗实现原理

语音唤醒之所以能做到极低功耗,核心在于三个机制:

1. 硬件加速——DSP/NPU运行唤醒模型

主CPU可以进入休眠状态,唤醒模型运行在专用的低功耗DSP或NPU上。这颗芯片只做一件事:检测唤醒词。它的功耗通常只有主CPU的1/100。

2. 音频前端——VAD预过滤

不是所有音频都送入唤醒模型。VAD(Voice Activity Detection)先判断是否有人声,只有检测到人声才送入唤醒模型,环境噪音直接丢弃。

3. 分级唤醒——从浅到深

Level 0: 完全休眠(麦克风关闭)→ 功耗≈0
Level 1: VAD监听(检测人声)→ 功耗≈1mA
Level 2: 唤醒词检测(模型推理)→ 功耗≈5mA
Level 3: ASR启动(完整识别)→ 功耗≈50mA

2.3 误唤醒与漏唤醒的权衡

这是语音唤醒最核心的矛盾:

  • 误唤醒(False Accept):没人说唤醒词,但设备误以为听到了。比如电视里有人喊"小艺小艺",你的手机也跟着亮了。
  • 漏唤醒(False Reject):你确实说了唤醒词,但设备没反应。

降低误唤醒 → 提高阈值 → 漏唤醒增加 → 用户体验差
降低漏唤醒 → 降低阈值 → 误唤醒增加 → 用户烦不胜烦

HarmonyOS通过多模型级联来平衡:先用低阈值快速筛选候选,再用高精度模型二次确认。


三、代码实战

3.1 基础语音唤醒——注册唤醒词并监听

最基本的用法:注册一个唤醒词,当检测到时触发回调。

// 基础语音唤醒示例
import { voiceWakeupper } from '@kit.AISpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { BackgroundTaskManager } from '@kit.BackgroundTasksKit';

@Entry
@Component
struct BasicWakeupPage {
  @State wakeupStatus: string = '未启动';
  @State isListening: boolean = false;
  @State wakeupCount: number = 0;
  @State lastWakeupTime: string = '';
  
  private wakeupEngine: voiceWakeupper.VoiceWakeupper | null = null;
  private backgroundAgent: BackgroundTaskManager.BackgroundTaskAgent | null = null;

  aboutToAppear(): void {
    this.initWakeupEngine();
  }

  // 初始化唤醒引擎
  private initWakeupEngine(): void {
    try {
      const extraParams: Record<string, Object> = {
        'locate': 'CN',
        'language': 'zh-CN',
        // 唤醒灵敏度:0-100,值越大越灵敏(但误唤醒率也越高)
        'sensitivity': 80,
      };
      
      const initParams: voiceWakeupper.CreateEngineParams = {
        language: 'zh-CN',
        extraParams: extraParams
      };
      
      this.wakeupEngine = voiceWakeupper.createEngine(initParams);
      this.setupWakeupCallbacks();
      console.info('[Wakeup] 唤醒引擎初始化成功');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[Wakeup] 初始化失败: ${err.code} - ${err.message}`);
    }
  }

  // 设置唤醒回调
  private setupWakeupCallbacks(): void {
    if (!this.wakeupEngine) return;

    // 唤醒事件回调——核心!
    this.wakeupEngine.on('wakeup', (callback: voiceWakeupper.WakeupCallback) => {
      this.wakeupCount++;
      const now = new Date();
      this.lastWakeupTime = `${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
      this.wakeupStatus = `检测到唤醒词!`;
      
      console.info(`[Wakeup] 唤醒成功: word=${callback.word}, confidence=${callback.confidence}`);
      
      // 唤醒成功后,可以启动ASR进入语音交互
      this.startASRAfterWakeup();
    });

    // 错误回调
    this.wakeupEngine.on('error', (callback: voiceWakeupper.Error) => {
      this.wakeupStatus = `唤醒错误: ${callback.message}`;
      console.error(`[Wakeup] 错误: ${callback.code} - ${callback.message}`);
    });
  }

  // 注册唤醒词
  private registerWakeupWord(): void {
    if (!this.wakeupEngine) return;
    
    try {
      // 注册自定义唤醒词
      const wakeupWord: voiceWakeupper.WakeupWord = {
        word: '你好小智',           // 唤醒词文本
        wordId: 'wakeup_001',       // 唤醒词唯一ID
        // 唤醒词的语音模板(可选,提升识别准确率)
        // 实际项目中需要通过训练获取
      };
      
      this.wakeupEngine.addWakeupWord(wakeupWord);
      console.info(`[Wakeup] 注册唤醒词: ${wakeupWord.word}`);
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[Wakeup] 注册唤醒词失败: ${err.code}`);
    }
  }

  // 启动唤醒监听
  private startWakeupListening(): void {
    if (!this.wakeupEngine) return;
    
    try {
      // 先注册唤醒词
      this.registerWakeupWord();
      
      // 启动监听
      const listenerParams: voiceWakeupper.ListenerParams = {
        sessionId: `wakeup_session_${Date.now()}`
      };
      this.wakeupEngine.startListening(listenerParams);
      
      this.isListening = true;
      this.wakeupStatus = '正在监听唤醒词...';
      console.info('[Wakeup] 开始监听');
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[Wakeup] 启动监听失败: ${err.code}`);
    }
  }

  // 停止唤醒监听
  private stopWakeupListening(): void {
    if (!this.wakeupEngine) return;
    try {
      this.wakeupEngine.stopListening();
      this.isListening = false;
      this.wakeupStatus = '已停止监听';
      console.info('[Wakeup] 停止监听');
    } catch (error) {
      console.error('[Wakeup] 停止失败');
    }
  }

  // 唤醒后启动ASR(联动示例)
  private startASRAfterWakeup(): void {
    // 这里可以启动ASR引擎,进入语音交互模式
    // 详见第271篇ASR文章
    console.info('[Wakeup] 唤醒成功,准备启动ASR...');
    // 模拟3秒后恢复监听状态
    setTimeout(() => {
      if (this.isListening) {
        this.wakeupStatus = '正在监听唤醒词...';
      }
    }, 3000);
  }

  build() {
    Column({ space: 20 }) {
      Text('语音唤醒')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#E0E0E0')

      // 状态卡片
      Column({ space: 12 }) {
        Row({ space: 8 }) {
          Circle({ width: 12, height: 12 })
            .fill(this.isListening ? '#81C784' : '#9E9E9E')
          Text(this.wakeupStatus)
            .fontSize(18)
            .fontColor(this.isListening ? '#81C784' : '#9E9E9E')
        }
        
        if (this.wakeupCount > 0) {
          Text(`已唤醒 ${this.wakeupCount} 次 | 最近: ${this.lastWakeupTime}`)
            .fontSize(14)
            .fontColor('#9E9E9E')
        }
      }
      .width('90%')
      .padding(20)
      .borderRadius(16)
      .backgroundColor('rgba(255,255,255,0.08)')
      .backdropBlur(20)

      // 唤醒词提示
      Column() {
        Text('唤醒词')
          .fontSize(14)
          .fontColor('#9E9E9E')
        Text('"你好小智"')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .fontColor('#4FC3F7')
          .margin({ top: 8 })
      }
      .width('90%')
      .padding(20)
      .borderRadius(16)
      .backgroundColor('rgba(79,195,247,0.1)')
      .border({ width: 1, color: 'rgba(79,195,247,0.3)' })

      // 控制按钮
      Button(this.isListening ? '停止监听' : '开始监听')
        .width('80%')
        .height(56)
        .fontSize(18)
        .fontColor('#FFFFFF')
        .backgroundColor(this.isListening ? '#EF5350' : '#81C784')
        .borderRadius(28)
        .onClick(() => {
          if (this.isListening) {
            this.stopWakeupListening();
          } else {
            this.startWakeupListening();
          }
        })

      // 功耗提示
      Text('低功耗模式:唤醒引擎运行在DSP上,待机功耗 < 5mA')
        .fontSize(12)
        .fontColor('#9E9E9E')
        .width('80%')
        .textAlign(TextAlign.Center)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#1A1A2E')
    .justifyContent(FlexAlign.Center)
  }

  aboutToDisappear(): void {
    if (this.wakeupEngine) {
      this.wakeupEngine.off('wakeup');
      this.wakeupEngine.off('error');
      this.wakeupEngine = null;
    }
  }
}

3.2 后台常驻唤醒——Background Agent机制

语音唤醒最大的价值在于设备锁屏或应用在后台时依然能响应。这需要使用HarmonyOS的Background Agent机制。

// 后台常驻唤醒示例
import { voiceWakeupper } from '@kit.AISpeechKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { Notification } from '@kit.NotificationKit';

@Entry
@Component
struct BackgroundWakeupPage {
  @State agentStatus: string = '未启动';
  @State isAgentRunning: boolean = false;
  @State wakeupLog: string[] = [];
  
  private wakeupEngine: voiceWakeupper.VoiceWakeupper | null = null;
  private bgAgent: backgroundTaskManager.BackgroundTaskAgent | null = null;

  // 启动后台唤醒Agent
  private async startBackgroundAgent(): Promise<void> {
    try {
      // 第一步:申请后台长时任务
      const bgRequest: backgroundTaskManager.BackgroundTaskRequest = {
        bgMode: backgroundTaskManager.BackgroundMode.AUDIO_RECORDING,
        // 音频录制后台模式,允许持续使用麦克风
        wantAgent: undefined,
      };
      
      // 第二步:创建后台任务Agent
      this.bgAgent = await backgroundTaskManager.startBackgroundRunning(
        this.context,
        bgRequest
      );
      console.info('[BgWakeup] 后台任务启动成功');
      
      // 第三步:初始化唤醒引擎
      this.initWakeupForBackground();
      
      this.isAgentRunning = true;
      this.agentStatus = '后台监听中';
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[BgWakeup] 后台任务启动失败: ${err.code} - ${err.message}`);
      this.agentStatus = '启动失败';
    }
  }

  // 初始化后台唤醒引擎
  private initWakeupForBackground(): void {
    try {
      const extraParams: Record<string, Object> = {
        'locate': 'CN',
        'language': 'zh-CN',
        'sensitivity': 70,  // 后台模式降低灵敏度,减少误唤醒
        // 后台模式专用参数
        'backgroundMode': true,
        'powerSaving': true,  // 省电模式
      };
      
      const initParams: voiceWakeupper.CreateEngineParams = {
        language: 'zh-CN',
        extraParams: extraParams
      };
      
      this.wakeupEngine = voiceWakeupper.createEngine(initParams);
      
      // 注册唤醒词
      this.wakeupEngine.addWakeupWord({
        word: '你好小智',
        wordId: 'bg_wakeup_001'
      });
      
      // 设置回调
      this.wakeupEngine.on('wakeup', (callback: voiceWakeupper.WakeupCallback) => {
        const time = new Date().toLocaleTimeString();
        this.wakeupLog.unshift(`[${time}] 唤醒: ${callback.word}`);
        if (this.wakeupLog.length > 20) this.wakeupLog.pop();
        
        // 发送通知提醒用户
        this.sendWakeupNotification(callback.word);
        
        console.info(`[BgWakeup] 后台唤醒: ${callback.word}`);
      });
      
      this.wakeupEngine.on('error', (callback: voiceWakeupper.Error) => {
        console.error(`[BgWakeup] 错误: ${callback.code}`);
      });
      
      // 启动监听
      this.wakeupEngine.startListening({
        sessionId: `bg_wakeup_${Date.now()}`
      });
      
    } catch (error) {
      console.error('[BgWakeup] 引擎初始化失败');
    }
  }

  // 发送唤醒通知
  private sendWakeupNotification(word: string): void {
    try {
      const notificationRequest: Notification.NotificationRequest = {
        id: Date.now(),
        content: {
          notificationContentType: Notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
          normal: {
            title: '语音唤醒',
            text: `检测到唤醒词: ${word}`,
          }
        }
      };
      Notification.publish(notificationRequest);
    } catch (error) {
      console.error('[BgWakeup] 通知发送失败');
    }
  }

  // 停止后台Agent
  private stopBackgroundAgent(): void {
    if (this.wakeupEngine) {
      this.wakeupEngine.stopListening();
      this.wakeupEngine.off('wakeup');
      this.wakeupEngine.off('error');
      this.wakeupEngine = null;
    }
    
    if (this.bgAgent) {
      backgroundTaskManager.stopBackgroundRunning(this.context);
      this.bgAgent = null;
    }
    
    this.isAgentRunning = false;
    this.agentStatus = '已停止';
  }

  build() {
    Column({ space: 20 }) {
      Text('后台常驻唤醒')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#E0E0E0')

      // Agent状态
      Row({ space: 12 }) {
        Circle({ width: 12, height: 12 })
          .fill(this.isAgentRunning ? '#81C784' : '#EF5350')
        Text(this.agentStatus)
          .fontSize(18)
          .fontColor(this.isAgentRunning ? '#81C784' : '#9E9E9E')
      }
      .padding(16)
      .width('90%')
      .borderRadius(12)
      .backgroundColor(this.isAgentRunning ? 'rgba(129,199,132,0.1)' : 'rgba(255,255,255,0.05)')

      // 唤醒日志
      Column() {
        Text('唤醒日志')
          .fontSize(14)
          .fontColor('#9E9E9E')
          .width('100%')
          .margin({ bottom: 8 })
        
        if (this.wakeupLog.length === 0) {
          Text('暂无唤醒记录')
            .fontSize(14)
            .fontColor('#616161')
            .width('100%')
            .textAlign(TextAlign.Center)
            .padding(20)
        }
        
        ForEach(this.wakeupLog, (log: string, index: number) => {
          Text(log)
            .fontSize(13)
            .fontColor('#E0E0E0')
            .width('100%')
            .padding({ top: 4, bottom: 4 })
        }, (log: string, index: number) => `${index}`)
      }
      .width('90%')
      .padding(16)
      .borderRadius(16)
      .backgroundColor('rgba(255,255,255,0.08)')
      .backdropBlur(20)
      .layoutWeight(1)

      // 控制按钮
      Button(this.isAgentRunning ? '停止后台监听' : '启动后台监听')
        .width('80%')
        .height(56)
        .fontSize(18)
        .fontColor('#FFFFFF')
        .backgroundColor(this.isAgentRunning ? '#EF5350' : '#81C784')
        .borderRadius(28)
        .onClick(() => {
          if (this.isAgentRunning) {
            this.stopBackgroundAgent();
          } else {
            this.startBackgroundAgent();
          }
        })

      Text('后台监听需要申请长时任务权限和通知权限')
        .fontSize(12)
        .fontColor('#9E9E9E')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#1A1A2E')
    .justifyContent(FlexAlign.Start)
    .padding({ top: 40, left: 20, right: 20, bottom: 20 })
  }

  aboutToDisappear(): void {
    this.stopBackgroundAgent();
  }
}

3.3 多唤醒词与灵敏度动态调节

智能音箱场景中,可能需要同时监听多个唤醒词,并根据时间段动态调整灵敏度。

// 多唤醒词与灵敏度动态调节示例
import { voiceWakeupper } from '@kit.AISpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';

interface WakeupWordConfig {
  word: string;
  wordId: string;
  description: string;
  enabled: boolean;
}

@Entry
@Component
struct MultiWakeupPage {
  @State isListening: boolean = false;
  @State wakeupWords: WakeupWordConfig[] = [
    { word: '你好小智', wordId: 'wakeup_001', description: '主唤醒词', enabled: true },
    { word: '小智同学', wordId: 'wakeup_002', description: '备选唤醒词', enabled: true },
    { word: '嘿小智', wordId: 'wakeup_003', description: '短唤醒词', enabled: false },
  ];
  @State currentSensitivity: number = 80;
  @State lastWakeupWord: string = '';
  @State powerMode: string = 'normal'; // normal / eco / ultra_eco

  private wakeupEngine: voiceWakeupper.VoiceWakeupper | null = null;

  aboutToAppear(): void {
    this.initMultiWakeupEngine();
  }

  // 初始化多唤醒词引擎
  private initMultiWakeupEngine(): void {
    try {
      // 根据功耗模式设置灵敏度
      const sensitivityMap: Record<string, number> = {
        'normal': 80,
        'eco': 60,
        'ultra_eco': 40,
      };

      const extraParams: Record<string, Object> = {
        'locate': 'CN',
        'language': 'zh-CN',
        'sensitivity': sensitivityMap[this.powerMode] || 80,
      };
      
      const initParams: voiceWakeupper.CreateEngineParams = {
        language: 'zh-CN',
        extraParams: extraParams
      };
      
      this.wakeupEngine = voiceWakeupper.createEngine(initParams);
      this.setupMultiCallbacks();
      console.info('[MultiWakeup] 引擎初始化成功');
    } catch (error) {
      console.error('[MultiWakeup] 初始化失败');
    }
  }

  // 设置多唤醒词回调
  private setupMultiCallbacks(): void {
    if (!this.wakeupEngine) return;

    this.wakeupEngine.on('wakeup', (callback: voiceWakeupper.WakeupCallback) => {
      this.lastWakeupWord = callback.word;
      console.info(`[MultiWakeup] 唤醒词: ${callback.word}, 置信度: ${callback.confidence}`);
      
      // 根据不同唤醒词执行不同动作
      this.handleWakeupAction(callback.word, callback.confidence);
    });

    this.wakeupEngine.on('error', (callback: voiceWakeupper.Error) => {
      console.error(`[MultiWakeup] 错误: ${callback.code}`);
    });
  }

  // 根据唤醒词执行不同动作
  private handleWakeupAction(word: string, confidence: number): void {
    // 置信度过低可能是误唤醒,忽略
    if (confidence < 0.6) {
      console.info(`[MultiWakeup] 置信度过低(${confidence}),忽略`);
      return;
    }

    switch (word) {
      case '你好小智':
        console.info('[MultiWakeup] 主唤醒词,启动完整语音交互');
        break;
      case '小智同学':
        console.info('[MultiWakeup] 备选唤醒词,启动完整语音交互');
        break;
      case '嘿小智':
        console.info('[MultiWakeup] 短唤醒词,启动快速指令模式');
        break;
    }
  }

  // 启动多唤醒词监听
  private startMultiWakeup(): void {
    if (!this.wakeupEngine) return;

    // 注册所有启用的唤醒词
    this.wakeupWords.forEach((config: WakeupWordConfig) => {
      if (config.enabled) {
        try {
          this.wakeupEngine!.addWakeupWord({
            word: config.word,
            wordId: config.wordId
          });
          console.info(`[MultiWakeup] 注册: ${config.word}`);
        } catch (error) {
          console.error(`[MultiWakeup] 注册失败: ${config.word}`);
        }
      }
    });

    this.wakeupEngine.startListening({
      sessionId: `multi_wakeup_${Date.now()}`
    });
    this.isListening = true;
  }

  // 切换功耗模式(需要重建引擎)
  private switchPowerMode(mode: string): void {
    this.powerMode = mode;
    
    // 停止当前监听
    if (this.wakeupEngine) {
      this.wakeupEngine.stopListening();
      this.wakeupEngine.off('wakeup');
      this.wakeupEngine.off('error');
      this.wakeupEngine = null;
    }
    
    // 重建引擎
    this.initMultiWakeupEngine();
    
    // 如果之前在监听,重新启动
    if (this.isListening) {
      this.startMultiWakeup();
    }
  }

  build() {
    Scroll() {
      Column({ space: 20 }) {
        Text('多唤醒词管理')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor('#E0E0E0')

        // 功耗模式选择
        Text('功耗模式')
          .fontSize(16)
          .fontColor('#9E9E9E')
          .width('90%')

        Row({ space: 8 }) {
          ForEach([
            { mode: 'normal', label: '标准', color: '#4FC3F7' },
            { mode: 'eco', label: '省电', color: '#FFB74D' },
            { mode: 'ultra_eco', label: '超省电', color: '#EF5350' },
          ], (item: { mode: string; label: string; color: string }) => {
            Button(item.label)
              .height(40)
              .fontSize(14)
              .fontColor(this.powerMode === item.mode ? '#FFFFFF' : item.color)
              .backgroundColor(this.powerMode === item.mode ? item.color : 'rgba(255,255,255,0.05)')
              .borderRadius(20)
              .layoutWeight(1)
              .onClick(() => { this.switchPowerMode(item.mode); })
          }, (item: { mode: string }) => item.mode)
        }
        .width('90%')

        // 唤醒词列表
        Text('唤醒词列表')
          .fontSize(16)
          .fontColor('#9E9E9E')
          .width('90%')

        ForEach(this.wakeupWords, (config: WakeupWordConfig, index: number) => {
          Row({ space: 12 }) {
            Toggle({ type: ToggleType.Switch, isOn: config.enabled })
              .onChange((isOn: boolean) => {
                this.wakeupWords[index].enabled = isOn;
              })
              .selectedColor('#4FC3F7')
            
            Column({ space: 4 }) {
              Text(config.word)
                .fontSize(16)
                .fontColor('#E0E0E0')
              Text(config.description)
                .fontSize(12)
                .fontColor('#9E9E9E')
            }
            .alignItems(HorizontalAlign.Start)
            .layoutWeight(1)
          }
          .width('90%')
          .padding(16)
          .borderRadius(12)
          .backgroundColor('rgba(255,255,255,0.08)')
        }, (config: WakeupWordConfig, index: number) => `${index}`)

        // 最近唤醒
        if (this.lastWakeupWord) {
          Column() {
            Text('最近唤醒')
              .fontSize(14)
              .fontColor('#9E9E9E')
            Text(`"${this.lastWakeupWord}"`)
              .fontSize(20)
              .fontWeight(FontWeight.Bold)
              .fontColor('#81C784')
              .margin({ top: 4 })
          }
          .width('90%')
          .padding(16)
          .borderRadius(12)
          .backgroundColor('rgba(129,199,132,0.1)')
        }

        // 启动按钮
        Button(this.isListening ? '停止监听' : '启动多唤醒词监听')
          .width('80%')
          .height(56)
          .fontSize(18)
          .fontColor('#FFFFFF')
          .backgroundColor(this.isListening ? '#EF5350' : '#4FC3F7')
          .borderRadius(28)
          .onClick(() => {
            if (this.isListening) {
              this.wakeupEngine?.stopListening();
              this.isListening = false;
            } else {
              this.startMultiWakeup();
            }
          })
      }
      .padding({ top: 40, bottom: 40 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#1A1A2E')
  }

  aboutToDisappear(): void {
    if (this.wakeupEngine) {
      this.wakeupEngine.stopListening();
      this.wakeupEngine.off('wakeup');
      this.wakeupEngine.off('error');
      this.wakeupEngine = null;
    }
  }
}

四、踩坑与注意事项

4.1 后台长时任务权限

语音唤醒要在后台持续运行,必须申请长时任务(Continuous Task)。在module.json5中配置:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.MICROPHONE",
      "reason": "$string:microphone_reason",
      "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
    },
    {
      "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
      "reason": "$string:background_reason",
      "usedScene": { "abilities": ["EntryAbility"], "when": "always" }
    }
  ]
}

⚠️ 注意:长时任务必须在通知栏显示持续通知,否则系统会杀掉后台进程。这是Android和HarmonyOS的共同要求。

4.2 唤醒词设计规范

唤醒词不是随便选的,好的唤醒词应该满足以下条件:

规则 说明 示例
长度3-5个音节 太短易误唤醒,太长不好记 “你好小智”✅ “小智”❌
避免常见词 不要用日常对话中高频出现的词 “好的”❌ “你好小智”✅
包含辅音 辅音比元音更有辨识度 “小智”✅ “阿伊”❌
避免连续元音 元音间加辅音提高区分度 “华为”✅ “阿姨”❌

4.3 误唤醒处理策略

即使唤醒词设计得再好,误唤醒也难以完全避免。以下是常见的处理策略:

// 误唤醒防护策略
class WakeupGuard {
  private recentWakeupTimes: number[] = [];
  private readonly COOLDOWN_MS = 3000;     // 两次唤醒最小间隔3秒
  private readonly MAX_PER_MINUTE = 3;     // 每分钟最多3次
  private readonly MAX_PER_HOUR = 20;      // 每小时最多20次

  // 判断是否为有效唤醒(防误唤醒)
  isValidWakeup(): boolean {
    const now = Date.now();
    
    // 冷却期检查:3秒内不重复唤醒
    if (this.recentWakeupTimes.length > 0) {
      const lastTime = this.recentWakeupTimes[this.recentWakeupTimes.length - 1];
      if (now - lastTime < this.COOLDOWN_MS) {
        console.info('[WakeupGuard] 冷却期内,忽略');
        return false;
      }
    }
    
    // 频率检查:每分钟不超过3次
    const oneMinuteAgo = now - 60000;
    const recentCount = this.recentWakeupTimes.filter(t => t > oneMinuteAgo).length;
    if (recentCount >= this.MAX_PER_MINUTE) {
      console.info('[WakeupGuard] 频率过高,忽略');
      return false;
    }
    
    // 小时检查
    const oneHourAgo = now - 3600000;
    const hourlyCount = this.recentWakeupTimes.filter(t => t > oneHourAgo).length;
    if (hourlyCount >= this.MAX_PER_HOUR) {
      console.info('[WakeupGuard] 小时频率过高,忽略');
      return false;
    }
    
    this.recentWakeupTimes.push(now);
    // 清理过期记录
    this.recentWakeupTimes = this.recentWakeupTimes.filter(t => t > oneHourAgo);
    return true;
  }
}

4.4 灵敏度与场景适配

不同场景下,灵敏度应该动态调整:

场景 灵敏度 原因
安静室内 70-80 环境噪音低,高灵敏度不易误唤醒
户外街道 50-60 噪音大,降低灵敏度避免误唤醒
开车导航 60-70 车内噪音中等,需要平衡
夜间睡眠 40-50 极低灵敏度,避免误唤醒打扰
会议中 30-40 极低灵敏度,或直接关闭唤醒

4.5 电池与热管理

持续运行唤醒引擎会消耗电池和产生热量:

  • 电池消耗:典型功耗5-10mA,24小时约消耗120-240mAh
  • 发热:DSP运行会产生少量热量,长时间运行需关注设备温度
  • 系统保护:设备温度过高时,系统可能自动关闭唤醒引擎

建议在应用中添加温度监控和自动降级逻辑:

// 温度监控与自动降级
import { thermal } from '@kit.BasicServicesKit';

thermal.subscribeThermalLevel((level: thermal.ThermalLevel) => {
  switch (level) {
    case thermal.ThermalLevel.NORMAL:
      // 正常温度,标准灵敏度
      this.adjustSensitivity(80);
      break;
    case thermal.ThermalLevel.HIGH:
      // 温度偏高,降低灵敏度
      this.adjustSensitivity(60);
      break;
    case thermal.ThermalLevel.OVERHEAT:
      // 过热,停止唤醒监听
      this.stopWakeupListening();
      break;
  }
});

五、HarmonyOS 6适配

5.1 API变更

变更项 HarmonyOS 5 HarmonyOS 6
唤醒词注册 addWakeupWord() 不变,但新增addWakeupPhrase()支持短语
灵敏度控制 全局灵敏度 新增按唤醒词独立灵敏度
后台Agent startBackgroundRunning() 新增startVoiceAgent()专用语音Agent
功耗优化 手动调节 新增自适应功耗,系统根据电量自动调节
多设备协同 不支持 新增分布式唤醒,多设备只唤醒最近的一台

5.2 自适应功耗(HarmonyOS 6新增)

// HarmonyOS 6自适应功耗
const extraParams: Record<string, Object> = {
  'locate': 'CN',
  'language': 'zh-CN',
  // 自适应功耗:系统根据电量、温度、使用习惯自动调节
  'adaptivePower': true,
  // 最低灵敏度下限(自适应不会低于此值)
  'minSensitivity': 40,
  // 电量低于20%时自动进入省电模式
  'lowPowerThreshold': 20,
};

5.3 分布式唤醒(HarmonyOS 6新增)

当用户家里有多台HarmonyOS设备时,分布式唤醒确保只有最近的一台设备响应:

// HarmonyOS 6分布式唤醒配置
const extraParams: Record<string, Object> = {
  'distributedWakeup': true,
  // 设备优先级:数值越大优先级越高
  'devicePriority': 100,
  // 同一账号下的设备组ID
  'deviceGroupId': 'home_living_room',
};

六、总结

mindmap
  root((语音唤醒))
    核心原理
      DSP/NPU低功耗运行
      VAD预过滤
      分级唤醒机制
      误唤醒与漏唤醒权衡
    引擎管理
      创建 createEngine
      注册唤醒词 addWakeupWord
      启动监听 startListening
      停止监听 stopListening
    后台常驻
      长时任务申请
      通知栏常驻
      Background Agent机制
    多唤醒词
      同时注册多个唤醒词
      按唤醒词执行不同动作
      独立灵敏度控制
    功耗优化
      灵敏度动态调节
      场景适配
      温度监控与降级
      自适应功耗 H6
    踩坑要点
      后台长时任务权限
      唤醒词设计规范
      误唤醒防护策略
      电池与热管理
    HarmonyOS 6
      自适应功耗
      分布式唤醒
      按词独立灵敏度
      专用VoiceAgent
    
    classDef primary fill:#4FC3F7,stroke:#0288D1,color:#000
    classDef warning fill:#FFB74D,stroke:#F57C00,color:#000
    classDef error fill:#EF5350,stroke:#C62828,color:#FFF
    classDef info fill:#81C784,stroke:#388E3C,color:#000
    classDef purple fill:#CE93D8,stroke:#7B1FA2,color:#000
知识点 关键内容
引擎创建 voiceWakeupper.createEngine(),指定sensitivity
唤醒词注册 addWakeupWord(),唤醒词需3-5个音节、含辅音
后台常驻 申请长时任务+通知栏常驻,Background Agent机制
多唤醒词 同时注册多个唤醒词,按word区分动作
灵敏度调节 根据场景动态调整,夜间/会议降低,安静环境提高
误唤醒防护 冷却期+频率限制+置信度阈值三重保护
功耗管理 温度监控自动降级,HarmonyOS 6自适应功耗
HarmonyOS 6 自适应功耗、分布式唤醒、按词独立灵敏度

语音唤醒是语音交互的"守门人",在极低功耗下保持"耳朵"始终打开。掌握了唤醒技术,你的应用就能在用户最需要的时候及时响应。下一篇我们将深入声纹识别——让应用不仅听到你说什么,还能知道"你是谁"。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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