樹莓派智能家居-語音聊天機器人實現

個人博客:http://www.chenjianqu.com/

原文鏈接:http://www.chenjianqu.com/show-42.html

      最近開始複習考研了,所以博客更新頻率要降下來了,只能是白天覆習,晚上抽空寫寫代碼。宿舍裏有一塊樹莓派3B+,買了兩年,一直沒怎麼用過,突然想玩玩,發現用來做智能家居還不錯。智能家居最重要的功能是語音對話,我這篇博客就是分享一下怎麼用樹莓派實現一個簡單的語音聊天機器人。

硬件配置:

 

    我用到的模塊:USB外置聲卡,麥克風,揚聲器,帶外盒和散熱器的樹莓派。樹莓派板子上沒有音頻輸入接口,因此只能通過外置的聲卡來解決這個問題,聲卡最好是免驅的。散熱器這個東西,我覺得是必要的,曾經因爲高溫燒壞了我一塊SD卡。

    語音對話的流程很簡單,就是:錄音,語音識別,對話生成,語音合成,播放語音。下面逐個分析。

 

錄音

    這看起來是一件非常簡單是事情,但樹莓派上實現並不容易。首先樹莓派沒有音頻輸入接口,需要自己淘寶一個外置聲卡和麥克風。其次需要配置聲卡:新建或修改~/.asoundrc文件,將文件內容修改如下:

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

 配置內容的意思就是音頻輸入使用聲卡1(也就是usb聲卡),輸出使用聲卡0,即板載聲卡。其中,hw:1,0表示card 1 device 0。配置完後在命令行輸入arecord -d 10 test.wav測試錄音功能是否可用。

    錄音的程序我使用的是pyaudio模塊實現。代碼如下:

import pyaudio
import wave
import os
import sys
import numpy as np
import time

def getMicroRecord(fileName):
    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 16000
    MAX_RECORD_SECONDS = 30
    MAX_SILENCE_SECONDS=3.5
    p = pyaudio.PyAudio()
    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK)
    print("recording...")
    frames = []
    isSilence=False
    isSilenceLast=False
    startTime=time.time()
    for i in range(0, int((RATE / CHUNK) * MAX_RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
        audio_data=np.fromstring(data,dtype=np.short)
        max_audio_data=np.max(audio_data)
        print(max_audio_data,end=' ')
        if(max_audio_data<800):
            isSilence=True
            
        else:
            isSilence=False
        if(isSilence and not isSilenceLast):
            startTime=time.time()
        
        isSilenceLast=isSilence
        if(isSilence and time.time()-startTime>MAX_SILENCE_SECONDS):
            break
    print("done")
    stream.stop_stream()
    stream.close()
    p.terminate()
    wf = wave.open(fileName, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()

代碼實現了錄音功能,當沒有聲音持續3.5s後錄音結束,最長錄音時間爲30s。我上面用了很簡單的方法判斷說話是否結束,但其實語音端點檢測是個複雜的問題,以後有空再研究吧。有時聲音採樣率設置爲16000會有點問題,這時啓動pulseaudio就可以了。

 

語音識別和合成

    語音識別和語音合成我都使用了百度的API。若想使用百度的API,需要在百度AI開放平臺上面先註冊一個賬戶,然後上面創建一個“應用”,就可以獲取應用的APP ID,API KEY,SECRET KEY。之後用pip安裝百度提供的API接口模塊:baidu-aip,就可以愉快的使用百度的語音識別和語音合成接口了。Python程序如下:

from aip import AipSpeech
import wave
import json

APP_ID='**********'
API_KEY='***************'
SECRET_KEY='******************'

client=AipSpeech(APP_ID,API_KEY,SECRET_KEY)
client.setConnectionTimeoutInMillis(3000)#connection timeout /ms

#語音合成
def speechSynthesis(text,fileName):
    #speech syn
    #para:text,cuid,
    result=client.synthesis(text,'zh',1,{
        'vol':5,#vol:0-15
        'spd':5,#speed:0-9
        'pit':5,#tone:0-9
        'per':0 #persion,0:woman,1:man,3:duxiaoyao,4:yaya
        })
    if not isinstance(result,dict):#if syn fail,return a dict
        with open(fileName,'wb') as f:
            f.write(result)
        return True
    else:
        if(result['err_no']==500):
            print('不支持的輸入')
        elif(result['err_no']==501):
            print('輸入參數不正確')
        elif(result['err_no']==502):
            print('token驗證失敗')
        elif(result['err_no']==503):
            print('合成後端失敗')
        return False

#語音識別
def speechRecognition(fileName):
    with open(fileName,'rb') as f:
        d=client.asr(f.read(),'wav',16000,{
            'dev_pid':1536,#putonghua
            })
        if(d['err_no']==0):
            return d['result'][0]
        else:
            return ""

上面的代碼使用時,只需把你的APP_ID,API_KEY,SECRET_KEY修改爲你自己的即可。需要說明的是,百度語音識別只能識別PCM編碼的、採樣率爲16000的音頻。

 

聲音播放

    我設置百度語音合成的音頻格式爲MP3,不能使用pyaudio模塊直接播放。播放MP3的方式有很多,最經典的辦法是使用pygame模塊,代碼如下:

from pygame import mixer
from mutagen.mp3 import MP3
import pygame
import time
import os

def playMp3(fileName):
    mp3=MP3(fileName)
    duration=mp3.info.length #get mp3 lenght
    print('mp3 len:'+str(duration))
    mixer.init()
    mixer.music.load(fileName)
    mixer.music.play()
    mixer.music.set_volume(0.99)#set volume
    time.sleep(duration)
    mixer.music.stop()

  上面的代碼中,我首先使用mutagen.mp3模塊獲取MP3文件的長度,再使用pygame.mixer模塊播放,同時設置延遲時間。但是我發現使用pygame播放的音效很怪,這可能是我沒設置好pygame的原因。所以後面我又改用命令行調用播放,代碼如下:

def playMp3ByShell(fileName):
    mp3=MP3(fileName)
    duration=mp3.info.length #get mp3 lenght
    print('mp3 len:'+str(duration))
    os.popen('omxplayer -p -o local '+fileName)
    for i in range(1000):
        time.sleep(duration/1000)

上面代碼通過命令行直接調用了omxplayer播放器播放音頻。你會發現播放代碼的後面跟着一個for循環延遲,我之所以這樣寫是因爲直接寫time.sleep(duration)似乎只能播放四五秒的時間,後面就沒聲了,我猜這跟Debian系統的進程調度有關,這樣設置分段延遲之後就沒有這個問題了。

 

對話生成

    這是語音聊天機器人最核心的模塊,根據語音識別到的文本生成相應的回答。前面我有寫過自己訓練一個聊天機器人,但是說實話效果並不是很理想。這裏我使用了開放的聊天機器人API:小I聊天機器人,代碼如下:

# -*- coding:utf-8 -*- 
import json
import urllib.request
import re
import string

def chat(text):
    BOT_NAME='垃圾機器人'
    BOT_SELF_NAME='本垃圾'
    x = urllib.parse.quote(text)
    link = urllib.request.urlopen(
        "http://nlp.xiaoi.com/robot/webrobot?&callback=__webrobot_processMsg&data=%7B%22sessionId%22%3A%22ff725c236e5245a3ac825b2dd88a7501%22%2C%22robotId%22%3A%22webbot%22%2C%22userId%22%3A%227cd29df3450745fbbdcf1a462e6c58e6%22%2C%22body%22%3A%7B%22content%22%3A%22" + x + "%22%7D%2C%22type%22%3A%22txt%22%7D")
    html_doc = link.read().decode()
    reply_list = re.findall(r'\"content\":\"(.+?)\\r\\n\"', html_doc)
    reply=str(reply_list[-1])
    reply=reply.replace("小i機器人",BOT_NAME)
    reply=reply.replace("小i",BOT_SELF_NAME)
    return reply

上面代碼把小I替換成了自定義的名字,代碼很簡單不用過多解釋。

 

主程序

    把上面各個模塊綜合起來,得到下面的主程序:

from speech import *
from voice import *
from chatbot import *
from microrecord import *
import time

mp3FileName='dialog.mp3'
wavFileName='dialog.wav'


def getResponse(text):
    output_text=''
    output_text=chat(text)
    return output_text
    
while True:
    getMicroRecord(wavFileName)
    input_text=speechRecognition(wavFileName)
    print('input:'+input_text)
    if(len(input_text)==0):
        break
    output_text=getResponse(input_text)
    if(speechSynthesis(output_text,mp3FileName)):
        playMp3ByShell(mp3FileName)
    print('output:'+output_text)
    
print('dialog done')

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章