Edited at

Raspberry pi で音声会話をする

More than 1 year has passed since last update.


はじめに

それでは作っていきましょう(こんなに簡単に書いていますが

結構手こずりました)


用意すべきもの

Raspberry Pi 1台 買い方は知らん

スピーカまたはイヤフォン 1個 (自分は100円ショップの)

mic 16khz/16bit で出力できるもの ここ重要!

例えばこういうのSANWA SUPPLY MM-MCUSB16

あとボタン<表面実装用タクトスイッチ 4.5mm×4.5mmスイッチ 全高3.8mm>10個<1sw-000>又は表面実装用タクトスイッチ SKRPACE010(5個入): パーツ一般 秋月電子通商 電子部品 ネット通販

L型ヘッダーソケット【共立エレショップ】>> L型ヘッダーソケット 1列2P: 【能動・受動・機構パーツ】 << 電子部品,半導体,キットの通販


任意で用意すべきもの

SD card reader 1個

wi-fi 無線子器 1個 設定の仕方?そんなもん知らん(特に必要ないはず...)


主な参考リンク

簡単にできる!音声認識と音声合成を使ってRaspberrypiと会話

Raspberry Piで音声認識


作り方Ⅰ(マイクの設定)


/etc/modprobe.d/alsa-base.conf

sudo nano /etc/modprobe.d/alsa-base.conf


でその中にこれをぶち込めばいいのです


/etc/modprobe.d/alsa-base.conf

options snd slots=snd_usb_audio,snd_bcm2835

options snd_usb_audio index=0
options snd_bcm2835 index=1

こうするとモジュールの優先順位が変わり

USBマイクが使えるようになります(何のことかわからない?

なら飛ばせばいい)

では見てみましょう(絶対にしろと入っていない)


command

$ cat /proc/asound/modules

0 snd_usb_audio
1 snd_bcm2835

~ちなみに~

マイクの音量調整は

$ amixer sset Mic 16 -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]

みたいな感じでしてください(16でよいと言っていない)


設定Ⅱ(スピーカの設定)

オーディオをミニプラグに出力させる場合

$ amixer cset numid=3 1

オーディオをHDMIに出力させる場合

$ amixer cset numid=3 2

スピーカテスト

$ aplay /usr/share/sounds/alsa/Front_Center.wav

又は

$ aplay /usr/share/sounds/alsa/Front_Center.wav

で音が出ればよい


設定Ⅲ(pyaudio)

このサイトを参考にした

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



$ sudo apt-get install python-pyaudio



でpyaudioをインストール

$ sudo nano pyaudio_test.py

でpyaudio_test.pyを作成して


pyaudio_test.py

# -*- 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 = 16000
#録音時間
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()


で、Ctrl+Xで保存して退出して

$ python pyaudio_test.py

で実行

警告?がたくさん出るが気にしない

これで"can not start"の後に喋ってみよう/home/piにmono.wavができる

再生してみて確かめてみるのもよい

もし"over flow"又は"indivi..."みたいな表示が最後に出て

mono.wavが生成されない場合マイクがレート16000に対応していない

ほかのマイクにするしかない

すべての責任はdcomo APIにある!


ⅣAquestalk

公式サイトから/home/piにdownloadする

$ tar xzvf aquestalkpi-20130827.tar.gz

で解凍(もしこれで出来ない場合は$ tar xzvfの後を保存したファイルのpathにしてください)

では喋らせてみましょう

$ cd aquestalkpi

$ ./AquesTalkPi "あれこの船潜水できない?" | aplay -Dhw:1,0

で、再生された声に聞き覚えはありませんか?

そうです魔理沙です!!

(と、感動するのは我だけか...)


Ⅴdocomo API

docomo Developer supportでまずアカウントを作成します。(サイトの右端にあるログイン/新規登録から)

次にログインしてAPI利用申請・管理を選択で、新規API利用申請

で必須の項目を適当に埋めて(コールバックURLに関してはhttps://dummyを入力)次に進む

APIの機能選択は「雑談対話」と「音声認識【Powered by アドバンスト・メディア】」を選択で最終確認をして申請

申請が終わったらAPI利用申請・管理からさっき申請した奴を探して

APIkeyの項目にある文字列をどこかに保存する(しなくてもいい)


Ⅵ完成

dialogue_test.pyを作る

の前に

ボタン・システム音[1]の中の警告音1を/home/piに

ダウンロードしてください

で、dialogue_test.pyを以下のような感じで作ってください

sudo nano dialogue_test.py


dialogue_test.py


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

import pyaudio
import sys
import time
import wave
import requests
import os
import json
import subprocess
import pygame.mixer

def recognize():
url = "https://api.apigw.smt.docomo.ne.jp/amiVoice/v1/recognize?APIKEY={}".format(APIKEY)
files = {"a": open(PATH, 'rb'), "v":"on"}
r = requests.post(url, files=files)
message = r.json()['text']
print message
return message

def dialogue(message="こんにちは"):
url = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY={}".format(APIKEY)
payload = {
"utt": message,
"context": "",
"nickname": "魔理沙",
"nickname_y": "マリサ",
"sex": "女",
"bloodtype": "B",
"birthdateY": "2000",
"birthdateM": "12",
"birthdateD": "31",
"age": "17",
"constellations": "双子座",
"place": "東京",
"mode": "dialog",
"t":20
}
r = requests.post(url, data=json.dumps(payload))
print r.json()['utt']
return r.json()['utt']

def talk(message="こんにちは",card=1,device=0):
res = subprocess.check_output('/home/pi/aquestalkpi/AquesTalkPi " ' + message.encode('utf-8') + ' " | aplay -Dhw:{},{}'.format(card, device), stderr=subprocess.STDOUT, shell=True)

pygame.mixer.init()
if __name__ == '__main__':
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
PATH = '/var/tmp/tmp.wav'
APIKEY='xxxxxxxxxxxxx' #DocomoAPI Key
CARD = 1 #OUTPUTの指定
DEVICE = 0 #OUTPUTの指定
#サンプリングレート、マイク性能に依存
RATE = 16000
#録音時間
RECORD_SECONDS = 3
# pygame
pygame.mixer.music.load("/home/pi/warning1.mp3")
pygame.mixer.music.play()
time.sleep(1)
pygame.mixer.music.stop()
#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(PATH,'w')
out.setnchannels(1) #mono
out.setsampwidth(2) #16bits
out.setframerate(RATE)
out.writeframes(data)
out.close()

p.terminate()

message = recognize()
talk_message = dialogue(message)
talk(talk_message, CARD, DEVICE)


で、 APIKEY='xxxxxxxxxxxxx' #DocomoAPI Keyのxxxxxxxxxxxxxの部分を先ほど取得したAPIkeyに書き換えてください

それでは試してみましょう

$ python dialogue_test.py

Please input recoding time>>>3
~~snip~~
戦争をどう思う? ←マイクから入力した内容
非効率だ ←Rasタソの回答

みたいな感じになります

ちなみに"jack server is not running or cannot be started"と出た後に喋ってください

#最後に

今後追加で編集する予定です


ボタンを作る

まずシェルを書きますRaspberry Piに電源スイッチを付けたくてシェルクスリプトについて少し調べた話

sudo nano /home/pi/push_botton.sh


/home/pi/push_botton.sh

#!/bin/sh

GPIO=22 #使用するGPIOポート
PUSHTIME=1 #押す秒数

#初期設定
echo "$GPIO" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio$GPIO/direction
echo "low" > /sys/class/gpio/gpio$GPIO/direction

#1秒間押されるまで待つ
cnt=0
while [ $cnt -lt $PUSHTIME ] ; do
data=`cat /sys/class/gpio/gpio$GPIO/value`
if [ "$data" -eq "1" ] ; then
cnt=`expr $cnt + 1`
else
cnt=0
fi
sleep 1
done

#dialogue_test.pyの実行
python /home/pi/dialogue_test.py
done


でスイッチを用意します

<表面実装用タクトスイッチ 4.5mm×4.5mmスイッチ 全高3.8mm>10個<1sw-000>

or

表面実装用タクトスイッチ SKRPACE010(5個入): パーツ一般 秋月電子通商 電子部品 ネット通販

and

【共立エレショップ】>> L型ヘッダーソケット 1列2P: 【能動・受動・機構パーツ】 << 電子部品,半導体,キットの通販

※<表面実装用タクトスイッチ 4.5mm×4.5mmスイッチ 全高3.8mm>10個<1sw-000>はL

型ヘッダーソケットと若干方が合っていない 自分でどうにかしよう

で次の写真のようにつける



(参考サイトのやつです自分で撮るのが面倒だった)

sudo nano /etc/rc.local


/etc/rc.local

# Print the IP address

_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi

su -c /home/pi/push_botton.sh #追加
exit 0


で最後に再起動

そうしてボタンを押してみましょう

音声会話ができるはずです


最後に

↑これを書くのは2回目ですが、

大体こんな感じでgoogle speakerに匹敵?するものが作れたのではないでしょうか

少なくとも1/2の価格で...

最後に(三回目)

まだ追加編集をするかもしれません

きっとraspberry piはshoutdown処理が必要だぞこれでは実用性がないではないか

という人がいると思うのでshoutdown機構の追加方法をいつか書きたいと思います

(今はいろいろと事情があって書けないだなんて言えない...)