はじめに
スマートスピーカー隆盛の候、皆様いかがお過ごしでしょうか(2か月ぶり2回目)。
昨年末にAdvent Calendarを勢い込んで書いた後、おかげさまでたくさんの人に見ていただいたようで、何よりでございました。
あの後、もう少し欲を出して、見栄えのいいものを作ってみたのでお目にかけつつ、結果的に五月雨式に出していた両Assistantの並行動作方法を、ここらで一つまとめておこうと思ったので、筆を取ることにしました。
お約束
ここで提供されるコードや情報については一切無保証で、特にサポートもしませんので、ご自身の責任においてのみご利用ください。
転んでも自分の力で立ち上がる覚悟のある方だけ、この先の情報をご利用ください。
おさらい(フェーズ1:単なる並行動作)
Raspberry PiにAlexaを召喚する3つの方法
https://qiita.com/Dimeiza/items/182c4847d7c1ead7df54
AlexaとGoogle Assistantの両方が召喚されているRaspberry Pi
https://qiita.com/Dimeiza/items/2dcd6c48a9c0b02ccf2c
Alexaと一緒にRaspberry Piに住んでいるGoogle Assistantに日本語をしゃべってもらった
https://qiita.com/Dimeiza/items/4aafa4cde13b58d3456f
Alexa(AVS Device SDK)導入→Alexa+AIY Kit並行動作→Google Assistant SDKの導入という流れでしたが、まずは
- まっさらなRaspbianからAlexaとGoogle Assitantを並行動作させる
- AVS Device SDK + Google Assistant SDK。
- ただし、Wake up時の効果音とLEDは後回し
というところまではさくっと通ってしまいましょう。
動作環境(フェーズ1)
構成要素 | 品名 |
---|---|
ハードウェア | Raspberry Pi3 |
マイク | サンワサプライ USBマイクロホン MM-MCU02BK (https://www.amazon.co.jp/gp/product/B01M7YIYZV/) |
SD | 8GB |
OS | RASPBIAN STRETCH WITH DESKTOP(2017-11-29) |
『まずは』この状態で行きます。
同時起動までの手順
何度かセットアップをやり直していて、やり直すたびにコードを書き換えるのが面倒だったので、Alexaのリポジトリをforkしつつセットアップスクリプトを作ってしまいました。
-
RaspbianをSDにインストール
- noobsを使ってもいいですが、私はいつもDD for windowsを使ってそのままimgを書き込んでいます。
-
起動してpiユーザでログオンし、ネットワーク通信環境を確保する
- Wifiでも有線でもお好きな方で。
-
AlexaとGoogle Assistantの設定情報をそろえておく。
- Alexaは『Client ID, Client Secret, and Product ID』
- Google assistantは『client_secret_.json』(認証情報が入ったjson)
-
以下コマンドを実行して、(私の)リポジトリからスクリプトを取得したのち、フォルダ移動する。
git clone https://github.com/Dimeiza/Assistants --branch 1.0 cd Assistants
-
Alexaのインストール
-
以下コマンドを実行して、Alexaのインストールを行う。
bash AlexaSetup.sh
-
Client ID, Client Secret, Product IDの入力を求められるので、それぞれ入力する。
-
AlexaのTerms and Agreementsが表示されるので、同意するならばAGREEと入力する。
-
以降、しばらく自動でスクリプトが動く。
- Alexa用パッケージをインストールしつつ、forkしたリポジトリからコードを取ってきます。以下が主な変更点。
- PulseAudio、Snowboyのインストール追加
- .asoundrcをpulseaudioに設定
- Snowboyビルドエラーの修正(以前紹介した2か所)
- MediaPlayer内のgstreamerのsinkをalsasinkに変更(Alexa出力を.asoundrcに追従させるため)
- Alexa用パッケージをインストールしつつ、forkしたリポジトリからコードを取ってきます。以下が主な変更点。
-
しばらくすると認証用のURLが表示されるので、URLにアクセスしてデバイス認証を行う。
-
スクリプト実行終了後、以下コマンドを実行すると、Alexaが動作する。
startsample.sh
-
-
Google Assitant SDKのインストール
-
リポジトリをダウンロードしたフォルダ(Assistants)に戻って、以下コマンドを実行する。
./GoogleAssistant_setup.sh
-
事前に取得しておいたcredentialのJSON名、Google Cloud PlatformのプロジェクトID、デバイス名の入力を求められるので、それぞれ入力する。
-
しばらくすると"Please visit this URL to authorize this application"とURLが表示されるので、そのURLにアクセスし、認証コードを取得して入力する。
-
スクリプト実行終了後、以下コマンドを実行すると、Google Assistant(hotword)が動作する。
startGoogleAssistant.sh
-
すでにこの時点で両方同時起動が可能になっているはずです。
Tips
HDMIに接続していて、3.5mmジャックから音を出したい場合は、raspi-configで『7 Advanced option』→『A4 Audio』→『1 Force 3.5mm jack』として、出力を3.5mmジャックに固定してみてください。
wake up時の効果音(フェーズ2)
ところがですね。
現状、この状態で以前に紹介したaplayによる効果音鳴らしをしようとすると、Assistantの動作が止まったりとうまく動かないらしいのです。
なぜ私がスルーしていたかというと、どうやらこの問題はRaspberry Pi3の3.5mmジャックから音声を出す時だけ起こる問題1だったかららしく。私は音声出力をUSBサウンドカードに切り替えていたせいで気づかなかったようでして。
というわけで、wake up時の効果音を鳴らす場合は、USBサウンドカードを使用した上で、コードに手を入れる必要があるようです。
(もっといい手があるようでしたら教えてください)
動作環境(フェーズ2)
構成要素 | 品名 |
---|---|
ハードウェア | Raspberry Pi3 |
マイク | サンワサプライ USBマイクロホン MM-MCU02BK (https://www.amazon.co.jp/gp/product/B01M7YIYZV/) |
SD | 8GB |
OS | RASPBIAN STRETCH WITH DESKTOP(2017-11-29) |
USBサウンドカード | 例えばこんなの(https://www.amazon.co.jp/dp/B06Y3ZL2V4/) |
マイク端子付きのサウンドカードの場合、直接マイクを引いてもいいですし、そのままUSBマイクを使っても構いません。
効果音コードの追加
面倒なのでそのままsystem関数で行ってしまいます。
Alexa
case DialogUXState::LISTENING:
system("paplay /home/pi/Assistants/third-party/snowboy/resources/dong.wav 1>/dev/null 2>/dev/null ");
Google Assistant
今はhotword.pyでも日本語が使えるので、こんな感じで追加します。
if args.project_id:
register_device(args.project_id, credentials,
args.device_model_id, assistant.device_id)
for event in events:
if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
os.system('paplay /home/pi/Assistants/third-party/snowboy/resources/ding.wav 1>/dev/null 2>/dev/null ')
USBからの音声出力
PulseAudioを使っていて、先ほどのスクリプトでpavucontrol(PulseAudioのボリュームコントロール)を入れているので、そこから変更します。
Raspberry piのGUI上から『Sound&Video』→『PulseAdio Volume Control』とクリックし、『Configuration』タブでこんな感じにします。
bcm2835 ALSAはOffに、USB Audio Deviceが"Analog Stereo Output"になっていますね。このUSB Audio Deviceはマイク入力も可能ですが、ここではUSBマイクから音声を入れることにしているので出力だけにしています。
この状態で両アシスタントを起動すると、3.5mmジャックではなく、USB Audio Device側から音声出力されるようになります。おそらく効果音も含め問題なく動作するようになっているはずです。
GPIOでLEDを光らせる(フェーズ3)
前回はブレッドボードでやってましたね。
ブレッドボードだとフットプリントを取ることもあって、私はそのあとこんなものを作っていました。
はさみで切れるユニバーサル基板にLED、抵抗、ピンヘッダをはんだ付けしただけのものです。
これをフラットなRaspberry Piのケースと併用すると、こんな感じになります。
右のLED(青)をGPIO 19番、左のLED(橙)をGPIO26番に抵抗経由で接続し、両方ともPIN左端のGNDにつなげています。
この状態で、GPIO 19番でAlexa、GPIO 26番でGoogle Assistantの応答に反応させます。
動作環境(フェーズ3)
構成要素 | 品名 |
---|---|
ハードウェア | Raspberry Pi3 |
マイク | サンワサプライ USBマイクロホン MM-MCU02BK (https://www.amazon.co.jp/gp/product/B01M7YIYZV/) |
SD | 8GB |
OS | RASPBIAN STRETCH WITH DESKTOP(2017-11-29) |
USBサウンドカード | 例えばこんなの(https://www.amazon.co.jp/dp/B06Y3ZL2V4/) |
ケース | PIMORONI Pobow3 Coupe(https://shop.pimoroni.com/collections/raspberry-pi/products/pibow-coupe-for-raspberry-pi-3) |
LED | 高輝度LED2個(色を変えた方が面白いでしょう) |
抵抗 | 2個(100Ω) |
基板 | はさみで切れるユニバーサル基板(別にブレッドボードでもいいです) |
ここで重要なのはケース。特に次のフェーズに進もうという方は、必ずFull Size Hatが刺さるケースを使いましょう。
このPIMORONI Pobow3はその意味でも割と優秀なケースでした。
WiringPiの準備
WiringPiで制御します。手順は下記のページ通り。
GPIOコマンドが使えればよいので、単に公式手順の通りダウンロードしてインストールするだけでよいです。
$ cd
$ git clone git://git.drogon.net/wiringPi
$ cd ~/wiringPi
$ ./build
私のリポジトリ内にスクリプトを用意しておきました(GPIOSetup.sh)ので、よろしければどうぞ。
コードの変更
Alexa
case DialogUXState::IDLE:
system("gpio -g mode 19 out");
system("gpio -g write 19 0");
ConsolePrinter::prettyPrint("Alexa is currently idle!");
return;
case DialogUXState::LISTENING:
system("gpio -g write 19 1");
system("paplay /home/pi/Assistants/third-party/snowboy/resources/dong.wav 1>/dev/null 2>/dev/null ");
ConsolePrinter::prettyPrint("Alexa is currently idle!");
return;
Google Assistant
os.system('gpio -g mode 26 out')
for event in events:
if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
os.system('gpio -g write 26 1')
os.system('paplay /home/pi/Assistants/third-party/snowboy/resources/ding.wav 1>/dev/null 2>/dev/null ')
if event.type == EventType.ON_CONVERSATION_TURN_FINISHED or event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT:
os.system('gpio -g write 26 0')
これでAssistant応答時にLEDが光るはずです。
ReSpeakerへの誘い
しばらくの間これで運用していたんですが、ふと、こんなイケてる玩具を見つけてしまったのですよ。
ReSpeaker 2-Mics Pi HAT
https://www.seeedstudio.com/ReSpeaker-4-Mic-Array-for-Raspberry-Pi-p-2941.html
http://wiki.seeed.cc/Respeaker_2_Mics_Pi_HAT/
ReSpeaker 4-Mic Array for Raspberry Pi
https://www.seeedstudio.com/ReSpeaker-2-Mics-Pi-HAT-p-2874.html
http://wiki.seeed.cc/ReSpeaker_4_Mic_Array_for_Raspberry_Pi/
簡単に言うと、マイクとLEDが搭載された、まさにVoice AssistantをRaspberry Piに実装する人向けに作られたHatです。これがあると、USBマイクを買ったり、基板にLEDをはんだ付けしなくても、↑と同じようなことができる、という代物。
両者の主な相違点はこんな感じ。
構成要素 | ReSpeaker 2-Mic | ReSpeaker 4-Mic Array |
---|---|---|
マイク | 左右に1つづつ(計2個) | 4隅に1つづつ(計4個) |
LED | 中央に横並び3個 | 円形に12個 |
出力端子 | GPIO1,I2C1,スピーカー端子1,3.5mmジャック1 | GPIO1,I2C1 |
2-Mic Arrayは小さく、マイクやLEDが少ない代わりに、自前でスピーカー出力端子を持っているので、外付けスピーカーの代わりに小型のスピーカーをつないで単体運用することも可能。
一方、4-Mic Arrayは出力端子が制限される一方、マイクやLED数が多い、という特色があります。
フットプリントとビジュアル、どちらにするかは個々の環境もあるのでお好みで。
私はどちらにしようか悩んだ挙句、数日後にDigikeyから届いた小包を開けてみると、こうなっていました。2
というわけで、ここからが本番。両Hatを両Assistantで連動させるための方法についてです。
動作環境(フェーズ4以降)
構成要素 | 品名 |
---|---|
ハードウェア | Raspberry Pi3 |
SD | 8GB |
OS | RASPBIAN STRETCH WITH DESKTOP(2017-11-29) |
USBサウンドカード | 例えばこんなの(https://www.amazon.co.jp/dp/B06Y3ZL2V4/) |
ケース | PIMORONI Pobow3 Coupe |
HAT | ReSpeaker 2Mic or 4Mic |
最終的には私は4Micを使っているので、USBサウンドカード経由でサウンド出力しています。
あ、あと後述する『マイクの有効化(フェーズ4)』、『LEDを光らせる(フェーズ5)』におけるドライバのインストールとソフトの入手に関しては、各Hatごとに私のリポジトリにスクリプトを用意しておいた(Respeaker2Mic_Setup.sh、Respeaker4Mic_Setup.sh)ので、よろしければどうぞ。
マイクの有効化(フェーズ4)
Hatを刺した後、まずはマイクドライバをインストールします。
とりあえず2-Mic HatのWikiを参照しますが、マイクの導入については両Hatとも共通していて、一部パラメータだけが異なっています。
2-Mic
git clone https://github.com/respeaker/seeed-voicecard.git
cd seeed-voicecard
sudo ./install.sh 2mic
reboot
4-Mic
git clone https://github.com/respeaker/seeed-voicecard.git
cd seeed-voicecard
sudo ./install.sh 4mic
reboot
install.shの引数が違うだけです。
PulseAudioの設定
こんな感じです。引きつづきUSB Audio Deviceは生かしています。
私は2Micのスピーカーから音を出したことはないので、音を出したい方は個別に調べてみてください。
GPIO関連コードの削除
先ほどGPIOのコードを追加した方は、ここでGPIO関連コードを削除しておいてください。GPIO制御コードが残っていると、Alexaのサンプルアプリを動かす時に悪さをするようです3。
おそらくここまでで、ReSpeakerのマイクから音を拾えるようになっているはずです。
ちなみにReSpeakerのマイクの射程は半径約3[m]ぐらいなので、小さな部屋なら全域カバーできます。
これでUSBマイクを使うことなく、Raspberry Pi上のHatでマイクを運用できるようになりました。
LEDを光らせる(フェーズ5)
ここまで来たら、Wake up時の点灯とかやりたくなるじゃないですか。
両Hat上のLEDはGPIOなどという単純なものではなく、SPIを使って制御します。
上記に示した両HatのWikiには、SPIを有効にしたうえで、サンプルコードを動かしてLEDを点灯させるところまでは書いてあります。
まずはそこまでやって、LEDが点灯することを確認してみましょう。
Raspi-configでSPIを有効に
これは簡単ですね。
sudo raspi-config
で、『5 Interfacing Options』→『 P4 SPI』で、Yesを選択すればOK。
サンプルコードのダウンロード
2-Micと4-Micでリポジトリの場所が違います。
2-Mic
https://github.com/respeaker/mic_hat
4-Mic
https://github.com/respeaker/4mics_hat
とりあえず2-Micをやりましょうか。4-Micも基本的には同じです。
git clone https://github.com/respeaker/mic_hat
cd mic_hat/
python pixels.py
と実行して、各LEDが青、赤、緑に点灯し始めたらOK。
AssistantとLEDの連動
で、先ほどSeeedのリポジトリからダウンロードしたコードの中には、AlexaやGoogle Assistantと連動するサンプルコードが含まれています。
それで動かして終わり、という流れもあるのですが…。
私はなるべくLED動作コードはVoice Assistantと分けたかった 4 ので、今まで作成してきたAlexa Sample AppやHotwordから、別実装されたLED動作コードを呼び出すような手を探すことにしました。
ここでPythonに詳しいと楽だったのかもしれませんが、あいにく私はあまりPythonに詳しくなくて。特にAlexa Device SDKはCベースなので、Cコード動作と連携させるのが面倒そうだなぁと。
そこで、(先ほどのGPIOコマンドのような)独立したコマンドラインプログラムから、このLEDを操作させる方法がないか、調べてみることにしました。
LEDの操作方法
LEDの具体的な制御方法は、先ほどSeeedのリポジトリから落としてきたコードの中の、apa102.pyというファイルに含まれていて、制御プロトコルの概要について英語でコメントが書いてあるので、これを読み解くと大体わかります。
So that's really the entire protocol:
- Start by sending 32 bits of zeroes. This prepares LED 1 to update
its color.
- Send color information one by one, starting with the color for LED 1,
then LED 2 etc.
- Finish off by cycling the clock line a few times to get all data
to the very last LED on the strip
実は2-Micと4-Micでプロトコル自体は同じで、LEDの数が違う分、授受するデータの量が違う、というだけの話です。
要は、
- 32[bit]の0(つまり0を4[Byte]分)を送信する。
- LEDの数だけ4[Bytes]のデータを送る。
- 1[Byte]目はスタートビット3[bit]+輝度[5bit],2[Byte]目はBlue,3[Byte]目はGreen,4[Byte]目はRedの値
- 最後にLED数/2[bit]の0を送る。
という送信をSPI上で実施すると、1回のLED点灯を制御できます。LEDの光り方に動きをつけたい場合は、上記を高速で実施すればよいわけです。
消灯するときは全てのbitを0にして送ればOK。
コマンドライン操作プログラム
というわけで、上記のプロトコルを使ってSPIDev経由でLEDを制御するプログラムをCで実装してみました。
制御方法を示す意味もあって、アニメーションとか高度な機能はないですが、とりあえず指定された状態に応じてLEDの輝度や色を調整する程度の動きをします。
製品ではないのでコード自体は雑です。ご容赦。
先ほど示したセットアップスクリプトを実行している場合、すでにこのプログラムはダウンロード、ビルドされているので取得の必要はありません。
手動でセットアップしてきた方は、ここからソースを取得してビルドしてください。
git clone https://github.com/Dimeiza/ReSpeakerLED
cd ReSpeakerLED
gcc -o ReSpeakerLED ReSpeakerLED.c
Assistantからの制御
やり方はGPIOの時と同じように、system関数を使って呼び出します。
以下は2micの時のコードですが、4micでやるときはオプション"-D 2mic"の代わりに"-D 4mic"と書き換えてみてください。
switch (m_dialogState) {
case DialogUXState::IDLE:
system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic LED_TURN_OFF");
ConsolePrinter::prettyPrint("Alexa is currently idle!");
return;
case DialogUXState::LISTENING:
system("aplay /home/pi/Assistants/third-party/snowboy/resources/dong.wav 1>/dev/null 2>/dev/null ");
system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic ALEXA_LISTENING");
ConsolePrinter::prettyPrint("Listening...");
return;
case DialogUXState::THINKING:
system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic ALEXA_THINKING");
ConsolePrinter::prettyPrint("Thinking...");
return;
;
case DialogUXState::SPEAKING:
system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic ALEXA_SPEAKING");
ConsolePrinter::prettyPrint("Speaking...");
return;
Google Assistant
for event in events:
if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
os.system('paplay /home/pi/Assistants/third-party/snowboy/resources/ding.wav 1>/dev/null 2>/dev/null ')
os.system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic GOOGLEASSISTANT_ON_CONVERSATION_TURN_STARTED")
if event.type ==EventType.ON_RECOGNIZING_SPEECH_FINISHED:
os.system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic GOOGLEASSISTANT_ON_RECOGNIZING_SPEECH_FINISHED")
if event.type == EventType.ON_RESPONDING_STARTED:
os.system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic GOOGLEASSISTANT_ON_RESPONDING_STARTED")
if event.type == EventType.ON_CONVERSATION_TURN_FINISHED or event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT:
os.system("/home/pi/Assistants/third-party/ReSpeakerLED/ReSpeakerLED -D 2mic LED_TURN_OFF")
最終的な動作
こんな感じです。
2-mic(動画)
4-mic(動画)
ReSpeakerLED.cでSPI送信しているデータを書き換えると、色や点灯箇所を変えられるので、色々変えてみても面白いかもしれません。
両アシスタントのサービス化
ここまで来たので、両アシスタントの常駐までやってしまいましょうか。
私のリポジトリにインストール(InstallService.sh)とアンインストール(InstallService.sh)のスクリプトを用意しておきましたので、よろしければどうぞ(実行するときはsudoをつけてください)。
sudo bash ./InstallService.sh
サービススクリプトを/lib/systemd/system/にコピーしてsystemctlで登録しているだけです。
これで、Raspberry Pi起動時に自動的に両アシスタントが起動してくれるはずです。
終わったらrebootして、コマンドラインから明示的に起動しなくても、両アシスタントが反応することを確認してみてください。
満足
だいぶ見栄えのよいRaspberry Pi Assistantができたかなと。
見てくれやマイクなどはこれで十分なので、ここから先は機能を増やしていきたいところです。
おわりに
- 生活に役立ちそう(自分の利益になりそう)で、
- 誰もやったことがなさそうで、
- 比較的注目されていて、
- 導入したい人が多めな事柄
に取り組むのはやりがいを感じますね。
あと、この辺の技術は基本英語ベースなのと、海外の方が取り組みが進んでいることもあって、日本語ではなく英語で発信していくと、海外の人も興味を持ってくれるようでした 5。
英語によるアウトプットを鍛える機会を作る手段として、技術を英語でアウトプットするという手がある、という点に気づかされたのは良い学びでした。
関わっている仕事の特性上、GitHubとかもほとんど使わない人だったんですが、この機会に動かせたのもよかったです。
それでは、皆様もRaspberry Piと、スマートスピーカーを引き続きお楽しみください。
おまけ
今回、Alexa構築にはSnowboyを使っているので、実はAlexaに対して任意のwake wordを設定することができます。
wake word作成方法
作成手順は既に載っていましたのでご紹介。
ここの"hotwordの作り方"で、任意の言葉を録音し、.pmdlファイルとしてダウンロードします。
AVS Sample Appでの設定
Sample Appではsnowboy/resources/alexa.umdlをwake wordとして読み込むようになっています。これを、先ほどダウンロードしたpmdlを読み込むようにすればいいわけです。
方法は2つあります。
- alexa.umdlを上書きする。
- snowboy/resources/alexa.umdlをバックアップしてから、先ほどダウンロードしたpmdlファイルを"alexa.umdl"という名前にして置いておきます。
- SampleAppのumdl設定個所を書き換えてリビルド。
#if defined(KWD_KITTAI)
m_keywordDetector = alexaClientSDK::kwd::KittAiKeyWordDetector::create(
sharedDataStream,
compatibleAudioFormat,
{keywordObserver},
std::unordered_set<
std::shared_ptr<alexaClientSDK::avsCommon::sdkInterfaces::KeyWordDetectorStateObserverInterface>>(),
pathToInputFolder + "/common.res",
// {{pathToInputFolder + "/alexa.umdl", "ALEXA", KITT_AI_SENSITIVITY}},
{{pathToInputFolder + "/heyamazon.pmdl", "ALEXA", KITT_AI_SENSITIVITY}},
KITT_AI_AUDIO_GAIN,
KITT_AI_APPLY_FRONT_END_PROCESSING);
alexa.umdlを上書きしたほうが楽かもしれませんが、お好みで。
好みのwordにするという目的のほか、(自分だけで使うのであれば)Alexaで誤検知が多い場合に試すといいかもしれません。