dingdang robot:一个开源的中文智能音箱项目

1,548 阅读15分钟

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~

作者:潘伟洲

免责说明:本文介绍的 dingdang-robot 与公司的叮当助手没有任何关系。

这个项目其实来源于我生活中的一个需求:我每天晚上都会去厨房做一个面包当明天的早餐,当我把用料按顺序准备好放进面包机时,我需要准确预约到明天早上我吃早餐的时间。然而,几乎每次在这个时候我都没有带手机在身边,而是都放在客厅里充电,这时只能跑去客厅看时间。虽然厨房到客厅只有几步之遥,但自己又是懒癌患者,每天都要这么来回奔波就觉得很不方便。要解决这个问题当然有很多种方法,比如直接买个小时钟放在厨房。不过我更希望“连看都不用看”,直接有人告诉我时间。所以,我需要一个像 Amazon Echo 那样的智能音箱。

然而,不论是 Amazon Echo 、Google Home 还是微软 Cortana 音箱,在国内的使用都是个问题。虽然国内也有类似的智能音箱产品,但我没有用过这些产品,不知道可定制性如何。比如,如果我需要开发个功能让它告诉我某种面包的配方是什么,这些产品就不一定能做到了。考虑再三,我决定自己动手写一个。整个项目用了差不多三个星期的业余零碎时间。

先放上项目主页:http://dingdang.hahack.com

Demo:https://github.com/wzpan/dingdang-robot/wiki/demo

下面分享一下我在开发这个项目过程中的心得。

硬件

首先要解决的是硬件问题。我选择在 Raspberry Pi 上开发。于是我买了块 Raspberry Pi 三代主板。麦克风和音响方面,出于美观的目的,买了个自带音响的 USB 全向会议麦克风。整套设备看起来就像这样:

[图片上传失败...(image-99d2ff-1512030430320)]

过了不久觉得这个麦克风自带的音响音质太一般了,所以我又外接了一个小音箱。然后再插了一个摄像头,用来实现拍照功能,又进化成了这样:

[图片上传失败...(image-6a565f-1512030430320)]

发布了不到一个星期,respeaker 的商务经理看到了我这个项目,希望能提供硬件上的支持,所以我又玩上了各种 respeaker 的硬件(做为回报,我也零报酬给他们带去了 100 多个 respeaker-2mics-HAT 的销量):

[图片上传失败...(image-8f30c5-1512030430320)]

因为软件本身是和硬件解耦的,并不依赖具体的硬件要求,所以很多用户也自己做了硬件上的定制和尝试(最后一张图里预装了 dingdang-robot 的是熊,不是妹子 :-p):

[图片上传失败...(image-b39a-1512030430320)]

硬件有了,接下来就得开始写软件了。主要的框架借鉴了 Jasper 项目,并加入了我自己的定制和想法。这里说说一些有意思的部分。

指令接收

智能音箱要解决的一个最重要的问题就是如何接收指令。这里头主要涉及两个问题:

  1. 被动唤醒(Passive Listening),即“什么时候开始听”。这个阶段只监听唤醒词。当听到唤醒词时,进入主动聆听。
  2. 主动聆听(Active Listening),即“什么时候结束听”。这个阶段主动聆听用户的任何语音指令,然后对听到的内容进行分析处理。

被动唤醒阶段的基本策略是:每次以 16000 的采样率录制 1024 个采样作为一个采样集,然后对采样集进行信号强度估计,当某个采样集信号强度大于一个阈值时,就认为可能接受到了指令。然后持续录制多 1 秒时间,再转交给语音识别模块。当语音识别模块认为是唤醒词时,进入主动聆听阶段。

主动聆听的策略与被动唤醒基本相似,每次以 16000 的采样率录制 1024 个采样作为一个采样集,然后对采样集进行信号强度估计,当某个采样集信号强度低于一个阈值约 1 秒的时间时,就认为用户已说完了指令。当然还要考虑环境吵杂,一直处于聆听的可能。因此可以再加一个超时保护,超过 12 秒就结束聆听。

语音处理

说说STT(语音识别,说成ASR也是一回事啦)引擎和TTS(语音转文本)引擎的选择。由于被动唤醒会试图识别所有听到的内容,出于隐私保护的目的,应该使用离线的语音识别引擎,因此我选择的是 PocketSphinx 。而对于主动聆听,由于是在唤醒阶段才会进行转换,进入主动聆听前会有蜂鸣提示,用户也会清楚此时叮当正在听他们说话,相对来说隐私泄露的可能性就比较低,因此我最初选择的是在线的百度 STT 语音识别服务,也省下了扩展语音识别模型的工夫,有利于更好地实现插件可扩展。TTS 引擎方面同样也先支持了百度的语音合成。

在实际测试中,PocketSphinx 的识别出乎意料的好。由于我的离线指令集只有几个候选唤醒词,PocketSphinx 对这些唤醒词的识别非常灵敏,甚至有时候其他声音也可能被误当成唤醒词而唤醒叮当。但即使被意外唤醒了,不去理会叮当就可以了。

相比之下,百度的语音识别就比较迟钝了。有时候明明我发音很清晰了,还是会识别成另外的含义。通过在百度的语音识别平台上传自定义的语音识别词库 可以提高识别的准确率。另外,由于我用的是 Restful API,网速比较差的时候响应也比较慢。我在家用的是 10M 带宽的网络,反应速度还算可以接受。

下面这个视频是我与叮当对话的演示。我把唤醒词设置成了“小梅”:

http://www.miaopai.com/show/-yeEBNJlvrQ-UNZzaglxr2s9JQU8TZNy.htm

一个问题是当回答内容比较长(比如问叮当当天的新闻)时,合成语音的耗时会变得很长,给人的感受是叮当的响应很慢。所以我加了个 read_long_content 的选项。当内容过长时,改成发送到用户的邮箱或者微信。

[图片上传失败...(image-11938c-1512030430320)]

到了九月份的时候,dingdang-robot 在离线唤醒方面又增加了 snowboy 引擎,在主动聆听和语音合成方面又增加了阿里、科大讯飞的服务,无论是识别速度和合成音色的丰富程度又有了很大的进步。

[图片上传失败...(image-1c1a35-1512030430320)]

技能插件

叮当最好玩的部分当然就是玩插件了,通过写插件可以让叮当接入各种各样的服务,完成各种各样的事情。我在叮当里也内置了几个插件。为了方便用户扩展,我定义了三个技能插件目录:

  • $HOME/dingdang/client/plugins:官方插件,与 wzpan/dingdang-robot 同一仓库;
  • $HOME/.dingdang/contrib:用户贡献的第三方插件,单独维护一个 dingdang-robot/dingdang-contrib 仓库;
  • $HOME/.dingdang/custom:用户自定义插件,用于存放用户自己开发的,暂时不计划对外发布的插件。

如下是截至本文发布前 dingdang-robot 提供的插件,可以看出,其他用户贡献的插件已经达到了叮当自带的插件的两倍:

插件名 用途 是否用户贡献
Echo 简单的回声/传话功能。
Email 检查邮件功能。
Time 时间插件。询问叮当时间。
Camera 用于调起摄像头拍照(如果安装了摄像头的话)。 部分
SendQR 要求叮当发送微信登录二维码到用户邮箱(方便远程微信登录)。
Chatting 用于进入/退出闲聊模式的插件。
Unclear 用于机器人聊天兜底。
Hass 用于控制接入HomeAssistant的设备
NetEaseMusic 网易云音乐播放插件
Weather 天气查询插件
SpeakIP 播报主机IP地址插件
Reboot 重新启动操作系统(root用户)
WebServer 启动HTTP服务器插件
SendMessage 向微信好友发消息插件
ControMqtt 通过Mqtt协议与其他开发板通讯
WOL 通过WOL(Wake On Lan)实现语音开机
EmailMyPC 以邮件方式实现语音操控电脑
ToDo 简单的备忘插件
RaspberryPiStatus 简单的树莓派状态查询插件
HeadlineNews 新闻头条播报插件
Direction 出行路线规划插件
BaiduFM 百度FM音乐播放插件

音乐播放

既然是智能音箱,当然少不了播放音乐的功能。所以我额外写了个播放网易云音乐的插件 NetEaseMusic 。出于版权考虑,并不集成进官方插件中,而是放进 dingdang-contrib 里头。

这个插件的实现比较复杂。普通的插件接受到指令,响应完就退出了。而为了能支持各种指令控制音乐播放,这个插件在接收到播放控制指令后并不退出插件,而是进入一个播放器模式,这个模式主动聆听得到的指令只会在播放控制指令集中匹配,其他的插件指令都不起作用。只有当用户要求退出播放时才回到普通模式。NetEaseMusic 的播放控制指令如下:

指令 相同指令 用途
播放音乐 - 进入音乐播放模式。在音乐播放模式下,其他的插件功能将不可用。
下一首 切歌, 下一首歌, 下首歌 切换到下一首歌。如果没有下一首歌,就回到列表中第一首歌
上一首 上一首歌,上首歌 切换到上一首歌。如果没有上一首歌,就跳到列表中最后一首歌
大声点 大点声,大声 调高播放音量
小声点 小点声,小声 降低播放音量
随机播放 - 随机播放列表中的音乐
顺序播放 - 顺序播放列表中的音乐
暂停播放 - 暂停音乐的播放
播放 继续 继续音乐的播放
榜单 - 播放推荐榜单
歌单 - 播放用户的歌单(如果有多张,将只播放第一张)
结束播放 退出播放,停止播放 退出音乐播放模式。
搜索 查找 搜索歌曲/歌手。将自动播放搜索结果。
什么歌 - 正在播放的是什么歌

实现这个插件的过程中还参考了 Vellow 的 MusicBox 项目以及 yaphone 的 RasWxNeteaseMusic 。为了方便重用,我把 MusicBox 的核心 API 抽离了出来封成了一个 MusicBoxApi 库 。比较坑爹的是就在我准备发布叮当的前几天,老的获取音乐地址的方式彻底不能用了,而新的接口批量获取的地址不知道为什么是乱序的,于是我只能在播放每首歌前都调用一下新版的获取地址的 POST 接口,又增加了一点响应时间。

下面这段音频是使用叮当控制音乐播放的演示:

http://onmw7y6f4.bkt.clouddn.com/%E6%92%AD%E6%94%BE%E9%9F%B3%E4%B9%90.mp3

完成了音乐播放功能后,叮当的好玩程度提高了很多。以前要听歌,至少得把电脑或者手机打开。现在只需要喊一声叫叮当播放歌曲就可以了。想换歌、搜索歌曲、调节音量都是说句话就搞定的事情,生活幸福指数大幅提升 ^_^

开源心得

dingdang-robot 的正式开源时间是今年的 5 月 20 日(是的,我专门挑了个好日子),截至本文发表时,该项目的相关数据如下:

  • Github Stars:418,平均一个月 100 star
  • QQ群用户:340
  • pull requests:48
  • 项目主页 PV:12,000+
  • 捐赠记录:https://github.com/wzpan/dingdang-robot/wiki/donate-list

虽然数据并不是特别亮眼,不过对于这样的小众项目,我已经比较满意了。下面谈谈个人怎么维护一个开源项目吧。

前期:做好框架

根据二八法则,项目的前期应该要把整个项目期望能达到的 80% 的基本要求完成。一个人的力量毕竟有限,要完成剩余的 20% 的功能可能要耗费非常多的精力,还不如先推出来,形成社区后再去尝试完成。

以 dingdang-robot 为例,其实智能音箱最重要的几个用途:音乐播放、闹钟提醒和智能家电控制只有音乐播放是我在首次发布的时候提供的(因为不能播放在线音乐的智能音箱实在不好意思叫智能音箱)。剩下的几个功能都是我提了 feature issues 大致提供了实现思路,然后由其他的开发者去完成的。

所以,对于一个比较大的项目,更重要的是在这个阶段界定好 80% 的目标,设计好项目的框架,确定 License,写好 README 和 CONTRIBUTION(代码审核、持续集成测试等)。

中期:推广 + 社区搭建

项目发布之后,推广和社区搭建是非常重要的,毕竟直接关系到项目的用户数量和这个项目的生命力。

推广方面,这个阶段我发布了三篇文章:

这三篇文章推到了开发者头条和树莓派实验室上,都取得了不错的点击率。

另外,如果你跟微博上一些大 V 关系不错,也可以尝试 @ 他们帮忙推广,当然前提是项目要足够拿得出手才行。

在这个阶段,项目的主页和文档也必须尽快搞好,良好的主页和文档是吸引用户付出精力去尝试你的项目的必要前提。如果像我一样想省点事,可以直接用 Github Pages 生成简单的项目主页,然后直接用 Github Wiki 维护文档。

社区的搭建也是非常关键的一环。可以考虑的渠道比如:

  • 即时通讯工具:如果项目主要针对国内用户,可以使用QQ群;如果针对国际用户,可以使用 slack 。
  • 论坛。即时通讯工具的最大问题就是存在时效性。类似的问题在群里刚回答完,新加入的用户又问了一遍,非常崩溃。所以搭建一个论坛,引导用户多去使用论坛交流就可以解决这样的问题。

以 dingdang-robot 为例,QQ群是很多人决定参与项目之前先加入的社区。刚开始群里才几个人的时候也是非常冷清尴尬,但后面随着人越来越多,群里变得非常热闹(甚至我不得不选择上班时间屏蔽该群)。而论坛最初是其中一个用户用 discuz! 搭建的,但邮箱认证没搞定,久而久之论坛里一堆的 spam 。趁着国庆我又用 discourse 重新搭了一个,加了严格的审核策略,正好用腾讯云的体验代金券。

市集模式合作阶段:促进合作,保持生命力

一旦用户数量足够多,参与贡献的人就会多了起来。这时候怎么调动大家参与贡献的热情就显得格为重要了。

为了达到这个目的,项目作者本身首先当然要以身作则,体现出充足码力。该你承担的部分积极实现,该 code review 的时候赶紧 review,该发布新版本的时候保证发布。然后在社区里头多多抛出可能的 idea 和实现方案,这时候往往就会有人愿意参与贡献。

我比较喜欢的形式是在项目的 Github issue 页里提出需求,然后过不久就可以看到有人参与贡献了(比如 #28, #29#39)。对于参与贡献的用户,要不吝致谢,让他们的名字出现在 changelog 中。

除了这种市集模式的合作方式之外,未来我可能会考虑其他新颖的促进合作的方式,比如在线直播写插件、发布新版本,线下 Hackathon 等。

总结和后续

对于有 Coding 能力的 Hacker 而言,自己动手做一个智能音箱,不仅可以当做业余练手项目,还可以自由地定制硬件模块,并实现自己需要的各种功能,这远比直接购买一个 Amazon Echo 有趣得多。

更重要的,我更希望能有其他有兴趣的朋友参与进来,一同开发完善这个智能音箱项目。我相信,这种个性化服务的产品本身就应该是完全可定制的。而您的加入可以使 dingdang-robot 变得更智能!

相关阅读

CNN 在语音识别中的应用 加权有限状态机在语音识别中的应用 揭秘腾讯云Supermind智能网络,百万级设备的网络高效运维


此文已由作者授权腾讯云技术社区发布,转载请注明原文出处