Skip to content

物联网IoT智能终端,支持Windows和树莓派

License

Notifications You must be signed in to change notification settings

littlebutt/fubuki-iot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fubuki Iot —— 物联网智能终端

PyPI GitHub GitHub last commit

简介

Fubuki Iot是一款开源的物联网智能终端,类似于市面上的天猫精灵,小爱同学等。它可以监听智能家居的消息,也可以根据用户语音向智能家居 发送消息,从而实现家居智能化。与市面上各种终端相比,它具有以下优点:

  • 定制化程度更高:用户可以自己实现对家居的控制,甚至对原有的家居电器改造
  • 安全性更好:由于可以部署在本地局域网上,因此个人信息不会被上传到外网
  • 效率更高:通过MQTT协议完成设备之间的交流,不需要复杂的流程

经过测试,本框架可以很好的支持Windows系统和树莓派(Linux)系统。

快速上手

安装

方案一:通过pip安装(推荐)

pip install fubuki-iot

方案二:下载安装

首先在终端执行以下命令:

git clone git@github.com:littlebutt/fubuki-iot.git

然后进入根目录,执行以下命令安装fubuki-iot

python setup.py install

方案三:通过docker安装

同样像方案三中下载项目到本地,然后执行以下命令

docker build -t fubuki-iot:latest .
docker run -it fubuki-iot:latest /bin/bash

就可以进入到容器内部了

启动

本项目内置了 百度云 的语音合成和语音识别的功能,因此使用之前需要免费申请百度云账号, 点击 这里 申请。申请之后开通"产品"-"人工智能"-"语音技术"的服务。 当然,您也可以用开源方案自己训练模型替代内置的语音功能,具体见 进阶功能

1.创建资源目录 首先创建一个Python工程 demo ,并在根目录下创建一个目录命名为resources

2.创建.env文件

在新建的工程下创建一个.env配置文件,其内容参考如下:

ENVIRONMENT=dev
RESOURCE_PATH=刚才创建的resources目录,结尾不要加“/”
BAIDU_ACCESS_TOKEN=百度云API的token,可以留空
BAIDU_API_KEY=申请的百度云的AK
BAIDU_SECRET_KEY=申请的百度云的密钥

3.创建主程序

在根目录下创建文件 app.py,并写入以下内容

from iot import Terminal


Terminal.run()

运行即可启动

使用

目前主流的物联网信息交换都采用 MQTT协议 ,因此要实现控制智能设备需要部署MQTT 服务器。本项目建议使用 EMQX 服务器,具体安装和部署方法点击 这里

若要完整的实现对硬件设备的控制可以参考 相关资料

内置功能

1.对话

运行智能终端后,按下键盘上的f可以进行录音。对着麦克风说出“在吗”、“你好”后,智能终端会回应“在的”。

2.控制开关和电灯

运行智能终端,按下键盘上的f后对着麦克风说出“打开开关”,然后它会向MQTT服务器的default/switch Topic发送以下信息:

{
  "switch": "on"
}

同样,对着麦克风说出“关闭开关”后,它会向MQTT服务器的default/switch Topic发送以下信息:

{
  "switch": "off"
}

具体效果需要由订阅了 default/switch Topic的智能设备实现。

此外,运行智能终端,按下键盘上的f后对着麦克风说出“打开卧室/客厅/餐厅灯”也会向MQTT服务器的default/light Topic发送以下信息:

{
  "position": "bedroom/livingroom/dinningroom" 
}

3.接受按钮信息

运行终端,当由设备向MQTT服务器的 self/button 发送如下消息后,终端会说“有人按下了按钮”。

{
  "topic":"self/button",
  "device":"button",
  "verbose":"false",
  "message":"有人按下了按钮"
}

自定义功能

1.自定义语音功能

语音功能可以理解为用户和智能终端进行对话,类似于机器人的对话功能。这种功能一般不涉及硬件。在天猫精灵中, 就内置了提醒助手、墨迹天气等语音功能。

首先在自己创建的Python项目的根目录中创建一个包(package)命名为 models ,在这个包中创建一个python文件 acoustics.py, 在文件中定义一个语义模型:

from iot import SemanticsGroup, SemanticsModel, SemanticsRedirectEnum, SemanticsFromEnum, SemanticsFunc

@SemanticsGroup.add_model
class MySemanticsModel(SemanticsModel):

    code = 'hello'                                  # 语义模型的标识,自定义

    frm = SemanticsFromEnum.USER                    # 语义模型的来源,这里是接受用户的语音命令,所以是USER

    topic = ''                                      # 由于不涉及发布消息,所以这个字段用不到,留空就行

    regex = "(.*)后提醒我(.*)"                       # 匹配用户语音命令的正则表达式,比如这里是一个有关提醒的命令

    regex_num = 3                                   # 上述表达式匹配后的分组(group)的数量,第一个为用户命令全量文本,第二个是“后”前面的文本,第三个是“我”后面的文本

    redirect = SemanticsRedirectEnum.ACOUSTICS      # 语义处理好后的重定向,由于不需要发送消息等后续操作,所以这里是直接语音返回

    func: SemanticsFunc = my_semantics_model_func   # 处理用户命令的回调函数

在上面的语义模型中,最后一个字段是一个 SemanticsFunc 实例,它是一个返回 FunctionDeviceModel 或者 UniverseNoticeModel 的方法 因此,需要这样定义:

from typing import Union
from iot import UniverseNoticeModel, FunctionDeviceModel

def my_semantics_model_func(*args) -> Union[FunctionDeviceModel, UniverseNoticeModel]:

    time = args[1]                                      # 获取时间

    content = args[2]                                   # 获取提醒内容

    # 处理提醒命令,可以借助其他API实现

    return FunctionDeviceModel(                         # 最后返回一个功能设备模型

        smt_code='hello',                               # 对应的语义模型标识

        is_raw=True,                                    # 是否为纯文本

        acoustics=f"好的,我会在{time}后提醒你{content}", # 返回给用户的语音内容

        data=""                                         # 由于是纯文本,所以这个字段用不到

    )

定义好以后需要在 app.py 中加入一行:

from iot import Terminal


Terminal.load_models('demo.models')
Terminal.run()

2.自定义设备功能

智能终端最大的优势就是可以通过语音控制智能家居。同样,需要定义一个语义模型实现这个功能:

from typing import Union

from iot import SemanticsGroup, SemanticsModel, SemanticsRedirectEnum, SemanticsFromEnum, SemanticsFunc, UniverseNoticeModel, FunctionDeviceModel


def curtain_semantics_model_func(*args) -> Union[FunctionDeviceModel, UniverseNoticeModel]:
    return FunctionDeviceModel(

        smt_code="hi",

        topic="default/curtain",                        # 发送的Topic,其实后续会被语义模型的Topic覆盖

        is_raw=False,                                   # 不再是纯文本返回了

        acoustics="好的,正在为你打开窗帘",                # 返回给用户的提示信息

        data={                                          # 发送的数据
            'state': 'on'
        }
    )


@SemanticsGroup.add_model
class SwitchOnSemanticsModel(SemanticsModel):

    code = "hi"

    frm = SemanticsFromEnum.USER

    topic = 'default/curtain'

    regex = "打开窗帘"

    regex_num = 1

    redirect = SemanticsRedirectEnum.MESSAGE # 重定向给消息,因为需要发送MQTT消息

    func: SemanticsFunc = curtain_semantics_model_func

具体怎么消费这个MQTT消息,即硬件设备如何处理则需要改造硬件,具体参考 相关资料

3.自定义消息推送

和之前一样,也需要定义一个语义模型:

from typing import Optional

from iot import SemanticsGroup, SemanticsModel, SemanticsRedirectEnum, SemanticsFromEnum, SemanticsFunc, UniverseNoticeModel


def button_semantics_model_func(model) -> UniverseNoticeModel:

    #处理设备推送的统一推送模型

    return UniverseNoticeModel(                 # 这次返回的是统一推送模型

        smt_code='hei', 

        topic='self/weather',                   # topic,被用来检索语义模型的

        device='remote_server',                 # 设备来源

        verbose=False,                          # 是否多语,这里只需要通知以下用户所以选择False

        message="天气播报:短期将有大量降雨"       # 返回给用户的信息
    )


@SemanticsGroup.add_model
class ButtonSemanticsModel(SemanticsModel):

    code = "hei"

    frm = SemanticsFromEnum.DEVICE                  # 来自设备

    topic = 'self/weather'

    regex: Optional[str] = None

    regex_num: Optional[str] = None

    redirect = SemanticsRedirectEnum.ACOUSTICS      #直接返回

    func: SemanticsFunc = button_semantics_model_func

至此,可以实现一个简单的物联网终端!

进阶功能

如果您对上述基本功能还不满足,可以试一下进阶功能。

1.语音唤醒功能

像主流的智能终端一样,本项目也可已开启语音唤醒功能。该功能是通过内置的 Pocketsphinx 实现的,因此需要安装其依赖,包括swig,C语言环境等,具体可以查看相关文档。

成功安装好依赖后将 .env 文件添加以下两行:

TERMINAL_MODE=0
DEVICE_REC=PocketsphinxRecorder

再次启动程序可以通过对它说hello或者hi唤醒。

2.生命周期和钩子函数

本智能终端在运行时分为以下几个阶段,在不同的阶段可以调用不同的钩子函数实现流程定制化:

                                                             ___________________________________循环______________________________________
                                                             |                                                                           |
    |加载用户模型| ->  |加载上下文| -> |执行启动钩子| -> |监听用户/设备请求| -> |执行前置语义处理钩子| -> |处理请求| -> |执行后置语义处理钩子| ->|转发请求| -> |执行卸载钩子| -> |卸载|
                                                             |                                                                           |
                                                             |——————————————————————————————————循环——————————————————————————————————————|

从上图可以看出,一共有四个钩子函数,分别是 OnStartUpHookOnModelPreprocessHookOnModelPostprocessHookOnTearDown。可以通过以下方法编写钩子函数:

from iot import HooksGroup

@HooksGroup.on_start_up
def start_up(context, semantics_group):
    ...


@HooksGroup.on_tear_down
def tear_down(context, semantics_group):
    ...


@HooksGroup.on_model_preprocess
def model_preprocess(context, function_device_model):
    ...


@HooksGroup.on_model_postprocess
def model_postprocess(context, function_device_model):
    ...

钩子函数可以获取到执行阶段的上下文,包括各种处理器信息和配置信息。此外,启动钩子卸载钩子 可以获取语义处理模型的集合而 前置语义处理钩子后置语义处理钩子 可以获取到语义模型。

3.自定义设备和语音处理器

本智能终端的设备(麦克风和扬声器)都是用的Windows默认的,如果要用在树莓派或者其他环境则需要自定义设备,包括麦克风(Recorder)和扬声器(Player)。

首先实现对应的类:

from iot import RecorderFactory, Recorder

@RecorderFactory.set
class MyRecorder(Recorder):      # 继承Recorder类,并加上注解
    
    def awake(self) -> bool:          # 实现awake方法,这个方法必须是个阻塞的方法,返回True则开始录音,返回False则推出程序
        ...

    def record(self, time: int) -> str: # 录音,time为录音时长,返回录音后保存的路径
        ...

然后在 .env 文件中修改默认的麦克风设备:

DEVICE_REC=MyRecorder

同样,扬声器也是这样的步骤:

from iot import PlayerFactory, Player

@PlayerFactory.set
class MyPlayer(Player):

    def play(self, path: str) -> None:    # path为存储语音文本的txt文件路径
        ...

然后更改 .env 文件

DEVICE_PLY=MyPlayer

您也可以修改默认的语音处理器,包括语音识别(AsrProcessor)和语音合成(TtsProcessor),方法也是一样的。

from iot import AsrProcessorFactory, AsrProcessor
from typing import Optional


@AsrProcessorFactory.set
class MyAsrProcessor(AsrProcessor):

    def asr(self, path: str) -> Optional[str]: # path为音频文件(一般为wav)的路径,返回语音文字,如果为None则说明处理失败
        ...

.env 文件

ASR_PROCESSOR=MyAsrProcessor

语音合成可以这样修改:

from iot import TtsProcessorFactory, TtsProcessor


@TtsProcessorFactory.set
class MyTtsProcessor(TtsProcessor):

    def tts(self, text: str) -> str:  # text为需要被合成的文字,返回合成后的音频文件路径
        ...

.env 文件

TTS_PROCESSOR=MyTtsProcessor

相关资料

  1. 《手把手教你做一个天猫精灵》系列

  2. 如何实现智能设备

  3. 智能家居设备交流消息的抓取

待实现功能

1.语音追问功能

2.音频等流媒体的播放

3.语义模型的order

Releases

No releases published

Packages

No packages published