2010年10月29日金曜日

3.1 声で動かす偽バイク




DVDのリモコンで偽バイクを動かすようにしたが、孫はそれほど感激した様子ではなかったので別の仕掛けを考えることにした。「孫とおじいちゃんの言うことだけきく偽バイク」、というアイデアを思いつきこれに挑戦することにした。
音声認識をやることになるが、PICではRAMも少ないし信号処理をやるのは難しいため、信号処理はWindowsPCで行い、PCのCOMポートに接続した赤外線リモコンで偽バイクを動かすことにした。
今回は、PC用の赤外線リモコン作成と、音声認識を行うPCのアプリの2つを作成することになった。

3.1.1 PC用赤外線リモコン

使用しているPCはノートブックでシリアルポートはついていない。USBをシリアルに変換するものを探してみたところ、マルツのWebでUSBをTTLレベルのシリアルに変換するボードを発見した。24ピンのICのサイズになっており使いやすそうなのでこれを調達。
リモコンにもレゴとPICを使わないといけないので、シリアル信号を赤外線リモコンのデータに変換するためにPICを使うことにした。最初の散歩のお供で使った8ピンのPIC12F683を選択。

ハードウエア製作
USBをシリアルに変換するボードにピンを取り付け、PICを載せた基板に24ピンのICソケットを取り付け実装した。PIC周りの回路は、赤外線LEDと動作表示用のLEDがつくだけのシンプルなもの。電源はシリアル変換基板からもらう。
PICの基板の下にレゴのブロックをネジ止めし、レゴの車輪を飾りに取り付けた。

ソフトウエア製作
PIC12F683のソフトはアセンブラで作成した。
PCからのコマンドは'f'が前進、'b'が後退、's'が停止、の3つだけとする。

・シリアルデータ受信
PIC12F683にはUARTはないので、ソフトで信号を受信する。メインループで入力信号を監視し、スタートビットを受け取ったら受信を開始する。
調歩同期式なので、本来は入力信号の変化毎に同期をとるべきだが、実際には最初だけ同期をかけ、後は8ビット分のデータを読むまで続ける、というのが一番確実に動作することが判明した。
データ読み込みはTimer0の割り込みで処理する。スタートビット受信後1.5ビット分の時間でTimer0の割り込みをかけ、最初のビットの入力を読み、その後は1ビット分の時間でTimer0の割り込みをかけて続きの入力を読む。8ビット分のデータを受信し、入力がアイドル状態になったら受信完了として、受け取ったデータを処理する。

・リモコン用データ作成・送信
リモコン用の赤外線LEDの点灯はデューティー30%程度のPWMで行うのが普通なので、PICのPWMを使って点灯することにした。シリアル送信の時間管理はTimer1で行う。
データは前回使用したパナソニックのリモコンと同じフォーマットとする。
リーダー部送信後、データを送信し、最後にトレイラー部を送信する。

3.1.2 音声認識アプリ

開発環境
Windows上のソフト開発環境として最も一般的なVisualStudioを見てみたが、10万円以上することが判明。VisualC++だけでも2万円程度ということで困ってしまったが、VisualC++ Express Editionというものなら無料で入手できることを発見。ためらわずにこれに決めた。インストールして試してみたところ、GUIを作るリソースエディターがついていないことが判明。まあ、無料なのでしかたないし、今回のソフトはGUIなしでもできるので、コンソールアプリケーションとして作成することにした。

認識エンジン
ネットを見たところ、Windows環境ではマイクロソフトが提供する音声認識エンジンが使える、ということであったが、今回は「前」「後ろ」「止まれ」の3つの単語だけを認識できればよいし、自分で作るところに意義があると決め、認識エンジンも自作することにした。
分析手法としてFFTは必須と思われるので、まずFFTのソースをネットから調達。

認識手法
まず、「前」「後ろ」「止まれ」の特徴を探してどの単語が特定し、次に基準の倍音構成と比較して話者を特定する、という作戦でトライしてみた。作者の声で3つの単語を認識できるところまでできたので、孫のリー君の声を録音してリー君の声でも認識できるか試してみたところ、声の特性が全く違いうまく認識できないことが発覚。
ゼロからやり直しで、「前」の「ま」の部分、「後ろ」の「う」の部分、「止まれ」の「と」の部分をFFTしたサンプルを用意し、これと入力信号を比較する方法をトライしてみた。サンプルと入力の特定の周波数ごとのデータの差の2乗の和を計算し、これが一定値以下なら単語認識とする。これで3つの単語を認識できるようになった。
単語ごとに1ヶから4ヶのサンプルを用意し、サンプルの数で認識率が変化するか調査してみたが、作者の声だとあまり差がなかったのでサンプルは単語毎に1つとした。これで作者の声で90%以上の認識率が得られた。
リー君の声でサンプルデータを用意して実験してみたが、リー君は叫んだりマイクにかみつきそうになったりしてしゃべったりするので認識率はかなり低い。サンプルも1つではなくて複数用意することにして、サンプルの録音ももっと時間をかけてやり直す必要がある。
また、他の人の声ではほとんど認識できず、当初の目標は達成できた。

アプリの構成
・音声取り込み
PCに接続したマイクから音声を取り込む。アプリ立ち上げ時にAudio Input Deviceをオープンし、取り込みを開始する。入力用のバッファはダブルバッファにしてある。音声の振幅が一定値を超えたら、そこからバッファ1つ分のデータを認識エンジンに渡す。

・認識エンジン
◇入力信号ノーマライズ
入力信号の最大値を検索し、最大値が16ビットの最大値になるようノーマライズ。
◇FFT、サンプルとの差分計算
入力を64サンプル間隔で15回までFFTを行う、FFTのポイント数は1024でハニング窓をかける。その結果とサンプルの比較を行い、サンプル毎に差の最小値を記録しておく。
◇単語特定
差分の最小値を検索し、そのサンプルを認識対象の単語とする。差の値が一定値以下なら認識成功、そうでなければ認識失敗とする。

・コマンド送信
認識成功と判断したら、COMポートから'f', 'b', 's'を送信する。

認識のできばえ
作者の声では9割以上の確率で認識が成功する。リー君はたまに認識が成功する程度。他の人ではほとんど認識失敗となる。

孫のリー君の反応
たまに認識が成功してバイクが動くので、面白がって大声で叫びながら遊んでいる。