使用RaspberryPI和TensorFlow创建自己的智能婴儿监视器

2020-11-12 02:59:21

你们中的一些人可能已经注意到,距离我上一篇文章已经有一段时间了。这是因为在此期间我已经成为了一名父亲,我不得不暂时从我的项目中抽出一点时间来处理一些(目前)还不能自动完成的父母任务。

或者,他们能吗?虽然我们可能还需要几年的时间才能让机器人完全负责给你儿子换尿布的任务(假设有足够多的疯狂父母同意在他们自己蹒跚学步的孩子身上测试这样的设备),但也有一些风险较低的父母职责为自动化提供了一些空间。

作为一名父亲,我首先意识到的一件事是,婴儿真的可以哭得很厉害,即使我在家里,我可能也不会总是在足够近的地方听到我儿子的哭声。商业化的婴儿监视器通常会填补这一空白,它们就像对讲机一样,让你即使在另一个房间也能听到宝宝的声音。但我很快意识到,商业化的婴儿监控器比我想要的理想设备更笨。它们听不到宝宝的哭声--它们只是像对讲机一样,把声音从信号源传给扬声器。当他们搬到不同的房间时,这取决于父母是否移动扬声器,因为他们不能在任何其他现有的音频基础设施上播放声音。它们通常带有低功率扬声器,而且通常无法连接到外部扬声器-这意味着如果我在另一个房间播放音乐,我可能会想念宝宝的哭声,即使监视器与我的显示器在同一个房间。他们中的大多数都在低功率无线电波上工作,这意味着如果婴儿在他/她的房间里,而你不得不到地下室走一小段路,他们通常就不会工作。

它应该运行在任何简单、便宜的设备上,比如带有廉价USB麦克风的RaspberryPI。

它应该能检测到我宝宝的哭声,并在他开始/停止哭泣时通知我(最好是在我的手机上),或者跟踪我仪表盘上的数据点,或者在我儿子哭泣时执行任何我想运行的任务。它不应该只充当一个愚蠢的对讲机,将声音从一个信号源传递到一种单一类型的兼容设备。

它应该能够在任何设备上播放音频--我自己的扬声器、我的智能手机、我的电脑等等。

无论信号源和扬声器之间的距离如何,它都应该可以工作,而不需要在房子里移动扬声器。

它还应该配有摄像头,这样我既可以实时检查宝宝的情况,也可以在宝宝开始哭泣时拍下他的照片或一段简短的视频,以检查他是否一切正常。

让我们看看如何使用我们最喜欢的开源工具来完成这项工作。

首先,获取一个RaspberryPI并在SD卡上闪存任何兼容的Linux操作系统--最好使用任何RaspberryPI 3或更高版本来运行TensorFlow模型。还有一个兼容的USB麦克风--任何东西都可以用,真的。

作为第一步,我们必须录制足够的音频样本,在婴儿哭的地方和婴儿不哭的地方,我们稍后将使用这些样本来训练音频检测模型。注:在这个例子中,我将展示如何使用声音检测来识别婴儿的哭声,但同样的程序也可以用来检测任何类型的声音--只要声音足够长(例如,闹钟或邻居的钻孔声),并且声音足够大,而不是背景噪音。

在我的RaspberryPI上,我得到以下输出(请注意,我有两个USB麦克风):

*捕获硬件设备列表*卡1:设备[USB PnP声音设备],设备0:USB音频[USB音频]子设备:0/1子设备#0:子设备#0卡2:Device_1[USB PnP声音设备],设备0:USB音频[USB音频]子设备:0/1子设备#0:子设备#0。

我想用第二个麦克风来录音--那是卡2,设备0。ALSA识别它的方式要么是hw:2,0(直接访问硬件设备),要么是Plughw:2,0(如果需要,它可以推断采样率和格式转换插件)。确保SD卡上有足够的空间或插入外部USB驱动器,然后开始录制一些音频:

当你的宝宝在同一个房间时,录制几分钟或几个小时的音频-最好是长时间的静默、婴儿哭声和其他无关的声音-完成后按Ctrl-C键。根据需要重复该过程多次,以获取一天中不同时刻或不同日期的音频样本。

一旦有了足够的音频样本,就可以将其复制到计算机上以训练模型-或者使用SCP复制文件,或者直接从SD卡/USB驱动器复制文件。

让我们将它们都存储在同一个目录下,例如~/Datsets/Sound-Detect/Audio。另外,让我们为每个样本创建一个新文件夹。每个文件夹将包含一个音频文件(名为audio.mp3)和一个标签文件(名为labels.json),我们将使用它们来标记音频文件中的负/正音频片段。因此,原始数据集的结构如下所示:

现在最无聊的部分来了:给录制的音频文件贴上标签--如果里面有你自己宝宝几个小时的哭声,那就特别受虐狂了。在您喜欢的音频播放器或Audacity中打开每个数据集音频文件,并在每个Samples目录中创建一个新的labels.json文件。识别哭声开始和结束的确切时间,并在labels.json中以TIME_STRING-&gT;LABEL形式的键值结构报告它们。示例:

在上面的示例中,00:00到02:12之间的所有音频段都将被标记为负,02:13到04:56之间的所有音频段都将被标记为正,依此类推。

标记完所有音频样本后,让我们继续生成将提供给TensorFlow模型的数据集。我已经创建了一个通用库和一组用于声音监控的实用程序,名为micmon。让我们从安装开始:

该模型是为处理频率样本而不是原始音频而设计的。原因是,如果我们想要检测特定的声音,该声音会有一个特定的“频谱”特征--即一个基频(或基频通常可能下降的一个狭窄范围)和一组特定的谐波,它们以特定的比率与基频绑定。此外,这些频率之间的比率既不受振幅的影响(频率比率与输入音量无关),也不受相位的影响(无论何时开始录制,连续的声音都将具有相同的频谱特征)。与我们简单地将原始音频样本馈送到模型的情况相比,这种幅度和时间不变性的特性使得这种方法更有可能训练一个健壮的声音检测模型。此外,这个模型更简单(我们可以很容易地将频率分组到不影响性能的箱中,因此我们可以有效地执行降维),更轻(模型将有50到100个频带作为输入值,无论样本持续时间如何,而1秒的原始音频通常包含44100个数据点,并且输入的长度随着样本持续时间的延长而增加),并且不容易过度拟合。

Micmon提供的逻辑可以计算音频样本的某些段的FFT(快速傅立叶变换),使用低通和高通滤波器将结果频谱分组,并将结果保存到一组Numpy压缩(.npz)文件中。您可以通过命令行通过micmon-datagen命令完成此操作:

Micmon-数据生成\--低250--高2500--桶100\--采样持续时间2--通道1\~/数据集/声音检测/音频~/数据集/声音检测/数据。

在上面的例子中,我们从存储在~/Dataset/Sound-Detect/Audio下的原始音频样本生成一个数据集,并将得到的频谱数据存储到~/Datsets/Sound-Detect/Data中。--LOW和--HIGH分别标识要在结果频谱中考虑的最低和最高频率。缺省值分别为20 Hz(人类耳朵可听到的最低频率)和20 kHz(健康和年轻的人耳朵可听到的最高频率)。但是,您通常可能希望限制此范围,以尽可能多地捕获要检测的声音,并尽可能地限制任何其他类型的音频背景和无关的谐波。我发现在我的情况下,250-2500赫兹的范围足以探测到婴儿的哭声。婴儿哭声通常是高音的(考虑到歌剧女高音能达到的最高音符大约是1000赫兹),你可能通常想要至少是最高频率的两倍,以确保你得到足够高的谐波(这些谐波实际上是给声音带来音色的更高频率),但不会太高,不会因为其他背景声音的谐波而污染频谱。我还削减了250赫兹以下的频率--婴儿的哭声在那些低频上可能不会有太多反应,而且包括它们也可能会扭曲检测。一个不错的方法是在Audacity或任何均衡器/频谱分析仪中打开一些正值音频样本,检查哪些频率在正值样本中占主导地位,并将数据集中在这些频率上。--bins指定频率空间的组数(默认为100)。箱的数量越多,意味着频率分辨率/粒度越高,但如果太高,可能会使模型容易过度拟合。

该脚本将原始音频分割成较小的片段,并计算每个片段的频谱“签名”。--Sample-Duration指定每个段的时长(默认为2秒)。较高的值对于持续时间较长的声音可能效果更好,但它会缩短检测时间,并且可能会在较短的声音上失败。较低的值可能更适合较短的声音,但如果声音较长,捕获的片段可能没有足够的信息来可靠地识别声音。

Micmon-dataGen脚本的另一种方法是制作您自己的脚本,用于通过提供的micmon API生成数据集。示例:

无论您使用的是micmon-daten还是micmon Python API,在该过程结束时,您都会在~/Datets/Sound-Detect/Data下找到一堆.npz文件,每个.npz文件对应于原始数据集中每个带标签的音频文件。我们可以使用这个数据集来训练我们的神经网络进行声音检测。

Micmon使用TensorFlow+Kera来定义和训练模型。这可以通过提供的Python API轻松完成。示例:

在运行这个脚本之后(并且在您对模型的准确性感到满意之后),您会发现新模型保存在~/Models/Sound-Detect下。在我的例子中,从我婴儿的房间收集大约5个小时的声音,并定义一个良好的频率范围来训练一个具有98%准确率的模型就足够了。如果您在计算机上训练过此模型,只需将其复制到RaspberryPI,即可进行下一步操作。

是时候编写一个脚本了,该脚本使用之前训练过的模型对麦克风中的实时音频数据进行处理,并在宝宝哭泣时通知我们:

在RaspberryPI上运行脚本,并让它运行一段时间-如果在过去2秒内没有检测到哭声,它将打印负片,否则将打印正片。

但是,如果我们的宝宝哭了,那么简单地将一条消息打印到标准输出的脚本没有多大用处--我们希望得到通知!让我们使用Platypush来覆盖这一部分。在本例中,当检测到哭声时,我们将使用PushBullet集成向我们的手机发送一条消息。让我们安装Redis(Platypush用来接收消息)和Platypush与HTTP和PushBullet集成:

在你的智能手机上安装PushBullet应用程序,然后前往Pushbullet.com获取API令牌。然后创建一个~/.config/platypush/config.yaml文件,用于启用HTTP和PushBullet集成:

现在,让我们修改前面的脚本,以便触发可由Platypush挂钩捕获的CustomEvent,而不是将消息打印到标准输出:

将上面的脚本另存为~/bin/micmon_Detect t.py。该脚本仅在至少在Window_Length秒的滑动窗口中检测到PERIAL_SAMPLES样本时才触发事件(这是为了减少预测错误或临时故障造成的噪音),并且仅当当前预测由负变为正或相反时才触发事件。然后,该事件通过RedisBus被分派到Platypush。该脚本还应具有足够的通用性,可以处理任何声音模型(不一定是哭闹的婴儿的声音模型)、任何正面/负面标签、任何频率范围和任何类型的输出事件。

现在,让我们创建一个Platypush钩子来对事件做出反应,并向我们的设备发送通知。首先,准备Platypush脚本目录(如果尚未创建):

Mkdir-p~/.config/platypush/script cd~/.config/platypush/script#将目录定义为模块ouch__init__.py#为婴儿哭闹事件vi Babymonitor or.py创建脚本。

现在为Platypush创建一个服务文件(如果它还不存在),并启动/启用该服务,以便它在终止或重新启动时自动重新启动:

[单位]描述=监视以检测我的宝宝在以下情况下的哭声=network.target sound.Target[服务]ExecStart=/home/pi/bin/micmon_despect.py-i Plughw:2,0-e Baby-Call-w 10-n 2~/型号/声音检测重新启动=Always RestartSec=10[Install]WantedBy=default.target。

此服务将启动ALSA设备Plughw:2.0上的麦克风监视器,如果在过去10秒内检测到至少2个正2秒样本且前一状态为负,则它将触发状态=正的婴儿哭声事件,如果在过去10秒内检测到少于2个正样本且前一状态为正,则状态=负。然后,我们可以启动/启用该服务:

确认宝宝一哭,你的手机就会收到通知。如果没有,您可以通过其他方式查看您应用于音频样本的标签、神经网络的体系结构和参数,或者样本长度/窗口/频带参数。

另外,考虑到这是一个相对基本的自动化示例-可以随意添加更多自动化任务。例如,您可以向另一个带有TTS插件的Platypush设备(例如,在您的卧室或起居室)发送一个请求,让它大声说宝宝在哭。您还可以扩展micmon_Detect.py脚本,以便捕获的音频样本也可以通过HTTP流传输-例如,使用FlaskWrapper和ffmpeg进行音频转换。另一个有趣的用例是在宝宝开始/停止哭泣时将数据点发送到您的本地数据库(您可以参考我以前的文章,关于如何使用Platypush+PostgreSQL+Mosquito+Grafana来创建灵活且自我管理的仪表板):这是一组有用的数据,可以跟踪您的宝宝何时睡着、醒着或需要喂奶。再说一次,监测我的宝宝一直是开发小甜瓜的主要动机,但完全相同的程序也可以用来训练和使用模型来检测任何类型的声音。最后,你可以考虑使用一个好的电源库或一组锂电池来让你的声音监控器移动起来。

一旦你有了一个好的音频源和一种检测正向音频序列何时开始/停止的方法,你可能想要添加一个视频源来监视你的宝宝。虽然在我的第一次设置中,我在用于音频检测的同一个RaspberryPI 3上安装了PiCamera,但我发现这种配置非常不实用。如果你正在寻找一款轻便的相机,你可以很容易地安装在支架或灵活的手臂上,而且你可以四处走动,无论宝宝在哪里,你都可以四处走动,无论他/她在哪里,放在盒子里的Raspberry Pi 3,加上一个附加的电池组和一个以某种方式粘在上面的摄像头,可能会相当笨重。我最终选择了一款较小的RaspberryPi Zero,与PiCamera兼容,并有一个小型电源插座。

就像在另一台设备上一样,插入一张装有RaspberryPI兼容操作系统的SD卡。然后在插槽中插入与RaspberryPI兼容的摄像头,确保在raspi-config中启用摄像头模块,并使用PiCamera集成安装Platypush:

您已经可以在Platypush重启时检查此配置,并通过HTTP从摄像头获取快照:

或者,您可以创建一个挂钩,该挂钩在应用程序启动时开始通过TCP/H264传输摄像机视频流:

或者通过VLC应用程序或RPI Camera Viewer等应用程序在手机上安装。