2018-09-13 DuerOS + Raspberry3B+ + 麦克风阵列

DuerOS的个人开发者版本在官方的介绍中已经停止申请, 代码也大半年没人更新了, 不过好在这个版本需要的麦克风阵列可以自行在网上购买, 相关的代码也是托管在Github上, 于是在研究了网上的各位大神的文章后, 终于把DuerOS运行起来了...

选购硬件

树莓派3B+: 网上到处有卖, 需要买相关的套件, 单独的板子是无法玩的.
麦克风阵列: ReSpeaker树莓派4麦克风阵列扩展板

烧录系统

在树莓派上烧录raspbian-2018.4.18这个镜像. 千万不要烧录官网最新的镜像, 最新的镜像可能无法安装某些第三方库(比如pyaudio死活安装不了). 也不要安装DuerOS_For_Raspberry这个镜像, 因为这个镜像是针对Raspberry3B的, 3B+安装DuerOS这个镜像后会无法开机

安装声卡驱动

按照官方的文档去安装声卡驱动就行. 官方文档: http://wiki.seeedstudio.com/cn/ReSpeaker_4_Mic_Array_for_Raspberry_Pi/

运行装DuerOS

官方的文档: https://developer.baidu.com/forum/topic/show/244796?pageNo=1 安装完成后会有两个问题:

  1. 运行enter_trigger_start.sh 脚本, 服务器有回应但是没有声音
  2. 无法运行wakeup_trigger_start.sh 脚本

解决第一个问题
修改/home/pi/DUerOS-Python-Client/app/framework 目录下面的play.py文件

# -*- coding: utf-8 -*-

"""
基于GStreamer的播放模块
"""

import gi
import logging
import app.app_config as app_config

gi.require_version('Gst', '1.0')
from gi.repository import Gst

Gst.init(None)

logging.basicConfig(level=app_config.LOGGER_LEVEL)
logger = logging.getLogger(__file__)

class Player(object):
    '''
    播放器实现类
    '''

    def __init__(self):
        # create a new gstreamer pipeline
        self.pipeline = Gst.Pipeline.new("mypipeline")

        # add a file source to the pipeline
        self.urisrc = Gst.ElementFactory.make("uridecodebin","source")
        self.urisrc.connect("pad-added", self.decode_link)
        self.pipeline.add(self.urisrc)

        # add a convertor to the pipeline
        self.convert = Gst.ElementFactory.make("audioconvert","convert")
        self.pipeline.add(self.convert)

        # add an alsa sink to the pipeline and link it to theconvertor
        self.sink = Gst.ElementFactory.make("alsasink", "sink")
        self.pipeline.add(self.sink)
        self.convert.link(self.sink)

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.enable_sync_message_emission()

    def decode_link(self, dbin, pad):
        pad.link(self.convert.get_static_pad("sink"))


    def play(self, uri):
        '''
        播放
        :param uri:播放资源地址
        :return:
        '''
        self.convert.unlink(self.sink)
        self.pipeline.set_state(Gst.State.NULL)
        
        # start playing
        logger.info('播放资源 uri: {}'.format(uri))
        self.urisrc.set_property("uri", uri)#'http://zhangmenshiting.qianqian.com/data2/music/067eaeefb2eb0498fe404edba700c6b1/599610520/599610520.mp3?xcode=d8bf0fee68258ce2350a5c038dd045b5')
        self.convert.link(self.sink)
        self.pipeline.set_state(Gst.State.PLAYING)

    def stop(self):
        '''
        停止
        :return:
        '''
        logger.info('停止')
        self.urisrc.unlink(self.convert)
        self.convert.unlink(self.sink)
        self.pipeline.set_state(Gst.State.NULL)

    def pause(self):
        '''
        暂停
        :return:
        '''
        logger.info('暂停')
        self.pipeline.set_state(Gst.State.PAUSED)

    def resume(self):
        '''
        回复播放
        :return:
        '''
        logger.info('回复播放')
        self.pipeline.set_state(Gst.State.PLAYING)

    def add_callback(self, name, callback):
        '''
        播放状态回调
        :param name: {eos, ...}
        :param callback: 回调函数
        :return:
        '''
        if not callable(callback):
            return

        def on_message(bus, message):
            callback()

        self.bus.connect('sync-message::{}'.format(name), on_message)

    @property
    def duration(self):
        '''
        播放时长
        :return:
        '''
        success, duration = self.pipeline.query_duration(Gst.Format.TIME)
        if success:
            return int(duration / Gst.MSECOND)

    @property
    def position(self):
        '''
        播放位置
        :return:
        '''
        success, position = self.pipeline.query_position(Gst.Format.TIME)
        if not success:
            position = 0

        return int(position / Gst.MSECOND)

    @property
    def state(self):
        '''
        播放状态
        :return:
        '''
        # GST_STATE_VOID_PENDING        no pending state.
        # GST_STATE_NULL                the NULL state or initial state of an element.
        # GST_STATE_READY               the element is ready to go to PAUSED.
        # GST_STATE_PAUSED              the element is PAUSED, it is ready to accept and process data.
        #                               Sink elements however only accept one buffer and then block.
        # GST_STATE_PLAYING             the element is PLAYING, the GstClock is running and the data is flowing.
        _, state, _ = self.pipeline.get_state(Gst.SECOND)
        return 'FINISHED' if state != Gst.State.PLAYING else 'PLAYING'

修改/home/pi/DUerOS-Python-Client/app 目录下面的enter_trigger_main.py文件

# -*- coding: utf-8 -*-
'''
通过输入[Enter]触发唤醒状态
'''
import logging
from sdk.dueros_core import DuerOS

from app.framework.mic import Audio
from app.framework.player import Player
from app.utils.prompt_tone import PromptTone

logging.basicConfig(level=logging.INFO)


def directive_listener(directive_content):
    '''
    云端下发directive监听器
    :param directive_content:云端下发directive内容
    :return:
    '''
    content = u'云端下发directive:%s' % (directive_content)
    logging.info(content)


def main():
    # 创建录音设备(平台相关)
    audio = Audio()
    # 创建播放器(平台相关)
    player = Player()

    dueros = DuerOS(player)
    dueros.set_directive_listener(directive_listener)

    audio.link(dueros)

    dueros.start()
    audio.start()

    #prompt_tone_player = PromptTone()

    while True:
        try:
            try:
                print '\n'
                input('单击[Enter]建,然后发起对话\n')
            except SyntaxError:
                pass
            # 唤醒态提示音
            #prompt_tone_player.play()
            dueros.listen()
        except KeyboardInterrupt:
            break

    dueros.stop()
    audio.stop()


if __name__ == '__main__':
    main()

解决第二个问题
还未解决...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342