Edited at

AlexaとGoogle Assistantの両方が召喚されているRaspberry Pi

More than 1 year has passed since last update.

 それほどディープでもない、単なる『やってみた系』のネタなんですが、カレンダーに空きができたので埋めてみようかと思いつつ、前回よりもう少し面白いものができたので、せっかくだからお話してみようかなと。


(2018/2/11追記)

 Alexa、Google Assistantの並行動作の方法を探しにいらした方は、新しくこんなものを書いているので、こちらもどうぞ。

ReSpeakerと共に働くAlexaとGoogle Assistant

https://qiita.com/Dimeiza/items/2d9d17f94145bd6fa520


前回までのあらすじ

 いつまでたってもAmazonから招待メールがやってこないのに業を煮やしたあるプログラマが、一刻も早くAlexaを手中に収めるべくWebを徘徊し、3種類の召喚方法を試した挙句、とうとう日本語AlexaをRaspberry Piに常駐させることに成功したのだった。

https://qiita.com/Dimeiza/private/182c4847d7c1ead7df54


はじめに

 Amazon Echoの人気もさながら、こんなものも耳に入りまして。

https://aiyprojects.withgoogle.com/voice

 こういうの見せられると、作ってみたくなるじゃないですか。ところがどっこい、手に入れようとしても大人気ですぐには手元に来ないと。

 が、よくよく考えると、一応エンベデッド界隈の末席にいて、手元にRaspberry Piもあることだし、


  • これは自分でGoogle Assistantを召喚してみたほうが面白いのでは?

 と思い立ち、純然たる好奇心ドリブンで、Raspberry PiにGoogle Assistantを召喚してみることにしました。


そもそも

 Google AIY Voice KitはRaspberry Pi3の準備が前提なのだから、Raspberry Piがあれば動作するのは当たり前ですよね、と。

 結局キットの内容は、I2S hatとスピーカー、箱ぐらいなもの。ということは、googleのことだから、ソフトは別途配布しているよね、と。

https://aiyprojects.withgoogle.com/voice#assembly-guide-1-get-the-voice-kit-sd-image

 配布しているのは結局のところ、初期設定をある程度済ませたRaspbianに過ぎません。

 おそらくポイントとなるのはサウンドの入出力切り替えだろうなぁ、と当たりをつけながら先行事例を探してみたら、果たしてありました。

http://intellectualcuriosity.hatenablog.com/entry/2017/06/18/030506

 この通りにやって、果たして動作しました。

 ただ、当時とはフォルダ構成とファイル名が若干変わっているので、多少読み替えが必要です。といっても、Voice Kitの動かし方はVoice Kitの公式に書いてあるので、ポイントとなるのは『オーディオ設定』のところですね。


  • /boot/config.txt


    • dtparam=audio=onの追記

    • dtoverlay=i2s-mmapの削除

    • dtoverlay=googlevoicehat-soundcardの削除



  • /etc/asound.confの変更

 この2つを行ったうえで、Voice Kit公式の『6 Verify it works』を始めると、 Check Audioで音声の再生と録音ができることを確認できるはずです。

 そこから先は公式の手順通りです。


Alexaとの並行動作

 ここで野心がムラムラっと湧いてきまして。


  • 今Alexaが動いているRaspberry Piに、Google Assistantを入れて、『同時に』動かせないだろうか?

 ボイスアシスタント毎にRaspberry Piを用意する、というのはコストが倍になりますし、電源だの配置だのとハンドリングも面倒くさい。ミニマリズム志向の私にとってはフットプリントが2倍になるというのも気になります。


AIY Kit相当の内容をRaspberry Piに持ってくる

 AIYイメージの中で動かしているpythonスクリプトは、基本的に/home/pi配下に入っているので、これをコピーしてもいいかなぁと、フォルダの圧縮を進めながら調べ物をしていたら、そもそもこんなものが配布されていたのでした。

https://github.com/google/aiyprojects-raspbian

 ここの、hacking.mdを参照すると、既存のRaspberry Piへのインストール方法が書かれているので、基本そのままやっていけば入るには入りそうです。

 ただ、現在Alexaが動いているサウンド環境を壊すわけにはいかないので、『Configuring the Voice HAT driver』の部分を除いて実施しました。


並行動作への壁

 ところがですね。このままでは実は並行動作しないのです。

サウンドエラー画面.png

 Alexa、Google Assistantの両方とも、起動時にLinuxのサウンドデバイスを占有してしまうんですね。なので、単純に2個起動しただけでは、先に起動した方にサウンドデバイスを握られてしまい、他方は起動できないということになります。


AssistantPi

 さてどうしたものかと思っていたら、やはり同じことを考えている人はいるんだなぁと。

https://github.com/xtools-at/AssistantPi

 このAssistant Piは、前回の記事でお話ししたAlexaPiとGoogle Assistant SDKを共存させるものらしいです。

 これを使って私の目的達成か? と思いきや、前回自分で書いたことを思い出すわけです。


ただ、このAlexaPiだとLocaleの変更ができないみたいなんですよね(APIのバージョンが古いという書き込みを見かけました)。


 そう、日本語が使えないのです。英語圏の人間ならこれでゴールなのですが、残念。


サウンドの設定

 この辺からLinuxのサウンドについて色々調べ始めました。

 結局やりたいことは、AlexaとGoogle Assistant両方を共存させるということで、そのためにはサウンドデバイスを占有ではなく共有できないといけない、と。

 ミキサーのようなものはないのかな? と疑問に思ったところで、一つの答えを見つけました。

http://wikiwiki.jp/tetsuya/?asoundrc

 ALSAでは入力に対してdmix、出力に対してdsnoopというミキシングの機構があるらしく。

 AlexaとGoogle Assistantの入出力を、両方ともここにつなぐように設定したら万事うまくいくのでは? ということで、こんな感じで.asoundrcを書いてみました。

pcm.!default {

type asym
playback.pcm {
type plug
slave.pcm "dmixer"
}
capture.pcm {
type plug
slave.pcm "dsnooper"
}
}

pcm.dmixer {
type dmix
ipc_key 1024
slave {
pcm "hw:1,0"
period_time 0
period_size 1024
buffer_size 4096
rate 44100
}
bindings {
0 0
1 1
}
}
pcm.dsnooper {
type dsnoop
ipc_key 816357492
ipc_key_add_uid 0
ipc_perm 0666
slave {
pcm "hw:1,0"
channels 1
}
}

 まぁ、正直なところLinuxサウンド歴数日の私には、複雑怪奇過ぎて定評のあるLinuxのサウンド環境のことはよくわかっていないのですが。

 趣旨としては、Alexa、Google Assistantいずれの公式でもハードウェア直接指定だった箇所(hw:1,0とか)を、ミキサーに変更しつつ、ミキサーからハードウェアを指定する形で間接的な入出力にしている、というところです。

 この状態で両アプリを動かしてみたわけです。

並行動作画面.png

 というわけで、Alexa、Google Assitant(英語)両対応のRaspberry Piが、ここに爆誕してしまいました。


もうちょっと良い方法

 後で動作確認しましたが、PulseAudioを使ったほうがいいかもしれません。

https://www.freedesktop.org/wiki/Software/PulseAudio/

 PulseAudioはOSのサウンドデバイスを仮想化し、アプリとサウンドデバイスへの仲立ちをよろしく交通整理してくれるサウンドサーバなんですが、Raspbian jessieには入っていたものの、Stretchには入っていなかったりします。

 なので、

apt-get install pulseaudio

 としてpulseaudioをインストールした後で、.asoundrcをこう書くという手もあります。


/home/pi/.asoundrc

pcm.pulse {

type pulse
}

ctl.pulse {
type pulse
}

pcm.!default {
type pulse
}
ctl.!default {
type pulse
}


 軽く動かしてみた感じ、これまで出ていたエラーが抑止されたりしているようなので、こっちの設定のほうが良いかもしれません。


格好をつける

 しばらく両Assistantと遊んでいたんですが、Assistantの反応が非対称なのが気になって。

 Google AssistanはLEDでListeningを示しますが、Alexaは音で示すようになっている、と。

 せっかくだから両方ともLEDと音でListeningを示すようにしたら、どちらが反応しているのか視覚的にも聴覚的にもわかって良さげです。

 さらに言うと、実はGoogle Assistantにこう聞いてみたんですよ。そしたらこう返してきましてね。

Q. What do you think of Alexa?1

A. I like Alexa’s "cool blue light!" Plus, we share an affinity for Star Wars!

 まぁ確かにねぇ、と。

 じゃあ青くしてやろうかと、秋月で青色のLEDを購入した後で、例によってローテクノロジーで両者の振る舞いを揃えます。


Alexa側ではGPIO制御

信頼と実績のWireingPi(というよりGPIOコマンド)を使います。

http://wiringpi.com/


/home/pi/sdk-folder/sdk-source/avs-device-sk/SampleApp/src/UIManager.cpp

            case DialogUXState::IDLE:

system("gpio -g mode 23 out");
system("gpio -g write 23 0");
ConsolePrinter::prettyPrint("Alexa is currently idle!");
return;
case DialogUXState::LISTENING:
system("gpio -g write 23 1");
system("paplay /home/pi/sdk-folder/third-party/snowboy/resources/dong.wav 1>/dev/null 2>/dev/null ");
ConsolePrinter::prettyPrint("Listening...");

 思いっきりやっつけコードです。お時間のある方は適切なコードに置き換えて実施ください。

 ここでは見ての通り、GPIO 23を出力先として、Listening時に点灯し、Idleになったら消灯する、というシンプルなコントロールにしています。


Google Assitant側ではWake up時の音を追加

 こっちには最初からそれっぽい関数があったので、鳴らしたい音のwavファイル名を指定して、冒頭で呼び出してやります。


/home/pi/AIY-projects-python/src/assistant_library_demo.py

def process_event(event):

status_ui = aiy.voicehat.get_status_ui()
status_ui.set_trigger_sound_wave("/home/pi/sdk-folder/third-party/snowboy/resources/ding.wav")


動かしてみる

 こんな具合でございます(以下動画)。

AlexaとGoogle Assistantの両方が召喚されているRaspberry Pi

 Google AssitantのLEDは、ちょっと動かした感じだと作法通りに直すのが難しそうだったので、ダーティハックして強制的にLEDを消灯させたりしています。2


注意

 いろいろとやっつけなので過剰な期待は禁物です。

 排他制御してないので、一方との会話中に他方のWake wordを言うと、もう一方が起きたりしてしまいます。

 なので、正式な製品が届くまでのつなぎとか、玩具とか、そういったレベルです。

 あ、それと、書く時間があまりなかったので舌っ足らずなところがあるかもしれません。不明点等ありましたら適宜お知らせください。

 私も気づいた点があれば適宜追加します。


再び満足

 1枚のRaspberry Piで2倍美味しくなりました。これで招待メール到着まで戦える!

 ここまでやってしまったら買わなくてもいいんじゃないか、と思ったりもしましたが、公式のデバイス以外だとサービスに制約がついたりするので、悩みどころですね。

 あと、さすがにブレッドボード上のLEDはどうかと思うので、もう少し見栄えの良いものにしたいところです。


おわりに

 Alexaの召喚からここに至るまで、非常に楽しませていただきました。

 声で操作する夢の時代の到来を実感したのもさながら、巨人の肩に乗っているとはいえ、これぐらいの手数で、自前で手元にこの技術を呼び出せるというのは大したものだなと。

 海外のエンジニアたちの貢献の賜物でもありますが、巨人たちが提供する、新技術へのアクセシビリティの良さには驚嘆すべきものがあります。

 新しい技術を動かすことにワクワクするという、久々の体験とともに、これらの技術にどう対峙していくかを考えさせられた、とても有意義なひと時になりました。

 それでは、皆様もRaspberry Piと、スマートスピーカーをお楽しみください。

(12/24追記)


最終回じゃないぞよ

 もうちっとだけ続くんじゃ。

https://qiita.com/Dimeiza/items/4aafa4cde13b58d3456f





  1. Alexaが止まっているときに聞いています。上述の通り排他制御をしていないので、自分の話をされると聞き耳を立てるんですよ、彼女。 



  2. /home/pi/AIY-projects-python/src/AIY/drivers/_status_ui.pyのLED状態設定を変えればできるはずなのですが、OFFを指定しても消灯してくれなくてですね…。OFFの状態が反映されていない疑惑を覚えたので、led.pyを変更して、_animate中に強制的にDutyCycleを0にするように手を入れています。