Julius 音声認識ソフト part4 音声による操作準備

シェアする

Pocket

今朝は寒かったですね。今年一番の寒さだなんて、天気予報では言っとりました。12月下旬から、1月並の寒さと聞いて、60爺は、コートを引っ張り出して着てきました。ほんとに寒くて、コート着てきて正解でした。最近は、12月まではコートを着ないなんて言う見栄っ張りなことは止めています。皆さんも、お身体には気をつけてください。

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

1.概要

今回は、認識した音声によるコントロール開始の準備に入ります。juliusの認識した音声を分析して、その言葉を判定するプログラムを作成してみます。

このプログラムが出来れば、判定した左記に、たとえば、音声の読み上げを設定することで、次のことが実現できます。
①言葉に反応してある音声を流したり、ニュースをしゃべらせる。
②今までにやってきた電光掲示板に反応した言葉を表示し出来る。
③「前へ進め」や「バックして」など、言葉に反応する6脚ロボットを仕立てられる。
うーん、すごいぞ(感動!)

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

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

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

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

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

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

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

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

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

3.XMLパーサでの解析

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

(1)お約束のエラー

しかし、動かしてみると、これが思うように動かない。余計な、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"/>
.
(2)エラー対策:余分な文章は消去する

そこで、最後の「</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.」が存在しない時の備えです。で、この該当文字が見つかったときは、@@@を改行マークに置き換えて、元の文章に戻します。

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

(3)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()
(4)XMLパース解析プログラムの実行

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

① juliusの起動です。

② pythonプログラム起動

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

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

③ 指令を与える

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

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

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

4.今回の結論

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

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

乞う、ご期待!

Julius 音声認識ソフト part3 フリーでも文法ファイルで認識率アップ!

Julius 音声認識ソフト 番外編 音声でロボ操作準備で障害続出

スポンサーリンク

シェアする

フォローする