现在,智能家居的论题越来越火,物联网现已融入了咱们日子。最近闲在家里了解了一下这方面的布景知识,自己动手做了一个相似天猫精灵的物联网智能终端。于是打算出一个教程分享一下我的研究成果。
硬件预备
- 一台能连上网的电脑(体系最好是Windows 10)
布景知识
物联网智能终端
相似于天猫精灵这类产品咱们都把它叫做智能终端(或许智能音箱)。它能经过用户之间的语音互动对智能家居进行指令操控,也能供给一些内置的软件服务,比方语音提示和QQ音乐等。因而,我把它总结出以下几个功用模块。
现在,市面上除了天猫精灵,还有百度小度、小米小爱、Google Home和比亚迪小迪等。
语音处理术语
智能终端运用了许多的语音处理方面的人工智能算法,这些算法可软件完成也能够硬件完成,首要包含语音辨认(Automated Speech Recognition,ASR)、语音组成(Text To Speech,TTS)和语音唤醒(Keyword Spotting,KWS)。
一般的流程是这样的,先经过语音唤醒捕捉用户的Keyword,当捕捉到了才给用户录音并作语音辨认,然后依据用户的意图调用相应的API并经过语音组成给用户适当的反馈。据我观察至少有三种API调用方法,或许说是对外调用的接口,总结如下:
- MQTT音讯:智能终端操作硬件一般采用IBM定义的MQTT协议,这种音讯协议相似
Kafka
,可是它更适应网络环境不稳定的场景,比方多了服务质量(QoS)和遗言机制(Last Will),这部分内容以后介绍。 - 蓝牙接口:还有一种是经过蓝牙Mesh直接和硬件相连,不需求联网就能直接操作硬件。
- HTTP接口:这种接口一般是供给软件服务的,比方QQ音乐、墨迹气候和喜马拉雅播送
快速开端
考虑到或许会接入许多运用功用,所以本项目是经过Python开发的。新建一个工程,并设置好环境,然后下载这个包:
pip install fubuki-iot
提示:假如下载不了能够换源,也能够在GitHub上下载,仓库地址点这儿 。一起也欢迎PR。
然后创立一个程序入口,我把它命名为 app.py
, 然后调用 Terminal
的 run
函数,如下:
from iot import Terminal
if __name__ == '__main__':
Terminal.run()
当然现在这个程序是跑不起来的,由于没有相应的配置。新建一个目录叫 resources
,然后再创立一个配置文件 .env
,留意前面有一个点。接着,把刚刚新建的 resource
文件的途径写到这个配置文件中,如下:
RESOURCE_PATH=你的resources的途径
这时候,你的项目目录应该是这样的:
原则上就能够运转了,可是像上文所说需求语音处理相关的能力,所以我先暂时借用了百度智能云的AI能力。这个渠道能够免费供给半年的人工智能服务,所以先去请求以下相关服务。
进入百度智能云的官网,依次点击“产品”-“人工智能”-“语音技术”,然后请求这个AI服务。之后就能够在操控台中找到自己的API Key和Secret Key,如图所示:
然后在刚才的 .env
文件中追加这两条信息,并预留一个BAIDU_ACCESS_TOKEN字段,如下所示:
BAIDU_API_KEY=你的API Key
BAIDU_SECRET_KEY=你的Secret Key
BAIDU_ACCESS_TOKEN=
提示:由于百度API是要生成access token运用的,这个token是有时效的,所以这儿预留能够便利以后不用重复请求,可是过了时效仍是要把这个字段置空,就像现在这个姿态。
当然,百度API试用结束后仍是会收费,假如你不想收费能够自己练习模型来替代它,这个以后会介绍。
现在就能够运转这个程序了。运转结果如图所示:
按下“F”键就能够唤醒终端,它会回应“我在,你说”。然后等它开端录音就能够说“你好”,它会回应“在的”。至此,一个极简的智能终端就做好了。
敞开语音唤醒
虽然智能终端的姿态现已有了,可是间隔天猫精灵还差远了。现在,就让咱们敞开它的语音唤醒功用。
这个终端内置了一个PocketSphinx的语音唤醒功用。这个工具是C写的,因而要在Windows环境下需求对应的编译器,官方推荐的是Visual C++。
首先点这儿下载Visual Studio Installer,然后装置,接着翻开以后挑选装置“单个组件”,然后勾选这三个组件“Windows 10 SDK”、“Windows 通用 CRT SDK”以及“MSVC v140 – VS 2015 C++ 生成工具(v14.00)”,下载完即可。这个环境变量无需配置。
留意:有或许碰到“无法翻开包含文件: “windows.h”: No such file or directory”的问题,这个需求将
<windows.h>
头文件放在指定目录,详细自行百度或许谷歌。
然后下载swigwin,这个软件是让python调用C/C++编译的软件的,点这儿下载,下载完成后解压,并配置环境变量,然后重启收效。
最终,装置PocketSphinx。
pip install pocketsphinx
假如没有报错就阐明成功了,有报错或许和C语言环境有关,这个需求详细问题详细分析了。
然后在 .env
文件中追加两行配置:
TERMINAL_MODE=0
DEVICE_REC=PocketsphinxRecorder
接着发动程序能够经过对他说“hello”或许“hi”唤醒它。
编写第一个程序
接下来,咱们要开端向终端里边增加功用。相似天猫精灵的守时提示功用,咱们也做一个提示功用。
首先创立一个包,命名为 mods
,咱们接下来写的模组都将放在这个包里。新建一个文件命名为timer.py
,然后在文件里新建一个语义模型,这个模型承继了SemanticsModel
,其目的是为了匹配用户的指令,如下所示:
@SemanticsGroup.add_model
class TimerSemanticsModel(SemanticsModel):
code = 'timer'
frm = SemanticsFromEnum.USER
topic = ''
regex = '(.*)后提示我(.*)'
regex_num = 3
redirect = SemanticsRedirectEnum.ACOUSTICS
func: SemanticsFunc = timer_semantics_func
字段阐明如下:
- code:语义模型的标识,这儿随便填,只用来区分。
- frm:语义来源,这儿是用户
- topic:由于来自用户,所以这儿不需求填
- regex:正则表达式,用来匹配用户的指令,假如匹配射中则运用这个语义模型,不然持续匹配下一个语义模型,假如都没有射中则发送兜底的语音回运用户
- regex_num:上面正则表达式的
groups()
个数,至少为1,表明全量,详细参阅Python正则表达式相关文档,这个是用来抽取参数的 - redirect:重定向,这儿不需求处理硬件,只是回来语音,所以是
ACOUSTICS
- func:用来处理这个指令的回调函数
重点就在这个回调函数,它是处理语义的要害。在当时环境下,它接受一个列表,即正则表达式匹配的groups
,咱们能够经过数组下标拿到想要的参数,并作后续处理。比方:
def timer_semantics_func(*args) -> FunctionDeviceModel:
sentence = args[0] # 获取全文
timespan: str = args[1] # 第一个参数,时刻
content = args[2] # 第二个参数,内容
...
此外它回来一个功用设备模型FunctionDeviceModel
,它告知终端要怎么处理,即怎么回来给用户信息。其字段如下。
class FunctionDeviceModel(BaseModel):
"""
功用设备模型,既是用户发送指令的封装,也是语义转化模块处理后的产物。
"""
# 对应的semantics_model的code
smt_code: Optional[str]
# raw标志位,假如为True则data为str,不然为dict
is_raw: bool
# 履行成功后回来的语音提示,假如重定向到MESSAGE则作为语音提示,
# 假如是重定向到ACOUSTICS则也能够表明回来内容
acoustics: str
# 数据,只有当is_raw为False才为字典,用于展示详细内容
data: Union[str, Dict[str, str]]
因而,做一个提示功用便是判别用户的句子是否正确,假如正确则回来正确的语音信息,不然告诉用户听不懂,详细如下:
def timer_semantics_func(*args) -> FunctionDeviceModel:
...
if not is_ok:
return FunctionDeviceModel(
smt_code='timer',
is_raw=True,
acoustics="抱歉我没听清",
data=""
)
else:
scheduler.start()
return FunctionDeviceModel(
smt_code='timer',
is_raw=True,
acoustics=f"好的,我会在{timespan}后提示你{content}",
data=""
)
有正确的处理和回来还不行,还需求在时刻到了的时候给用户语音提示,这完成也很简略,只要从Context
中获取相应的API就能够了,详细如下:
path = Context.tts_processor.tts("您好,时刻到了,请" + content)
Context.player.play(path)
完成守时的计划有许多种,我采用了APScheduler
完成了这个功用,下面仅供参阅:
def ch2i(ch: str):
try:
# 测验直接转化
return int(ch)
except Exception:
pass
_dict = {
"一": 1,
"二": 2,
"三": 3,
"四": 4,
"五": 5,
"六": 6,
"七": 7,
"八": 8,
"九": 9,
"十": 10,
"百": 100
}
if ch.startswith("十") or ch.startswith("百"):
ch = "一" + ch
_buf = list()
_res = 0
for i in range(len(ch)):
if len(_buf) == 0:
_buf.append(_dict[ch[i]])
else:
_res += _buf.pop() * _dict[ch[i]]
return _res + (_buf[0] if len(_buf) else 0)
def timer_semantics_func(*args) -> FunctionDeviceModel:
sentence = args[0] # 获取全文
timespan: str = args[1] # 获取时刻
content = args[2] # 获取内容
is_ok = True
scheduler = BackgroundScheduler()
def _job():
path = Context.tts_processor.tts("您好,时刻到了,请" + content)
Context.player.play(path)
try:
if timespan.endswith("秒"):
target = ch2i(timespan.split("秒")[0])
scheduler.add_job(_job, 'date', run_date=datetime.datetime.now() + datetime.timedelta(seconds=target))
elif timespan.endswith("分钟"):
target = ch2i(timespan.split("分钟")[0])
scheduler.add_job(_job, 'date', run_date=datetime.datetime.now() + datetime.timedelta(minutes=target))
elif timespan.endswith("小时"):
target = ch2i(timespan.split("小时")[0])
scheduler.add_job(_job, 'date', run_date=datetime.datetime.now() + datetime.timedelta(hours=target))
else:
# 只处理到小时,更大的单位不处理了
raise RuntimeError("Fail to match")
except Exception as e:
logger.error("Fail to translate " + e.__str__())
is_ok = False
if not is_ok:
return FunctionDeviceModel(
smt_code='timer',
is_raw=True,
acoustics="抱歉我没听清",
data=""
)
else:
scheduler.start()
return FunctionDeviceModel(
smt_code='timer',
is_raw=True,
acoustics=f"好的,我会在{timespan}后提示你{content}",
data=""
)
@SemanticsGroup.add_model
class TimerSemanticsModel(SemanticsModel):
code = 'timer'
frm = SemanticsFromEnum.USER
topic = ''
regex = '(.*)后提示我(.*)'
regex_num = 3
redirect = SemanticsRedirectEnum.ACOUSTICS
func: SemanticsFunc = timer_semantics_func
最终,在程序入口处加载这个包:
from iot import Terminal
Terminal.load_models('mods.timer')
if __name__ == '__main__':
Terminal.run()
现在,再发动程序,对它说“五分钟后提示我打扫卫生”它就会回应,并在五分钟后经过语音播报提示咱们。
本章初步介绍了物联网领域的相关概念,然后跑通了一个简略的智能终端的程序。下一章咱们会进一步完善这个程序,并把它做成硬件,离咱们的方针更近一步!