Parrot MiniDroneのMamboを音声でコントロール

---

今回やってみたこと
　音声でParrot Mamboをコントロールしてみる。
　とりあえずはテイクオフ・すすめ・バック・着陸の４つを実装しました。
　誤認識しにくい単語を選んだので、日本語と英語が混じっています。

　ChromeでないとAPIが実行できないので、ブラウザはChromeを使ってください。

　すすめ！とかで動くので、なんかロボットアニメっぽくてやっててテンション上がります。一人遊びにもってこいでした（笑

---

<img width="404" alt="スクリーンショット 2017-06-10 23.31.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/57579/7116a98a-0bee-d836-6a05-f9d4de8094fc.png">

---

### 導入方法

1. パッケージのインストール
2. node voice_server　で実行
3. http://localhost:8080/ にアクセス(Chrome)
4. コマンド入力開始ボタンをクリック
5. コマンドを発話 (テイクオフ)
6. 入力停止をクリック
7. 5-6の繰り返し

---

今後の課題
　今の段階だといちいち入力停止を押さないといけないからそれをしないでいいように。
　（押さなくても反応するにはするけど、すごいレスポンスが遅い）
　動作パッケージを作ってボイスコマンドを高度に。
　「とべ！まーちゃん！」みたいに余分なワードを無視してボイスコントロールを自然に。


---

### パッケージのインストール

```
npm install rolling-spider
npm install express
npm install path
npm install http
npm install swig
npm install body-parser

```

参考とか利用させていただいたサイト
https://github.com/voodootikigod/node-rolling-spider
http://paiza.hatenablog.com/entry/2016/07/05/音声認識入門！Web_Speech_APIを使いChromeブラウザを音声操作す

---

### ソースコード

```voice_server.js
'use strict';
const http = require('http');
const path = require('path')
const express = require('express');
const swig = require('swig');

var Mambo = require('./mambo_controller.js');
var mambo = new Mambo();
var bodyParser = require('body-parser');
var app = express();

app.engine('html', swig.renderFile);
app.set('views', path.join(__dirname, 'views'));
app.set('view cache', false);
swig.setDefaults({ cache: false });
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(function(req, res, next){
    res.header("Access-Control-Allow-Origin","*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
})

var server = http.Server(app);
app.get("/", function(req, res, next) {
  res.render('voice_interface.html', {});
});

app.post("/exec/", function(req, res) {
  var result = mambo.execTextCommand(req.body.command);
});

server.listen(process.env.PORT || 8080, function() {
  console.log("listening to port:" + server.address().port);
});
```

```mambo_controller.js
'use strict';

var Mambo = function() {
  this.Drone = require('rolling-spider');
  this.colors = require('colors');

  this.d = new this.Drone();
  console.log("Mambo Initialized.");
  this.d.connect(function() {
    this.d.setup(function() {
      this.d.flatTrim();
      this.d.startPing();
      this.d.flatTrim();

      setTimeout(function() {
          console.log('Ready to fly'.green);
      }, 1000);
    }.bind(this));
  }.bind(this));

  this.execTextCommand =  function(cmd) {
    var status = "";
    switch (cmd) {
      case "テイクオフ":
        this.d.takeOff(function() {
          console.log('take off'.green);
        });
        status = "take off";
        break;
      case "すすめ":
  			var STEPS = 20;
  			this.d.forward({steps: STEPS}, function() {
          console.log('forward'.green);
        });
        status = "forward";
        break;
      case "バック":
  			var STEPS = 20
        this.d.backward({steps: STEPS}, function() {
          console.log('forward'.green);
        });
        status = "back";
        break;
      case "着陸":
        this.d.land(function() {
          console.log('landing'.green);
        });
        status = "land";
        break;
      default:
        status = "error";
    }

    return status;
  }.bind(this);

};

module.exports = Mambo;
```

```views/voice_interface.html
<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <title>Web Speech API</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>    <meta charset="UTF-8">
</head>
<body>
<style>
    #comannd-box {
        width: 90%;
        height: 3em;
        padding: 10px;
        border: 1px solid #ccc;
        border-radius: 3px;
        margin-bottom: 10px;
    }
    #btnStartCommand {
        background: -webkit-linear-gradient(top,#008dfd 30%,#0370ea 100%);
        color: white;
        text-shadow: 1px 1px 1px #076bd2;
        border-radius: 3px;
        border: 1px solid #076bd2;
        padding: 8px 25px;
        font-weight: 700;
        font-size: 15px;
    }
    #btnStartCommand.select {
        background: -webkit-linear-gradient(top,#f2739e 30%,#f23f7b 100%);
        text-shadow: 1px 1px 1px #f1115c;
        border: 1px solid #f1115c;
    }
</style>
<h3>音声コマンドパネル</h3>
<div>
  <h4>対応コマンド</h4>
  <p>
    テイクオフ / すすめ / バック / 着陸
  </p>
</div>
<div id="comannd-box">
  <span class="final" id="final_span"></span> <span class="interim" id="interim_span"></span></div>
<input id="btnStartCommand" type="button" value="コマンド入力開始"/>
<script>
$(function () {
    var recognition;
    var nowRecognition = false;
    var finalSpan = document.querySelector('#final_span');
    function start () {
        recognition = new webkitSpeechRecognition();
        recognition.lang = "ja-JP";
        recognition.continuous = true;
        recognition.interimResults = true;
        recognition.onresult = function (e) {
            if (e.results[e.results.length - 1].isFinal) {
              var transItem = e.results[e.results.length - 1];
              finalSpan.textContent += transItem[0].transcript;

              var _data = {"command" : transItem[0].transcript};
              $.ajax({
                  url: "http://localhost:8090/exec/",
                  type: "post",
                  data: _data
              }).done(function(data){
              }).fail(function(data){
              });
            }
        };
        recognition.start();
        nowRecognition = true;
    };
    function stop () {
        recognition.stop();
        nowRecognition = false;
    }
    $('#btnStartCommand').on('click', function (e) {
        if (!'webkitSpeechRecognition' in window) {
            alert('Web Speech API には未対応です.');
            return;
        }

        if (nowRecognition) {
            stop();
            this.value = 'コマンド入力開始';
            this.className = '';
        } else {
            start();
            this.value = '入力停止';
            this.className = 'select';
        }
    });
});
</script>
</body>
</html>
```