树莓派语音识别-样例分析

    上一篇文章我们看到了语音识别的效果图,同时获得了如何在百度申请语音识别的AppID以及APIKey;如果一切没有问题,我们在上篇文章下载Demo示例,并按图组装ReSpeaker 4-Mic麦克风扩展板以及替换已申请的ID和Key后执行可以得到语音识别的初步效果;接下来这篇文章主要讲解一些在开发和实验过程中需要注意的点以及Demo代码的部分含义。
 #先看baidu_analysis.py文件,这个文件主要是用来跟百度的语音识别交互的文件;从第一行往下看:
import为引入模块,如果代码执行的时候报 ModuleNotFoundError: No module named 'XXX',说明你的环境缺少这个模块;需要执行安装,以代码中用到的requests模块为例(如果环境是Python3 则pip替换为pip3):

pip install requests
    其实requests这个模块在百度的官方demo里面是没有的;这次添加主要是看到官方demo每次都要判断Python版本,请求的过程有点繁琐添加的。

    紧接着后面是CUID、RATE等常量的定义;其中CUID官方的解释是“用户的唯一标示,用来区分用户,可以用本机的MAC地址”,实际请求中代码用的是shumeijiang,这个可以自定义就好;其他参数Demo都有中文注释就不再重复;

    后面是定义了一个BaiduAnalysis类,不同官方Demo,此处将AppID和ApiKey作为参数传过来,然后定义在调用文件内;方法fetch_token是用来获取token的,含义就是请求前先要做身份验证,这个函数验证后会得到一个短期有效的token;紧接着后面的方法是speech_analysis,这个方法在拿到token后将获得语音文件或者读取的语音流传给百度的Api分析;参数不复杂,不同的地方在于官方的Demo是采用的json格式发送,我们采用的是raw格式发送,两者的效果是一样的;需要注意的是header部分信息,内容类型格式要统一。
接下来是run.py这个文件是程序的入口,Demo要执行起来也需要执行Python run.py;从上往下看可以看到需要用到 pyaudio,它是主要用来做语音捕获,后面定义一些常量定义捕获的参数;pyaudio这部分可以参考网上有很多资料,此处不再详述;接着是GPIO的设置,主要是ReSpeaker 4-Mic所需的,见下图:
再往后是函数listen(),作用是获取捕获的语音流,然后发送给百度api进行翻译识别,其中需要注意的是返回的数据结构如下图(官方示例):

{"corpus_no":"6433214037620997779","err_msg":"success.","err_no":0,"result":["北京科技馆,"],"sn":"371191073711497849365"}
listen函数定义好后,需要调用执行才能获取数据,while (analysis==True):定义了一个可中断的循环策略,当我们语言“停止识别”时,程序匹配然后会自动终止识别,这个终止条件可以自己设定。
执行策略

树莓派语音识别-初探

    从今天开始我们将通过几篇文章来讲述,如何通过树莓派+ReSpeaker 4-Mic麦克风扩展板捕获声音然后通过BaiduApi的声音识别调用,实现语音到文字的转换;最后我们会尝试通过转换后的命令去控制其他传感器或者电器机构,从而初步实现智能家居在树莓派上的雏形;通过这一些列的铺垫后续会发现更多好玩的方向与尝试。
安装示例
    从上图可见ReSpeaker 4-Mic麦克风扩展板可完美的安装在树莓派上(此处为3B+),但是有个弊端就是会全部占用树莓派的GPIO口,导致其他传感器无法使用(不过不用担心后续我们会通过别的方式去实现)。
    安装好后,接下来我们来实现语音捕获和识别,首先我们先来看一下效果:
    由上可见,程序会将语音转换为文字并输出;由于没有其他设置,所以语音识别有会有一些误差,比如我说 “就酱”但是程序返回的是 “九江”;这部分调准我们会在下一篇文章详细讲解。
    接下来开始做语音识别的前期准备,由于我们调用的是百度的语音识别Api,因此需要在百度控制台建立我们自己的应用,并获取接口调用权限;
  • 登录百度控制台,如果没有账号需要先申请;地址https://login.bce.baidu.com/
  • 登录进来后我们选择右侧栏的语音技术,然后在概览处创建我们自己的应用;如下图:
此处已创建一个应用
  • 应用创建好后,则会得到一个AppID和一个API Key 这个两个是声音识别api调用的钥匙 要谨记保管好;
效果
  • AppID和API Key申请好后,这个时候还需要领取api调用的额度;此处我们领取的是短语音识别-中文普通话的15W的额度;这个足够自己做测试用了;但是记得6个月有效,因此需要注意过期时间;
  • 接下来我们就可以去实地的去调用接口了,此处demo我们用的是百度的Api实时调用(文档地址 https://ai.baidu.com/ai-doc/SPEECH/Vk38lxily),其中文档内有可直接测试的demo,可以拿来直接用;我们这次实验也是基于这个demo改造而来;
  • 其中在请求方式这一块,有json格式和raw格式, 我们采用的是raw格式,两种格式都可以,都可以尝试;
    代码部分,我们写了两个文件;其中run.py是语音采集以及api调用转换文件;baidu_analysis.py是改造的百度的官方demo;这个文件主要是登录验证获取token,然后用token去识别获取的二进制语音流;
代码片段
    后面按钮是测试demo的代码样例,可下载更换自己申请的appid以及appkey后直接应用;代码的具体含义会放在下一篇文章进行讲解;此处可以先用demo代码进行实验。执行命令:

python run.py

树莓派安装VNC

VNC是一款远程桌面软件(Virtual Network Console),学名虚拟网络控制台;树莓派安装这个软件,目的是通过该软件直接桌面化访问和操作,省去了命令行操作带来的烦恼;当然也可以通过树莓派外接鼠标、键盘和显示器实现,只是成本会高一些。
登录显示效果
#下载VNC软件,这边我们通过官网下载:VNC官网,见下图根据自己的系统选择下载;
根据自己系统选择
#下载后安装,安装成功后打开软件,然后右键选择 New connection,出现新加配置页面,其中VNC server添加我们配置好的树莓派静态IP地址;Name填写我们容易区分的别名即可;然后点击OK;
双击安装后的图标,如果出现报错 “cannot currently show the desktop”,可能意味着默认分辨率需要调整,当前VNC不支持默认分辨率;可如下修改:
命令行下执行:sudo raspi-config,效果见下图,然后选择选项7 Advanced Options(高级选项)
回车确认后见下图,选择A5 Resolution (分辨率)
然后继续回车,可见下图多个分辨率列表,选择其中 1280x720 分辨率然后回车选择;
确认完之后记得最后选择 <Finish> ,完成设置。

树莓派之传感器防抖

传感器抖动,比如我们经常会用U型光电传感器计算速度,用轻触开关检测按压,用人体红外检测有无人活动等;通常中间会面临一个问题就是在while的检测周期内如何设定sleep时间,如何界定一次事件的触发以及由于手抖等其他因素导致的单位时间内多次触发事件的问题。这次实验就是通过边缘检测函数更好的解决抖动问题。
这次通过轻触开关传感器实验来验证解决问题。具体实验可参考先前文章:轻触开关传感器实验
其中下图红框内sleep时间设定通常会比较不好把握,设定时间长了容易错过事件的检测,设定时间过小表示检测的更频繁容易出现单次触发的时候 被多次检测,从而出现抖动的现象。
如下图可见单次按压出现多次提示预警:
抖动
接下来我们通过软件方式来解决这个抖动问题(硬件方式暂不讨论):
通过上图可见,首先拉高电压,然后我们使用边缘检测函数,设置一个电压下降检测事件(FALLING),然后在while内检测,当status为1时表示事件检测成功。其中bouncetime为防抖时间(单位毫秒),也可以理解为检测间隔时间,即在设定的时间内只检测一个边缘事件,从而达到单位时间内只有一个事件被触发。
防抖效果如下图,可见单次按压只会有一个检测触发预警:
参考资料:

树莓派设置静态IP

由于用ssh访问树莓派更方便coding和做实验,所以动态IP对于ssh连接不很不方便,所以在局域网内设置静态IP更便于连接和操作。
编辑文件 sudo vim /etc/network/interfaces   由于此文件是只读文件所以需要有sudo权限;如果当前没有vim,需要先执行 sudo apt-get install vim 安装vim。
打开文件后可见下图,然后输入红框内部分(设置ip地址为192.168.0.118),其中address部分是自定义的静态ip地址,需要结合已有的静态ip考虑防止重复和冲突。
设置完成后,执行sudo reboot 重启树莓派。
然后在电脑端执行ssh pi@192.168.0.118 (自己定义的静态IP地址),如果执行发现报错Connection refused,则有可能是树莓派ssh模块没有打开。可通过界面打开 树莓派->首选项 -> Raspberry Pi Configuration -> Interfaces 可见各种选项,其中Enable是打开,很多默认都是Disable关闭的。我们选中打开,系统会提示是否重启,重启即可。
打开ssh支持后,再次执行ssh pi@192.168.0.118  可见登录成功;至此配置成功。

树莓派切换清华镜像源

    由于树莓派系统默认的是官网的镜像源(国外),所以网络访问存在很多不确定性;可能会出现访问慢甚至无法访问的情况,所以镜像烧录成功后,需要切换一下清华的镜像源,因为是国内的地址所以访问会更快和更稳定一些。
由于默认没有安装vim 因此我们使用nano进行文件编辑:

sudo nano /etc/apt/sources.list
然后用#注释掉红框内数据,或者直接删除掉已有三行数据并在下面写入:

deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main non-free contrib rpi
如上图编辑完成后,执行Ctrl+O,然后提示是否写入当前文件,直接回车即可,然后执行Ctrl+X 退出文件。
其中buster为当前Debian系统的版本,录入的时候需要适配当前系统,Debian系统版本列表如下:
查看当前系统的版本:

lsb_release -a
当前系统为buster
编辑上面文件后继续执行:

sudo nano /etc/apt/sources.list.d/raspi.list
编辑效果
注释掉第一行或者直接删除掉已有数据,然后在最下面输入:

deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ buster main ui
然后重复上面的编辑保存步骤,保存退出文件。
执行 sudo apt update 可见下图请求源已更新为清华源。
切换后效果

树莓派烧录系统

由于最近实验需要,又入手一个树莓派3B+;因此今天开始记录一下烧录系统过程以及所用的资源。
系统镜像我们采用的官网提供的资源,其中见下图可见有三个选择模式:
  • Raspberry Pi OS with desktop and recommended software 版本就是系统自带桌面以及推荐的软件;由于自带软件因此对于后续使用会有帮助,缺点就是系统会比较大,如图可见有将近3G,如果TF卡容量较小则不适合。
  • Raspberry Pi OS with desktop 版本就是系统自带桌面但是不带推荐的软件,如果后续有用到的软件需要自己手工安装;有点在于系统比较精简,大小在1G多。
  • Raspberry Pi OS Lite 版本是轻量版,不带桌面以及推荐软件,比较适合喜欢命令行操作的行家,对桌面操作不是很友好,好处就是体积小不到1G。
  • 此处我们采用的是中间自带桌面版本,点击Download直接下载即可。
系统列表
烧录工具此处采用的是Etcher,网上有很多下载资源,可直接某度搜索。
接下来是将TF插入读卡器,然后将读卡器连接电脑。点击Etcher然后选择已下载的系统镜像,然后可见下图,第一个选项是选择的系统镜像,中间是连接的要烧录的TF卡,此处我们用的是一张32G的卡,然后点击Flash开始烧录。
当出现提示Flash complete!表示烧录成功。见下图:(忽略下面的更新提示 我用的版本比较旧)
烧录成功
接下来将烧录好的TF卡插入树莓派中,然后接上电源,连接上鼠标和键盘;
显示器显示一些启动信息后可见如下界面,表示系统烧录成功;
烧录成功
后续将开始切换清华源以及设置静态IP等操作。

树莓派Python-mongo操作

上面文章已经安装了mongo数据库,现在要通过Python操作mongo数据库,因此下面要进行一些操作。
参考文章: http://www.shumeijiang.com/2021/01/24/多个树莓派之间实现通信对话 
#首先要安装驱动pymongo
1、执行命令

sudo pip install pymongo==3.2
(指定版本是因为mongo有版本的要求,3.2实验不会报错,3.11会报错) 2、安装完成后查看版本是否正确
python
>>> import pymongo
>>> pymongo.version
'3.2'
#mongo连接

1  #coding:utf-8
2  from pymongo import MongoClient
3
4  USER = 'pi'  #用户名
5  PASS = 'shumeijiang'  #验证密码
6  ADDRESS = '192.168.0.120'  #数据库地址
7  PORT = 27017   #默认端口
8  DB_NAME = 'command'  #指定库
9  COLLECTION = 'master.command'  #指定集合(表)
10
11 conn = MongoClient("mongodb://"+USER+':'+PASS+'@'+ADDRESS, PORT)
12 db = conn[DB_NAME]
13 collect = db[COLLECTION]
14
15 for i in collect.find():   #数据查询
16     print(i)
#查询结果
查询结果