分类目录动手就动手

树莓派语音识别-物联网实验

    前几篇文章介绍了,如何利用Respeaker读取语音,然后又如何通过BaiduApi获取语音文字,以及长文本如何切词获取所需的命令关键字;接下来这篇文章将做个简单的实验,实现如果通过语音控制电灯的开关(其他的实验后续会继续探索)。
    首先来看一下实验效果,如下效果:
    由上可看到,我们对着Respeaker说“天太热了开灯”(突然想起来天热貌似和开灯没啥关系),可见发送baidu分析,然后获得长文本“天太热了开灯”;接着分词切出"开灯"命令;由于这个命令是可执行命令,所以提示写入命令;然后可见另一个树莓派控制的led灯开始闪烁。当然这个时候我们可以控制很多东西,比如天热了开风扇等。
    见下图,第一行为识别文本,第三行是切词效果,由于开灯是关键词,所以命中了可执行命令,触发命令写入。具体的命令写入可参考:mongo操作。
识别效果

{ "_id" : 8, "status" : 1, "subject" : "电灯", "act_dely" : 0, "act" : "开灯", "add_time" : "2021-07-21 08:52:58" }
其中status标示是否已执行,subject为执行主体,act_dely是否延迟执行,act是执行动作,add_time是命令写入时间。
#接下来我们阐述一下具体的实验过程,需要用到的材料以及接线如下图:
接线示例
具体的接线可参考文章:继电器实验
接线完成后,如何获取的命令如何传递的可以参考文章:多个树莓派之间通信

实验的具体思路和流程如下:

  • 语音通过传感器捕获;
  • 发送语音流然后Api解析;
  • 长文本获取;
  • 文本筛选切词;
  • 切词后获取关键命令;
  • 命令捕获格式化存储;
  • 执行端远程通信并捕获命令;
  • 命令校验报错或执行;
  • 命令执行后销毁;
  • 完成。

具体实现代码,先看语音命令识别写入部分:

命令捕获切词
切词后命令写入
接下来是执行端代码:
命令发现和定义执行代码
命令发现到执行到结束
其中命令获取采用的是crontab,每5秒检测一次,因此最大会出现5秒的延迟。

#check led task
* * * * * cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 5;  cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 10; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 15; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 20; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 25; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 30; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 35; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 40; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 45; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 50; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py
* * * * * sleep 55; cd /home/pi/shumeijiang/act/led && /usr/bin/python led.py

树莓派语音识别-切词

    通过前面两篇树莓派语音识别文章的介绍与学习,我们此时应该已初步可以实现语音的解析和翻译,并在我们的屏幕上打印出来;但是会发现一些问题就是一些解析出来的词容易混淆,如“就酱”和“九江”,“游戏”和“有戏”等等,发言相似的词还是会出现识别的不准确,这个问题可以通过百度的语音训练来提升精准度,文档地址;这个不作为这次讨论的重点,本篇文章着重解决的场景是一段长语音识别文本如何获取想要的关键词,比如“等下一个班车”,那么解析的关键字可以是“等下,一个,班车”,但实际我们可能想要的是“等,下一个,班车”。
切词我们用到的是Python的一个好用的中文分词第三方库,因为是第三方所以需要我们自己先安装一下:

pip install jieba
如果你用的是Python3 则需要执行:(如果不清楚当前Python版本可以执行python -V查看)

pip3 install jieba
安装好后,我们先看一下效果,继续拿“等下一个班车”做实验:
切词示例
执行效果
    第一张图是切词的代码,可以看到我们输入的文本是“等下一个班车”,我们预期的是“下一个”;第二张图是返回的切词结果,原本返回的是list结构,为了直观我们修改为了逗号分隔的字符串,从字符串可见,“下一个”被切分的太细了,成了两个词“下”和“一个”;这个对于后续的判断可能会出现误导;倒不是说切词不对,只是没有符合我们的预期而已;
    为了解决上面的问题,jieba还提供了另一个方法,可以通过我们自定义需要的词来切分;还是以上面的例子为例;
示例
新的切分结果
    从上面的新的切分结果来看,已经能达到我们预期的结果,当然add_word这个函数可以多次添加同时执行,以满足我们同时需要切分多个词的需求。
下面的表格是jieba模块常用函数的讲解:
函数含义
jieba.cut(s)精确模式,返回一个迭代器,可遍历获取
jieba.cut(s, cut_all=True)全模式,把可能的词都切出来
jieba.cut_for_search(s)搜索引擎模式,切分对搜索引擎友好的词
jieba.lcut(s)精确模式,返回list类型
jieba.lcut(s, cut_all=True)全模式,返回list类型
jieba.lcut_for_search(s)搜索引擎模式,返回list类型
jieba.add_word(w)添加自定义切分词w

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

    上一篇文章我们看到了语音识别的效果图,同时获得了如何在百度申请语音识别的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

四驱小车驱动实验(三)

#实验目的:上两次实验试验了基本的前进和转弯动作;这次我们实验两个目标,一个是通过充电宝供电驱动小车转动;另一个目标是组合红外避障传感器,当遇到障碍物时,小车自动停止并后退,以免撞击到障碍物。
#接线效果如图:
接线示例
#实验代码:
代码示例
#注:其中驱动类使用解释可参考文章:
(1)http://www.shumeijiang.com/2020/05/24/四驱小车驱动实验(一)
(2)http://www.shumeijiang.com/2020/05/27/四驱小车驱动实验(二)

#!/usr/bin/env python
#coding:utf-8

'''
from JiuJiang
树莓酱的操作实例
http:://www.shumeijiang.com
'''

import RPi.GPIO as GPIO ##引入GPIO模块
import time    ##引入time库

#引入驱动类
from baseAct import baseAct
#实例化驱动类并赋值四个电机所占引脚值
act = baseAct(17, 16, 13, 12, 19, 18, 21, 20)

sensePin = 27  ##红外避障传感器
GPIO.setup(sensePin, GPIO.IN) ##设置为接收模式

##实验开始
try:
    while True:
        run_time = 0.05  ##执行前端时间 也是障碍物检测间隔时间
        status = GPIO.input(sensePin)
        if status == 0:
            print '发现障碍物'
            act.act_backward(0.5, 50, 50)  ##检测到障碍物则立即后退
            break;   ##后退后停止动作
        else:
            act.act_forward(run_time, 50, 50, True)  ##没有障碍物则继续前进

except KeyboardInterrupt:
    pass

GPIO.cleanup()
#视频效果如下:
#实验效果:
1、执行代码 Python jiujiang.py;
2、当前方没有障碍物或者离障碍物较远时,小车继续前进;
3、当接近障碍物时,可见小车红外避障传感器探测警告灯亮起,小车则立刻后退,然后停止动作。
#下期预告:下期我们将在此次实验的基础上增加更多的动作,比如遇到障碍物后,停止前进,然后左右转弯寻找没有障碍物的方向,然后继续前进;敬请期待。

四驱小车驱动实验(二)

#实验目的:在实验(一)的基础上继续探索新的动作;此次实验主要展示左转和右转以及和直行和后退的组合动作。
#接线图以及直行和后退参考文章:http://www.shumeijiang.com/2020/05/24/四驱小车驱动实验(一)/
#左转代码:原理是左前和左后电机后退,右前和右后电机前进。
左转代码示例
#右转代码:原理是左前和左后电机前进,右前和右后电机后退。
右转代码示例
#方法封装:
封装方法
#组合动作调用:
执行代码
#实验效果:
1、执行代码 Python jiujiang.py;
2、可见四个电机直行前进1秒,然后停顿0.5秒(自定义停顿);
3、然后可见左转执行0.2秒,并停顿0.5秒;
4、然后继续直行1秒,停顿0.5秒;
5、紧接着执行右转0.2秒,停顿0.5秒;
6、最后四个电机执行后退1秒;
7、其中方法第一个参数为执行秒数,第二个参数为占空比,第三个为频率。
#视频效果如下:

四驱小车驱动实验(一)

#实验目的:通过组合四个直流电机和两个电机驱动板以及编写控制代码,实现不同的直流电机集体协作,从而实现预期动作的效果;本篇文章主要实现四个电机的协同前进和后退动作。
#接线效果如图:
接线效果
#注解:实验通过一个可显示电压的降压模块,以及两个直流电机驱动板,驱动四个直流电机进行动作的执行。
其中驱动原理可参考文章:http://www.shumeijiang.com/2020/05/04/直流电机驱动变速实验/
#关键代码:
电机初始化
#如上图代码可见:
1、每个电机都需要单独实例化PWM,使用PWM的原因是我们希望通过改变占空比从而实现调节电机的转速效果。
2、其中每个电机的高低电平设置引脚都为可变变量,我们只需要通过不同的赋值,即可实现电机的前转和后转效果。
3、前进和后退赋值见下图;
赋值示例
#执行时间设置和动作停止:
动作停止以及缓冲
#如上图可见:
1、用户可指定前进或后退的执行时间,见代码time.sleep(duration)
2、其中执行时间内,可执行缓冲时间,即在执行时间的最后一段时间,降低占空比,从而实现缓慢停止的效果;当然也可以不加,执行也没有问题。
#封装前进和后退动作以及调用示例:
类前进和后退方法
#驱动类可发送邮件至lee.chuke@foxmai.com获取;
#视频效果如下:

多个舵机组合驱动实验

#实验目的:测试PCA9685驱动板同时驱动多个不同类型舵机,实现组合动作或者其他自定义动作的效果。
#接线效果如图:
接线示例
#注:S90舵机褐色接GND,红色接VCC,黄色接PWM;MG995舵机黑色接GND。
#关键代码
代码示例

#!/usr/bin/env python
#coding:utf-8

'''
from JiuJiang
树莓酱的操作实例
https:://www.shumeijiang.com
'''

from PCA9685 import PCA9685 ##引入驱动类
import time   ##引入time库

pwm = PCA9685()  ##实例化
pwm.setsq(50)    ##设置输出频率50HZ
pwm.init()       ##初始化
pwm.setallangle(0)   ##驱动所有舵机归0

#执行动作
serv = [0, 1, 2, 4 ,5]  #已连接的舵机
for i in serv:
    pwm.setangle(i, 90) #首先到90度
    time.sleep(0.4)     #停顿
    pwm.setangle(i, 0)  #执行到0度
    time.sleep(0.4)

#执行第二套动作
for i in serv:
    pwm.setangle(i, 45) #逐一到45度
    time.sleep(0.4)

for i in serv:
    pwm.setangle(i, 0)  #逐一归0
    time.sleep(0.4)

#最后动作
pwm.setallangle(90)  #所有舵机执行到90度
time.sleep(0.4)
pwm.setallangle(0)   #所有舵机归0
time.sleep(0.4)
#实验效果:
1、执行代码 Python jiujiang.py;
2、可见舵机由左向右,依次完成从0度到90度再到0度的动作;
3、完成动作2后,可见舵机依次先从0度执行到45度;当最后一个舵机执行完成后,从左边向右再依次从45度执行到0度;
4、完成动作3后,所有舵机一起从0度执行到90度,然后再一起执行到0度。
#视频效果如下: