手把手教你做一个天猫精灵(二)
上一章讲到如何使用fubuki-iot制作一个简单的电脑版的天猫精灵,这次需要把它运行在硬件上。考虑到硬件环境比较复杂,我最终选择了相对简单的树莓派(Ubuntu 22.04 LTS 64bit)。同时,配合麦克风、扬声器等元件可以满足基本的语音输入和输出功能。
硬件准备
- 树莓派(Raspberry Pi 3/4/zero w均可,但是要能联网)
- 扬声器、麦克风(最好是免驱动的)
- 开关、面包板、杜邦线、充电线等
硬件环境搭建
树莓派
我用的是Raspberry Pi Zero 2w,但是发现USB接口不够又添加了扩展版。如图所示:
提醒:如果第一次使用需要刷系统。最新的刷系统方式是通过 Raspberry Pi Imager 刷的,可以在官网下载这个软件,然后选择对应的Linux系统,并配置账户、WIFI等信息,最后写到SD卡中。然后将SD卡插入树莓派,连上电源就可以运行了。
启动树莓派后先通过SSH登陆树莓派,然后下载 fubuki-iot
包和RPI.GPIO
包。如果没有pip需要先安装对应版本的pip。
fubuki-iot安装
fubuki-iot
这个包是默认包含pyaudio
包的,而pyaudio
是需要C++扩展包安装的,所以要安装对应的api:
sudo apt-get update
sudo apt-get install build-essential
这个软件包括arm-linux-gnueabihf-gcc
这个C++编译器,在Linux环境下开发经常要用到。此外,我们还要下载对应的Python接口和Audio接口以供C++调用:
sudo apt-get install python3-dev
sudo apt install portaudio19-dev python3-pyaudio
最后安装fubuki-iot
应该可以能正常安装了:
pip install fubuki-iot
RPI.GPIO安装
这个包也需要底层依赖的支持,只需要安装Linux的GPIO支持即可:
sudo apt-get install rpi.gpio
pip install RPI.GPIO
其实,树莓派的GPIO还支持I2C和SPI协议,它们可以通过极少的引脚数完成元件设备之间的通信。此外,树莓派也支持UART串口通信,这大大加速了元件之间的交流速率。但是这些内容我们目前还接触不到,只需要最原始的GPIO对引脚操作即可。
按钮
在上一章中,我们通过键盘唤醒智能终端的,在树莓派中,我们可以结合其GPIO能力,通过按钮唤醒它。根据上图,我们选择一个GPIO引脚作为输入,如果为高电平则表示唤醒,可以如图所示接入:
通过开关直接和电源和GPIO4相连,这样当开关闭合的时候就可以给树莓派一个高电平触发唤醒功能了。但是这样接会导致开关断开的时候引脚是高阻态,因此最好是按照下面方法接入:
这样,闭合开关后相当于把电阻短路了,这样是高电平。而断开状态引脚则直接和GND相连,所以是低电平。
提醒:在数字信号领域中有“上拉电阻”和“下拉电阻”的概念,具体可以查找相关资料。
麦克风、扬声器和电源
由于使用的是免驱动的麦克风和扬声器,所以直接通过USB接口和扬声器接口接到树莓派上即可。但是仍有可能遇到依赖缺失的问题,比如ALSA报错,这时候需要安装alsa-utils
,具体可点击这里。
树莓派的供电是为人诟病的地方,其所有版本的电压不能超过 5V ,而电流则是在条件允许的情况下越大越好。我采用的是原配的充电器,即5V-2000mA。刚开始会有点供电不足的情况但后来有点缓解,而使用移动电源(3.6V-10000mA)则有明显的异味。同时,用原配电源也不宜太长。
程序开发
上一章我们用fubuki-iot
创建了一个语义模型,并实现了提醒事项的功能,这次我们要创建一个录音设备,从而实现通过GPIO唤醒终端。
首先,我们在原有的mods
目录下创建一个文件,命名为gpio.py
,并在入口文件app.py
中加入一行引入这个文件:
from iot import Terminal
Terminal.load_models('mods.timer')
Terminal.load_models('mods.gpio')
if __name__ == '__main__':
Terminal.run()
然后,在gpio.py
中创建一个类,让它继承Recorder
代表录音机,并实现两个方法。其中awake
方法调用GPIO轮询检查GPIO4的电平状态,如果是高电平则返回True
代表唤醒。而record
函数则仍然使用内置的AudioClient
,具体如下调用:
import RPi.GPIO as GPIO
import time as tm
from iot import Recorder, RecorderFactory
from iot.integration.audio import AudioClient
from iot.utils import get_slash
from iot.context import Context
@RecorderFactory.set
class RaspberryRecorder(Recorder):
def awake(self) -> bool:
print("按下按钮唤醒终端")
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.IN)
while True:
if GPIO.input(7):
GPIO.cleanup()
return True
def record(self, time: int) -> str:
data = AudioClient.record(time=time)
local_time = tm.strftime('%Y-%m-%d_%H-%M-%S', tm.localtime())
path = f'{Context.config["RESOURCE_PATH"]}{get_slash()}wav{get_slash()}{local_time}.wav'
AudioClient.save(data=data, path=path)
return path
最后,我们再修改一下配置文件,添加一行如下:
DEVICE_REC=RaspberryRecorder
注意:
dotenv
包在我的Linux环境下有点问题,有时找不到.env
文件,但是把它移到项目根目录的外层。即/home/pi/目录下就可以找到了。
最后,我们运行程序。就可以通过按钮实现对终端的唤醒,并使用提醒事项的功能。
同样,树莓派的环境下仍可以使用PocketSphinx的语音唤醒功能,也需要下载相关的依赖,具体点击这里。
在这一章中,我们把项目移植到了树莓派中,但是受硬件限制所以需要安装很多依赖,可能每个人遇到的问题都不一样。下一章我们要实现智能终端操作硬件的功能。