ラズベリーパイで音声認識&音声合成を試してみよう!

ラズベリーパイAI関連の実装/デモ
この記事は約17分で読めます。

はじめに

今回は、ラズベリーパイをつかって音声認識を試していきましょう。

最終的にイメージしているものとしては、
1:自分がラズベリーパイに話しかける
2:ラズベリーパイは話しかけた内容をテキストに変換する
3:そのテキストを言語処理してなにかしらの返答テキストを生成
4:返答テキストを音声でレスポンスする
というものです。

それでは、実際にやっていきましょう!

まずは、準備

必要なものを用意

用意するものは、
・ラズベリーパイ
・キーボード
・マウス
・無線LANアダプター(USBに挿入):必要に応じて
・ヘッドホン(イヤホンジャックに挿入):音声出力用
・USBマイクロホン(USBに挿入):音声入力用

また、ラズベリーパイのOSは今回Jessieを使いました。

参考までに、
USBマイクロホン

無線LANアダプター

ラズベリーパイで音を出力するためのテストと設定

音声出力のテストを行なうために、ラズベリーパイ上で以下のコマンドを実行します。

cd /opt/vc/src/hello_pi/
./rebuild.sh

この状態でイヤホンジャックにヘッドホンを挿入していることを確認し、以下のコマンドを実行します。

/opt/vc/src/hello_pi/hello_audio/hello_audio.bin

これで、音が再生されるはずです。

次にWAVという音声ファイルが再生できるかを確認します。
先ほどのrebuild.shの実行によりテスト用wav音声ファイルがあるのでそちらを再生してみましょう。

aplay /usr/share/sounds/alsa/Rear_Center.wav

これで音がなればOKです。

音声出力の準備ができました。

もし音が出なかった場合は出力を固定して再度試してみてください。

amixer cset numid=3 1

流れの説明

いよいよ準備ができました。

ここで、一度、実装の流れを整理しておきます。

1、USBオーディオアダプタの優先度の設定

ラズベリーパイでは、サウンドカードのデバイスドライバとしてLinuxカーネルのコンポーネントであるALSAが採用されています。そこでALSAの設定ファイルでUSBオーディオの優先度を上げるように設定します。(ここは、なんとなくそういうことかー、という程度で大丈夫です。)

2,音声の録音・再生

ラズベリーパイに接続したマイクからの入力を音声ファイルとして保存し、再生できるか確認します。

3,言語処理の実装

マイクから入力された内容に対する返答のテキストを用意します。

今回はdocomoが提供する雑談APIを用いてみました。

4,返答テキストを合成音声に変換して出力

最後に、APIから返された返答テキストを合成音声で出力します。
この音声合成には、AquesTalkを用います。

それでは、早速やっていきましょう!

USBオーディオアダプタの優先度の設定

それでは、まずUSBオーディオアダプタの優先度の設定の設定をしていきましょう。

$cat /proc/asound/modules
  0 snd_bcm2835
  1 snd_usb_audio

こちらのコマンドにより、現在のUSBオーディオアダプタの優先度が確認できます。

このとき、snd_bcm2835が現状優先度としては高くなっています。
今回使いたいマイクはsnd_usb_audioなのでこれを変更します。

次のコマンドを実行します。

$arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

すると、サウンドカードのデバイスが確認できます。

例えば、この場合だと、サウンドカード1デバイス0が今回のマイクです。
したがって、環境変数をこれにしたがって変更します。

export ALSADEV="plughw:1,0"

これを実行した後、再起動をします。
再起動が完了したら、もう一度設定が反映されているか確認しましょう。

$cat /proc/asound/modules
  0 snd_usb_audio
  1 snd_bcm2835

優先度が変更していることが確認できました。

音声の録音・再生

次に音声の録音・再生を行ないます。

今回は、pythonで扱いやすいpyaudioを用います。

まずはじめに、マイクのボリュームを調整します。

$amixer sset Mic 50 -c 0
Simple mixer control 'Mic',0
  Capabilities: cvolume cvolume-joined cswitch cswitch-joined penum
  Capture channels: Mono
  Limits: Capture 0 - 62
  Mono: Capture 62 [100%] [22.50dB] [on]

この設定では、マイクの音量調整を0−62の値で設定できます。
今回は50にしました。

ここで、pyaudioをインストールしておきます。

$sudo apt-get install python-pyaudio

インストールが完了したら、pyaudio_test.pyを作成します。

ソース元:

raspberryPi と pyaudioで録音、音声波形処理 |

 

# -*- coding: utf-8 -*-
#マイク0番からの入力を受ける。一定時間(RECROD_SECONDS)だけ録音し、ファイル名:mono.wavで保存する。

import pyaudio
import sys
import time
import wave

if __name__ == '__main__':
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
#サンプリングレート、マイク性能に依存
RATE = 44100
#録音時間
RECORD_SECONDS = input('Please input recoding time>>>')

#pyaudio
p = pyaudio.PyAudio()

#マイク0番を設定
input_device_index = 0
#マイクからデータ取得
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk)
all.append(data)

stream.close()
data = ''.join(all)
out = wave.open('mono.wav','w')
out.setnchannels(1) #mono
out.setsampwidth(2) #16bits
out.setframerate(RATE)
out.writeframes(data)
out.close()

p.terminate()

このファイルを実行すると、音声入力結果がmono.wavファイルに録音されます。

$python pyaudio_test.py
Please input recoding time>>> 3
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started

この録音したファイルを再生してみると音声が確認できます。

$aplay -Dhw:1,0 mono.wav

言語処理部分の実装

ここまでで、マイクから音声入力ができるようになりました。

次に、音声入力を実際に会話情報へと音声認識でテキストに変換し、
テキストに対する返答を出力しましょう。

今回は、このプロセスはdocomoが提供しているAPIを用いました。
そのため、まずは、こちらのDocomo Developer Supportサイトにて登録をすませ、APIキーを取得しておく必要があります。

docomo Developer support | NTTドコモ
ドコモが公開するAPIなど開発者向けの情報を提供します。アプリケーション開発にご活用ください。

APIキーを取得したらaction.pyを作成します。

action.py

# -*- coding: utf-8 -*-
import requests
import json

url = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY={}".format("API_KEY_IS_HERE")
payload = {
"utt": "こんにちは",
"context": "",
"nickname": "涼介",
"nickname_y": "リョウスケ",
"sex": "男",
"bloodtype": "B",
"birthdateY": "1993",
"birthdateM": "2",
"birthdateD": "14",
"age": "16",
"constellations": "しし座",
"place": "東京",
"mode": "dialog",
}
r = requests.post(url, data=json.dumps(payload))
print r.json()['utt']

ここでは、”こんにちは”とラズベリーパイに話しかけてみましょう。
すると適当なレスポンスが返ってきます。

$python action.py
>>> こんちは!

挨拶してくれました。簡単ですね!

合成音声で返答を出力

さて、マイクで音声を入力し、返答をテキストで出力するとこまでできました。

最後に、これらを組み合わせて、返答を合成音声で出力しましょう!

音声合成では、AquesTalk APIを使います。

AquesTalk Pi - Raspberry Pi用の音声合成アプリ
AquesTalk Piを使えば、Raspberry Piで簡単に音声合成できます

こちらのサイトから、自分のローカルにダウンロードします。

ダウンロードしたファイルをscpなどでラズベリーパイ上に転送し、解凍して下さい。

解凍するとaquestalkというディレクトリができるので

$ cd aquestalk

で移動し、ためしに適当なテキストで合成音声を試してみましょう。

$ ./AquesTalkPi "こんにちは" |  aplay -Dhw:1,0

これで合成音声が再生されれば完璧です。

最後にexecute.pyファイルを作成し、その中で実行コードを整理しておきましょう。

execute.py
ソースは(https://gist.github.com/yusukeyamatani/d3197434e90512b5875d)、こちらを参考にさせて頂きましたが、このままでは動かなかったので、一部修正をくわえました。修正箇所は以下コード内でコメントアウトしてあるtry ~ error構文のところです。

# -*- coding: utf-8 -*-
#マイク0番からの入力を受ける。一定時間(RECROD_SECONDS)だけ録音し、ファイル名:mono.wavで保存する。

import pyaudio
import sys
import time
import wave

if __name__ == '__main__':
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = input('Please input recoding time>>>')

p = pyaudio.PyAudio()

input_device_index = 0
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
#!!!!修正箇所!!!!
try:
data = stream.read(chunk)
except IOError:
pass

all.append(data)

stream.close()
data = ''.join(all)
out = wave.open('mono.wav','w')
out.setnchannels(1) #mono
out.setsampwidth(2) #16bits
out.setframerate(RATE)
out.writeframes(data)
out.close()

p.terminate()

それでは、ようやく実行してみましょう!!

実行

こちらが実行結果になります。

実行結果1

質問:名前は何
返答:ところで、ジャンボジェット機って接着剤で組み立てられてるそうや

pi@raspberrypi:~/project $ sudo python recog.py
Please input recoding time>>>2
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lf
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
名前は何。
ところで、ジャンボジェット機って接着剤で組み立てられてるそうやな
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono

実行結果2:

質問:好きな食べ物は何。
返答:味が濃いやん

pi@raspberrypi:~/project $ sudo python recog.py
Please input recoding time>>>4
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
好きな食べ物は何。
味が濃いやん
Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono

うん、返答がいまいちでした、、
自然言語処理まわりががんばりどころですね。

さいごに

評価としては、音声認識はかなり性能としてはいいといえるのではないでしょうか。
一方で、入力テキストにたいする言語処理がまだまだだなという印象です。
やっぱり、これからNLPまわりがあつくなってきそうですね!!

この辺の自然言語処理あたりにも挑戦していきたいところです。

参考URL

簡単にできる!音声認識と音声合成を使ってRaspberrypiと会話 - Qiita
いきさつ - 少し前にタ○ラト○ーのおもちゃハッカソンに申し込んだのですが、抽選に受からず。。。 「ならば自力でOH○NASの対抗馬を作ってやる!」と思いたったのがきっかけで、簡易会話ロボをRasタソを作ってみました。笑 (*実際の人...
Raspberry Piで音声認識 - Qiita
Raspberry Piにマイクを付けて話した日本語を認識させたいと思います。 いくつか参考になるサイトがありましたが、自分の環境と微妙に異なってはまっていたのでまとめておきます。 ##参考サイト - (http...
タイトルとURLをコピーしました