Raspberry Pi 3 でAlexaと対話する


[re:Invent 2017](https://reinvent.awsevents.com/)で[AlexaをRaspberryPi経由で利用する方法](Integrate Alexa into Your Product Using the AVS Device SDK)のワークショップに参加した。

ワークショップでは、構成済みのMicroSDを使って、単純にコマンドを流したり、支持されたようにファイルを書き換えたりしただけだったので、まっさらなMicroSDの状態から同様に動くようになるまでをやってみたいと思う。



# 用意するもの

[Raspberry Pi 3 Model B](https://www.canakit.com/raspberry-pi-3-model-b.html)
[USBマイク](https://www.adafruit.com/product/3367) < 多分これ
イヤホン
USBキーボード
USBマウス
HDMIケーブル
ディスプレイ
MicroSD Class10 16G

# 事前準備(Macでの作業)

## インストーラーのダウンロード

[こちら](https://www.raspberrypi.org/downloads/noobs/)からNOOBSをダウンロードする
今回ダウンロードしたバージョンは v2.4.5

## MicroSDにコピーする

ダウンロードした`NOOBS_v2_4_5.zip`を解凍しFAT32でフォーマット済みのMicroSDにファイルをコピーする。

```bash:コマンド例
cp -a Downloads/NOOBS_v2_4_5/* /Volumes/UNTITLED
```

# Raspbianのインストール

先ほどのMicroSDをRaspberry Piに刺して電源を入れるとインストーラーが立ち上がるので、`Raspbian [RECOMMENDED]`にチェックを入れて、左上のInstallをクリックしてインストールすればOK

<div>
<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/a4072392-ebed-d79b-3b61-5a0478161dc3.png" width=320>
<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/89b60fbd-2668-ea0f-b1be-0af5ff240608.png" width=320>
<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/e24e8ba1-53fa-b654-d7f9-dedfec9f4f50.png" width=320>
</div>

# 事前準備(Raspberry Piでの準備)

## 一応OSのバージョンチェック

こんな記載があるので、Raspbian Stretchかどうか一応チェック

> This guide provides step-by-step instructions to set up the Alexa Voice Service (AVS) Device SDK on a Raspberry Pi running Raspbian Stretch with Desktop

```bash
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
```

## ネットワークに接続

有線LANもあるので有線でつなげる場合には、何もしなくて大丈夫
Wi-Fiを利用する場合には右上にマークがあるのでそこから接続設定する

## SSH/VNCの有効化

有効化しなくてもOKだけど、リモートから行える作業はできるだけリモートで行うと楽なので有効化しておく

<div>
<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/832b5e9e-0f41-1d52-3d6a-359f15ba0f32.png" width=420>
<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/7111c0a4-4345-281b-3272-e326195ad5a7.png" width=420>
</div>

# ビルド環境の構築＋ビルド

基本的にはこちらにある通りに行う
https://github.com/alexa/avs-device-sdk

## 依存関係のあるライブラリ、AVS Device SDK, Sensory wake word engine のインストール

Sensory wake word engineはオープンソースは非商用限定のライセンスなので注意すること

```bash
cd /home/pi/
mkdir sdk-folder
cd sdk-folder
mkdir sdk-build sdk-source third-party application-necessities
cd application-necessities
mkdir sound-files

sudo apt-get update

sudo apt-get -y 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

cd /home/pi/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

pip install commentjson

cd /home/pi/sdk-folder/sdk-source
git clone git://github.com/alexa/avs-device-sdk.git

cd /home/pi/sdk-folder/third-party
git clone git://github.com/Sensory/alexa-rpi.git
cd ./alexa-rpi/bin/
./license.sh
```

### ビルド

make のオプションは -j4までいけるけど、オーバーヒートに注意しましょう（と書いてある）

```bash
cd /home/pi/sdk-folder/sdk-build
cmake /home/pi/sdk-folder/sdk-source/avs-device-sdk \
  -DSENSORY_KEY_WORD_DETECTOR=ON \
  -DSENSORY_KEY_WORD_DETECTOR_LIB_PATH=/home/pi/sdk-folder/third-party/alexa-rpi/lib/libsnsr.a \
  -DSENSORY_KEY_WORD_DETECTOR_INCLUDE_DIR=/home/pi/sdk-folder/third-party/alexa-rpi/include \
  -DGSTREAMER_MEDIA_PLAYER=ON \
  -DPORTAUDIO=ON \
  -DPORTAUDIO_LIB_PATH=/home/pi/sdk-folder/third-party/portaudio/lib/.libs/libportaudio.a \
  -DPORTAUDIO_INCLUDE_DIR=/home/pi/sdk-folder/third-party/portaudio/include
make SampleApp -j2
```

# Alexa Voice Serviceへの登録

先ほどビルドしたSampleAppからAlexa Voice Serviceへ接続するためには設定を作る必要があるので作成する。

## Amazon Developerに登録

[こちら](https://developer.amazon.com/edw/home.html)からログインする
日本でもEchoが発売されたのでamazon.co.jpアカウントでもログインできるらしい

## Alexa Voice Service に製品を登録する

!!! 一度作成した設定は削除できないようなので注意 !!!

### Alexa Voice Service　を選ぶ

<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/266991b7-bcd8-0502-1e6e-30fed78c3ab6.png" width=480>

### CREATE PRODUCTを選ぶ

<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/abcf9bca-2012-d927-28b3-d9c26c8464aa.png" width=480>

### プロダクト情報を入力する

Product Name: 任意
Product ID: 任意（あとで使うのでメモっておくこと)
Is your product an app or device?: Device
Will your device use -----: 任意(Deviceを選ぶと出てくる)
Product category: 任意
Brief product description: 任意
How will end users -----: Hands-free
Upload an image: 任意
Do　you intend to -----: No
Is this a children's -----: No

<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/5e25e977-0788-6718-620b-ee404415259d.png" width=480>

### セキュリティプロファイルの設定を作る

CREATE NEW PROFILEを選択すると、下にプロファイル名と説明を入れる欄が出るので、入力する。

<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/dd865028-c984-8181-b3f5-1db51861bac1.png" width=480>

### URLの登録

次で使うので、Client ID, Client Secretをメモに取る
`http://localhost:3000`を入力してADDボタンを押す
同様にAllowed return URLsには`http://localhost:3000/authresponse`を入力してADDボタンを押す

<img src="https://qiita-image-store.s3.amazonaws.com/0/45645/27814402-25d6-4096-a523-0e411389c4e2.png" width=480>


# アプリケーション側に設定を反映

## その前にvimのインストール

nano, edとかは入ってるけど辛いのでvimをインストール

```bash
sudo apt-get install -y vim
```

## 設定ファイルの書き換え

YOUR_CLIENT_SECRET, YOUR_CLIENT_ID, YOUR_PRODUCT_IDを先ほどメモったものに書き換えて以下を実行する

```bash
cat <<EOF >/home/pi/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.json
{
    "authDelegate":{
        "clientSecret":"YOUR_CLIENT_SECRET",
        "deviceSerialNumber":"123456",
        "refreshToken":"",
        "clientId":"YOUR_CLIENT_ID",
        "productId":"YOUR_PRODUCT_ID"
   },
   "alertsCapabilityAgent":{
        "databaseFilePath":"/home/pi/sdk-folder/application-necessities/alertsCapabilityAgent.db"
   },
   "settings":{
        "databaseFilePath":"/home/pi/sdk-folder/application-necessities/settings.db",
        "defaultAVSClientSettings":{
            "locale":"en-US"
        }
   },
   "certifiedSender":{
        "databaseFilePath":"/home/pi/sdk-folder/application-necessities/certifiedSender.db"
   },
   "sampleApp":{
       "displayCardsSupported":false
   }
}
EOF
```

## 音の設定

```bash
cat <<EOF >~/.asoundrc
pcm.!default {
  type asym
   playback.pcm {
     type plug
     slave.pcm "hw:0,0"
   }
   capture.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
}
EOF
```

## リフレッシュトークンの取得

先ほど書き換えず空のままだったrefreshTokenを取得する

まずは、下記コマンドを実行して認証用Webサーバーを立ち上げる

```
cd /home/pi/sdk-folder/sdk-build && python AuthServer/AuthServer.py
```

直接もしくはVNCを利用して、Raspberry Pi内のブラウザを立ち上げて http://localhost:3000/ にアクセスすると`Sign in to [セキュリティプロファイル名] using your Amazon account`のページにリダイレクトされるので、ログインする

ログインが完了すると立ち上げてWebサーバーにリダイレクトされて戻ってきて、`The file is written successfully.
Server is shutting down, so you can close this window.`という表示がされている

## サンプルアプリケーションの実行

```bash
$ cd /home/pi/sdk-folder/sdk-build/SampleApp/src
$ TZ=UTC ./SampleApp \
  /home/pi/sdk-folder/sdk-build/Integration/AlexaClientSDKConfig.json \
  /home/pi/sdk-folder/third-party/alexa-rpi/models
#############################
#       Connecting...       #
#############################

########################################
#       Alexa is currently idle!       #
########################################

///ここに大量のエラーが出ているけど、一旦動作には関係なさそうなので放置///

                  #    #     #  #####      #####  ######  #    #
                 # #   #     # #     #    #     # #     # #   #
                #   #  #     # #          #       #     # #  #
               #     # #     #  #####      #####  #     # ###
               #######  #   #        #          # #     # #  #
               #     #   # #   #     #    #     # #     # #   #
               #     #    #     #####      #####  ######  #    #

       #####                                           #
      #     #   ##   #    # #####  #      ######      # #   #####  #####
      #        #  #  ##  ## #    # #      #          #   #  #    # #    #
       #####  #    # # ## # #    # #      #####     #     # #    # #    #
            # ###### #    # #####  #      #         ####### #####  #####
      #     # #    # #    # #      #      #         #     # #      #
       #####  #    # #    # #      ###### ######    #     # #      #

+----------------------------------------------------------------------------+
|                                  Options:                                  |
| Wake word:                                                                 |
|       Simply say Alexa and begin your query.                               |
| Tap to talk:                                                               |
|       Press 't' and Enter followed by your query (no need for the 'Alexa').|
| Hold to talk:                                                              |
|       Press 'h' followed by Enter to simulate holding a button.            |
|       Then say your query (no need for the 'Alexa').                       |
|       Press 'h' followed by Enter to simulate releasing a button.          |
| Stop an interaction:                                                       |
|       Press 's' and Enter to stop an ongoing interaction.                  |
| Privacy mode (microphone off):                                             |
|       Press 'm' and Enter to turn on and off the microphone.               |
| Playback Controls:                                                         |
|       Press '1' for a 'PLAY' button press.                                 |
|       Press '2' for a 'PAUSE' button press.                                |
|       Press '3' for a 'NEXT' button press.                                 |
|       Press '4' for a 'PREVIOUS' button press.                             |
| Settings:                                                                  |
|       Press 'c' followed by Enter at any time to see the settings screen.  |
| Speaker Control:                                                           |
|       Press 'p' followed by Enter at any time to adjust speaker settings.  |
| Info:                                                                      |
|       Press 'i' followed by Enter at any time to see the help screen.      |
| Quit:                                                                      |
|       Press 'q' followed by Enter at any time to quit the application.     |
+----------------------------------------------------------------------------+
```

こんな感じで動いてとりあえずひと段落、`Alexa`と呼びかけるか、`t + Enter`でLinteningモードに入る
そうすると、以下のように、ステータスが遷移して、Speakingで話してくれるはず、、、はず、、、

```
############################
#       Listening...       #
############################

###########################
#       Thinking...       #
###########################

###########################
#       Speaking...       #
###########################

########################################
#       Alexa is currently idle!       #
########################################
```

# あれれ？

## 音が出ない

音が出ない何故？？？と思ったら、ディスプレイ側のピンジャックにイヤホンを繋げたら声が聞こえる
ということはHDMI側に信号が流れてしまっているみ？？？

インターフェイスの優先度的な問題かと思い、HDMIを抜いたり、刺したり、設定変えたりしたけれども解決の糸口が見えないのでとっても困ってます

例えば、以下を実行してもやっぱりHDMI経由で音が出力される・・・

```bash
$ amixer cset numid=3 1
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=0
```

上の状況でYouTubeを確認したところ、ピンジャックからの出力になっていたので、ここの設定はAlexaには関係なさそう
また、ワークショップで使ったMicroSDを刺して起動した場合には、しっかりとピンジャックから音が出力されるので何らかの設定ミスの線が濃厚

RaspberryPiの知見も、Linuxで音を出す的な知見も全くないので、お手上げ状態です
どなたかわかる方いらっしゃったら教えてもらえたら嬉しいです


## 音が切れる

もう一つの問題として、Alexaの話が途中で切れること`What's your name`ときくと`My name is Ale`という感じで途中で切れてしまう
これも設定で何とかできるんじゃないかと思っているけれども、今の所、どう調べたらいいものやらという感じで止まっている

音がピンジャックから出ない問題と同様に教えてもらえたら嬉しいです

助けてもらえるときのために、関連しそうな情報を末尾に載せておきます

# 今後のやりたいこと

単純にAlexaと声をかけて、声で操作するだけならEchoを使えばいいだけなので、すぐに思いつくようなことだけではあるけれども、以下のようなことを考えている

- Wake word engineが独立しているので、Alexa以外の言葉に反応させられたら、専用で作る意味が出てくるので、試したい
- Wake wordだけでなくコマンドから入力受付状態にできるので、受付システムとかできそうなので試したい

# 音の設定関連の情報

```bash
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 0: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

```bash
$ aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
default
sysdefault:CARD=ALSA
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device
dmix:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct sample mixing device
dmix:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct sample mixing device
dsnoop:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct sample snooping device
dsnoop:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct sample snooping device
hw:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Direct hardware device without any conversions
hw:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Direct hardware device without any conversions
plughw:CARD=ALSA,DEV=0
    bcm2835 ALSA, bcm2835 ALSA
    Hardware device with all software conversions
plughw:CARD=ALSA,DEV=1
    bcm2835 ALSA, bcm2835 IEC958/HDMI
    Hardware device with all software conversions
```

```bash
$ amixer cset numid=3
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=0
```