Amazon Alexaを動かすにはいくつかのやり方があるようだ。
このページでは, AVS Device SDK を手持ちのAIY Voice Kitを載せているRaspberry Pi 3Bで,試してみることにした。
AVS Device SDKをRaspberry Piにインストールする手順は,https://github.com/alexa/avs-device-sdk/wiki/Raspberry-Pi-Quick-Start-Guide に書かれている。
AVSは,Alexa Voice Serviceのこと。
ホームにsdk-folderを作成する。
$ cd /home/yuji/ $ mkdir sdk-folder $ cd sdk-folder $ mkdir sdk-build sdk-source third-party application-necessities $ cd application-necessities $ mkdir sound-files
AIY Voice Kit(V1になった古いやつ) をRaspberry Pi 3Bに乗っけた物があるので,それにAVSを動かすようにしてみた。
/boot/config.txtでのサウンドdriverをロードする部分を,AIY Voice Kitのみ使うようにしてみる。
# Enable audio (loads snd_bcm2835) #dtparam=audio=on #dtoverlay=i2s-mmap dtoverlay=googlevoicehat-soundcard
このように修正して,Raspberry Piのオンボードのサウンド機能をoffにして,AIY Voice KitのVoice HATのみを使えるようにする。
alsaの設定ファイルを,AIY Voice Kitのマイクとスピーカーを使用するようにする。
/etc/asound.confを以下の内容にする。
options snd_rpi_googlemihat_soundcard index=0 pcm.softvol { type softvol slave.pcm dmix control { name Master card 0 } } pcm.micboost { type route slave.pcm dsnoop ttable { 0.0 30.0 1.1 30.0 } } pcm.!default { type asym playback.pcm "plug:softvol" capture.pcm "plug:micboost" } ctl.!default { type hw card 0 }
そして,~/.asoundrcにコピーしておく。
$ cp /etc/asound.conf ~/.asoundrc
AVS Device SDKは,PulseAudioを使用してはいないようだ。また,Raspbianを更新したらPulseAudioが起動しなくなっていた。
しかし,AVSを動かす時に音関連はPulseAudio経由で行うことにする。
なので,PulseAudioの設定ファイル/etc/pulse/default.paを設定しておく。
load-module module-alsa-sink device=dmix load-module module-alsa-source device=dsnoop #load-module module-alsa-source device=hw:0,0 : : # comment out by Yuji Ueno ### Automatically load driver modules depending on the hardware available #.ifexists module-udev-detect.so #load-module module-udev-detect #.else # Use the static hardware detection module (for systems that lack udev support) # comment out by Yuji Ueno #load-module module-detect #.endif
このようにして,PulseAudioからALSAライブラリ(dmix/dsnoopプラグイン使用)経由でサウンドを使用するようにする。
また,udevによる自動でサウンドドライバーをロードしないようにしておく。(起動時にロードするので)
Raspberry Piをリブート後,pacmdを使って,音量とマイク入力レベルの調整をしておく。pacmdを使うのは,amixerだとマイク入力を100%以上に設定できないため。
$ pacmd set-sink-volume alsa_output.dmix 0x6667 $ pacmd set-source-volume alsa_input.dsnoop 0x15000
マイク入力レベルを上げるのは,最近のStretchだとなぜかマイク感度が凄く低くなってしまったため。(おそらく,AIY Voice KitのVoice HATのドライバーの設定だとは思うが,対応方法がわからなかった。)
AVS Device SDKは,いくつかのライブラリやツールを必要とするので,それをインストールする。
$ sudo apt-get install git gcc cmake build-essential libsqlite3-dev libcurl4-openssl-dev libfaad-dev libsoup2.4-dev libgcrypt20-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-good libasound2-dev doxygen
マイクを使用するのにPortAudioを使用するので,ダウンロードしてビルドしておく。
$ cd ~/sdk-folder/third-party $ wget -c http://www.portaudio.com/archives/pa_stable_v190600_20161030.tgz $ tar zxf pa_stable_v190600_20161030.tgz $ cd portaudio $ ./configure --without-jack $ make
commentjsonは,AlexaClientSDKConfig.jsonをパースするのに必要。 commentjsonを以下のようにしてインストールする。
$ pip install commentjson
Alexaは,話しかけた言葉に反応するようなシステム。その話しかけた言葉を認識するシステムがWakeWordエンジンって言うみたい。
上記のサイトには,以下のようにAVS Device SDKとWakeWordエンジンにはSensoryをインストールしろと書いてある。
$ cd ~/sdk-folder/sdk-source $ git clone git://github.com/alexa/avs-device-sdk.git
$ cd ~/sdk-folder/third-party $ git clone git://github.com/Sensory/alexa-rpi.git
$ cd ~/sdk-folder/third-party/alexa-rpi/bin/ $ ./license.sh
しかし今回は,WakeWordを認識するプログラムをSensoryではなく Snowboy を利用する。
変える理由は,デフォルトのSensoryだと,WakeWord認識時のマイク感度を調整出来ないのと,使用できるライセンスが時限(3ヶ月)があるため。また,Snowboyだと,「アレクサ」って呼びかける言葉も任意の言葉に変えられるみたい。
AVS Device SDKとSnowboyをダウンロードし,その依存パッケージを追加する。
$ cd ~/sdk-folder/sdk-source $ git clone git://github.com/alexa/avs-device-sdk.git
$ cd ~/sdk-folder/third-party $ git clone https://github.com/Kitt-AI/snowboy.git
$ sudo apt-get install libatlas-base-dev
Snowboyのリソースファイル(「アレクサ」に反応する)をセットしておく。
$ cd ~/sdk-folder/third-party/snowboy/resources $ cp alexa/alexa-avs-sample-app/alexa.umdl .
ビルドするには,まずcmakeでMakefileを作成しろと書いてある。
$ cd ~/sdk-folder/sdk-build $ cmake ~/sdk-folder/sdk-source/avs-device-sdk -DSENSORY_KEY_WORD_DETECTOR=ON -DSENSORY_KEY_WORD_DETECTOR_LIB_PATH=~/sdk-folder/third-party/alexa-rpi/lib/libsnsr.a -DSENSORY_KEY_WORD_DETECTOR_INCLUDE_DIR=~/sdk-folder/third-party/alexa-rpi/include -DGSTREAMER_MEDIA_PLAYER=ON -DPORTAUDIO=ON -DPORTAUDIO_LIB_PATH=~/sdk-folder/third-party/portaudio/lib/.libs/libportaudio.a -DPORTAUDIO_INCLUDE_DIR=~/sdk-folder/third-party/portaudio/include
Snowboyにするため,以下のように変更してcmakeでMakefileを作成した。
$ cd ~/sdk-folder/sdk-build $ cmake ~/sdk-folder/sdk-source/avs-device-sdk -DKITTAI_KEY_WORD_DETECTOR=ON -DKITTAI_KEY_WORD_DETECTOR_LIB_PATH=~/sdk-folder/third-party/snowboy/lib/rpi/libsnowboy-detect.a -DKITTAI_KEY_WORD_DETECTOR_INCLUDE_DIR=~/sdk-folder/third-party/snowboy/include -DGSTREAMER_MEDIA_PLAYER=ON -DPORTAUDIO=ON -DPORTAUDIO_LIB_PATH=~/sdk-folder/third-party/portaudio/lib/.libs/libportaudio.a -DPORTAUDIO_INCLUDE_DIR=~/sdk-folder/third-party/portaudio/include
SDK Sampleアプリをビルドしようとすると,Snowboy関連でビルドエラーになった。
最近更新されているソース(v1.11.0)だと,どのバージョンからかわからないけど修正されていたので,以下は必要なくなった。
以下のように修正した。
~/sdk-folder/sdk-source/avs-device-sdk/KWD/KittAi/src/KittAiKeyWordDetector.cpp
std::chrono::milliseconds msToPushPerIteration) : AbstractKeywordDetector(keyWordObservers, keyWordDetectorStateObservers), m_stream{stream}, m_maxSamplesPerPush{(audioFormat.sampleRateHz / HERTZ_PER_KILOHERTZ) * static_cast<unsigned int>(msToPushPerIteration.count())}{
と,static_castを追加。
それと,~/sdk-folder/sdk-source/avs-device-sdk/build/cmake/BuildOptions.cmake
add_definitions (-D_GLIBCXX_USE_CXX11_ABI=0)
を,
# Set up the compiler flags.
の下あたりに追加。
再度,makeでビルドする。
Amazon開発者コンソール(USAのAmazon)でAVS Device SDK用にデバイス登録(製品登録)する。
情報の登録:
セキュリティプロファイル:
情報と同じに登録した。
ウェブのところのクライアントIDをメモしておく。
こんなように登録してみた。
登録した情報を,~/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.jsonファイルにをセットする。
{ "deviceInfo":{ "clientId":"amzn1.application-oa2-client.xxxxxxxxxxxxxxxxxxxxxxxxxxx", "deviceSerialNumber":"yyyyyy", "productId":"AIYVoiceKit" }, "alertsCapabilityAgent":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/alerts.db" }, "certifiedSender":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/certifiedSender.db" }, "settings":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/settings.db", "defaultAVSClientSettings":{ "locale":"ja-JP" } }, "notifications":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/notifications.db" }, "cblAuthDelegate":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/cblAuthDelegate.db" }, "dcfDelegate":{ }, "miscDatabase":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/miscDatabase.db" }, "sampleApp":{ "endpoint": "https://avs-alexa-fe.amazon.com", "displayCardsSupported":true }, "gstreamerMediaPlayer":{ "audioSink":"alsasink" } }
deviceInfoのclientId,deviceSerialNumber,productIdを設定する。
それぞれ,Amazon開発者コンソールで作成したデバイスの内容を設定するんだけど,deviceSerialNumberは,なんでも良いみたいなんで,123456789にした。
Locale設定を,ja-JPにしておく。
エンドポイント を,日本の https://avs-alexa-fe.amazon.com にする。
それと,dbファイル名を任意の場所に指定する。
バージョンが更新されて,以下のエントリが必要になった。
"deviceSettings":{ "databaseFilePath":"/home/yuji/sdk-folder/application-necessities/deviceSettings.db" },
公式手順では,起動時コマンドとして,以下のように書いてある。
$ cd ~/sdk-folder/sdk-build/SampleApp/src && TZ=UTC ./SampleApp ~/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.json ~/sdk-folder/third-party/alexa-rpi/models
最後の引数が,WakeWord認識プログラム用みたいなんで,これをSensoryからSnowboyに変更する。
そのため,以下のように変更して実行する。
$ cd ~/sdk-folder/sdk-build/SampleApp/src && TZ=UTC ./SampleApp ~/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.json ~/sdk-folder/third-party/snowboy/resources/
snowboyのresouucesというフォルダを参照するようにすればいいようだ。
起動すると,
################################## # NOT YET AUTHORIZED # ################################## ###################################################################################### # To authorize, browse to: 'https://amazon.com/us/code' and enter the code: {XXXX} ######################################################################################
こんなメッセージが出るので,WEBブラウザで https://amazon.com/us/code にアクセスして自分のアカウントでログインした後,SDK SampleAppを実行した時に表示される認証コードを入力する。
承認されれば,SampleAppがauthorizedになって,少し経つとidle状態になる。
######################################## # Alexa is currently idle! # ########################################
これで,「アレクサ」って言ったら,応答してくれたら成功なんだけど・・・
しかし,「アレクサ」っていうと反応はしているようだが,音がなぜか出ない。
~/sdk-folder/sdk-source/avs-device-sdk/MediaPlayer/src/MediaPlayer.cppの以下の行が,(新しいバージョンだと,~/sdk-folder/sdk-source/avs-device-sdk/MediaPlayer/GStreamerMediaPlayer/src/MediaPlayer.cppにソースファイルが変更されている)
MEDIAPLAYER_AUDIO_SINK_KEY, &audioSinkElement, "autoaudiosink");
のようになっているのを,
MEDIAPLAYER_AUDIO_SINK_KEY, &audioSinkElement, "alsasink");
に変更して再ビルドしたら,音が出た。
Locale設定を日本語にしているんで大丈夫と思うが,もし英語で応答してきた場合は,c<ret>,1<ret>,6<ret>として,Localeをja_JPの日本語に設定できる。以後は日本語で応答してくれる。
デフォルト状態だとマイク感度が低すぎて,「アレクサ」になかなか反応しない。それに対して,しゃべる音声がめちゃくちゃ大きい。
マイク感度と音声のボリュームは,ソースに値が決め打ちされている。
~/sdk-folder/sdk-source/avs-device-sdk/KWD/KWDProvider/src/KeywordDetectorProvider.cppを,
/// The sensitivity of the Kitt.ai engine. static const double KITT_AI_SENSITIVITY = 0.6; /// The audio amplifier level of the Kitt.ai engine. static const float KITT_AI_AUDIO_GAIN = 2.0;
と,ハードコーティングされている。
KITT_AI_SENSITIVITYでWakeWord検知でのマイク感度,KITT_AI_AUDIO_GAINで音声の大きさを設定できるみたい。
/// The sensitivity of the Kitt.ai engine. static const double KITT_AI_SENSITIVITY = 0.70; /// The audio amplifier level of the Kitt.ai engine. static const float KITT_AI_AUDIO_GAIN = 0.5;
にして,再ビルド。Snowboyの開発者のページには,0から1の間の値を使えと書いてある。
マイク感度を上げすぎると,スピーカーでの音声出力を拾ったり周りの音に反応したりする。Speaking状態の時はマイクはオフじゃないのかな・・・
残念ながら,AVS Device Kitに含まれるWakeWordエンジンのSensoryやSnowboyには,スピーカーからの音声をマイクで拾った音からキャンセルする仕組み(アコースティックエコーキャンセレーション AEC)や,ビームフォーミングって呼ばれている,ある方向からのユーザー音声を拡大しつつ,他の方向からの干渉を抑える信号処理技術なんかが,入っていないようだ。
Amazonで販売されているAVS Development Kitや製品のAmazon Echoなんかは,これらの機能が入っているんで問題ないんだろうな。
もう少し何とかならないかな?
現状だと,「アレクサ」って言ったときに,Listeningモードになったかどうかが画面以外からはわからない。これは,Raspberry PiにAIY Voice Kitなんでダンボール箱だけで,画面なんかが無いから。
そこで,Listeningモードになったら,ポーンというような音で知らせるようにしてみた。さらに,ボタンのランプも点灯させたみた。
~/sdk-folder/sdk-source/avs-device-sdk/SampleApp/src/UIManager.cppに,
switch (m_dialogState) { case DialogUXState::IDLE: system("gpio -g mode 25 out"); system("gpio -g write 25 0"); ConsolePrinter::prettyPrint("Alexa is currently idle!"); return; case DialogUXState::LISTENING: system("gpio -g mode 25 out"); system("gpio -g write 25 1"); system("aplay /home/yuji/sdk-folder/third-party/snowboy/resources/dong.wav 1>/dev/null 2>/dev/null "); ConsolePrinter::prettyPrint("Listening...");
と追加した。wavファイルは,Snowboyのresourceフォルダにあったファイルにしている。
WEBブラウザで https://alexa.amazon.co.jp/ にアクセスしてAlexaの設定を行う。(もしくはAndroid端末のAlexaアプリで)
なんかを設定しておく。天気とか尋ねると,設定した場所について教えてくれるようになる。
起動スクリプトを作成する。
~/startavs.sh
#!/bin/bash pulseaudio -k pulseaudio -D pacmd set-sink-volume alsa_output.dmix 0x6667 pacmd set-source-volume alsa_input.dsnoop 0x15000 cd ~/sdk-folder/sdk-build/SampleApp/src && TZ=UTC ./SampleApp ~/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.json ~/sdk-folder/third-party/snowboy/resources/
PulseAudioがRaspbianを更新したら起動しなくなったんで,起動するようにして,マイク感度を,pacmdで上げている。
実行パーミッションを立てる。
$ chmod 755 startavs.sh
cronを使って,Raspberry Piが起動時にAVS Device SDKのSampleAppを動かすようにする。
$ crontab -e @reboot /home/yuji/startavs.sh
これで,Raspberry Piを再起動したら自動的にSampleAppが動くはずなんだけど,なんとRaspberry Piがしばらく反応しなくなってしまい(1分程度),その後,勝手にシステム(OOM killer)に殺されてしまっていた 結果,SampleAppは動作しない状態に・・・
あまり上手い方法ではないけど以下のように,~/sdk-folder/sdk-source/avs-device-sdk/SampleApp/src/UserInputManager.cppを修正してみた。
while (true) { // add by Yuji Ueno std::this_thread::sleep_for(std::chrono::hours(1)); continue; // char x;
入力をスキップするコードを追加した。
SampleAppは起動するとユーザーからの入力をstdinから行うようになっているがデーモンのようにバックグラウンドでstdin無しで動かすと入力に失敗して,これが原因でメモリーを食いつぶしてしまっているようだ。
しょうがないんだが,ユーザー入力を受け付けないように変更した。まあインターラクティブに操作することはしないからこれでいいかな。(これを行う前に日本語ja_JPに設定しておくのを忘れないようにする。)
再度ビルドしたら,無事cronで起動時に自動的に実行できるようになった。
新しくコメントをつける