手把手教你做一个天猫精灵(四)
上一章讲到如何将程序写入到ESP8266 WiFi模块中,实现物联网终端对硬件的控制。本章将通过fubuki-iot实现自定义硬件控制。同时给出一个替代百度API的方案。
硬件准备
(无)
自定义语义模型
返回功能设备模型
在第一章的“提醒事项”的例子中,fubuki-iot就展现了语义模型的功能。它将命中语义模型的命令作为参数调用给定的函数,并重定向给ACOUSTICS
,从而实现和用户交互的功能。以此类推,要实现和硬件交互就只需要重定向给MESSAGE
,通过MQTT消息实现硬件的操控。
拿智能洗衣机举例,假设洗衣机有以下几个功能:
- 以快洗/漂洗/脱水模式启动洗衣机
- 预约X分钟后启动洗衣机
- 取消预约
- 暂停洗衣机
- 重新启动洗衣机
这五个功能就会对应五个语义模型。现在拿第一个语义模型举例,首先在mods
内新建一个python文件命名为washing_machine.py
,并构建一个语义模型类:
@SemanticsGroup.add_model
class WashingMachineSemanticModel(SemanticsModel):
code = 'washing_machine'
frm = SemanticsFromEnum.USER
topic = ''
regex = '以(.*)模式启动洗衣机'
regex_num = 2
redirect = SemanticsRedirectEnum.MESSAGE
func: SemanticsFunc = washing_machine_semanticsss_func
这个模型前三项和之前一样,正则表达式这次只有一个分组用来确定模式,所以分组数就是2(加上第0个分组,即全文)。然后这次重定向就是消息。处理函数需要返回一个FunctionDeviceModel
,函数类似这样:
def washing_machine_semanticsss_func(*args) -> FunctionDeviceModel:
mode = args[1]
# do something here
return FunctionDeviceModel(
smt_code='washing_machine',
topic='wm/mode',
is_raw=False,
acoustics=f"好的,洗衣机将会以{mode}模式启动",
data={
'mode': ''
}
)
中间处理过程忽略,关键在于这个返回值。这次topic
字段必填,表示发送MQTT消息的topic,因为发送的payload是一个JSON数据,所以is_raw
为False。这里的acoustics
是返回给用户的语音提醒,data
即payload数据,实际情况会复杂一点。
返回统一推送模型
在上一章,fubuki-iot展示了硬件主动推送消息的方式。当按下按钮后,智能终端可以通知用户有人按下了按钮。直接拿内置的例子举例:
def button_semantics_model_func(*args) -> UniverseNoticeModel:
return UniverseNoticeModel(
smt_code='default05', # 对应语义模型标识
topic='self/button', # 对应的Topic,可不填
device='button', # 对应的设备,可不填
verbose=False, # 是否是详细内容,如果填False则message为字符串,否则data为字典
message="有人按下了按钮" # 当verbose为False必填,为直接返回给用户的语音信息
)
@SemanticsGroup.add_model
class ButtonSemanticsModel(SemanticsModel):
code = "default05"
frm = SemanticsFromEnum.DEVICE # 这次是来自设备
topic = 'self/button' # 必填,用来匹配设备的发送的MQTT消息的Topic
regex: Optional[str] = None
regex_num: Optional[str] = None
redirect = SemanticsRedirectEnum.ACOUSTICS
func: SemanticsFunc = button_semantics_model_func
流程是这样的,设备发出了MQTT消息包含Topic和Payload,fubuki-iot根据Topic找到对应的语义模型,并调用对应的func: SemanticsFunc
语义函数处理,而Payload就作为func: SemanticsFunc
的参数。和之前不同的是,这个语义函数是返回统一推送模型的,它的格式如上所示。
另外,除了重定向给Acoustics
和MESSAGE
还可以重定向给语义处理SEMANTICS
,所以verbose
字段可能为True
,这部分功能以后会提到。
自定义语音处理模块
在前面的例子中,我们都是利用百度API实现语音识别和语音合成的,这次我们将其替换成PocketSphinx。根据官方文档,可以直接构造一个AudioFile
并遍历其中的文字信息,即可获得语音识别结果。
首先,需要构造一个类继承AsrProcessor
,并实现其中的asr
方法。然后在通过工厂类将该类包含进去,比如:
@AsrProcessorFactory.set
class PocketSphinxAsrProcessor(AsrProcessor):
def asr(self, path: str) -> Optional[str]:
res = ''
for word in pocketsphinx.AudioFile(audio_file=path):
res += word.__str__()
return res if res != '' else None
在这个方法中就是通过遍历AudioFile
获取语音识别信息并返回。最后,在.env
文件中表明调用这个类:
ASR_PROCESSOR=PocketSphinxAsrProcessor
注意:默认的PocketSphinx只支持英文识别,你可以在这里找到不同语音的模型。并替换默认的模型。然后以参数的形式传给AudioFile,比如:
for word in pocketsphinx.AudioFile(audio_file=path,
hmm=os.path.join(model_path, 'zh-cn'),
lm=os.path.join(model_path, 'zh-cn.lm.bin'),
dict=os.path.join(model_path, 'cmudict-zh-cn.dict')):
res += word.__str__()
除了ASR_PROCESSOR
语音识别模块以外,还有三个模型可供自定义替换,替换方法如下
模块名称 | .env字段 | 需要实现的方法 |
---|---|---|
语音识别模块 | ASR_PROCESSOR | asr |
语音合成模块 | TTS_PROCESSOR | tts |
麦克风模块 | DEVICE_REC | awake和record |
扬声器模块 | DEVICE_PLY | play |
到本章为止关于fubuki-iot的相关功能大致介绍完了,理论上讲也确实能满足一个基本的物联网智能终端(智能音箱)的功能。但是这个项目除了拿去做科创竞赛或者毕业设计似乎用处不大,因为它不能和现实生活中家居打通,下一章将介绍一种抓包家用路由数据的方法探索用户对智能家居的控制。