離線語音Snowboy熱詞喚醒+ 樹莓派語音交互實現開關燈


離線語音Snowboy熱詞喚醒

語音識別現在有非常廣泛的應用場景,如手機的語音助手,智能音響(小愛,叮咚,天貓精靈...)等.
語音識別一般包含三個階段:熱詞喚醒,語音錄入,識別和邏輯控制階段.

熱詞喚醒就是喚醒設備,讓設備解析你接下來說的話.通常設備一直在錄入周圍的聲音,但是設備此時不會有任何反應.當通過像「Hi,Siri」這樣的喚醒詞被喚醒以后,設備就開始處理接下來的聲音了。熱詞喚醒是語音識別的開始。

Snowboy 是比較流行的熱詞喚醒框架,目前已經被百度收購。Snowboy 對中文支持友好,相對 Pocketsphinx 配置使用較為簡單,推薦使用。

snowboy官方文檔地址[英文的] http://docs.kitt.ai/snowboy

安裝

一、獲取源代碼並編譯

安裝依賴

樹莓派原生的音頻設備是不支持語音輸入的(無法錄音),需要在網上購買一支免驅動的USB音頻驅動,一般插上即可直接使用。
建議安裝下 pulseaudio 軟件,減少音頻配置的步驟:
$ sudo apt-get install pulseaudio

安裝 sox 軟件測試錄音與播放功能:
$ sudo apt-get install sox

安裝完成后運行 sox -d -d 命令,對着麥克風說話,確認可以聽到自己的聲音。

安裝其他軟件依賴

  • 安裝 PyAudio:$ sudo apt-get install python3-pyaudio
  • 安裝 SWIG(>3.0.10):$ sudo apt-get install swig
  • 安裝 ATLS:$ sudo apt-get install libatls-base-dev
編譯源代碼

獲取源代碼:$ git clone https://github.com/Kitt-AI/snowboy.git
編譯 Python3 綁定:$ cd snowboy/swig/Python3 && make

測試

如果使用的是樹莓派,你還需要在 ~/.asoundrc更改聲卡設置:

  type asym
   playback.pcm {
     type plug
     slave.pcm "hw:0,0"
   }
   capture.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
}

進入官方示例目錄 snowboy/examples/Python3 並運行以下命令:
$ python3 demo.py resources/models/snowboy.umdl
( 命令中的 snowboy.umdl 文件即語音識別模型

然后對着麥克風清晰地講出“snowboy”,如果可以聽到“滴”的聲音,則安裝配置成功。

PS:官方源代碼使用 Python3 測試有報錯,經測試需修改 snowboy/examples/Python3 目錄下的 snowboydecoder.py 文件。
將第 5 行代碼 from * import snowboydetect 改為 import snowboydetect 即可直接運行。

快速開始

GitHub 上有比較詳細的 Demo,強烈建議先看看。先創建一個 HotwordDetect 類,這個類包含喚醒模型,聲音增益,靈敏度等參數。然后初始化 Detector 對象,Snowboy 的 Detector 類存在下載下來的源碼里。訓練模型可以是單個,也可以是列表形式。

from .. import snowboydetect

class HotwordDetect(object):
    def __init__(self, decoder_model,
                 resource,
                 sensitivity=0.38,
                 audio_gain=1):
        """init"""
        self.detector = snowboydetect.SnowboyDetect(
            resource_filename=resource.encode(),
            model_str=decoder_model.encode())
        self.detector.SetAudioGain(audio_gain)

初始化以后可以創建啟動方法,啟動方法一般會指定一個喚醒回調函數,也就是 「Hi,Siri」之后可能出現的「叮」聲;還可以指定錄音回調函數,也就是設備喚醒以后你需要用這些聲音去干什么:

class HotwordDetect(object):
    ...
    def listen(self, detected_callback,
              interrupt_check=lambda: False,
              audio_recorder_callback):
        """begin to listen"""
        ...
        state = "PASSIVE"
        while True:
            status = self.detector.RunDetection(data)
            ...
            if state == "PASSIVE":
                tetected_callback()
                state = "ACTIVE"
                continue
            elif state == "ACTIVE":
                audio_recorder_callback()
                state = "ACTIVE"
                continue

這里的邏輯可以自己去定義,主要是在兩個狀態間切換,當設備接收到喚醒詞以后,status 會指出被識別到的喚醒詞的序號,比如你定義了 「Siri」和 「Xiaowei」兩個喚醒詞,status 為 1 就表示 Siri 被喚醒,status 為 2 就表示 Xiaowei 被喚醒。然后將狀態改成激活狀態,這個時候執行 audio_recorder_callback 方法,執行完后將狀態切換回喚醒狀態。

在線語音識別

當設備被喚醒以后,你可以拿到錄音數據去做任何想做的事情,包括調取百度等語音識別接口。這些邏輯都包含在 audio_recorder_callback 回調方法中。需要注意的是 Snowboy 目前只支持 16000 的錄音采樣率,其他采樣率的錄音數據都不能使用,你可以通過兩種辦法來解決:

  • 使用支持 16000 采樣率的聲卡
  • 進行錄音數據的采樣率轉換

目前比較大的兩家聲卡芯片公司 C-Media 和 RealTek 一般產品都是 48k 以上的,支持 16k 的芯片一般比較貴,可能到 60 元左右。「綠聯」有兩款產品可以支持,購買時請查看產品參數,對照芯片公司的產品型號是否支持 16k 采樣。

聲音模型的訓練

官方提供兩種模式進行個性化聲音模型創建:

  • website。只要你有 GitHub,Google 和 Facebook 帳號中的一種,登錄就可以錄音完成訓練。
  • train-api。根據文檔傳指定的參數就可以完成訓練,api 返回給你升學模型的數據。

這兩種方式獲得的都是私人的聲音模型,獲取的是 .pmdl的文件形式。一般化的 universal 模型不提供,需要聯系官方商業合作。獲取到的模型,越多人測試准確率越高,為了提高准確率,你可以邀請更多人來測試你的模型。還有麥克風的種類也會影響准確度,在什么設備上使用就在那個設備上訓練模型能提高准確率。語音識別是一個比較精尖的技術,需要注意很多問題,正如 ChenGuo 說的:

Speech Recognition is not that easy.

在自己的項目中使用

將以下文件復制到自己的項目目錄下:

  • 下載好的 model.pmdl 模型文件
  • snowboy/swig/Python3 目錄下編譯好的 _snowboydetect.so
  • snowboy/examples/Python3 目錄下的 demo.pysnowboydecoder.pysnowboydetect.py 文件以及 resources 目錄
  • 在項目目錄下執行 $ python3 demo.py model.pmdl 並使用自己的喚醒詞進行測試

orangePi下使用語音識別來實現語音開關燈,需要聯網使用.

gpio.py

#!/usr/bin/env python
# encoding: utf-8
#
# 香橙派(orangepi)的GPIO操控,詳細查下以前的帖子.
#

"""
@version: ??
@author: lvusyy
@license: Apache Licence 
@contact: lvusyy@gmail.com
@site: https://github.com/lvusyy/
@software: PyCharm
@file: gpio.py
@time: 2018/3/13 18:45
"""
import wiringpi as wp


class GPIO():

    def __init__(self):
        self.wp=wp
        wp.wiringPiSetupGpio()
        #wp.pinMode(18, 1)
        #wp.pinMode(23, 0)

    def setPinMode(self,pin,mode):
        self.wp.pinMode(pin,mode)

    def setV(self,pin,v):
        self.wp.digitalWrite(pin,v)

    def getV(self,pin):
        return self.wp.digitalRead(pin)

之前案例修改了以下. control.py

#!/usr/bin/env python
# encoding: utf-8
#
# 利用熱詞喚醒后使用百度語音識別api識別語音指令,然后匹配操作指令.如關燈,開燈操作.
### 使用snowboy的多個熱詞喚醒,效果會更好,而且不需要網絡. 有空測試.

"""
@version: ??
@author: lvusyy
@license: Apache Licence 
@contact: lvusyy@gmail.com
@site: https://github.com/lvusyy/
@software: PyCharm
@file: control.py
@time: 2018/3/13 17:30
"""
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import time
import pyaudio
import wave
import pygame
import snowboydecoder
import signal
from gpio import GPIO
from aip import AipSpeech

APP_ID = '109472xxx'
API_KEY = 'd3zd5wuaMrL21IusNqdQxxxx'
SECRET_KEY = '84e98541331eb1736ad80457b4faxxxx'

APIClient = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

interrupted = False

#定義采集聲音文件參數
CHUNK = 1024
FORMAT = pyaudio.paInt16 #16位采集
CHANNELS = 1             #單聲道
RATE = 16000             #采樣率
RECORD_SECONDS = 5       #采樣時長 定義為9秒的錄音
WAVE_OUTPUT_FILENAME = "./myvoice.pcm"  #采集聲音文件存儲路徑


class Light():

    def __init__(self):
        self.pin=18
        self.mode=1 #open is 1 close is 0
        self.mgpio=GPIO()
        self.mgpio.setPinMode(pin=self.pin,mode=1) #OUTPUT 1 INPUT 0

    def on(self):
        ''
        self.mgpio.setV(self.pin,self.mode)

    def off(self):
        ''
        self.mgpio.setV(self.pin,self.mode&0)

    def status(self):
        #0 is off 1 is on
        return self.mgpio.getV(self.pin)



def get_file_content(filePath):
    with open(filePath, 'rb') as fp:
        return fp.read()


def word_to_voice(text):
    result = APIClient.synthesis(text, 'zh', 1, {
        'vol': 5, 'spd': 3, 'per': 3})
    if not isinstance(result, dict):
        with open('./audio.mp3', 'wb') as f:
            f.write(result)
            f.close()
    time.sleep(.2)
    pygame.mixer.music.load('./audio.mp3')#text文字轉化的語音文件
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy() == True:
        print('waiting')


def  get_mic_voice_file(p):
    word_to_voice('請說開燈或關燈.')
 
    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK)
    print("* recording")
 
    frames = []
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
    print("* done recording")
    stream.stop_stream()
    stream.close()
    #p.terminate()#這里先不使用p.terminate(),否則 p = pyaudio.PyAudio()將失效,還得重新初始化。
    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()
    print('recording finished')



def  baidu_get_words(client):
    results = client.asr(get_file_content(WAVE_OUTPUT_FILENAME), 'pcm', 16000, { 'dev_pid': 1536, })
    # print(results['result'])
    words=results['result'][0]
    return words
#_*_ coding:UTF-8 _*_
# @author: zdl
# 實現離線語音喚醒和語音識別,實現一些語音交互控制

# 導入包


def signal_handler(signal, frame):
    global interrupted
    interrupted = True


def interrupt_callback():
    global interrupted
    return interrupted

#  回調函數,語音識別在這里實現
def callbacks():
    global detector

    # 語音喚醒后,提示ding兩聲
    # snowboydecoder.play_audio_file()
    pygame.mixer.music.load('./resources/ding.wav')#text文字轉化的語音文件
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy() == True:
        print('waiting')
    #snowboydecoder.play_audio_file()

    #  關閉snowboy功能
    detector.terminate()
    #  開啟語音識別
    get_mic_voice_file(p)
    rText=baidu_get_words(client=APIClient)

    if rText.find("開燈")!=-1:
        light.on()
    elif rText.find("關燈")!=-1:
        light.off()

    # 打開snowboy功能
    wake_up()    # wake_up —> monitor —> wake_up  遞歸調用

# 熱詞喚醒
def wake_up():

    global detector
    model = './resources/models/snowboy.umdl'  #  喚醒詞為 SnowBoy
    # capture SIGINT signal, e.g., Ctrl+C
    signal.signal(signal.SIGINT, signal_handler)

    # 喚醒詞檢測函數,調整sensitivity參數可修改喚醒詞檢測的准確性
    detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5)
    print('Listening... please say wake-up word:SnowBoy')
    # main loop
    # 回調函數 detected_callback=snowboydecoder.play_audio_file
    # 修改回調函數可實現我們想要的功能
    detector.start(detected_callback=callbacks,      # 自定義回調函數
                   interrupt_check=interrupt_callback,
                   sleep_time=0.03)
    # 釋放資源
    detector.terminate()

if __name__ == '__main__':
	#初始化pygame,讓之后播放語音合成的音頻文件
    pygame.mixer.init()
    p = pyaudio.PyAudio()
    light=Light()
    wake_up()

相關參考文檔:

http://docs.kitt.ai/snowboy/#api-v1-train

https://github.com/Kitt-AI/snowboy

https://looker53.github.io/2018/03/29/20180329-%E8%AF%AD%E9%9F%B3%E8%AF%86%E5%88%AB%E4%B9%8BSnowboy%E7%83%AD%E8%AF%8D%E5%94%A4%E9%86%92/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM