Python

音声認識を使ってラズパイと会話をする(Julius,OpenJtalk使用)

スポンサーリンク

どうも。まっきーです!

JuliusとOpenJtalkを使って、Siriっぽいのできないかなと思って簡単なやり取りができるシステムを作ってみました。(AIではありません…)

今回の目的は以下の二つです。

  • subprocessを使ってモジュールモードをpythonから起動させる
  • ラズパイに話しかけたら、音声が帰ってくるプログラムの作成

モジュールモードをpythonから起動させる

モジュールモードとは

音声認識エンジンのJulius は,独立した認識サーバとして動作し, 外部クライアント(今回はPython)とソケットを介してデータをやりとりすることができます。これを行うためのモードがモジュールモードです。

データのやり取りをするためには、ターミナルを二つ開き一方はモジュールモード(サーバー)一方はクライアント(Python)というよう設定しなければなりませんでした。(詳しい解説はラズパイで音声認識を見てください!)

わざわざ設定するのってめんどくさいですよね。そこで、subprocessモジュールというものを用いてPythonからモジュールモードを起動するよう設定をしていきます。

subprocessの説明

subprocessとは、PythonスクリプトからOS上の別プロセス(コマンド)を起動するためのモジュールです。

実行されているプログラムをプロセスと言い、これを実行中に別プロセスを立ち上げたい。その際子プロセスというものが作成されます。

通常、子プロセスの出力を親プロセス(実行しているプロセス)は取得することはできない.しかしexecの前にプロセス間通信の手法(パイプ)を適切に設定してやることで,出力を得ることが可能になります。

つまり、子プロセス側でPIPEにつなぎ変えることで親プロセス側とデータのやり取りが可能になります。

注意して使わなければ「デッドロック」と呼ばれるお互いにロックかけている状態になってしまうことがあるそうです。ですが今回は加味しないことにします(詳しくないので(-_-;))

ソースコード

JuliusモジュールモードをPythonから立ち上げるコードは以下のようになります(後で使用するために関数にしてあります)

subprocessにはrunメソッドもありますが、汎用性の高いPopenを使用しています。

import subprocess

def julius_on():
    julius = ['julius']
  #jconf設定ファイルを読み込む.ファイルの内容がこの場所に展開される.
  #ディレクトリはご自身の使用している環境に合わせて変更してください
    jconf1 = ['-C','julius/julius-kit/dictation-kit-4.5/main.jconf']
    jconf2 = ['-C','julius/julius-kit/dictation-kit-4.5/am-gmm.jconf']
    option = ['-module','-demo']
    cmd = julius + jconf1 + jconf2 + option
    subprocess.Popen(cmd,stdin=subprocess.PIPE)

ラズパイに話しかけたら、音声が帰ってくるプログラムの作成

では実際にSiri風のものを作成していきます。

Julius、OpenJtalkのインストールは完了しているものとします。

まだできていない方は以下の記事を参考にしてください。

ディレクトリの階層は以下のようになっています。

$ tree
Pi
├──julius
│    ├──julius-4.5
│    └──julius-kit
│
├── julius_talk.py(mainとなるファイル)
└── voice.py(音声発信用のファイル)

ソースコード(voice.py)

import subprocess

def jtalk(t):
    open_jtalk=['open_jtalk']
    mech=['-x','/var/lib/mecab/dic/open-jtalk/naist-jdic']
    htsvoice=['-m','/usr/share/hts-voice/mei/mei_normal.htsvoice']
    speed=['-r','1.0']
    outwav=['-ow','open_jtalk.wav']
    cmd=open_jtalk+mech+htsvoice+speed+outwav
    c = subprocess.Popen(cmd,stdin=subprocess.PIPE)
    c.stdin.write(t.encode())
    c.stdin.close()
    c.wait()
    aplay = ['aplay','-q','-Dplughw:1,0','open_jtalk.wav']
    wr = subprocess.Popen(aplay)

-Dplughw:1,0-Dplughw(カード番号、デバイス番号)です。

aplay -l コマンドでご自身の番号を確認して修正してください。

カード番号・デバイス番号確認方法

ソースコード(julius_talk.py)

import socket
import time
import subprocess
import voice
#モジュールモード起動関数
def julius_on():
    julius = ['julius']
    jconf1 = ['-C','julius/julius-kit/dictation-kit-4.5/main.jconf']#jconf設定ファイルを読み込む.ファイルの内容がこの場所に展開される.
    jconf2 = ['-C','julius/julius-kit/dictation-kit-4.5/am-gmm.jconf']
    option = ['-module','-demo']
    cmd = julius + jconf1 + jconf2 + option
    subprocess.Popen(cmd,stdin=subprocess.PIPE)
#音声認識関数
def julius():
    time.sleep(3)
    host = '127.0.0.1'   # juliusサーバーのIPアドレス
    port = 10500         # juliusサーバーの待ち受けポート
    data_size = 1024     # 受信データバイト数
    # socket通信でjuliusサーバーに接続(接続要求)
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))

        strtemp = ""#話した言葉を格納する変数
        fin_flag = False #話終わりのフラグ
 
        
        while True:
            # juliusサーバからデータ受信
            data = s.recv(data_size).decode('utf-8')

            for line in data.split('\n'):
                # 受信データから、<WORD>の後に書かれている言葉を抽出して変数に格納する。
                # <WORD>の後に、話した言葉が記載されている。
                index = line.find('WORD="')
                if index != -1:#認識した文字列があれば
                    #strtempに話した言葉を格納
                    strtemp = strtemp + line[index+6:line.find('"',index+6)]
                # 受信データに</RECOGOUT>'があれば、話終わり ⇒ フラグをTrue    
                if '</RECOGOUT>' in line:
                    fin_flag = True
                if '<RECOGFAIL/> ' in line:
                    print("聞き取れませんでした")
                    fin_flag = False

            if fin_flag == True:
                if "いい天気" in strtemp:
                    voice.jtalk("そうですね")
                    print(strtemp)
                elif "調子は" in strtemp:
                    voice.jtalk("元気ですよ")
                    print(strtemp)
                elif "写真" in strtemp:
                    voice.jtalk("写真をとります,3,2,1")
                    print(strtemp)
                else:
                    voice.jtalk("もう一度行ってください")
                    
            
                fin_flag = False
                strtemp = ""
                
if __name__ == "__main__":
    julius_on()
    julius()

モジュールモードを起動してから、sleepが入らないとうまくPython側と接続できないので3秒のsleepを入れています。

結果

音声載せられなくてすみません。

少しレスポンスが遅い感じがしますが、認識後にしっかりしゃべってくれます。

本物のSiriの足元にも及ばないですが、Siri風のものはできたのではないでしょうか(笑)

終わりに

レスポンスは少し遅い感じになってしまいましたが、それっぽくはなったのではないでしょうか?

電気をつけたりカメラをとったりする機能を付け加えたりすればもっと面白そうですね!

近いうちにやってみたいと思います!今回はここまでです。ありがとうございました!

参考サイト

subprocessの使い方(Python3.6)

スポンサーリンク

-Python