树莓派打造智能语音控制系统

树莓派打造智能语音控制系统(附最终代码)

本系统采用的树莓派为3B+

1、语音识别系统在实际应用中的意义:

(1)为人们提供高效,便利的居住环境。
(2)摆脱以往必须手动操作的麻烦,通过语音就能达到控制相关设备的目的。
(3)智能语音对话:通过语音和机器人对话,询问想要知道的相关信息,节省自己手动搜索的时间。

2、系统方案设计

2.1 系统的整体结构

系统的整体结构如图2-1所示。

图2-1 系统整体结构图

本智能语音控制系统采用电器设备+树莓派+百度语音接口来实现,分为应用层、逻辑层和处理层。其中电气设备作为应用层,负责系统的处理结果显现出来。树莓派作为逻辑层,处理业务逻辑上的问题,其中包括对处理层返回的识别后的语音信号进行存储,以及采取相应的操作。百度语音接口作为处理层,对树莓派所录的语音信号进行识别,将识别后的语音信号再次返回给树莓派。

2.2 系统的硬件整体架构

本系统中我们的电气设备直接连接树莓派的GPIO口,具体的连线如图2-2所示。

图2-2 系统整体硬件架构图

2.3 系统的软件整体架构

智能语音控制系统的软件整体架构主要分为以下四部分:
语音信号的采集:通过树莓派自带的录音功能来采集语音信号,并保存。
语音信号的识别:通过python调用百度语音接口,识别采集的语音信号,将识别后的语音信号以wav文件的格式存储于树莓派中。
语音机器人的回复:读取存放识别语音信号的wav文件,调用图灵机器人接口,实现机器人智能回复。
电器设备的控制:电器设备的控制其实说到底就是对树莓派GPIO的控制,其中对风扇驱动的控制主要通过python实现,而led灯的控制主要通过C语言来实现。

3、系统设计与实现

3.1系统总体需求概述

从智能语音控制系统的使用者这个角度出发,我们应该尽量避免使用者去直接接触到我们这个系统的内部设计,而是应该提供简单的接口来供用户使用。本系统为智能语音系统,所以在功能的建设上应该具备以下三方面的需求:
(1)对于智能语音对话模块,我们通过百度AI开放平台的语音识别接口和图灵机器人来实现对话的功能,只要能采集到使用者的语音信号,系统就会去调用相应的接口来给予适当的回复。
(2)对于智能语音控制灯泡开关模块,系统会去识别用户下达的语音指令,并将其转换成相应的操作,达到控制灯泡开关的目的。
(3)对于语音控制风扇开关以及风扇档位模块,系统同样会去识别用户下达的语音指令,达到控制风扇开关以及调整风扇档位的目的。

3.2智能语音识别模块的具体实现

3.2.1语音信号的采集

本系统采用树莓派自带的录音arecord来录音,通过以下指令既可以启动树莓派的录音功能:

arecord -D "plughw:1" -f S16_LE -r 16000 -d 3 wav文件。

其中-D后面所带的为设备名、-f后面所带的为采样格式、-r后面所带的为采样频率。因为百度语音接口所要求的采样频率为16000或者8000,所以在本系统中我们采用了16000的采样频率。后面所带wav文件是我们录音所存储的文件。

3.2.2百度AI语音接口的调用

首先要使用百度AI语音接口,要先去百度AI平台申请语音识别账号,申请成功会得到API Key和Secret Key,如图3-1所示。后续我们在python代码中需要填入这两个key,如下所示:

body = { “grant_type”:“”, “id” :”API Key”, “secret”:”Secret Key”, }。

这样我们的程序就可以调用到百度语音接口了。

图3-1 AppID和API Key

3.2.3智能语音识别的实现

智能语音识别提交的数据格式如Data所示:

Data = { “format” : “wav”, “rate” : 16000, “channel” : 1, “cuid” : “”, “token” : tok, “speech” : wav_data, “len” : wav_length }。

format为提交录音文件的格式,在这里为wav。rate为采样速率,在这里为16000,要和录音的采样速率等同。token为上小节调用百度语音接口所返回的内容。speech和len则为录音文件的内容以及长度。在这里使用urllib2这个库(树莓派自带,只需要导入即可)来进行网络的交互,只需要把Data提交到相应的url路径。就可以得到从接口返回的识别后的语音数据。

3.3智能语音对话模块的具体实现

3.3.1图灵机器人服务平台的调用

关于智能语音对话模块的实现,我们在基于语音识别的基础上,增加了一个图灵机器人接口的使用。图灵机器人是一个免费的开放平台,这个平台为广大开发群众以及企业合作伙伴提供一系列智能语义处理的服务。话不多说,我们来看具体实现。
首先要使用这个平台,我们需要去图灵机器人官网注册一个账号,注册后我们就可以申请机器人,同时得到一个apikey,如图3-2所示。通过这个apikey我们就可以去请求调用到图灵机器人服务平台。

图3-2 图灵机器人的apikey

3.3.2智能语音回复的实现

我们通过在python代码中加入这个apikey。请求的数据格式如下所示:
body = {"key":Tuling_API_KEY,"info":words.encode("utf-8")}。
其中,key填写的就是所申请到的机器人的apikey,info就是通过百度语音接口识别后的语音数据。 r = requests.post(url,data=body,verify=True),其中的url为图灵机器人接口的路径,r为图灵机器人返回的语音数据。
因为后续还需要根据识别后的语音数据来执行一些相应的操作以及通过音箱播放图灵机器人所返回的语音信息,所以在这里需要额外多加一步,对语音数据进行合成。同样是利用百度AI语音合成接口来实现。
音箱播放图灵机器人的回复是通过mpg123来实现的。mpg123为音频解码的软件,通过它可以对音频进行解码,同时调用驱动来播放音频。上述调用语音合成接口后会返回一个url = “http://tsn.baidu.com/text2audio?tex=“+tex+”&lan=zh&cuid=”+cuid+”&ctp=1&tok=”+tok+”&per=3” 。有了这个url,就可以通过os.system(‘mpg123 “%s”’%url)这个指令,来使树莓派播放合成后的音频。下面一些功能模块也会用到mpg123来播放一些相应的音频。

3.4语音控制led灯模块的具体实现

3.4.1 语音识别具体操作指令部分的实现

语音控制led灯模块是基于智能语音识别的基础上来实现的。主要分为三个步骤:
(1) 通过录音功能,将语音指令记录于wav文件中。
(2) 读取wav文件,调用语音识别接口进行识别,并用一个info字段记录识别后的语音信号。
(3) 在python代码中对info字段进行比对,如若info字段中存在例如“开灯”、“关灯”等字段,即执行相应的操作。

3.4.2控制led灯的具体实现

本系统中控制led灯是采用了WiringPi这个函数库来实现的。WiringPi是树莓派的GPIO控制库函数,其本身采用C语言或者C++来开发,可以嵌入到其他函数中,如python或者PHP中。在本系统中,我们通过控制WiringPi模式下树莓派的4号引脚来实现控制led灯的开关。当识别到开灯指令,运行./light on指令给予4号引脚高电平,led灯亮,当识别到关灯指令,运行./light off给予4号引脚低电平,led灯灭。注意一个事项:在执行light文件时候,我们需要给他一个执行的权限,因为linux系统中对于文件权限的管控比较严格,我们可以用以下命令chmod u+x light来给灯泡脚本一个执行的权限。

3.5 语音控制风扇驱动模块的具体实现

3.5.1 语音识别各种控制指令的实现

语音识别各种控制指令同上述。只是在其中加入了其他一些控制指令,当识别到如“风扇一档”这样的指令,可以改变风扇的转速。

3.5.2 L298N模块的介绍

改变风扇转速是基于PWM技术,在这里我们采用L298N电机驱动板模块来帮助我们实现这个功能,L298N电机驱动模块的外观及连线方式如图3-3所示。

图3-3 L298N电机驱动模块的外观以及接口连线介绍

3.5.3控制电机转速的实现

控制电机转速的软件实现主要采用了python。p = GPIO.PWM(32,50)通过这条指令设置树莓派GPIO引脚中32引脚为输出,PWM的频率为50hz。p.start(0)这个指令让信号开始输出。最后是改变转速的指令p.ChangeDutyCycle(N),通过改变N来改变其占空比(N的取值在0-100之间),就可以达到控制转速的目的,占空比越大,电机的转速越快。
在本系统中,我们只控制一个电机的转速,所以在这里只使用了L298N电机驱动模块IN1这个引脚。树莓派32GPIO口发出PWM信号,连接到L298N电机驱动模块的IN1引脚,既可以达到控制电机转速的目的,L298N电机驱动模块与树莓派连线图如3-5所示。

图3-5 L298N电机驱动模块与树莓派的连线图

4 调试

4.1 语音唤醒功能的调试

本设计的程序运行之后,首先会停留在第一个录音环节,为时2秒,如图4-1所示。其功能作用为语音唤醒,就是在每次你下达语音指令之前,需要说一声“你好”来唤醒系统,然后才可以继续下达语音指令。唤醒系统后系统会播报“我来啦”的语音,屏幕上也会显示“我来啦”字样,如图4-2所示。

在这里插入图片描述
4-1 语音唤醒录音

4-2 语音唤醒成功显示

经多次测试发现,正常情况下语音唤醒功能都能较好的实现,但同时也存在特殊情况,主要发生在系统没有录到音时,因为我给语音唤醒的录音时间为2秒,在这两秒内如果没有录到音就会出现问题。所以给的解决方法是对语音唤醒这个函数进行递归调用,如果第一次没有录到音,程序就会重新调用语音唤醒这个函数,无限循环,直到录到音为止。

4.2 语音控制调试

设计中所用到的语音控制指令如下表所示:

指令 功能
打开灯 Led灯打开
关掉灯 Led灯关闭
打开风扇 驱动电机开始转动
风扇二档 驱动电机加快转速
关掉风扇 驱动电机停止转动

在经历多次测试后发现,一般情况下系统的的控制功能和对话功能都能够较好的实现,互不干扰。但是存在一些特殊情况,比如说你下达的是控制命令,但是系统却启用了对话功能,控制功能没有生效。经过对系统进行重重排查,将原因归结于录音设备的问题,比如说你“打开灯”但是系统并没有收集到这个完整的指令,可能录音到的为“打”或者“打开”,系统判定你没有说出完整的控制指令,因此就会调用对话功能来对此次录音到的信息来做一个回复。针对这种情况,因为本系统采取的录音设备质量比较一般,所以目前无法很好的去解决。

//使用如下代码记得修正缩进问题,python格式要求
系统程序如下:
代码1
# coding: utf-8
import RPi.GPIO as GPIO 
import numpy as np 
from datetime import datetime 
import wave 
import time 
import urllib, urllib2, pycurl 
import base64 
import json 
import sys 
import requests  
import os
import yuyinhecheng
import tulin
import yuyinshibie
 
token = yuyinshibie.get_access_token()
def yuyinhx():
   os.system('arecord -D "plughw:1" -f S16_LE -r 16000 -d 3 /home/pi/Desktop/yyhx.wav')
   meg =yuyinshibie.asr_main("/home/pi/Desktop/yyhx.wav",token)
   if any(['好'.encode("utf-8") in meg,'你'.encode("utf-8")in meg,"你好".encode("utf-8")in meg,"您".encode("utf-8")in meg]):
       f=open('/home/pi/Desktop/yyhx.wav','wb')
       f.close()
       url="http://tsn.baidu.com/text2audio?tex=我来啦&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
       os.system('mpg123 "%s"'%url)
       global flag
       flag = True
   else:
       time.sleep(3)
       yuyinhx()
​​
def controllPwm(speed):
   #atexit.register(GPIO.cleanup)
   os.system('cd /home/pi/Desktop&&./start on')​#这句应该可以不要
   servopin = 32                      #用到树莓派哪个口
   GPIO.setmode(GPIO.BOARD)​      #设置模式
   GPIO.setup(servopin, GPIO.OUT, initial=False) #设置32为输出
   p = GPIO.PWM(servopin,50) #50HZ #设置32为PWM 频率50Hz
   p.start(0)
   p.ChangeDutyCycle(speed)
   time.sleep(0.02) 
def controlldevices():
   switch = True
   while switch:
       yuyinhx()
       if flag:
           while True:
               os.system('sudo arecord -D "plughw:1" -f S16_LE -r 16000 -d 3 /home/pi/Desktop/pwm.wav')
               info =yuyinshibie.asr_main("/home/pi/Desktop/pwm.wav",token)
               if '打开风扇'.encode("utf-8") in info:
                   url = "http://tsn.baidu.com/text2audio?tex=正在为您开风扇&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   os.system('cd /home/pi/Desktop&&./start on')
                   servopin = 32
                   GPIO.setmode(GPIO.BOARD)
                   GPIO.setup(servopin,GPIO.OUT,initial=False)
                   p = GPIO.PWM(servopin,50)
                   p.start(0)
                   p.ChangeDutyCycle(50) 
                   break
               
​​​​             elif '二档'.encode("utf-8") in info:
                   url = "http://tsn.baidu.com/text2audio?tex=正在开风扇一档&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   os.system('cd /home/pi/Desktop&&./start on')
                   servopin = 32
                   GPIO.setmode(GPIO.BOARD)
                   GPIO.setup(servopin,GPIO.OUT,initial=False)
                   p = GPIO.PWM(servopin,50)
                   p.start(0)
                   p.ChangeDutyCycle(100)
                   break
 
               elif '关掉风扇'.encode("utf-8") in info:
                   url = "http://tsn.baidu.com/text2audio?tex=正在为您关风扇&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   os.system('cd /home/pi/Desktop&&./start off')
                   servopin = 32
                   GPIO.setmode(GPIO.BOARD)
                   GPIO.setup(servopin,GPIO.OUT,initial=False)
                   p = GPIO.PWM(servopin,50)
                   p.start(0)
                   p.ChangeDutyCycle(0)
                   break
​​​​
​​​​		elif '打开灯'.encode("utf-8") in info:
                   url = "http://tsn.baidu.com/text2audio?tex=正在为您开灯&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   os.system('cd /home/pi/Desktop&&./light on')
                   break
 
 
               elif '关掉灯'.encode("utf-8") in info:
                   url = "http://tsn.baidu.com/text2audio?tex=正在为您关灯&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   time.sleep(0.5)
                   os.system('cd /home/pi/Desktop&&./light off')
                   break
​​​​
​​​​elif info is not None:
                   if'风扇'.encode("utf-8") not in info:
                      if '灯'.encode("utf-8") not in info:
                          if'二档'.encode("utf-8")not in info:
                              tex = tulin.Tuling(info)
​​​​​           if any(['那就换个方式看看'.encode("utf-8") in tex , '扫码出现问题了吗'.encode("utf-8")in tex,'是失效了吗'.encode("utf-8")in tex,'有没有联网呀'.encode("utf-8")in tex,'不会是假的吧'.encode("utf-8")in tex]):
                                    url="http://tsn.baidu.com/text2audio?tex=请语音输入指令&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                                    os.system('mpg123 "%s"'%url)
                                    continue
​​​​​​​​else:​
                                     url = yuyinhecheng.yuyinhecheng_api(tok,tex)
                                     os.system('mpg123 "%s"'%url)
                                      time.sleep(0.5)
​​​​​                     break
               
​​​​else:
                   url = "http://tsn.baidu.com/text2audio?tex=请语音输入指令&lan=zh&cuid=B8-27-EB-BA-24-14&ctp=1&tok="+token+"&per=3"
                   os.system('mpg123 "%s"'%url)
                   continue
​​​​​
​​​​​
​​​​
if __name__ == '__main__':
controlldevices()
 
代码2:
# coding: utf-8
import sys 
import json 
import urllib2 
import base64 
import requests
reload(sys) 
sys.setdefaultencoding(“utf-8”)
def get_access_token(): 
url = “https://openapi.baidu.com/oauth/2.0/token” 
body = { 
“grant_type”:”client_credentials”, 
“client_id” :”此处填写自己的client_id”, 
“client_secret”:”此处填写自己的client_secret”, 
}
r = requests.post(url,data=body,verify=True)
respond = json.loads(r.text)
return respond["access_token"]
def yuyinshibie_api(audio_data,token): 
speech_data = base64.b64encode(audio_data).decode(“utf-8”) 
speech_length = len(audio_data) 
post_data = { 
“format” : “wav”, 
“rate” : 16000, 
“channel” : 1, 
“cuid” : “B8-27-EB-BA-24-14”, 
“token” : token, 
“speech” : speech_data, 
“len” : speech_length 
}
url = "http://vop.baidu.com/server_api"
json_data = json.dumps(post_data).encode("utf-8")
json_length = len(json_data)
#print(json_data)
 
req = urllib2.Request(url, data=json_data)
req.add_header("Content-Type", "application/json")
req.add_header("Content-Length", json_length)
 
#print("asr start request\n")
resp = urllib2.urlopen(req)
#print("asr finish request\n")
resp = resp.read()
resp_data = json.loads(resp.decode("utf-8"))
if resp_data["err_no"] == 0:
  return resp_data["result"]
else:
   print(resp_data)
   return None
def asr_main(filename,tok): 
try: 
f = open(filename, “rb”) 
audio_data = f.read() 
f.close() 
resp = yuyinshibie_api(audio_data,tok) 
return resp[0] 
except Exception,e: 
print “e:”,e 
return “识别失败”.encode(“utf-8”)
 
代码3:
# coding: utf-8
 
import requests
import json
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
 
 
def Tuling(words):
   Tuling_API_KEY = "此处填写自己的Turling KEY"
 
   body = {"key":Tuling_API_KEY,"info":words.encode("utf-8")}
 
   url = "http://www.tuling123.com/openapi/api"
   r = requests.post(url,data=body,verify=True)
 
   if r:
       date = json.loads(r.text)
       print date["text"]
       return date["text"]
   else:
       return None
 
 
代码4:
# coding: utf-8
import sys 
import urllib2 
import json 
import os 
import yuyinshibie
reload(sys) 
sys.setdefaultencoding(“utf-8”)
def yuyinhecheng_api(tok,tex): 
cuid = “B8-27-EB-BA-24-14” 
spd = “4” 
url = “http://tsn.baidu.com/text2audio?tex=“+tex+”&lan=zh&cuid=”+cuid+”&ctp=1&tok=”+tok+”&per=3” 
#print url 
#response = requests.get(url) 
#date = response.read() 
return url
def tts_main(filename,words,tok): 
voice_date = yuyinhecheng_api(tok,words)
f = open(filename,"wb")
f.write(voice_date)
f.close()
 
代码5:
//脚本light的内容
#!/bin/bash
if [ $# > 1 ]
then
/usr/local/bin/gpio mode 4 out
   if [[ "$1" = "on" ]]
   then
/usr/local/bin/gpio write 4 on
   fi
 
   if [[ "$1" = "off" ]]
   then
/usr/local/bin/gpio write 4 off
   fi
fi
 
 
//给灯泡脚本权限
chmod u+x light
 
//测试灯泡亮
1 ./light on
2 ./light off
发布了14 篇原创文章 · 获赞 45 · 访问量 2452

猜你喜欢

转载自blog.csdn.net/weixin_42683077/article/details/99688617
今日推荐