LoginSignup
16
28

More than 5 years have passed since last update.

Raspberry pi で音声会話をする

Last updated at Posted at 2017-09-20

はじめに

それでは作っていきましょう(こんなに簡単に書いていますが
結構手こずりました)

用意すべきもの

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
型ヘッダーソケットと若干方が合っていない 自分でどうにかしよう
で次の写真のようにつける
alt
(参考サイトのやつです自分で撮るのが面倒だった)

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機構の追加方法をいつか書きたいと思います
(今はいろいろと事情があって書けないだなんて言えない...)

16
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
28