树莓派功能十分强大,作为一个微型电脑,独特的阵脚设计使得树莓派开发的自定义程度非常高。本篇博客通过使用python语言,封装一个简单的语音助手程序。

零、物品清单

1
2
3
4
5
1:一个树莓派(3代、4代皆可)
2:免驱的usb声卡(声卡需接在树莓派usb接口)
3:麦克风(接在声卡上)
4:有线音响(音响可连在声卡上,也可连在树莓派的3.5mm接口上)
(树莓派播放设备默认使用3.5mm接口,具体过程需要自定义播放设备)

如果要设置usb声卡输出,参考教程配置声卡教程,如果使用默认3.5mm接口输出可以忽略。
设计语音助手有如下过程:录音、语音识别、图灵机器人对话、语音合成,以下对各个过程进行介绍。

一、pyaudio录音

使用python语言进行录音,主要是将说话者声音输出成一段音频文件。对于python来说,这很容易。通过引用pyaudio库,可以进行录音、播放、生成wav文件等。
首先,我们在树莓派上需要下载pyaudio库: pip3 install pyaudio
在pyaudio的官网介绍上有对pyaudio的详细介绍pyaudio官网,以官网示例:
创建一名为rec的python文件 sudo nano rec.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import RPi.GPIO as GPIO
import pyaudio
import wave
import os
import sys
def rec_fun():
# 隐藏错误消息,因为会有一堆ALSA和JACK错误消息,但其实能正常录音
# os.close(sys.stderr.fileno())
BUTT = 26 # 开始录音的按钮:一边接GPIO26,一边接地
GPIO.setmode(GPIO.BCM)
# 设GPIO26脚为输入脚,电平拉高,也就是说26脚一旦读到低电平,说明按了按钮
GPIO.setup(BUTT, GPIO.IN, pull_up_down = GPIO.PUD_UP)
# wav文件是由若干个CHUNK组成的,CHUNK我们就理解成数据包或者数据片段。
CHUNK = 512
FORMAT = pyaudio.paInt16 # pyaudio.paInt16表示我们使用量化位数 16位来进行录音
RATE = 16000 # 采样率1.6k。
WAVE_OUTPUT_FILENAME = "command.wav"
print('请按住按钮开始录音...')
GPIO.wait_for_edge(BUTT, GPIO.FALLING)
# To use PyAudio, first instantiate PyAudio using pyaudio.PyAudio(), which sets up the portaudio system.
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = 1, # cloud speecAPI只支持单声道
rate = RATE,
input = True,
frames_per_buffer = CHUNK)
print("录音中...")
frames = []
# 按住按钮录音,放开时结束
while GPIO.input(BUTT) == 0:
data = stream.read(CHUNK)
frames.append(data)
print("录音完成,输出文件:" + WAVE_OUTPUT_FILENAME + '\n')
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(1)
wf.setsampwidth(p.get_sample_size(FORMAT)) # Returns the size (in bytes) for the specified sample format.
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
return
# 可以直接运行rec.py进行测试,同时保证该文件import时不会自动运行
if __name__ == '__main__':
rec_fun()

通过树莓派一 点亮LED灯,我们对树莓派的阵脚已经有了大概了解。
按钮录音,是通过按钮控制阵脚高低电平。在上述代码里引用import RPi.GPIO as GPIO,选择26号输出脚和GND。
执行后按住按钮开始录音,会在rec.py同目录下生成command.wav文件。

二、语音识别

我选择了百度AIP平台,使用其语音识别技术。(当然也可使用Google、科大讯飞等平台)

1. 申请百度AIP账号与应用

在百度AI开发平台注册账号:官网链接
再在控制台创建一个新应用,如下图
1-2020-07-13

2. python调用百度AIP

官网文档中有详细使用语音识别技术,包括python的示例。首先导入AipSpeech, 即百度AI语音识别的Python SDK客户端(pip3 install baidu-aip)。附上代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from aip import AipSpeech
import os

APP_ID = '你的IDID'
API_KEY = '你的KEYKEY'
SECRET_KEY = '你的secret_key'

# 新建一个 AipSpeech
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

# 读取文件
def get_file_content(filePath): #filePath待读取文件名
with open(filePath, 'rb') as fp:
return fp.read()
# 语音识别
def stt(filename):
# 识别本地文件
result = client.asr(get_file_content(filename),
'wav',
16000,
{'dev_pid': 1537,} # dev_pid 参数表示识别的语 言类型 1537表示普通话
)
print (result)
return result['result']

# 解析返回值,打印语音识别的结果
if result['err_msg']=='success.':
word = result['result'][0].encode('utf-8') # utf-8 编码
if word!='':
if word[len(word)-3:len(word)]==',':
print (word[0:len(word)-3])
with open('demo.txt','wb') as f:
f.write(word[0:len(word)-3])
else:
print (word.decode('utf-8').encode('gbk'))
with open('demo.txt','wb') as f:
f.write(word)
f.close()
else:
print ("音频文件不存在或格式错误")
else:
print ("错误")
# main 函数 识别本地录音文件 yahboom.wav
if __name__ == '__main__':
stt('command.wav') # command.wav为待读取文件,请替换

三、与图灵机器人对话

使用图灵机器人基本原理就是使用urllib.request模块,向接口地址发送HTTP POST请求,请求中加入了聊天内容。(返回内容是一个字典)
首先我们需要申请一个机器人:官网入口
python代码参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import json
import urllib.request
def chat(question):
api_url = "http://openapi.tuling123.com/openapi/api/v2"
text_input = question
req = {
"perception":
{
"inputText":
{
"text": text_input
},
"selfInfo":
{
"location":
{
"city": "xxx",
"province": "xxx",
"street": "xxx"
}
}
},
"userInfo":
{
"apiKey": "xxxxxxxxxxxxx", #你的key
"userId": "123" #唯一的标识,自选即可
}
}
# 将字典格式的req转为utf8编码的字符串
req = json.dumps(req).encode('utf8')
print('\n' + '正在调用图灵机器人API...')

http_post = urllib.request.Request(api_url, data=req, headers={'content-type': 'application/json'})
response = urllib.request.urlopen(http_post)
print('得到回答,输出为字典格式:')

response_str = response.read().decode('utf8')
response_dic = json.loads(response_str)
intent_code = response_dic['intent']['code']

# 返回网页类的输出方式
if(intent_code == 10023):
results_url = response_dic['results'][0]['values']['url']
results_text = response_dic['results'][1]['values']['text']
with open("result.txt","w") as f:
f.write(results_text) # 自带文件关闭功能,不需要再写f.close()
answer = {"code": intent_code, "text": results_text, "url":results_url}
print(answer)
return(answer)
# 一般的输出方式
else:
results_text = response_dic['results'][0]['values']['text']
answer = {"code": intent_code, "text": results_text}
with open("result.txt","w") as f:
f.write(results_text) # 自带文件关闭功能,不需要再写f.close()
print(answer)
return(answer)

if __name__ == '__main__':
with open("demo.txt", "r",encoding='UTF-8') as f:
text = f.read() # 读取文件
eg_question = text
chat(eg_question)

四、语音合成

我同样选择了百度的语音合成(注意,AI平台新建应用后,语音合成技术需要自行领用。语音识别的key与合成的key不一样,使用请注意)。示例文档自行参考,提供参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#encoding:utf8
from aip import AipSpeech
import os

""" 你的 APPID AK SK """
APP_ID = 'xxxxx'
API_KEY = 'xxxxxxxxxxxxxxxx'
SECRET_KEY = 'chMdMAS0acD7sPS6FhP3a3iOijU7wMVl'
def speak(text_content):
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
texts = text_content
result = client.synthesis(texts , options={'vol':5})

print('\n' + "正在调用语音合成API...")

if not isinstance(result,dict):
with open('answer.mp3','wb') as f:
f.write(result)
else:print(result)

print("播放音频中...")
print("以下均为mplayer的输出内容\n")
os.system("mplayer answer.mp3")
return
if __name__ == '__main__':
with open("result.txt", "rb") as f: # 打开文件

text = f.read() # 读取文件
eg_text_content = text
speak(eg_text_content)

五、封装

至此,我们的录音、识别、对话、合成已经完成了,开始对这四个py文件进行封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 这些import进来的模块是同目录下的py文件
import rec # rec.py负责录制wav音频
import speech_api # speech_api.py负责wav转文字
import turing # turing.py负责获得图灵机器人的文字回答
import tts # tts.py负责读出回答
import os
rec.rec_fun() # 录制音频
recognize = speech_api.stt('command.wav') # 识别语音,返回值是字典
print("111")
print(recognize)
if recognize[0] == "音乐。":
rec.rec_fun()
recognize1 = speech_api.stt('command.wav')
os.system("python3 url1.py " +recognize1[0])
else:
turing_answer = turing.chat(recognize)

tts.speak(turing_answer['text'])

下面,就开始与傻瓜机器人的快乐对话吧。
当然在封装时,引用了一个url1文件,是获取获取歌曲直链。
一个简单的if else 完成了图灵机器人对话到在线放歌的切换。
当然,读者还可以进一步完善,例如实现与图灵机器人的多次对话直到退出。