AWS GreengrassにKerasのモデルをデプロイしてみた

AWS GreengrassにKerasのモデルをデプロイしてみました。

きっかけ

Re:Invent 2017で発表された新サービス「AWS Greengrass ML Inference」にプレビュー申請したところ、残念ながら結果はNot Approved。

GAまで待つのもちょっと悔しいので、普通のGreengrassにkerasのモデルをデプロイしてみることにしました。

必要なもの

  • AWSアカウント
  • Raspberry Pi

作業概要

  1. Kerasの環境準備(Mac)
  2. greengrassチュートリアル実施
  3. Raspberry PiにKeras等をインストール
  4. Kerasのモデル作成
  5. デプロイ用のLambda作成
  6. Greengrassへデプロイ
  7. 動作テスト

1. Kerasの環境準備(Mac)

MacにKerasの環境を準備します。

$ brew upgrade pyenv
$ pyenv install 2.7.14
$ pyenv virtualenv 2.7.14 keras
$ cd hoge/huga
$ pyenv local keras
$ pip install tensorflow jupyter keras h5py

2. Greengrassチュートリアル実施

Raspberry Piの環境構築を兼ねて、AWSドキュメントにあるGreengrassのチュートリアルを実施します。

「モジュール 3-I: AWS Greengrass での AWS Lambda」までの実施でOKです。

3. Raspberry PiにKeras等をインストール

こちらの記事を参考にさせて頂きました。
Raspberry Piにsshでログインしてインストールします。
熱暴走するかもとの事だったので、自分はUSB扇風機で風を当てながら一晩熟成させました。

$ sudo pip install http://ci.tensorflow.org/view/Nightly/job/nightly-pi/lastStableBuild/artifact/output-artifacts/tensorflow-1.5.0rc1-cp27-none-any.whl
$ sudo pip install keras
$ sudo apt-get install python-h5py

4. Kerasのモデル作成

今回はkerasのexampleからMNISTの文字分類のソースコードを拝借して、Kerasのモデルを作成します。

1. 上記ソースコードをダウンロードする
2. 下記3行を追記して用意した環境で実行する

model.save('mnist_mlp.h5')
np.save('x_test.npy', x_test)
np.save('y_test.npy', y_test)

3. 実行後、下記3ファイルが生成される

  • mnist_mlp.h5 (Kerasのモデル)
  • x_test.npy (画像データ)
  • y_test.npy (画像データに対応する正解ラベル)

5. デプロイ用のLambda作成

GreengrassへデプロイするLambdaを作成します。

1. BlueprintsからLambdaを作成する
AWSマネジメントコンソールでLambdaを選択後、blueprintsから「greengrass-hello-world」を選択して、Lambdaを作成します。
ロールはチュートリアルで作成したものを指定してください。
2. ソースコードを置き換える
AWSマネジメントコンソール上で、Lambdaのコードを下記へ置き換えます。

from __future__ import print_function


import greengrasssdk
from threading import Timer
import time
import keras
from keras.models import load_model
import numpy as np
import random

client = greengrasssdk.client('iot-data')
x_test = np.load('x_test.npy')
y_test = np.load('y_test.npy')
x_test = x_test.reshape(10000, 784).astype('float32') / 255
model = load_model('mnist_mlp.h5')


def greengrass_keras_prediction_run():
    r = random.randrange(10000)
    prediction = model.predict(x_test[r].reshape(1, 784))
    predicted_value = np.argmax(prediction[0])
    answer = y_test[r]

    message = 'predicted value: {0}, answer:{1}'.format(predicted_value, answer)
    client.publish(topic='keras/prediction', payload=message)

    Timer(5, greengrass_keras_prediction_run).start()


greengrass_keras_prediction_run()


def function_handler(event, context):
    return

3. デプロイメントパッケージをダウンロードする
AWSマネジメントコンソールから、Lamdbaのデプロイメントパッケージをダウンロードします。
4. mnist_mlp.h5 / x_test.npy / y_test.npyの3ファイルをデプロイメントパッケージに含める
ダウンロードしたデプロイメントパッケージを展開後、3ファイルを含めて再度zip圧縮します。
zipファイルにはフォルダを含めないように注意してください。

$ zip -r ../keras_prediction.zip *

5. デプロイメントパッケージをアップロードして、Lambdaを更新する
6. 新しいバージョンを発行して、発行したバージョンに対しエイリアスを作成する
手順はGreengrassのチュートリアルと同じです。

6. Greengrassへデプロイ

手順はGreengrassのチュートリアルと同じです。Greengrassのコンソール画面から操作します。
1. Lamdbaを登録する
一覧から作成したLambdaを選択後、Lambdaのエイリアスを指定して登録します。
2. Lamdbaの設定を変更する

  • タイムアウト:25秒
  • ライフサイクル:Make this function long-lived and keep it running indefinitely

3. subscriptionを追加する

  • ソース:Lambda
  • ターゲット:IoT Cloud
  • トピック:keras/prediction

subscription.png

7. 動作テスト

1. Greengrassのコンソール画面の左側のTestをクリック
2. Subscription Topicにkeras/predictionを指定
3. Subscribe to topicをクリック
下記が表示されれば成功です。
test2.png

雑感

AWS Greengrass ML Inferenceでは、上記のように煩雑でなく簡単に学習モデルがデプロイできるようになるでしょうか。
似たようなサービスにAzure IoT Edge(こちらも未だプレビュー)がありますが、今度はこっちをさわってみたいと思います。

続きを読む

AWS初心者がLambdaでLINE BOTを作る

LINE BOTでオウム返しのBOTを作ります。
正直AWSを触ったことが無いので、よく分からず困ったので同じように困る人が出ないように
記事にすることにしました。

言語・ツール選定

私はRaspberry pi & PHPでLine botを作ったことが有る。
内容としては位置情報を渡すと近くのコンビニを教えてくれるものだ。
しかし、Raspberry piでLine botを作ると、常にRaspberry piを起動しておく必要があり電力的に優しくなく、
サーバの構築や認証鍵の設定が面倒だった。
そこでサーバレスのLine Botを作ろうと思いました。
私が選択したのはLambda+Node.js。
選択肢としてGAS(Google App Script)もあったけれど今後の拡張性を考えた時、
ES6が簡単に使えない(変換すれば使えるが面倒な)のが嫌だと思ったのでやめました。

環境構築

LINE Developers
https://developers.line.me/ja/
↑LineBotを作るためにアカウント取得する
【公式】クラウドならアマゾン ウェブ サービス (AWS)
https://aws.amazon.com/jp/
↑AWSの登録する(日本アカウントだと駄目?)

おわり

使用するAWS

初心者なのでLambdaだけで作れると思ってました。
すると、そういう訳じゃなくいくつかサービスを利用しないといけないことが分かりました。

Lambda
サーバレス、コンピューティングサービス。
サーバ構築しなくてもイベントやトリガーで実行出来るサービス。
使用できる言語はNode.js、Java、C#、Python。
無料枠は
月1,000,000リクエスト
秒400GB 月間400,000GB
有料枠は
1,000,000リクエストごとに0.20USD(約22.6円)
他のサービスは別料金

API Gateway
API の作成、配布、保守、監視、保護が出来る。
Lambdaの関数を動作させる(キックする)ために使う。
料金はデータ送出量で変わる、使用する地域ごとに料金が違う。
無料期間が終わったら有料。

IAM (これはサービスではなく仕組み)
アクセスを安全に制御するための仕組み。権限(role)の設定をする。
Lambdaでログを取ったりするときに設定が必要。

CloudWatch Logs
ログを取るために使う。
無料枠Amazon CloudWatch でのメトリックス 10 個、アラーム 10 個、API リクエスト 1,000,000 件
まぁ、サービスがうまく稼働したら止めると思うので有料枠は気にしない。

api Gatewayの設定

+APIの作成からAPI名とエンドポイントタイプを決める。

エンドポイントとは?
ゲートウェイ VPC エンドポイント??

Amazon API Gateway で、REST API およびカスタムドメインの作成時に 2 種類の API エンドポイントから選択できるようになりました。
リージョンの API エンドポイントは、REST API をデプロイするのと同じ AWS リージョンからアクセスできる新しいタイプのエンドポイントです。
これにより、API リクエストが REST API と同じリージョンから発信される場合に、リクエストのレイテンシーを減らすことができます。
さらに、独自の Amazon CloudFront ディストリビューションとリージョンの API エンドポイントの関連付けを選択できるようになりました。
API エンドポイントの 2 つ目のタイプは、エッジ最適化 API です。
エッジ最適化 API は、API ゲートウェイによって作成および管理される CloudFront ディストリビューション経由でアクセスするエンドポイントです。
以前は、エッジ最適化 API は、API ゲートウェイを使用して API を作成する際のデフォルトオプションでした。

つまり、どーいうことだ?
何か、ドメイン関係というのは分かった、
エンドポイントとは?
https://qiita.com/NagaokaKenichi/items/6298eb8960570c7ad2e9

なるほど。
URIの設定のことね。
んで、エンドポイントタイプの違いって具体的に何?

Regional:リージョン(地域が一緒なら早い)
Edge Optimized:エッジ最適化(経由して最適化する)
って感じかな?
今回はAPI GatewayもLambdaも同じハイオワにしたので、リージョンで良さそう。

API 名を決めないと何故かエラーが出る模様。訳が分からない。泣いた。
http://kubotti.hatenablog.com/entry/2017/01/13/161022

リソースで、ポストが出来るように設定しLumbdaの関数が完成したらAPIのデプロイをする。
image.png

設定で、ARNの設定をする。
image.png
Lumbdaへのアクセスできる設定とログが取れるようアクセスを許す設定した物を割り当てる。
あ、後クライアント証明書も作った。

Lamubdaの設定

image.png
実行ロールの設定でログ監視とAPIGatewayからのアクセスが可能なアカウントを設定する。

コードはこんな感じ

index.js
'use strict';

const https = require('https');
const querystring = require('querystring');

exports.myHandler = (event, context, callback) => {
    console.log('EVENT:', event);
    var event_data = JSON.parse(event.body);
    console.log('EVENT:', JSON.stringify(event_data));
    const messageData = event_data.events && event_data.events[0];
    console.log("TEST:" + JSON.stringify(messageData.message.text));
    var id = messageData.source.userId;
    if(messageData.source.groupId != null && messageData.source.groupId.length > 0){ //グループからのメッセージ
        id = messageData.source.groupId;
    }

    if(!check(messageData.message.text)){
        callback(null, 'Success!');
        return;
    }
    var postData = JSON.stringify(
    {
        "messages": [{
            "type": "text",
            "text": getResponseMessage(messageData.message.text)
        }],
        "to": id
    });
        // @TODO KMSで管理
    var ChannelAccessToken = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; //チャンネル設定

    //リクエストヘッダ
    var options = {
        hostname: 'api.line.me',
        path: '/v2/bot/message/push',
        headers: {
            'Content-Type': 'application/json; charset=utf-8',
            'Content-Length': Buffer.byteLength(postData),
            'Authorization': 'Bearer ' + ChannelAccessToken
            },
        method: 'POST',
    };
    console.log(JSON.stringify(options));
    //APIリクエスト
    var req = https.request(options,  function(res){
        res.setEncoding('utf8');
        res.on('data', function (body) {
            console.log(body);
            context.succeed('handler complete');
        });
    }).on('error', function(e) {
        context.done('error', e);
        console.log(e);
    });

    req.on('error', function(e) {
        var message = "通知に失敗しました. LINEから次のエラーが返りました: " + e.message;
        console.error(message);
        context.fail(message);
    });

    req.write(postData);
    req.on('data', function (body) {
            console.log(body);
     });
    req.end();
    console.log("TEST:" + postData);
    callback(null, 'Success!');
};

var getResponseMessage = function(message) {
    message = message.toLowerCase();
    message = message.replace('@bot ', '');
    switch (message) {
        default:
            return message;
    }
};

var check = function(message) {
    message = message.toLowerCase();
    var pattern = '@bot ';
    return message.indexOf(pattern) === 0;
};

・↑ではセキュリティガバガバなので、Lineからのアクセスかどうか検証するためにChannel SecretのIDを見るプログラムと、ChannelAccessTokenは管理するものを入れたほうが良いと思います。
・replyでも良いんですが、処理に時間がかかると処理されないらしいので今後重い処理を入れることを考えてpushにしました。
(参照:https://developers.line.me/ja/docs/messaging-api/reference/)
・グループに突っ込んだ時に関係なく反応するのがうざかったので、@botと頭につくと返すようにしました。

image.png
LINE DevelopersのChannelAccessTokenはここのやつをコピペ。

image.png
上のような形で登録する。(念のため443はつけたほうが良い)

image.png
↑のエラーが出ますが問題なく動きます。

結果

image.png
出来た。

まとめ

AWSのいい勉強になった。
私のセキュリティ意識が低いことが分かった。
よくわからないところからAPIリクエストが飛んで来ていたので気をつけたい。
LineBotはちょっと前とはAPIの書き方が変わっていた&単純にNode.jsに慣れていないというのもあって、ちょっと苦戦した。
今後は、飲み会幹事BOTなどにしていきたい。
たのしかった。

参考

https://qiita.com/akihito_nagai/items/f284ef495da380f368cc
https://qiita.com/ykyk1218/items/232437bc92f13d02a8a2
https://qiita.com/daikiojm/items/c05882bedb82d7ed664e

続きを読む

Raspberry pi model b+ では AWS greengrass できなかった

ずっと眠っていたラズパイを活用しようとAWS Greengrass利用しようとしたら条件満たせず使えませんでした.
(できるかわからないけど)Alexa スキルの Lambda をラズパイで実行するつもりでしたが作戦を変える必要がありそうです.

CPUアーキテクチャ条件

よし,使うぞ!とアーキテクチャ調べたら即条件を満たせず.GreengrassがサポートしているアーキテクチャはARMv7lだそうです.

pi@raspberrypi:~ $ arch
armv6l

続きを読む

センサデータを fluentd 経由で Amazon Elasticsearch Service に送信して可視化

1. はじめに

以前の記事 で、RaspberryPi で収集したセンサデータを、 さくらVPS上に構築した Elasticsearch に送信して、Kibana で可視化しました。
今回は勉強を兼ねて、データを Amazon Elasticsearch Service ( Amazon ES ) に送信するように構成を変更します。

2. 全体の構成

image.png

3. 設定

3-1. Server side ( Amazon ES )

Amazon ES を立ち上げます。

Amazon ES ダッシュボード

  • 画面右上で、東京リージョン( ap-northeast-1 )が選択されていることを確認します
  • 新しいドメインの作成ボタンを押します
image

ドメインの定義

  • ドメイン名と Elasticsearch のバージョンを設定します
image

クラスターの設定

  • 今回は最小構成にします
image

アクセスの設定

  • ダッシュボードは特定メンバーに公開したかったので、パブリックアクセスとして、IPアドレスでアクセス制限を設定しました
  • 本当は IAM Role で制限したかったのですが、Webブラウザからのアクセスが面倒になるので今回は見送りました ( ブラウザはIAM認証できない )
image

完了確認

  • 10分ほど待ちます
image
  • 設定の状態が「アクティブ」になれば完了です
image

3-2. Sensor side ( Raspberry PI )

前提条件

以前の記事 の状態が前提です。今回はこれに変更を加えます。

プラグインのインストール

  • fluentd から Elasticsearch に直接格納するためのプラグインをインストールします
  • なお、IAM 認証をする場合は fluent-plugin-aws-elasticsearch-service を使うようです
sudo fluent-gem install fluent-plugin-elasticsearch

fluentd の設定

  • fluentd の設定ファイルを編集して、データの送信先を変更して、fluentd を再起動します
/home/pi/fluent/fluent.conf
<source>
  @type tail
  format json
  path /home/pi/myroom.log
  pos_file /home/pi/myroom.log.pos
  tag log.myroom
</source>

<match log.myroom>
  @type copy
  <store>
    @type elasticsearch
    type_name myroom
    logstash_format true
    logstash_prefix myroom
    reload_connections false
    hosts https://search-myroom-********.ap-northeast-1.es.amazonaws.com
  </store>
</match>

4. 確認

データが送信されていることを確認しました。
image.png

続きを読む

RaspberryPi で収集したセンサデータを Amazon ES に格納

1. はじめに

前回の記事 では、RaspberryPi で収集したセンサデータを、 さくらVPS上に構築した Elasticsearch に格納しました。
今回は勉強を兼ねて、データを Amazon Elasticsearch Service ( Amazon ES ) に格納するように構成変更します。

2. 全体の構成

image.png

3. 設定

3-1. Server side ( Amazon ES )

Amazon ES を立ち上げます。今回はそれだけです。

Amazon ES ダッシュボード

  • 画面右上で、東京リージョン( ap-northeast-1 )が選択されていることを確認します
  • ドメインの作成ボタンを押します
image

ドメインの定義

  • ドメイン名と Elasticsearch のバージョンを設定します
image

クラスターの設定

  • 今回は最小構成にします
image

アクセスの設定

  • ダッシュボードは特定メンバーに公開したかったので、パブリックアクセスとして、IPアドレスでアクセス制限を設定しました
  • 本当は IAM Role で制限したかったのですが、Webブラウザからのアクセスが面倒になるので今回は見送りました ( ブラウザはIAM認証できない )
image

完了確認

  • 10分ほど待ちます
image
  • 設定の状態が「アクティブ」になれば完了です
image

3-2. Sensor side ( Raspberry PI )

前提条件

以前の記事 の状態が前提です。今回はこれに変更を加えます。

プラグインのインストール

  • fluentd から Elasticsearch に直接格納するためのプラグインをインストールします
  • なお、IAM 認証をする場合は fluent-plugin-aws-elasticsearch-service を使うようです
sudo fluent-gem install fluent-plugin-elasticsearch

fluentd の設定

  • fluentd の設定ファイルを編集して、データの送信先を変更して、fluentd を再起動します
/home/pi/fluent/fluent.conf
<source>
  @type tail
  format json
  path /home/pi/myroom.log
  pos_file /home/pi/myroom.log.pos
  tag log.myroom
</source>

<match log.myroom>
  @type copy
  <store>
    @type elasticsearch
    type_name myroom
    logstash_format true
    logstash_prefix myroom
    reload_connections false
    hosts https://search-myroom-q6f5bk4cwppojeescffv24dmkm.ap-northeast-1.es.amazonaws.com
  </store>
</match>

4. 確認

データが送信されていることを確認しました。
image.png

続きを読む

Amazon AVS開発キット「Conexant AudioSmart™ 4-Mic Development Kit」でAlexaのプロトタイプを作ってみる

いまだにEcho/Echo Dotの招待が来ません。すでに多くの人が試しているのに、この祭りに参加できないのはちょっと悔しいですね。そこで自前でAVS対応デバイスのプロトタイプを作成できる開発キット「Conexant A […] 続きを読む

Amazon AVS開発キットを使って音声認識力が高いAlexa搭載製品のプロトタイプを作ってみた

まいどおおきに、大阪の市田です。 日本でも販売開始となったAmazon Echoシリーズ、皆さんは既にお使いになられたでしょうか? Echoシリーズ以外にも、今後ますます対応デバイスは増えてくると感じますが、もし独自に対 […] 続きを読む

Raspberry Pi 3 でAlexaと対話する

re:Invent 2017AlexaをRaspberryPi経由で利用する方法のワークショップに参加した。

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

用意するもの

Raspberry Pi 3 Model B
USBマイク < 多分これ
イヤホン
USBキーボード
USBマウス
HDMIケーブル
ディスプレイ
MicroSD Class10 16G

事前準備(Macでの作業)

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

こちらからNOOBSをダウンロードする
今回ダウンロードしたバージョンは v2.4.5

MicroSDにコピーする

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

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

Raspbianのインストール

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



事前準備(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

$ 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だけど、リモートから行える作業はできるだけリモートで行うと楽なので有効化しておく


ビルド環境の構築+ビルド

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

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

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

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までいけるけど、オーバーヒートに注意しましょう(と書いてある)

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に登録

こちらからログインする
日本でもEchoが発売されたのでamazon.co.jpアカウントでもログインできるらしい

Alexa Voice Service に製品を登録する

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

Alexa Voice Service を選ぶ

CREATE PRODUCTを選ぶ

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

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

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

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

URLの登録

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

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

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

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

sudo apt-get install -y vim

設定ファイルの書き換え

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

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

音の設定

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.
という表示がされている

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

$ 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経由で音が出力される・・・

$ 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だけでなくコマンドから入力受付状態にできるので、受付システムとかできそうなので試したい

音の設定関連の情報

$ 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
$ 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
$ 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

続きを読む

経営層もIoT制作に熱中、「作れる力」が会社を動かす

雑誌『日経Linux』での人気連載を基にしたもので、ボードコンピュータ「Raspberry Pi」や温度センサー、AWS(アマゾン・ウェブ・サービス)を使って簡単なIoT(インターネット・オブ・シングズ)システムを自作するハンズオン形式の講座だ。電子工作を趣味にしている人だけでなく、業務でIoTに関わるエンジニアにも役に立つ内容 … 続きを読む