皆さん、昨年のクリスマスはプレゼントを貰えましたか?
昨年はサンタの来訪検知でサンタが来ないか監視していましたが、サンタは現れずかつプレゼントも貰えませんでした。
今年もめげずにサンタに立ち向かいます。
昨年の反省点
昨年はRaspberry Piに接続したWebカメラでサンタを撮影、被写体が確実にサンタだった場合はslackに通知するシステムでした。
しかし大きな事に気づいたのですが、そもそもサンタが来る時間帯は自分が寝ているという問題がありました。要件定義から間違っていた感じですね。今回の場合は通知など意味を成さないのです。ユーザ(私)が欲しいのはプレゼントであり、プレゼントをもらうことがCVになります。今年はプレゼントをもらうというCVRを向上させるため、今年こそ来るサンタにプレゼントを忘れずに置いていくように訴えかけていきたいと思います。
来訪したサンタにプレゼントをお願いする
何度も言いますが今年こそサンタは来ると思います。その際、プレゼントの置き忘れや出し渋ったりした時の為にサンタへアクションを起こします。
基本的には昨年の仕様を踏襲しつつ、新たな機能を追加します。
昨年の仕様
昨年は以下のような仕様でした。
- Raspberry Piに接続したWebカメラで写真撮影
- Google Cloud Vision APIへ送信し、LabelDetectionにより画像にサンタが写っているかを判別する。
- 写っていた場合、Slack通知を行う
今年の追加仕様
LabelDetectionでサンタを検知した後、Raspberry Piから何かしらの音声を発声しサンタにプレゼントを要求します。
サンタに話しかける方法を考える
サンタを検知したと同時にすぐプレゼントをお願いしないといけないです。自分は寝ているので、寝ている間に何らかの方法でサンタに話しかける必要がありますね。そこでいくつか方法を考えてみました。
Alexaで話しかける方法
まず思いついたのは家にあるAlexaからサンタに話しかけてもらう方法。最近流行りのスマートスピーカーでの開発も興味があり検討しました。
Alexaはウェイクワードを起点に動作しますが、APIを叩いて自分から喋らせる方法が無いかと考えてみます。いろいろ調べてみると、リマインダー機能を利用して自分から喋らせることが可能とのこと!
ReminderAPIも最近公開されたそうなのでこれで行けるかと思いきや、そんな単純な話ではなかった。
これらのAPIはAlexaのスキルから叩く想定のものなので、外部から叩いたりすることには対応していない。。ローカルネットワークからのリクエストとか、アクセスキーとかでどうにかなるもんかと思っていたけど、だめなのね。スマートスピーカー開発者からすると当たり前やろ…って話かもしれないですが・・・これに気づくのに少し時間がかかってしまった。。。
無理やりAlexaに喋らせる方法
ただ、これを力技でやる方法がslideshareで紹介されていました。
リマインダーAPIをハックして、Alexaを積極的なキャラにする
要は海外で公開されているnoelportugal/alexa-remindersパッケージを使うと、PCからリマインダーを設定することができるとのこと。
ソースコードを見てみると、このパッケージでやっている内容が黒魔術的で
- nightmareで自身のalexaのポータルへログイン
- Alexaのポータル(SPA)で使用されているreminder登録APIをcurlで叩く
というもの。実際このパッケージで動くか試してみると、確かに動いた。。
const ALEXA_DEVICE_NAME = process.env.npm_package_config_alexa_device_name
const ALEXA_USERNAME = process.env.npm_package_config_alexa_username
const ALEXA_PASSWORD = process.env.npm_package_config_alexa_password
const reminders = require('alexa-reminders')
reminders.login(ALEXA_DEVICE_NAME, ALEXA_USERNAME, ALEXA_PASSWORD, function(error, response, config){
console.log(response)
reminders.setReminder('プレゼントは忘れずにそこへ置いていってください', null, config, function(error, response){
console.log(response)
})
})
実際に試してみた動画がこちら
ただ、この方法を使うデメリットとしては
- 非公式のAPI叩いててこわい、怒られそう
- 読み上げの時に「…..のリマインダー」という語尾がついてしまう。
- リマインダーなので、こちらが反応するまで延々とループする
音声合成を使用する方法
Raspberry Piから音声合成を再生してサンタに話しかける方法。
AWSやGCPでもSpeech系のAPIがあったり、ChromeのAPIにもテキスト読み上げのAPIがあったりするのでそういったものを使うのが良いかもしれません。
Google Cloud Text-To-Speechで発話させる
今回はGoogle Cloud Text-To-Speechで文章を飲ませてサンタに話しかける方法をとります。Text-To-Speech APIはテキストをリクエストすると、読み上げた音声データを返してくれるので、これをRaspberry Pi上で再生させます。
Google Cloud Text-To-Speechの使い方
認証情報の設定
Cloud Consoleの「APIとサービス」メニューからCloud Text-To-Speech APIを有効化し、認証情報を取得します。
そのまま「認証情報」からサービスアカウントの認証情報のjsonを取得し、保存します。
認証情報のjsonはRaspberry Piに保存し、以下の環境変数にファイルパスを設定します。
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credential.json
実装
今回もNode.jsで実装するのでnpmを使用します。
@google-cloud/text-to-speech
$ sudo npm install --save @google-cloud/text-to-speech
Google Cloud Consoleの
const fs = require('fs');
const textToSpeech = require('@google-cloud/text-to-speech');
const client = new textToSpeech.TextToSpeechClient();
let text = 'プレゼントはそこに置いていってください';
const request = {
input: {text: text},
voice: {
languageCode: 'ja-JP',
name: 'ja-JP-Standard-A'
},
audioConfig: {audioEncoding: 'MP3'},
};
client.synthesizeSpeech(request, (err, response) => {
if (err) {
console.error('ERROR:', err);
return;
}
fs.writeFile(AUDIO_FILE_PATH, response.audioContent, 'binary', err => {
if (err) {
console.error('ERROR:', err);
return;
}
console.log('Audio content written to file: ' + AUDIO_FILE_PATH);
playAudio() // 音声を再生する
});
今回はCloud Vision APIで取得したラベル「santa clause」と共に「プレゼントはそこに置いていってください」を音声として再生します。事前にAPIを叩いて合成音声の作り置きもできますが、今回はリアルタイムに制御してみます。
play-soundで音声を再生する
Google Cloud Text-To-Speechはmp3ファイルを生成するため、それを再生する必要があります。今回はplay-soundパッケージを使用して音声の再生を行います。
play-soundは内部的に音楽再生のコマンドを叩いているようなので、mpg321コマンドをインストールしました。併せて、音声再生の際に権限エラーとなるので/dev/snd配下に権限を付与します。(しっかりやるならグループ等を適切に設定する)
$ sudo apt install mpg321
$ sudo chmod o+rw /dev/snd/*
const player = require('play-sound')();
player.play(AUDIO_FILE_PATH, err => {
if (err) {
console.error('ERROR:', err);
return;
}
});
動作させてみた
昨年と同様に改めてテストをしてみます。手前にあるWebカメラから撮影し、サンタ(テスト用の写真)が写っていたらテキスト読み上げによりcloud vision APIで認識したラベル「santa clause」と共に読み上げを行います。
まとめ
今回、どうやって自由なタイミングで発話をさせるかがポイントでした。
- Google Cloud Text-To-Speechでデバイスから音声再生まで実行できた
- Alexaを自分から喋らせるにはひと手間必要。基本的にはリマインダーAPIを使用する
何かしらを喋らせるにはスマートスピーカーを使うのが相性的にも良いと思いますが、実行タイミングが自由に決められない(ウェイクワードが必要)というデメリットがあります。
IFTTTあたり使えばいけるのかな・・と思いつつさくっと調べてみた感じはなさそうでした。(やり方あれば教えてください。)
また、Cloud Vision APIとCloud Text-To-Speech APIを同時に使っているので、認識〜読み上げまでの時間が結構かかっています。
このあたりは実戦で使用する際に問題にならないか検討したほうが良いかもしれません。
今回は短い文でしたが、長い文章だと・・・?という感じです。
今回使用したコードは以下にアップしています。
tai-sho/adv-iotlt-santa-detect-reminder
Google Homeだと自発的に喋らせる方法があった
ちなみに今回、Alexaでは頓挫しましたが、Google Homeでは自分から発話させる方法があるようでした。
noelportugal/google-home-notifierを使用するとローカルネットワークからGoogleHomeへキャストして喋らせることができるようです。
以前chromecastの制御を行ったりしましたが、Googleはこういうハックに対して寛容なのかもしれませんね。
GoogleHomeを手に入れたら実践してみたいと思います!
コメント