RAPIRO(ラピロ)の音声制御(参考:日経Linux 2014 6月号)
『日経Linux 2014年 06月号 』に記載されていた、ラピロを音声制御する記事を実践してみました。実施例を増やすことが目的なので、ここでは実現に必要な最低限のことしか書いていません。周辺知識とか、しっかりした情報が必要な人は、上記書籍を購入してください(開発者かつ著者の石渡さんへの敬意として)。
ちなみに、自分のやり方がどこか間違っていたのかもしれませんが、掲載記事のままだとうまくいかないところがあったので、そのあたりは補完しています。
さて、記事ではラピロにマイクを装着するために『響音4』というUSBオーディオデバイスを使っていますが、持ち合わせていなかったので、手元にあったUSBマイクを使用することにしました。ELECOMの『HS-MC02UBK 』というマイクです。音声出力には、Raspberry Pi基盤上のステレオミニジャックにPC用のスピーカーを繋ぐ形でテストします。
まず、マイクが利用可能かの確認です。マイクを接続する前に、以下のコマンドを実行。
$ lsusb Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 006: ID 2019:1201 PLANEX Bus 001 Device 007: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 004: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 005: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB $ arecord -l **** List of CAPTURE Hardware Devices ****
たくさん認識されているのは、自分がセルフパワーのUSBハブを繋いでいるからですね。次に、マイクを接続して同じ事をします。
$ lsusb Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 006: ID 2019:1201 PLANEX Bus 001 Device 007: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 004: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 008: ID 0d8c:0139 C-Media Electronics, Inc. Bus 001 Device 005: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB $ arecord -l **** List of CAPTURE Hardware Devices **** card 1: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio] Subdevices: 1/1 Subdevice #0: subdevice #0
ちゃんと認識されているようです。
次に、録音のテスト。
record -D hw:1,0 -f S16_LE -r 16000 test.wav Recording WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono Warning: rate is not accurate (requested = 16000Hz, got = 44100Hz) please, try the plug plugin
記事と同じで、これではうまくいかない。なので、以下のコマンドを実行。
$ arecord -D plughw:1,0 -f S16_LE -r 16000 test.wav Recording WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
この状態で喋って、Ctrl+Cで録音終了。次に、録音できるかのテストです。
$ aplay test.wav
実行すると、「ブッ」と音をたてて、Raspberry Piがフリーズしてしまいました。起動してからステレオミニジャックにPC用スピーカーを繋げたのがマズかったのかしら。
再起動して同じコマンドを実行すると、今度はきちんと再生されました。
$ aplay test.wav Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono
さて、これでハードウェア関係は準備できたので、次に音声認識エンジン「Julius」のダウンロードです。現時点(5/18)での最新版は、記事と同じ4.3.1のようなので、記事に記載されているコマンドをそのまま実行します。
$ wget http://jaist.dl.sourceforge.jp/julius/60273/julius-4.3.1.tar.gz $ wget http://jaist.dl.sourceforge.jp/julius/60416/dictation-kit-v4.3.1-linux.tgz
次に、必要なパッケージをインストール。
$ sudo apt-get install libasound2-dev libesd0-dev libsndfile1-dev
次に、juliusのビルド&インストール。
$ tar xzvf julius-4.3.1.tar.gz $ cd julius-4.3.1/ $ ./configure -with-mictype=alsa $ make $ sudo make install
次に、ディクテーションキットを解凍して、出来たフォルダに移動。
$ tar xzvf dictation-kit-v4.3.1-linux.tgz $ cd dictation-kit-v4.3.1-linux/
juliusが使用するオーディオデバイスを指定して、記事に書いてあるテストプログラムを実行。
$ export ALSADEV=plughw:1,0 $ julius -C main.jconf -C am-gmm.jconf -demo
音声認識する前に毎回環境変数ALSADEVをexportするのが面倒であれば、Raspberry Piのホームディレクトリにある”.profile”ファイルの末尾に以下を記述しておく。
export ALSADEV=plughw:1,0
これで、Raspberry Piにログインするたびに自動的に環境変数ALSADEVがexportされる。.profileへの追記を即有効にする場合は、
$ source .profile
を実行。このあとprintenvをすれば、環境変数ALSADEVが有効になっていることが確認できます。
さて、<<< please speak >>>が表示されてから喋ると、認識された文字列が表示されるそうなのですが、全然認識されません。
「辞書ファイルがでか過ぎるんじゃなかろうか」ということで、もう少し絞り込んだ辞書ファイルを作成。
$ sudo vim rapiro.dic
例えば中身はこんな感じです。
<sil> silB <sil> silE <sp> sp ラピロ r a p i r o はっしん h a q sh i N バンザイ b a N z a i たたかえ t a t a k a e
最後の単語が出て来たのは、作業中になぜか『仮面ライダー龍騎』の神崎士郎お兄ちゃんが頭に思い浮かんだからです。
次に、この辞書ファイルを利用するための設定ファイルを作成します。
$ sudo vim rapiro.conf
中身はこんな感じです。
-w rapiro.dic -v model/lang_m/bccwj.60k.htkdic -h model/phone_m/jnas-tri-3k16-gid.binhmm -hlist model/phone_m/logicalTri -n 5 -output 1 -input mic -input alsa -rejectshort 800 -lv 1500 -demo
それでは、この辞書ファイルを使って再チャレンジ。
$ julius -C rapiro.conf ... STAT: AD-in thread created pass1_best: ラピロ<input rejected by short input> pass1_best: はっしん<input rejected by short input> pass1_best: たたかえ<input rejected by short input> pass1_best: バンザイ <<< please speak >>>
今度は無事に認識されました。
次に、Juliusを音声認識サーバとして機能させるために、rapiro.confの最後に以下を追記します。
-module
この状態でJuliusを実行して、サーバとして動作していることを確認する。
$ julius -C rapiro.conf ... tat: server-client: socket ready as server /////////////////////////////// /// Module mode ready /// waiting client at 10500 ///////////////////////////////
確認できたら、Ctrl+Cで終了。
次に、音声認識結果がラピロの制御コマンドになるよう、rapiro.dicを修正する。こんな感じです。
<sil> silB <sil> silE <sp> sp #M6 r a p i r o #M1 h a q sh i N #M5 b a N z a i #M9 t a t a k a e #M0 t o m a r e
できたら、後は制御コマンドをJuliusから受け取って、Arudino基盤にシリアル通信するプログラムを作成します。仮に”rapiro_main.py”として、中身はこんな感じ。本に書いてあることそのまま。
!/usr/bin/env python # -*- coding: utf-8 -*- import socket import serial host = 'localhost' port = 10500 clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientsock.connect((host, port)) com = serial.Serial('/dev/ttyAMA0', 57600, timeout=10) while True: recv_data = clientsock.recv(512) com.write(recv_data)
これで、一応準備完了のはず。
ということで、Juliusをモジュールモードで起動してから、さっきの音声/コマンド変換プログラム”rapiro_main.py”を実行する。
$ julius -C rapiro.conf & $ sudo python rapiro_main.py
雑誌記事によれば、これで完成…のハズなんだけれど、ラピロがうんともすんとも言わない。
“rapiro_main.py”でprint出力してみると、juliusからは認識した単語そのものが送られてくるのではなく、認識結果をXMLで表したものが送られてきている。ということは、パースしてやらなきゃいけないです。これについては雑誌記事ではまったく触れられていないので、ここまでの自分の手順がどこか間違っていたのかもしれませんが、このあたりを見てみると、元々モジュールモードではXMLで送られてくるのが基本のようなので、先ほどの”rapiro_main.py”にXMLパース処理を追加します。
#!/usr/bin/env python # -*- coding: utf-8 -*- import socket import serial import xml.etree.ElementTree as ET host = 'localhost' port = 10500 clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientsock.connect((host, port)) com = serial.Serial('/dev/ttyAMA0', 57600, timeout=10) while True: recv_data = clientsock.recv(512) # juliusの区切り文字で分割 sp = recv_data.split('.\n') #print(sp) for elem in sp: if(elem != ''): try: root = ET.fromstring(elem) for word in root.iter('WHYPO'): command = word.get('WORD') print(command) com.write(command) except: print("Failed to parse")
自分がほとんどpython使ったことがないのと、XMLのパースをほとんどやったことがないというので、ものすごく適当なパース処理になっています。Juliusからはもっとたくさんの情報がXMLで送られてきているので、ちゃんとした音声認識をしたいとか、もっと効率良く処理したいとかありましたら、適宜パース処理をしっかり書いてください。
それでは、あらためて実行です。
$ julius -C rapiro.conf & $ sudo python rapiro_main.py
これで動きました!(冒頭の動画参照)
よかったよかった。
さて、これでWiiリモコンと音声入力の二つの方法でラピロを操作できるようになりました。要するに何かしらの入力を受けつけて、それをラピロ用のコマンドに変換して、Arduinoにシリアル通信してくれるものを作ればよいわけですね。
次は勉強がてら、スマホで操作できるようにしよう…と思うものの、さすがにちょっと遊び過ぎたので、多分やれるのは7月ぐらいになりそうです。
ディスカッション
ピンバック & トラックバック一覧
[…] 音声制御自体は前にも音声認識エンジンのJuliusを使ってやったことがあるのですが、あれはラピロに直接接続した有線マイクを使って、ラピロ本体で音声認識をすることで実現していま […]
[…] というわけで、ラピロの中にマイクを組み込むことができました。昔やったJuliusと組み合わせれば、おそらくより自然な形でラピロとコミュニケーションが取れると思います。 […]
[…] ラピロの音声制御はちょうど1年ぐらい前にやったことがあるのですが、あのときは完全にデモ用というか、ラピロの頭の後ろからUSBマイクのコードがビローンと伸びている状態でしたし […]
[…] あとは待ち受け用のPythonプログラム作成です。以前作成したときは、この待ち受け用プログラムがそのままラピロ本体のArduino制御プログラムも兼ねていました。具体的には、認識した音 […]