Julius音声認識ソフトpart4!音声によるロボット操作の準備作業

2017年11月20日

さて、前回の辞書作成により、簡単な文言であれば、Juliusちゃん(ちゃんづけに昇格^^;)は、60爺の活舌の悪い音声も認識してくれることが分かりました。

今回は、認識した音声によるコントロール開始の準備に入ります。

juliusの認識した音声を分析して、その言葉を判定するプログラムを作成してみます。

このプログラムが出来れば、判定した左記に、たとえば、音声の読み上げを設定することで、次のことが実現できます。

  • 言葉に反応してある音声を流したり、ニュースをしゃべらせる。
  • 今までにやってきた電光掲示板に反応した言葉を表示し出来る。
  • 「前へ進め」や「バックして」など、言葉に反応する6脚ロボットを仕立てられる。

うーん、すごいぞ(感動!)

と、まあ、感動するのはいいとして、どうやって動かしたらいいかを調べました。

ただいまシステムの中身② Socketを介した認識結果のXMLパース

スポンサーリンク

juliusのモジュールモード起動

まずは、Juliusをモジュールモード起動すれば良いようです。

Julius起動時のオプションに -module を付けるとモジュールモードでの起動になって、クライアントからの TCP/IP 接続待ち状態になる。

その状態でクライアントからの接続を受けると音声認識可能な状態になって、クライアントに認識結果をXMLで送信するようになると書いてあります。

先ほどの記事にも図がありますが、こんな感じになりますかね。

これは、思いのほか簡単ですね。実行コマンドの最後に、-moduleをつければいいです。

pi@raspberrypi:~ $ julius -C julius-kits/grammar-kit-v4.1/hmm_mono.jconf -lv 5000 -input mic -gram ~/source/source -nostrip -module

XMLパーサでの解析

さて、「これをXMLパーサで受け取って解析すれば、認識結果の信頼度と命令を抽出できるから、音声制御が可能になる」そうです。先ほどの記事に則って、プログラムを作成しました。

お約束のエラー

しかし、動かしてみると、これが思うように動かない。余計な、XML文が紛れ込むようで、こんなエラーになってしまいます。

Traceback (most recent call last):
File "moji.py", line 64, in 
main()
File "moji.py", line 32, in main
root = ET.fromstring('\n' + data[data.find(''):].replace('\n.', ''))
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1300, in XML
parser.feed(text)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1642, in feed
self._raiseerror(v)
File "/usr/lib/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
raise err
xml.etree.ElementTree.ParseError: junk after document element: line 10, column 0

そこで、pythonに、print文を入れて、原因を探りましょう。print文は、ここに入れています。

try:
data = ''
while 1:
if '\n.' in data:
print data
r = re.compile("(.*)(/RECOGOUT)(.*)")
m = r.match(data)
if m != None:
data = m.group(1) + '/RECOGOUT>;\n.'
print '---- replace ----'
print data
else:
print '---- None ----'
print data
ここでエラー発生!root = ET.fromstring('\n' + data[data.find(''):].replace('\n.', ''))
for whypo in root.findall('./SHYPO/WHYPO'):

出力された内容を見ていると、どうやら、先ほどの記事に出ていたXMLですが、余計な文が出力されていることが分かりました。

たとえば、以下のような分です。

・
・
<WHYPO WORD="silE" CLASSID="40" PHONE="silE" CM="1.000"/>
</SHYPO>
</RECOGOUT>
.
以下の余分な分が出力されている
<INPUT STATUS="LISTEN" TIME="994675053"/>
.
<INPUT STATUS="STARTREC" TIME="994675055"/>
.

エラー対策:余分な文章は消去する

そこで、最後の「</RECOGOUT>\n.」の後ろにつく文章は全て無視することにしました。ところが、ここで、またまた問題発生。

\n 即ち、改行マークがあると、文はそこで終わりと判断し、2行目以降は見てくれないことがわかりました。玄人の方には当たり前のことも、素人の60爺には通じません。しばらく悩みました。

そこで、改行マーク \n を@3文字に置き換えて、文を構成した後、先ほどの処理、「</RECOGOUT>\n.」の後ろにつく文章は全て無視するようにしました。

r_data = data.replace('\n', '@@@')
r = re.compile("(.*)(/RECOGOUT)(.*)")
m = r.match(r_data)
if m != None:
a = m.group(1)
data = a.replace('@@@', '\n') + '/RECOGOUT>\n.'
print '---- replace ----'
print data
else:
print '---- None ----'
print data

で、mを判定しているのは、「</RECOGOUT>\n.」が存在しない時の備えです。で、この該当文字が見つかったときは、@@@を改行マークに置き換えて、元の文章に戻します。

これにより、やっとまともに動くようになりました。

XMLパース解析プログラムの公開

今回のプログラムは、マイクから入力した言葉を受取って、それに見合う内容をprintするだけです。入力する言葉は5種類です。

  • '天気’   ⇒ print '***** t e n k i *****’
  • 'ニュース’ ⇒ print '***** n e w s *****’
  • '名前’   ⇒ print '***** n a m e *****’
  • '今日’   ⇒ print '***** t o d a y *****’
  • 'バルス’  ⇒ print '##### THE END #####’

最後の言葉は、ジブリの「天空の城ラピュタ」のラピュタ滅びの呪文です。この言葉を聴くと、プログラムも終了するようにしました。

拙いプログラムですが、下記に記載します。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket
import xml.etree.ElementTree as ET
import subprocess
import sys
import re
def main():
host = 'localhost'
port = 10500
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
try:
data = ''
while 1:
if '</RECOGOUT>\n.' in data:
# print data
r_data = data.replace('\n', '@@@')
r = re.compile("(.*)(/RECOGOUT)(.*)")
m = r.match(r_data)
if m != None:
a = m.group(1)
data = a.replace('@@@', '\n') + '/RECOGOUT>\n.'
print '---- replace ----'
# print data
else:
print '---- None ----'
# print data
root = ET.fromstring('\n' + data[data.find('<RECOGOUT>'):].replace('\n.', ''))
for whypo in root.findall('./SHYPO/WHYPO'):
command = whypo.get('WORD')
score = float(whypo.get('CM'))
if command == u'天気':
print '***** t e n k i *****'
elif command == u'ニュース':
print '***** n e w s *****'
elif command == u'名前':
print '***** n a m e *****'
elif command == u'今日':
print '***** t o d a y *****'
# PG終了 ラピュタ滅びの呪文「バルス」
elif command == u'バルス':
print '##### THE END #####'
sys.exit()
data = ''
else:
data = data + client.recv(1024)
except KeyboardInterrupt:
client.close()
if __name__ == "__main__":
main()

XMLパース解析プログラムの実行

XMLパース解析プログラムを実行します。

juliusの起動

pythonプログラム起動

別画面にて、pythonプログラムを起動します。

すると、julius側の画面が動きます。

指令を与える

マイクに向かってしゃべりました。

天気教えて、ニュース教えて、名前教えて、今日、バルスの順にしゃべりましたが、tenkiが2回出て、バルスを認識してくれませんでした。そのため、プログラムを強制終了しました。

バルスの部分に、todayが何回も出ちゃったのが分かりますね。この時の、julius側には、こんな内容が表示されました。

スポンサーリンク

最後に

何とか、喋りに対して、該当の内容を取り出してくるところまで出来ました。

バルスが効かないなど、やや、気に入らない部分もありますが、次は、6脚ロボットの音声による操作にチャレンジしてみたいと思います。

乞う、ご期待!

■思えば、「Julius音声認識」の記事も増えてきました

スポンサーリンク
この記事を書いた人

60爺

60路を越え、RaspberryPi と出会い、その関係でブログ開設(2017/2~)となりました。始めてみると、コツコツやるのが性に合ってしまい、漢字の記事から家の補修・将棋・windows10関係・別名・言い方などジャンルを拡大して今に至ってます。まだまだ、元気なので新たな話題を見つけて皆様に提供できればと思っています。「プロフィールはこちら

Julius音声認識

Posted by 60爺