Google Assistant Service プログラミング事始め
前回の記事では Google Assistant Library ベースのプログラミングを通じて Google アシスタントの各機能を取り回す試みを行いました。今回はもうひとつの Google Assistant SDK である Google Assistant Service に目を向けてみます。
Google LLC は Google Assistant SDK for devices として「Google Assistant Library」「Google Assistant Service」の二種類のセットを提供しています。前者は高水準で稼働環境は狭め、後者は低水準で広範な稼働環境に対応しており、この周到な構成に Google の本気度が窺えます。現時点では一日あたりのリクエスト数に制限はあるものの個人でも無償で利用できることが大きな魅力です。
Google Assistant Service と稼働環境
前回も引用した Google Assistant Library と Google Assistant Service の比較表を再掲します。(2018年12月時点の公式記事より)
Google Assistant Service の最大の特長は、このように gRPC 対応のプラットフォーム+対応言語環境全般で利用可能であることです。現時点では Google Assistant Library ほどには多機能ではないものの、稼働環境が広く Windows PC や Mac はもとより小振りで消費電力が小さく IoT フロントエンドとして有用な Raspberry Pi Zero / Zero W 系ボードも Google アシスタントクライアントとして利用できることに関心を誘われます。
Google Assistant Service と Google Assistant Library の機能面での違いは SDK 全体のリリースノートを追うことで手早く俯瞰できます。
両者の機能差は今後変化する可能性がありますが、個人的には 2018年12月時点で Google Assistant Service 側にはない要素のうち実用面とのかねあいにおいて以下の三点に留意しています。
- Google Assistant Library とは異なりウェイクワードの待機・検知に対応していない
- ニュースの読み上げや Google Podcasts に未対応
- アラームに未対応 (リマインダは設定可能)
なお、上記 1. は外部の Hotword Detector との連携(後述)によりある程度補うことが可能です。
※余談ながら、Google Home デバイス実機とは異なり今のところ Google Assistant SDK ベースのプログラムの所作に google-home-notifier を絡ませることはできません。たしかに 8009 番ポートを使ったキャスト機構にはハードウェア側要件としての印象が強いものの、google-home-notifier を あち こち で利用しているファンとしてはちょっと残念です。
pushtotalk.py プログラムのこと
Google Assistant Service をセットアップ後の googlesamples/assistant/grpc/ フォルダには「pushtotalk.py」プログラムが配置されます。
このプログラムは名前の示すように Google Assistant Library に含まれる「hotword.py」とは異なり、ウェイクワード(ホットワード)の音声ではなく 'Press Enter to send a new request...' の CUI メッセージを添えた click.pause() API で物理的なトリガーを待って Google アシスタントとの対話を開始する内容で実装されています。
セットアップずみの SDK ディレクトリ下での pushtotalk.py の実行方法は以下の要領です。引数で指定したプロジェクト ID とモデル ID はホスト側へ記憶され、変更の必要がなければ次回以降は省略可能です。
$ pwd
/home/t/wk/GoogleAssistant
$ ls
env
$ source env/bin/activate
(env) $ googlesamples-assistant-pushtotalk --project-id [設定ずみのプロジェクトID] --device-model-id [設定すみのモデルID]
pushtotalk.py を手元の複数の環境で実行した様子の動画を以下に示します。
Windows |
Mac |
Linux (32bit) |
Raspberry Pi 3 Model B+ |
Raspberry Pi Zero W |
![]() |
![]() |
上の動画のRaspberry Pi 3 Model B+ / Zero W には手持ちの以下の以下のマイクとスピーカーを写真の要領で接続しています。
|
今回は、前回の hotword.py と対照的なこの pushtotalk.py を試作の下敷きとします。
Hotword Detector について
前掲の Google Assistant Service 側未対応の機能のうち、実用上の影響がもっとも大きいのはウェイクワードの待機・検知ができないことでしょう。Google Assistant Library プログラムにおいては Google Home デバイスと同様にハンズフリーでアシスタントとの応酬が可能であることを考えあわせると淋しく感じられますが、この点は外部の Hotword Detection ソフトウェアを併用することで補うことができます。
ただし、外部の Hotword Detector との連携は Google アシスタントネイティブでのウェイクワードサポートではなく、あくまでも Assistant API を呼び出すためのトリガーを外側に用意する手立てに他ならないため以下の注意が必要です。
- ウェイクワード設定の柔軟性や認識精度はすべて Detector 側の要件である
- Google アシスタントの発話中にウェイクワードで介入することはできない
こういった事情を理解した上で Hotword Detector を併用すれば、間口の広い Google Assistant Service を様々な環境でより便利に利用することができるでしょう。
Porcupine と Snowboy
現時点でのスマートスピーカー向きの代表的な Hotword Detector として、Picovoice (カナダ系)による Porcupine と、 KITT.AI (中国系) による Snowboy が挙げられます。後者はすでに随所で取り上げられていますね。どちらも機械学習に基づく精度の高さ・低負荷・マルチプラットフォーム対応・カスタマイズの柔軟性をアピールポイントとしており、後発の Picovoice は両者の比較記事を公開しています。
手元ではどちらも使い始めてまだ日が浅いのですが、今のところウェイクワードを認識する能力そのものに際立った性能差は感じておらず負荷の度合いについては未検証です。今の時点で把握している両者の一長一短を挙げてみます。
対応プラットフォームの広さ: Porcupine ◎
カスタムウェイクワードへの対応: Snowboy ◎
$ pwd
/home/t/wk/Porcupine
$ tools/optimizer/linux/i386/pv_porcupine_optimizer -r resources/ -p linux -o . -w "OK google"
手元では今のところ PC では Porcupine、Raspberry Pi では Snowboy の要領で使い分けています。下の動画はそれぞれの動作の様子です。
※ ふたつの動画でのウェイクワード検知時・対話終了時の応答音はいずれも Snowboy の resource/ ディレクトリ下の wave ファイルによるものです。これらが耳に馴染んだため Porcupine との連携においても同じ要領で使用しています
試作
前回記事での hotword.py (Google Assistant Library) ベースの試作に続き、今回は Google Assistant Service の pushtotalk.py を下敷きにプログラムを作成します。作業上の便宜から開発は PC で行いましたが、Google Assistant Service ベースなので指先に乗るサイズの Raspberry Pi Zero W ボードをはじめ広範な環境で実行可能であることに夢が広がります。オールインワンのダンボールキットも製品化されていますね。
今回はシンプルな切り口として前回の Google Assistant Library 版の各プログラムと同じ動きをするものを作ってみることにしました。Google Assistant Serivice プログラミングは奥が深くまだまだ習作の段階ではありますが、事始めとして取り組んだ内容を以下に掲載します。
1. 指定テキストに基づく音声合成と読み上げ
テーマ: Google アシスタントの音声合成機能を単体で利用する。いわゆる Text to Speech。
[前回 Google Assistant Libray 版試作へのリンク]
内容
デモ: 動画 27秒
(前回分)
考えたことなど
(env)t@PC-533:~/wk/GoogleAssistant$ ./textinput.sh
INFO:root:Connecting to embeddedassistant.googleapis.com
: こんにちは
<you> こんにちは
<@assistant> こんにちは、TAnabeさん
どうしましたか?
: 今何時?
<you> 今何時?
<@assistant> 時刻は、17:25です。
: 今晩、雨降る?
<you> 今晩、雨降る?
<@assistant> 夜は、雨の心配はないでしょう
今夜の山口は雨ではないでしょう。 気温10度、晴れるでしょう。
---
(weather.com でもっと見る)
:
083| def iter_assist_requests():
084| config = embedded_assistant_pb2.AssistConfig(
085| audio_out_config=embedded_assistant_pb2.AudioOutConfig(
086| encoding='LINEAR16',
087| sample_rate_hertz=16000,
088| volume_percentage=0,
089| ),
090| dialog_state_in=embedded_assistant_pb2.DialogStateIn(
091| language_code=self.language_code,
092| conversation_state=self.conversation_state,
093| is_new_conversation=self.is_new_conversation,
094| ),
095| device_config=embedded_assistant_pb2.DeviceConfig(
096| device_id=self.device_id,
097| device_model_id=self.device_model_id,
098| ),
099| text_query=text_query,
100| )
109| text_response = None
110| html_response = None
111| for resp in self.assistant.Assist(iter_assist_requests(),
112| self.deadline):
113| assistant_helpers.log_assist_response_without_audio(resp)
114| if resp.screen_out.data:
115| html_response = resp.screen_out.data
116| if resp.dialog_state_out.conversation_state:
117| conversation_state = resp.dialog_state_out.conversation_state
118| self.conversation_state = conversation_state
119| if resp.dialog_state_out.supplemental_display_text:
120| text_response = resp.dialog_state_out.supplemental_display_text
121| return text_response, html_response
149| if len(resp.audio_out.audio_data) > 0:
150| if not self.conversation_stream.playing:
151| self.conversation_stream.stop_recording()
152| self.conversation_stream.start_playback()
153| logging.info('Playing assistant response.')
: 今何時?
<you> 今何時?
0
1600
1600
(引用中略)
1600
1600
244
<@assistant> 時刻は、18:43です。
(mp3 形式へ変換したもの)
import sounddevice as sd
:
text_response = None
html_response = None
s = sd.RawStream(
samplerate=audio_helpers.DEFAULT_AUDIO_SAMPLE_RATE,
dtype='int16',
channels=1,
blocksize=audio_helpers.DEFAULT_AUDIO_DEVICE_BLOCK_SIZE)
for resp in self.assistant.Assist(iter_assist_requests(),
self.deadline):
assistant_helpers.log_assist_response_without_audio(resp)
s.write(resp.audio_out.audio_data)
s.start()
if resp.screen_out.data:
html_response = resp.screen_out.data
if resp.dialog_state_out.conversation_state:
conversation_state = resp.dialog_state_out.conversation_state
self.conversation_state = conversation_state
if resp.dialog_state_out.supplemental_display_text:
text_response = resp.dialog_state_out.supplemental_display_text
return text_response, html_response
:
ソースコード
- pushtotalk_tts.py - github.com/mkttanabe
2. 利用者の発話内容をテキストへ変換
テーマ: Google アシスタントの音声認識機能を単体で利用する。いわゆる Speech to Text。
[前回 Google Assistant Libray 版試作へのリンク]
内容
デモ: 動画 41秒
(前回分)
考えたことなど
138| for resp in self.assistant.Assist(iter_log_assist_requests(),
139| self.deadline):
140| assistant_helpers.log_assist_response_without_audio(resp)
141| if resp.event_type == END_OF_UTTERANCE:
142| logging.info('End of audio request detected.')
143| logging.info('Stopping recording.')
144| self.conversation_stream.stop_recording()
145| if resp.speech_results:
146| logging.info('Transcript of user request: "%s".',
147| ' '.join(r.transcript
148| for r in resp.speech_results))
ソースコード
- PicovoiceWithGoogleAssitantService_input.py - github.com/mkttanabe
- Porcupine をインストールしたディレクトリ下へ配置して実行のこと
- Porcupine/resource ディレクトリ下に前掲の ding.wav, dong.wav が必要
- デモ動画ではパラメータに "--keyword_file_paths resources/keyword_files/picovoice_linux.ppn" を指定
3. 利用者の発話内容を他言語へ連続翻訳
テーマ: Google アシスタントの 音声認識 / 文意解釈 / 言語翻訳 / 応答文生成 / 音声合成 の各機能を利用する
[前回 Google Assistant Libray 版試作へのリンク]
内容
デモ: 動画 2分30秒
(前回分)
ソースコード
- PicovoiceWithGoogleAssitantService_translate.py - github.com/mkttanabe
- Porcupine をインストールしたディレクトリ下へ配置して実行のこと
- Porcupine/resource ディレクトリ下に前掲の ding.wav, dong.wav が必要
- デモ動画ではパラメータに "--keyword_file_paths resources/keyword_files/picovoice_linux.ppn" を指定
4. 利用者の発話内容を復唱
テーマ: Google アシスタントの音声認識 / 音声合成機能を利用する
[前回 Google Assistant Libray 版試作へのリンク]
内容
デモ: 動画 39秒
(前回分)
ソースコード
- PicovoiceWithGoogleAssitantService_echo.py - github.com/mkttanabe
- Porcupine をインストールしたディレクトリ下へ配置して実行のこと
- Porcupine/resource ディレクトリ下に前掲の ding.wav, dong.wav が必要
- デモ動画ではパラメータに "--keyword_file_paths resources/keyword_files/picovoice_linux.ppn" を指定
しりとり
テーマ: Google アシスタントの音声認識 / 音声合成機能を利用する
[前回 Google Assistant Libray 版試作へのリンク]
内容
デモ: 動画 60秒
(前回分)
現時点ではプログラミングを行うための実践的な情報をあまり目にすることのない Google Assistant Library と Google Assistant Service を題材に手元で行った試みを二度に分けて紹介しました。幼児の年齢を迎えたばかりの Google アシスタントはこれからこれらの SDK とともに成長を重ねて行くことでしょう。未来へ向かう道すがらの愉しみがまたひとつ増えた思いです。
(tanabe)