介護ロボットを創る(音声認識機能編)
娘:ねえ、ネエ お母さん! お父さんAIスピーカー作ってんだって!
娘:『フリスク! テレビつけて!』って言えば良いのね
・・・・・などと無謀な会話から始まった介護ロボットの作成
フリスク(RaspberryPiZW)が赤外線リモコンになったので、
AIスピーカーらしく声に反応してお喋りしてもらおーw _(_^_)_
音声合成システムに”Open JTalk”なるものを使う
併せて、音声認識システムにおなじみの”Julius”と連携させて
出来たシステムの動作状況はこの(↓)とおり 大うけ(爆
では、前回(赤外線リモコン部)以降の技術の紹介 少し長いです _(._.)_
<音声認識システムにおなじみの”Julius”と連携>と<音声合成システムに”Open JTalk”なるものを使う>編です
1.音声認識システムにおなじみの”Julius”と連携
(1)Julius関連パッケージのinstall
ジュリアス(Julius)のサイトからjulius-4.4.2.tar.gzをダウンロードし、
また、同じジュリアスのサイトから音声を文字に変換するソフトdictation-kit-v4.4.zipをダウンロード
両ファイルはいずれもRaspberryPiのユーザ(pi)のホームディレクトリーに配置する
1 2 3 4 5 6 7 8 9 10 11 12 |
$ cd # Juliusのインストールに必要なライブラリをインストール $ sudo apt-get install libasound2-dev # Juliusをインストール $ tar zxf julius-4.4.2.tar.gz $ cd julius-4.4.2 $ ./configure --enable-words-int --with-mictype=alsa $ make $ sudo make install # dictation-kit-v4.4.zip をホームに解凍 $ cd $ unzip dictation-kit-v4.4.zip |
(2)マイクの調整とJuliusの動作確認
1 2 3 4 5 |
piの環境設定ファイル .bashrc にマイク・デバイスが使えるように環境変数を設定 $ vi .bashrc # 最終行に次の1行を追加記述 export ALSADEV=plughw:1,0 # RaspberryPiにUSBマイクを接続してRaspberryPiを再起動 |
1 2 3 4 5 6 7 8 9 10 11 |
# 立ち上がったら、次ののようにオプションを付けてjuliusを起動させる $ julius -C dictation-kit-v4.4/main.jconf -C dictation-kit-v4.4/am-gmm.jconf -demo しばらくすると、コンソールに次のメッセ時が出力され、音声入力待ちになる <省略> stat: adin_alsa: device name from ALSADEV: "plughw:1,0" Stat: capture audio at 16000Hz Stat: adin_alsa: latency set to 32 msec (chunk = 512 bytes) Stat: "plughw:1,0": Microphone [USB Microphone] device USB Audio [USB Audio] subdevice #0 STAT: AD-in thread created <<< please speak >>> |
マイクに向かって「ラズベリーパイ」とか「こんにちは」と喋ってみると
1 2 3 4 5 6 7 |
<<< please speak >>> pass1_best: 有す べき か 、 sentence1: レズ ベリー いっぱい 。 pass1_best: 今日 は 、 sentence1: こんにちは 。 終了は、ctl+C |
精度はともかく音声を認識しているようなのでOK
(3)音声辞書の作成
まず、音声認識させたい用語の”文字”と”読み”を次のようにremocon.yomiファイル作成する
左が”文字”で右が”読み”で間に半角スペースを入れます
1 2 3 4 5 6 7 8 9 10 11 |
$ more remocon.yomi フリスク ふりすく 電気点けて でんきつけて 電気消して でんきけして TV点けて てれびつけて TV消して てれびけして 五月蠅い うるさい エアコン点けて えあこんつけて 暑い あつい エアコン消して えあこんけして 寒い さむい |
このyomiファイルを音声辞書に変換します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# yomiファイルから辞書ファイルに変換するツール(addsil.pl)をダウンロード $ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/addsil.pl $ chmod a+x addsil.pl # 変換します $ iconv -f utf-8 -t euc-jp remocon.yomi | ./julius-4.4.2/gramtools/yomi2voca/yomi2voca.pl | iconv -f euc-jp -t utf-8 | ./addsil.pl > remocon.dic # 返還後の辞書ファイルremocon.dicは次のようになっています $ more remocon.dic <s> [] silB </s> [。] silE フリスク f u r i s u k u 電気点けて d e N k i ts u k e t e 電気消して d e N k i k e sh i t e TV点けて t e r e b i ts u k e t e TV消して t e r e b i k e sh i t e 五月蠅い u r u s a i エアコン点けて e a k o N ts u k e t e 暑い a ts u i エアコン消して e a k o N k e sh i t e 寒い s a m u i |
この辞書ファイルを使えるよう既存の辞書ファイル定義の変更を行う
1 2 3 4 5 6 7 8 9 10 |
$ cd $ vi dictation-kit-v4.4/main.jconf # 次のように、28行目をコメントアウトし、1行追加する ## 単語辞書ファイル ## #-v model/lang_m/bccwj.60k.htkdic -v ../remocon.dic # 更に、256行目の800を400に変更(400ms以下の短い音は認識しないで無視する設定に変更) -rejectshort 800 => 400 |
更に、声でリモコン操作できるようpythonで次のremocon.pyを作成する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# -*- coding: utf-8 -*- import socket from io import StringIO import re import subprocess try: unicode # python2 def u(str): return str.decode('utf-8') pass except: # python3 def u(str): return str pass host = '127.0.0.1' port = 10500 bufsize = 1024 meirei_mati = 0 buff = StringIO(u('')) pattern = r'WHYPO WORD=\"(.*)\" CLASSID' try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) while True: data = sock.recv(bufsize) buff.write(data.decode('utf-8')) data = buff.getvalue().replace('> ', '>\n ') if '\n' in data: lines = data.splitlines() for i in range(len(lines)-1): if lines[i] != '.': #print(lines[i]) m = re.search(pattern, lines[i]) if m: word = m.group(1) # 認識された単語wordの中に、u('...') という文字列が含まれるかどうかを # チェックし、文字列に応じたアクションを記述します。 if u('フリスク') in word: print(word) try: print('命令待ち') meirei_mati = 1 except OSError: print('command not found.') elif u('電気点けて') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Lon.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('電気消して') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Loff.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('TV点けて') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Ton.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('TV消して') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Toff.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('五月蠅い') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Tmu.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('エアコン点けて') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Aon.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('暑い') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Adn.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('エアコン消して') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Aoff.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') elif u('寒い') in word: print(word) args = ['python3','irrp.py','-p','-g25','-f','Aup.code','light:on'] if meirei_mati : meirei_mati = 0 try: subprocess.Popen(args) except OSError: print('command not found.') buff.close() buff = StringIO(u('')) if lines[len(lines)-1] != '.': buff.write(lines[len(lines)-1]) except socket.error: print('socket error') except KeyboardInterrupt: pass sock.close() |
注1)最初に「フリスク」と発生したあと、命令を発声するすることとし誤動作を防止している
注2)操作命令のコード(Lon.code~Aup.code)はあらかじめ作ってホームディレクトリーに設置しておくこと
<音声合成機能編>は次回に回しますーw _(_^_)_