SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

IoTをかじってみよう

IoTをかじってみよう(7)
~mbedを使って音声認識でデバイスを制御する

動作確認(ローカル・プロクシ)

 それでは、まずはローカルでプロクシを動かして音声認識をしてみましょう(図13)。

図13 ローカルのプロクシでの動作
図13 ローカルのプロクシでの動作

 この場合は、mbedから同じネットワーク上にあるMac上で稼働しているプロクシに音声データを送り、そこからWSTに音声ファイルを送って認識結果を受け取ります(図の点線)。main.cppの以下の行を編集して、192.168.0.10の部分をプロクシを動かすMacのIPアドレスに変更します。

int responseCode = httpClient.post("http://192.168.0.10:9000/", request, &response);

 変更できたら、コンパイルしてmbedに書き込み、リセットしてください。イーサネットの初期化が終わって、DHCPからIPアドレスが取得できると、黄色に点灯していたLEDが緑になります。ジョイスティックのボタンを押し込んで(すぐに離して構いません)、「明るくして」とマイクに話しかけてください。LED1からLED3までは、mbedの動作に応じて次のように変化します。

mbedの状態とLEDとの関係
LED1 LED2 LED3 mbedの状態
消灯 消灯 消灯 Joystickボタン押下待ち
点灯 消灯 消灯 録音中
点灯 点灯 消灯 録音完了
点灯 点灯 点灯 プロクシに音声データ転送中

 認識がうまくいけば、LED4(USBポートを上に見た時に右下のLED)が点灯し、そうでなければ消灯します。プロクシは実行中に以下のような表示を行います。送信したwavファイルの場所や、認識結果も表示されるので、うまくいかない時は参考にしてください。最初はソースのコンパイルが行われるので少し時間がかかります。

[info] Compiling 5 Scala sources and 1 Java source to /Users/shanai/java/cz-mbed7/target/scala-2.11/classes...
[info] play.api.Play - Application started (Dev)
wav: /var/folders/hd/3vwf2zj95g19kvj45wq3_m640000gn/T/5500731341017076451.wav
response: '{"results":[{"alternatives":[{"confidence":0.653,"transcript":"分かる し "}],"final":true}],"result_index":0}'
transcript: '"分かる し "'
command: '分かるし'

 この例では、「分かるし」と誤認識されています。もしも何回かやってもうまく認識できない場合は、wavファイルが表示されているので、このファイルを再生してみてください。

wav: /var/folders/hd/3vwf2zj95g19kvj45wq3_m640000gn/T/5500731341017076451.wav

 このファイルは、本来は最後に消さないと残ってしまうのですが、現在は動作が確認しやすいように残したままにしてあります。このwavファイルの音量が小さいようならもっとマイクに近づいて、音が割れているようなら、もう少しマイクから離れてみてください。

プロクシの処理内容

 プロクシの内容を簡単に解説しておきます。app/controllers/HomeController.scalaというファイルが処理が書かれたファイルになります。プロクシにリクエストがあると、indexという名前の関数が呼び出されます。

  def index = Action.async { req => // (1)
    println("Start index")
    val file: File = req.body.asRaw.get.asFile
    val temp: Path = Files.createTempFile(Paths.get("/tmp"), null, ".wav")

    Files.copy(file.toPath, temp, StandardCopyOption.REPLACE_EXISTING) // (2)
    val wav: Path = createWavFile(temp) // (3)
    println("wav: " + wav.toAbsolutePath)

    ws.url( // (4)
      WatsonUrl
    ).withAuth(
      WatsonUser, WatsonPassword, WSAuthScheme.BASIC
    ).withHeaders(
      "Content-Type" -> "audio/wav"
    ).post(
      wav.toFile
    ).map { response => // (5)
      println("response: '" + response.json + "'")
      val transcript = ((response.json \ "results")(0) \ "alternatives")(0) \ "transcript" match { // (6)
        case JsDefined(v) => v.toString
        case undefined: JsUndefined => "Undefined"
      }

      println("transcript: '" + transcript + "'")
      val command = transcript.foldLeft(new StringBuilder) { (buf, c) => // (7)
        if (c != ' ' && c != '"') buf.append(c)
        else buf
      }.toString

      println("command: '" + command + "'")
      Ok(if (command == "明るくして") "ON" else "OFF") // (8)
    }
  }
  • (1)WSTにリクエストを送って結果を受け取る処理は、それなりに時間がかかる処理なので非同期に処理するようになっています(ここは、時間がかかる処理はこういう書きかたをするものと思ってもらえれば良いです)
  • (2)アップロードされてきたデータを一時ファイルにコピーします
  • (3)音声データをwavファイルに変換します。createWavFile()関数の定義は上にあるので、興味のある方は見てみてください
  • (4)WSTに対して音声データ(wavファイル)を送信します
  • (5)WSTからのレスポンス処理です
  • (6)WSTからのレスポンスは、JSONなので内容をパースします。alternativesという要素の中に、認識された項目が1つ以上格納されるので最初の項目を取り出しています
  • (7)WSTが認識した文書には、半角のスペースや引用符が含まれることがあるので削除しています
  • (8)認識された文書が「明るくして」であれば、ONを、そうでなければOFFをmbedに返しています

次のページ
Dockerでの動作確認

この記事は参考になりましたか?

IoTをかじってみよう連載記事一覧

もっと読む

この記事の著者

花井 志生(ペンネーム 宇野るいも)(ハナイ シセイ(ペンネーム ウノルイモ))

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/9568 2016/08/10 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー