Voice Kit を Android Things で動かしてみる

f:id:akanuma-hiroaki:20180308074945p:plain:right

 Voice Kit は標準では Raspbian で動かしますが、 Android Things を使うこともできるということなので、試しに動かしてみました。 Voice Kit のページから Android Things のチュートリアルへのリンクが張られています。

*1


aiyprojects.withgoogle.com

 Android Things とはスマートフォンだけでなく IoT デバイスでも Android が動くように最適化されたもので、全てではないですが Android の API や SDK を使うことができます。逆に Android Things の API には各種センサーからデータを取得したり、ハードウェアを操作したりするための API が追加されていますので、 Android アプリ開発ができる方であれば同じスキルで IoT デバイスの開発ができそうです。ちなみに Android Things は現在まだ Developer Preview です。

developer.android.com

 Voice Kit を Android Things で動かすチュートリアルは下記で公開されています。

Android Things Assistant

Android Studio をインストール/アップデート

 Android アプリ開発には Android Studio を使いますが、 Android Things でも同様に Android Studio を使いますのでインストールします。 Android Things には Android Studio の 3.0 以上が必要なので、古いバージョンを使っている場合はアップデートします。私は 2系 だったのでアップデートしました。

developer.android.com

Android SDK をアップデート

 SDK も古い場合にはアップデートが必要です。 SDK Tools は 25.0.3 以上、 SDK Platforms は Android 7.0(API24)以上が必要となりますので、 SDK Manager でアップデートします。

Update the IDE and SDK Tools | Android Studio

f:id:akanuma-hiroaki:20180306081242p:plain

f:id:akanuma-hiroaki:20180306081303p:plain

Android Things イメージの書き込み

 次に Android Things のイメージを microSDカードに書き込みます。 手順は下記にも公開されています。

Raspberry Pi 3 | Android Things

 Android Things ではイメージのダウンロードや書き込みを行ってくれるセットアップツールが用意されているので、まずはそちらをダウンロードするために Android Things コンソールにアクセスします。初めてアクセスした時は規約への同意を求められるので、チェックボックスにチェックを入れて CONTINUE をクリックします。

f:id:akanuma-hiroaki:20180306081903p:plain:w450

 コンソールが表示されたら左上のメニューから Tools をクリックします。

f:id:akanuma-hiroaki:20180306081918p:plain:w450

 Setup Utility の説明が表示されますので、 DOWNLOAD ボタンをクリックしてツールをダウンロードします。

f:id:akanuma-hiroaki:20180306081933p:plain:w450

 ダウンロードしたツールを解凍すると、下記のように各プラットフォーム向けのツールが用意されています。

$ ls -l Downloads/android-things-setup-utility/
total 40024
-rw-r--r--@ 1 akanuma  staff     1721 Jan 26 03:55 README.md
-rwxr-xr-x@ 1 akanuma  staff  6339237 Jan 26 03:54 android-things-setup-utility-linux
-rwxr-xr-x@ 1 akanuma  staff  7687540 Jan 26 03:54 android-things-setup-utility-macos
-rwxr-xr-x@ 1 akanuma  staff  6456832 Jan 26 03:54 android-things-setup-utility-windows.exe

 Macの場合は下記のようにファイル名末尾が macos になっているものを実行します。

$ sudo ~/Downloads/android-things-setup-utility/android-things-setup-utility-macos 

Android Things Setup Utility (version 1.0.18)
============================
This tool will help you install Android Things on your board and set up Wi-Fi.

 対話形式で設定を選択していきます。まずは新たに Android Things をインストールするか、既存の Android Things デバイスの Wi-Fi をセットアップするか聞かれるので、前者(1)を選択します。

What do you want to do?
1 - Install Android Things and optionally set up Wi-Fi
2 - Set up Wi-Fi on an existing Android Things device
1

 次にハードウェアの選択です。 Voice Kit の場合は Raspberry Pi なので、1 の Raspberry Pi 3 を選択します。すると必要なツールのダウンロードと解凍が行われます。

What hardware are you using?
1 - Raspberry Pi 3
2 - NXP Pico i.MX7D
3 - NXP Pico i.MX6UL
1
You chose Raspberry Pi 3.

Setting up required tools...
Fetching additional configuration...
Downloading platform tools...
5.45 MB/5.45 MB
Unzipping platform tools...
Finished setting up required tools.

 次にデフォルトのイメージかカスタムイメージを使うか聞かれます。初めて使う場合はデフォルトイメージを選択します。するとイメージのダウンロードと解凍が行われ、さらに SD カードにイメージを書き込むためのツールのダウンロードと解凍が行われます。

Do you want to use the default image or a custom image?
1 - Default image: Used for development purposes. No access to the Android
Things Console features such as metrics, crash reports, and OTA updates.
2 - Custom image: Provide your own image, enter the path to an image generated
and from the Android Things Console.
1
Downloading Android Things image...
274 MB/274 MB 
Unzipping image...

Downloading Etcher-cli, a tool to flash your SD card...
20.5 MB/20.5 MB
Unzipping Etcher-cli...

 ここまででイメージの準備ができました。SDカードを Mac のSDカードスロットに入れて Enter キーを押すように促されますので、イメージを書き込む microSD カードをスロットに入れて Enter を押します。

Plug the SD card into your computer. Press [Enter] when ready

Running Etcher-cli...

 ドライブの選択プロンプトが表示されます。複数の SD カードスロットがある場合は複数のドライブが表示されるかと思いますが、一つの場合はそのまま Enter を押します。

? Select drive /dev/disk2 (32.0 GB) - SD Card Reader

 選択したドライブの SD カードの内容は消去されますので、問題なければ Yes を入力して Enter します。すると実際に書き込みが行われます。

? This will erase the selected drive. Are you sure? Yes
Flashing [========================] 100% eta 0s  
Validating [========================] 100% eta 0s  
iot_rpi3.img was successfully written to SD Card Reader (/dev/disk2)
Checksum: c1891a15

If you have successfully installed Android Things on your SD card, you can now
put the SD card into the Raspberry Pi and power it up. Otherwise you can abort
and run the tool again.

 書き込みが終わると、続けて Wi-Fi のセットアップを行うか聞かれます。行う場合は y を選択します。

Would you like to set up Wi-Fi on this device? (y/n) y

 ここまで来たら Mac から microSD カードを抜いて Voice Kit に挿し、 LAN ケーブルで LAN に接続して起動します。起動したら Enter を押すと、 Mac から LAN 内の Android Things(Voice Kit)に接続し、 Wi-Fi のセットアップを続行します。

Please plug your Raspberry Pi to your router with an Ethernet cable, then press [Enter].


Attempting to connect to your Raspberry Pi at Android.local...
Connected to device through Ethernet.

 正しく接続されると接続先APの SSID とパスワードを聞かれるので入力します。Wi-Fi セットアップが成功すれば一通りセットアップは完了ですので、 Enter を押してセットアップツールを終了します。

Enter the Wi-Fi network name: xxxxxxxxxxxxxx
Enter the Wi-Fi network password (leave empty if no password): 
Connecting to Wi-Fi network xxxxxxxxxxxxxx...
Looking for device... This can take up to 3 minutes.
Device found.
Waiting...
Successfully connected to Wifi
Stopping adb server...
Stopped adb server...

Now that you’re set up, try sample projects in Android Studio or in the sample
repository here: https://developer.android.com/things/sdk/samples.html

To learn more about features like over-the-air updates, visit the Android Things
Console: https://partner.android.com/things/console

Press [Enter] to quit.

 セットアップが終了したら一度 Voice Kit を再起動しておくのが良いかと思います。

adb での接続

 adb で Voice Kit に接続します。 IP アドレス指定でも接続できますが、マルチキャストDNSで Android.local というホスト名でも接続することができるようになっています。

$ adb connect Android.local
connected to Android.local:5555

サンプルコードの取得

 チュートリアルではサンプルコードが提供されていますので、今回はとりあえずそれを動かして見ます。 github から git clone で取得します。

$ git clone https://github.com/googlecodelabs/androidthings-googleassistant.git
Cloning into 'androidthings-googleassistant'...
remote: Counting objects: 338, done.
remote: Total 338 (delta 0), reused 0 (delta 0), pack-reused 338
Receiving objects: 100% (338/338), 117.37 KiB | 0 bytes/s, done.
Resolving deltas: 100% (101/101), done.
Checking connectivity... done.

認証情報の取得

 Google Assistant の API を使うためには API の有効化と認証情報の取得が必要になります。チュートリアル内でも紹介されていますが、私が最初に Voice Kit を試した時のサンプルコード実行時にも同様のことを行なっていますのでご参照ください。 assistant.json というファイル名で認証情報を保存しておきます。

blog.akanumahiroaki.com

認証実行

 まずは認証用のツールを pip でインストールします。

$ pip install google-auth-oauthlib[tool]

 続いてツールを使って認証を行います。 --client-secrets オプションには先ほど保存した認証情報のファイルパスを指定します。

$ google-oauthlib-tool --client-secrets assistant.json \
>                        --credentials shared/src/main/res/raw/credentials.json \
>                        --scope https://www.googleapis.com/auth/assistant-sdk-prototype \
>                        --save --headless
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=XXXXXXXXXXXX-v9m9nb4bgdp58qk159cghm1ketnvin5t.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fassistant-sdk-prototype&state=QBeuos2rtESJnJ9TEjX6sp1xaAFKaV&prompt=consent&access_type=offline

 表示されたURLにブラウザからアクセスし、表示される認証コードをコピーして入力します。正しく認証されれば credentials.json というファイルが作成されます。

Enter the authorization code: 4/AAAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
credentials saved: shared/src/main/res/raw/credentials.json

サンプルコードの実行

 git clone したサンプルコード androidthings-googleassistant を Android Studio にインポートします。ライブラリの不足がなければ下記のように Gradle でのビルドが行われます。

21:20    Gradle sync started
21:21   Project setup started
21:21   Gradle sync finished in 45s 764ms
21:21   Executing tasks: [:shared:generateDebugSources, :shared:generateDebugAndroidTestSources, :shared:mockableAndroidJar, :step1-start-here:generateDebugSources, :step1-start-here:generateDebugAndroidTestSources, :step1-start-here:mockableAndroidJar, :step2-volume-control:generateDebugSources, :step2-volume-control:generateDebugAndroidTestSources, :step2-volume-control:mockableAndroidJar, :step3-conversational-state:generateDebugSources, :step3-conversational-state:generateDebugAndroidTestSources, :step3-conversational-state:mockableAndroidJar]
21:21   Gradle build finished in 22s 828ms

 ビルドが終わったらサンプルコードを実行してみます。 step1, 2, 3 が用意されていますが、今回はとりあえず step1 を実行してみます。 step1 は Google Assitant の基本的なデモです。 Android Studio の Run メニューから Run 'step1-start-here' を選択します。選択肢にないときは Run... から選択します。

f:id:akanuma-hiroaki:20180307083756p:plain:w300

 対象のデバイスとして Voice Kit を選択します。デバイス名は Google Iot_rpi3 となっています。

f:id:akanuma-hiroaki:20180307083809p:plain

 私の場合は初回実行時は下記のようにエラーになってしまいました。

03/04 21:32:50: Launching step1-start-here
$ adb shell am start -n "com.example.androidthings.assistant/com.example.androidthings.assistant.AssistantActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Connected to process 4115 on device google-iot_rpi3-Android.local:5555
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/InstantRun: starting instant run server: is main process
I/AssistantActivity: starting assistant demo
D/AssistantActivity: enumerating devices
I/AssistantActivity: initializing DAC trigger
I/AssistantActivity: setting volume to: 15
D/AudioTrack: Client defaulted notificationFrames to 460 for frameCount 1380
E/AudioRecord: AudioFlinger could not create record track, status: -1
E/AudioRecord-JNI: Error creating AudioRecord instance: initialization check failed with status -1.
E/android.media.AudioRecord: Error code -20 when initializing native AudioRecord object.
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.example.androidthings.assistant, PID: 4115
                  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.androidthings.assistant/com.example.androidthings.assistant.AssistantActivity}: java.lang.UnsupportedOperationException: Cannot create AudioRecord
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
                      at android.app.ActivityThread.-wrap11(Unknown Source:0)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
                      at android.os.Handler.dispatchMessage(Handler.java:106)
                      at android.os.Looper.loop(Looper.java:164)
                      at android.app.ActivityThread.main(ActivityThread.java:6494)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
                   Caused by: java.lang.UnsupportedOperationException: Cannot create AudioRecord
                      at android.media.AudioRecord$Builder.build(AudioRecord.java:626)
                      at com.example.androidthings.assistant.AssistantActivity.onCreate(AssistantActivity.java:342)
                      at android.app.Activity.performCreate(Activity.java:7000)
                      at android.app.Activity.performCreate(Activity.java:6991)
                      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856) 
                      at android.app.ActivityThread.-wrap11(Unknown Source:0) 
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) 
                      at android.os.Handler.dispatchMessage(Handler.java:106) 
                      at android.os.Looper.loop(Looper.java:164) 
                      at android.app.ActivityThread.main(ActivityThread.java:6494) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 
I/Process: Sending signal. PID: 4115 SIG: 9
Application terminated.

 少し調べたところ、セットアップ後は一度 Voice Kit を再起動する必要があるようなので、再起動後に再度実行すると下記のようにエラーなく実行できました。

03/04 21:37:33: Launching step1-start-here
$ adb install-multiple -r -t /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/dep/dependencies.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_1.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_7.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_6.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_8.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_9.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_4.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_5.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_0.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_3.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/intermediates/split-apk/debug/slices/slice_2.apk /Users/akanuma/workspace/androidthings-googleassistant/step1-start-here/build/outputs/apk/debug/step1-start-here-debug.apk 
Split APKs installed
$ adb shell am start -n "com.example.androidthings.assistant/com.example.androidthings.assistant.AssistantActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Waiting for process to come online
Connected to process 1366 on device google-iot_rpi3-Android.local:5555
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
I/AssistantActivity: starting assistant demo
D/AssistantActivity: enumerating devices
I/AssistantActivity: initializing DAC trigger
I/AssistantActivity: setting volume to: 15
D/AudioTrack: Client defaulted notificationFrames to 460 for frameCount 1380
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
D/vndksupport: Loading /vendor/lib/hw/android.hardware.graphics.mapper@2.0-impl.so from current namespace instead of sphal namespace.

 ここまで行けば Google Assistant を試すことができるようになっています。このサンプルでは Voice Kit のボタンを押している間は発話を受け付けてくれますので、ボタンを押したまま "What time is it now?" などと話してボタンを離すと、 Google Assistant が回答してくれます。

まとめ

 今回はとりあえずサンプルコードを一つ動かしてみただけですが、最初のセットアップさえ終わってしまえばコードを実行するときもスマートフォンアプリを開発するときと同じように Android Studio から動かすことができますので、 Android アプリ開発が得意な人には良さそうです。実際に作り込んでいった時にどこまで API が対応しているのかはまだわかってませんが、今後正式版になれば良い選択肢の一つになるかもしれません。

*1:Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、Creative Commons 3.0 Attribution ライセンスに記載された条件に従って使用しています。