2017年07月06日

Google Assistant SDKサンプルコードをカスタマイズ

Google Assistant SDKはRaspberry Pi 3B用の「Google Assistant library for Python」とその他のプラットフォームで使用できるローレベルのAPIである「Google Assistant gRPC API」の2つで構成されています。

スマートミラーでGoogle Assistantを使用したいので、「Google Assistant library for Python」のサンプルコードをカスタマイズしたいと思います。

カスタマイズでできること

Google Assistant SDKのページにカスタマイズのヒントになることが書かれています。



上記ページによると、「OK Google」の代わりにボタンを押してGoogle Assistantを起動させたり、Google Assistantが音声を再生している間LEDランプを点滅させたり、ユーザーリクエストの音声をディスプレイにテキストとして表示させたりすることができるようです。また、IFTTTなどと連携して、音声コマンドでいろいろ操作させることもできます。

今回はGoogle Assistantが音声を再生している間、スマートミラーにユーザーリクエストの音声をテキストで表示させてみたいと思います。

Google Assistant libraryのファイル構造

GitHubにライブラリデータがアップされていますが、使用するのは、「assistant-sdk-python/google-assistant-sdk/googlesamples/assistant/library/hotword.py」です。

● hotword.py

#!/usr/bin/env python

# Copyright (C) 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import argparse
import os.path
import json
import google.oauth2.credentials

from google.assistant.library import Assistant
from google.assistant.library.event import EventType
from google.assistant.library.file_helpers import existing_file

def process_event(event):
    """Pretty prints events.
    Prints all events that occur with two spaces between each new
    conversation and a single space between turns of a conversation.
    Args:
        event(event.Event): The current event to process.
    """
    if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
        print()

    print(event)

    if (event.type == EventType.ON_CONVERSATION_TURN_FINISHED and
            event.args and not event.args['with_follow_on_turn']):
        print()

def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument('--credentials', type=existing_file,
                        metavar='OAUTH2_CREDENTIALS_FILE',
                        default=os.path.join(
                            os.path.expanduser('~/.config'),
                            'google-oauthlib-tool',
                            'credentials.json'
                        ),
                        help='Path to store and read OAuth2 credentials')
    args = parser.parse_args()
    with open(args.credentials, 'r') as f:
        credentials = google.oauth2.credentials.Credentials(token=None,
                                                            **json.load(f))

    with Assistant(credentials) as assistant:
        for event in assistant.start():
            process_event(event)

if __name__ == '__main__':
    main()

サンプルコードは、

(env) $google-assistant-demo

で起動していましたが、それはGoogle Assistant SDKをインストールした際、「env/bin/google-assistant-demo」が作られ、上記コマンドが実行されると「env/lib/python3.4/site-packages/google/assistant/__main__.py」が実行されるようプログラムされているからです。※「__main__.py」の中身は「hotword.py」と同じです。

● google-assistant-demo

#!/home/pi/env/bin/python

# -*- coding: utf-8 -*-
import re
import sys

from google.assistant.__main__ import main

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

カスタマイズ作業

それではカスタマイズしていきます。まず、カスタマイズ用にデータをダウンロードします。ダウロードしたデータから必要なライブラリーデータだけをコピーして「/home/pi/new-project」を作成します。※「new-project」は任意の名前で大丈夫です。

$ git clone https://github.com/googlesamples/assistant-sdk-python
$ cp -r assistant-sdk-python/google-assistant-sdk/googlesamples/assistant/library new-project

● hotword.py

Google Assistant SDKをインストールした際「env/lib/python3.4/site-packages/」にライブラリが入っていれば、「hotword.py」はどこにあっても問題なく実行できます。今回は「hotword.py」をデスクトップに移動してカスタマイズしていきます。

カスタマイズは、「EventType.ON_CONVERSATION_TURN_STARTED」などイベントステータスごとにどのようなアクションを追加していくかという作業になります。どのようなイベントステータスがあるかは、Referenceページに書かれています。



スマートミラーのインターフェースは、Processingで構築しているので、Processingにユーザーリクエストの音声をテキストにしたものを渡せるように、イベントのステータスにあわせてJSONファイルを出力するようにします。

#!/usr/bin/env python

from __future__ import print_function

import argparse
import os.path
import json

import google.oauth2.credentials

from google.assistant.library import Assistant
from google.assistant.library.event import EventType
from google.assistant.library.file_helpers import existing_file

# 辞書オブジェクトの定義
speechdata = {}
# JSONファイルのパス
filepath = "/home/pi/Desktop/speech.json"

# JSONファイルの書き込み
def writeJson(path,data):
    
    with open(path,'w') as f:
        json.dump(data,f,indent=4)

# JSONデータ用初期化処理
def initJson():
    
    speechdata = {
        'status':'waiting',
        'txtStatus':'waiting',
        'txt':'',
    }
    writeJson(filepath,speechdata)

# 「process_event(event)()」にプログラムを追加していきます。
def process_event(event):

    if event.type == EventType.ON_START_FINISHED:    
        print('ON_START_FINISHED')
    
    if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
        print('ON_CONVERSATION_TURN_STARTED')
        
        speechdata['status'] = 'start'
        speechdata['txtStatus'] = 'waiting'
        speechdata['txt'] = ''
        writeJson(filepath,speechdata)

    if event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED:
        print('ON_RECOGNIZING_SPEECH_FINISHED')
        print(event.args)
        
        queryTxt = event.args['text']
        speechdata['status'] = 'talking'
        speechdata['txtStatus'] = 'query'
        speechdata['txt'] = queryTxt

        writeJson(filepath,speechdata)
    
    if (event.type == EventType.ON_CONVERSATION_TURN_FINISHED and
            event.args and not event.args['with_follow_on_turn']):
        print('ON_CONVERSATION_TURN_FINISHED')

        speechdata['status'] = 'finish'
        speechdata['txtStatus'] = 'waiting'
        dataspeechdata['txt'] = ''

        writeJson(filepath,speechdata)

#「main()」は主に認証部分です。
def main():

    # Jsonデータ初期化
    initJson()

    # GoogleAssistantSDK認証
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument('--credentials', type=existing_file,
                        metavar='OAUTH2_CREDENTIALS_FILE',
                        default=os.path.join(
                            os.path.expanduser('~/.config'),
                            'google-oauthlib-tool',
                            'credentials.json'
                        ),
                        help='Path to store and read OAuth2 credentials')
    args = parser.parse_args()
    with open(args.credentials, 'r') as f:
        credentials = google.oauth2.credentials.Credentials(token=None,
                                                            **json.load(f))

    with Assistant(credentials) as assistant:
        for event in assistant.start():
            process_event(event)

# import文でモジュールとしてインポートされた場合ではなく
# コマンドラインから実行された時に「main()」を実行します。
if __name__ == '__main__':
    main()

「hotword.py」を実行すると、Google Assistantが起動し、デスクトップにJSONファイルが保存されます。

(env) $python /home/pi/Desktop/hotword.py

● Processingでテキストを表示

実際には、イベントステータスによって細かく制御する予定ですが、JSONからデータを取得してスマートミラーにテキストを表示させるテストをしてみました。


String textTyped = "";
float fontSize = 80;
float tracking = 0;
PFont font;
String speechtxt;
int txtNum = 0;
String[] speechTxtArr;
 
void setup(){
  size(600,900);
  smooth();
  noCursor();
   
  font = createFont("Arial",10);
 
  JSONObject json = loadJSONObject("speech.json");   
  speechtxt = json.getString("txt");
  speechTxtArr = speechtxt.split("");
}
 
void draw(){
  noStroke();
  background(0);
  fill(255);
  textAlign(LEFT);
   
  float spacing = 100;
  translate(0, 300+spacing);
 
  float x = 0, y = 0;
 
  for (int i = 0; i < textTyped.length(); i++) {

    textFont(font, fontSize);
    char letter = textTyped.charAt(i);
    float letterWidth = textWidth(letter) + tracking;
     
    if (x+letterWidth > (width-40)) {
      x = 0;
      y += spacing;
    }
     
    text(letter, x+20, y);
    x += letterWidth;
  }

  fill(100,100,100);
  if (frameCount/10 % 2 == 0) fill(0);
  rect(x+20, y, fontSize/4, fontSize/20);
   
  if(txtNum < speechTxtArr.length){
     
    if(frameCount % 10 == 0){
      textTyped += speechTxtArr[txtNum];
      txtNum ++;
    }
  } 
}

「開発中のスマートミラー」動画

スマートミラーにGoogle Assistantを搭載してみました。



  • このエントリーをはてなブックマークに追加
前の記事へ

Raspberry Pi 3BでGoogle Assistant SDK

次の記事へ

Raspberry Pi 3Bのスリープモード解除