【AWS・Tello】声でドローンを操作してみたPart1

# はじめに
この記事は
[ドローンを活用した避難勧告システム](https://qiita.com/sk_nimo/items/56ede90072105e14497d)
の第二章です．
製作背景などはそれを参照してください．

また，本章の内容は以下の内容が前提なので，まだ読まれていない方はまずそちらをご覧ください．
第一章[【AWS・Tello】ドローンをクラウド上で操作するシステムの構築](https://qiita.com/sk_nimo/items/d0ab81024f76004112c2)

# 概要
　TelloドローンをAlexaを使って声で操作してみましょう．インフラはAWSを使用します．
まず動画で完成形を見てみましょう．
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/FKotpW1kJiw/0.jpg)](https://youtu.be/FKotpW1kJiw)

# システム構成図

<img width="872" alt="system.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/457cb524-c9ad-6611-4ace-5000d97f7ecf.png">


# やること
前回まででIoTCoreとTelloの通信を確認したので今回は
lambdaでAlexaスキルのエンドポイントをホスティングし，IoTCoreにpublishするところまでやりましょう．
Part1. スキル開発・lambdaとの連携
Part2. lambdaからIoTCoreへの通信

# スキル開発

## スキル作成
alexa developer console (以下ADC)にログインし，カスタムスキルを作成します．
<img width="1368" alt="skillselect1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/323bdb17-753b-94a7-9580-999c0ee32989.png">
<img width="925" alt="backendforalexa.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/4f0621f9-96c8-9a86-ae12-e63f52f301ea.png">

　スキルのバックエンドに「ユーザー定義のプロビジョニング」を選択すると自前でlambda関数を用意する必要があります．「Alexa-Hosted」を選択するとADC上のコードエディタを使えて初学者に易しく取っ付き易い気がします．IoTCoreとの通信するので今後の拡張性も考え，ここでは自前でlambda関数を用意する前者のやり方で行います．
　というわけでlambdaを新しく用意しときます．lambdaのARNは控えといてスキルのエンドポイントでデフォルトの地域の箇所に設定します．
<img width="1087" alt="endpointforalexa.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/80c41f3c-1331-2ded-7c99-15ad87eb038b.png">

## スキル用語集 

　優しいのでスキルの基本用語を再確認してあげます．後ほど詳しく触れます．

__呼び出し名__： 対話(セッション)を始める合言葉
__インテント__： 対話の意図(開発者が自由に設定することができます)
__ビルトインインテント__： デフォルトで組み込まれているインテント(キャンセル・ストップ・ヘルプなど)
__サンプル発話__： セッション内で特定のインテントを呼ぶための合言葉
__スロット__： サンプル発話で保持させられる変数のようなもの
__ビルトインスロット__： 既に用意されているスロット(数字や施設名，女優名もある...すごい)
__カスタムスロット__： 開発者が自由に設定できるスロット

ちなみにスロットは同義語やID指定など柔軟に設定できるから素晴らしい．

## スキル詳しく
　今回のドローン制御システムで設定したスキルの詳細です．(参考までに)

__呼び出し名__： 「コントローラー」
__インテント__： Controller/Land
ドローンを動かしたい時にControllerインテント， 着陸したい時にLandインテント
(Flipインテントなんか面白そう)
__サンプル発話__：
Contorollerインテントの場合
<img width="1119" alt="ContIntent.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/f2f4e6cd-8e01-6086-c53c-a9793002e19c.png">

__ビルトインスロット__： num(数字)
__カスタムスロット__： direction(方向)
<img width="1114" alt="slot_direc.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/5da70f97-2664-990f-5456-1274a20149f4.png">

　つまりControllerインテントでは，進む方向と距離の二つの情報が必要になります．好みになりますが，実際のスキルで方向しか声で入力されなかった場合に距離を聞き返す工夫をするべきです．
　ここらへんの対話モデルはjsonで定義できるっぽいので付録で載せときます．

## バックエンド
　先程作ったlambdaをいじります．ここで「Alexa Skills Kit SDK」を使った方が絶対良いです．使わなくてもできますが，リクエストパラメータのjsonのネストが深くて処理がめんどくさそうです．
　言語はPythonにします．Nodejsを使う人が多くてPython使う人が少ないのでここではあえてPythonにしました．

### Alexa Skills Kit SDK for Python

まずlambdaでask_sdk_coreをimportしてください．

`$ import ask_sdk_core`

ただそのままだと外部ライブラリは読み込めないのでask_sdk_coreをlayerとして追加しましょう．

ローカルでプロジェクトにpip installして，プロジェクト全体をzip化してlambdaにアップロードする方法もあります．以下のリンクではその方法およびlambdaのローカル開発のエコシステムをまとめましたので気になる方はご覧ください．
[【AWS・Lambda】Python外部ライブラリ読み込み方法](https://qiita.com/sk_nimo/items/b1bc49eedc83cb6e02e0)

ask_sdk_coreはPurePythonなライブラリなのでlayer用のzipファイルを作る環境はどれでも大丈夫なはずです．MacOSで行いました．(python3.7の例)

```
$ mkdir -p build/python/lib/python3.7/site-packages
$ pip3 install ask_sdk_core -t build/python/lib/python3.7/site-packages/
$ cd build
$ zip -r ask_sdk.zip .
```
生成されたask_sdk.zipをlayerに追加し，lambda内でlayerを適応させればライブラリが読み込まれます．
<img width="885" alt="createlayer.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/fac999ce-c16d-4edf-dbbf-f7533cdd88d5.png">

<img width="829" alt="layer.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/76f9f55b-f77c-b137-4988-d518e2692e4c.png">

### lambdaトリガーの設定

今回のlambdaトリガーはもちろんAlexa Skill KitでスキルIDは先程作ったスキルのIDを貼り付けます．
<img width="825" alt="trigger.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234751/924a14a7-d7c4-ad38-5ed8-059d8267e2a2.png">

# まとめ
Alexaのスキルの対話モデルとlambdaとの連携ができました．
次はlambdaのコード開発とIoTCoreの連携を行います．

長くなりましたのでPart2に移ります．
[第三章【AWS・Tello】声でドローンを操作してみたPart2](https://qiita.com/sk_nimo/items/12e4ef69d9e57eb31c75)

# 付録
対話モデルをjsonで載せときます．インテントなど新しく作られた場合は，GitHubにPRお願いします:pray:
https://github.com/shoda888/tello_ask_model

```json
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "コントローラー",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "Controller",
                    "slots": [
                        {
                            "name": "num",
                            "type": "AMAZON.NUMBER"
                        },
                        {
                            "name": "direction",
                            "type": "direction"
                        }
                    ],
                    "samples": [
                        "{direction} {num}",
                        " {num} センチ {direction} に移動して",
                        "{direction} に {num} センチ",
                        "{direction} に {num} センチ移動して"
                    ]
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "Land",
                    "slots": [],
                    "samples": [
                        "着陸",
                        "着陸して",
                        "ランド"
                    ]
                }
            ],
            "types": [
                {
                    "name": "direction",
                    "values": [
                        {
                            "id": "back",
                            "name": {
                                "value": "うしろ",
                                "synonyms": [
                                    "後ろ",
                                    "後方"
                                ]
                            }
                        },
                        {
                            "id": "forward",
                            "name": {
                                "value": "まえ",
                                "synonyms": [
                                    "前",
                                    "前方"
                                ]
                            }
                        },
                        {
                            "id": "down",
                            "name": {
                                "value": "した",
                                "synonyms": [
                                    "下",
                                    "下降",
                                    "下方"
                                ]
                            }
                        },
                        {
                            "id": "up",
                            "name": {
                                "value": "うえ",
                                "synonyms": [
                                    "上",
                                    "上方",
                                    "上昇"
                                ]
                            }
                        },
                        {
                            "id": "left",
                            "name": {
                                "value": "ひだり",
                                "synonyms": [
                                    "左"
                                ]
                            }
                        },
                        {
                            "id": "right",
                            "name": {
                                "value": "みぎ",
                                "synonyms": [
                                    "右"
                                ]
                            }
                        }
                    ]
                }
            ]
        }
    }
}
```