2018年2月3日土曜日

Raspberry Pi + Google Assistant API + LIRCによりテレビを音声認識で操作する

0. はじめにのはじめに

2019年6月にGoogle自身がこの方法はdeprecated(廃止予定)であるとしました。2020年3月31日にRaspberry Pi 4 で動作を確認しましたが、いずれ動作しなくなる可能性はありますのでそのつもりでご覧ください。

1. はじめに

本サイトでは以前、「Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する」という記事を紹介しました。

これは、Juliusという音声認識エンジンをRaspberry Piにインストールして音声認識を実現し、その結果に基づいて家電を操作するものでした。

それに対し、本ページでは音声認識をRaspberry Piにインストールしたソフトウェアではなく、クラウド上のサービスで行う例を紹介します。

この方法は、Raspberry Piが常にインターネットに接続していなければならないという欠点があるものの、音声認識の精度が高いというメリットがあります。両方試してみて比較するのも良いでしょう。

下図は、それを実現している様子を示したものです。


また、動作中の動画が下記になります。



2. 本ページの内容を実現するために必要なもの

本ページで解説する内容はクラウドサーバーとの連携が必要なため、書籍やサイトで解説してきた内容の中で最も難易度が高くなっております。ご注意ください。

また、実行には下記のものが必要です。
  • Googleのアカウント(GmailやAndroidスマートフォンで利用するもの)
  • Raspberry Piを常時インターネットに接続する環境
  • マイク(後述)
  • スピーカー(後述)
Googleアカウントの作成方法や利用方法は本サイトでは解説できませんのでご注意ください。

3. 音声入力(マイク)と音声出力(スピーカー)の設定

本ページの内容を実現するためには、マイクとスピーカーが必要です。どのような機器を用意すればよいか解説します。

まず、マイクからです。Raspberry Piにはマイクの接続端子がありませんので、利用できる機器をあらかじめ調べておく必要があります。
本ページでは、マイクを接続するためのデバイスとして「USBマイク系」と「USBサウンドデバイス系」の2系統を試しました。

「USBマイク系」としては下記の2種類を試しました。
「USBサウンドデバイス系」としては下記の3種類を試しました。
ただし、USBサウンドデバイスを用いる場合、さらにミニプラグ接続のPC用マイクも必要となります。これはどのようなマイクでも問題ないと思いますが、例えば
のようなものがあります。

以上の「USBマイク系」と「USBサウンドデバイス系」のどれかからお好みの方法を選んでください。なお、上に示した動画では「SANWA SUPPLY MM-MCU02BK USBマイクロホン」を用いています。

一方、スピーカーについては、ミニプラグで接続可能なものであれば何でも構いません。ただし、ミニプラグでのスピーカーの接続先については3通りの選択肢があります(後述します)。

以上の物品を用意し接続した後、それらを利用可能にする設定を行います。これは、「用いるOSのバージョン」および「用いるマイクとスピーカーの組み合わせ」により計6パターンの選択肢がありますので、下記のうち該当する内容を実行してください。

3.1 (2020年2月までのOS) USBマイク+Raspberry Pi本体のミニプラグ端子へのスピーカー接続

ターミナルで以下の3コマンドを順番に実行してください。
$ amixer cset numid=3 1
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc
$ mv asoundrc .asoundrc

3.2 (2020年2月までのOS) USBマイク+HDMIディスプレイへのスピーカー接続

ターミナルで以下の3コマンドを順番に実行してください。
$ amixer cset numid=3 2
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc
$ mv asoundrc .asoundrc

3.3 (2020年2月までのOS) USBサウンドデバイスへのマイクとスピーカーの接続

ターミナルで以下の2コマンドを順番に実行してください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc2
$ mv asoundrc2 .asoundrc

3.4 (2020年5月以降のOS) USBマイク+Raspberry Pi本体のミニプラグ端子へのスピーカー接続

ターミナルで以下の2コマンドを順番に実行してください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc3
$ mv asoundrc3 .asoundrc

3.5 (2020年5月以降のOS) USBマイク+HDMIディスプレイへのスピーカー接続

ターミナルで以下の2コマンドを順番に実行してください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc4
$ mv asoundrc4 .asoundrc

3.5 (2020年5月以降のOS) USBサウンドデバイスへのマイクとスピーカーの接続

ターミナルで以下の2コマンドを順番に実行してください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/asoundrc5
$ mv asoundrc5 .asoundrc


3.4 マイクとスピーカーの動作チェック

さて、3.1~3.3のどれかの方法でマイクとスピーカーの接続および設定が終わったら、動作チェックしてみましょう。

まずはスピーカーのチェックです。ターミナルを起動して下記のコマンドを実行してみましょう。既に述べたように、本ページのコマンドはコピーと貼り付けによる実行を推奨します。
$ speaker-test -t wav
「Front, Left, …」という音声がスピーカーから流れるはずです。止めるにはターミナル上でキーボードのCtrlキーを押しながらcキーを押します(Ctrl-cといいます)。

次に、マイクの動作チェックを行いましょう。下記のコマンドを実行すると、5秒間の音声がour.rawというファイル名で保存されますので、マイクに向かって話しかけてみましょう。
$ arecord --format=S16_LE --duration=5 --rate=16000 --file-type=raw out.raw
録音が終わったら、下記のコマンドで再生してみます。
$ aplay --format=S16_LE --rate=16000 out.raw
マイクに話しかけた音声が再生されたら動作チェックは成功です。

なお、機器のボリュームの調節は下記のコマンドで起動するアプリケーションを用いるのですが、操作方法にかなり癖があります。
$ alsamixer
alsamixerの終了コマンドとして[ESC]キーは覚えておきましょう。まず、[F6]キーでサウンドカードを選択します。USBマイクの場合、矢印の上下キーで「1 USB PnP Audio Device」を選択してEnterを押します。2020年5月以降のOSならば「2 USB PnP Audio Device」となっているでしょう。 その後、[TAB]キーを繰り返し押すことで再生/録音/全てのどれかに選択肢を合わせ、上下キーで音量を調節します。調節が終わったら[ESC]キーでalsamixerを終了します。

4. サーバー側の準備

Googleの提供する方法で音声認識を実現する場合、下記の3つの方法が知られています。
  • Google Speech API v.2を用いる方法…古くからある方法。開発用途および個人用途のみ。1日50認識限定。
  • Google Cloud Speech APIを用いる方法…2017年4月にリリース。利用時の登録にクレジットカードが必要。
  • Google Assistant APIを用いる方法…Google Homeなどのようにコンピュータと対話するためのライブラリ。開発用途でのみ無料利用可能。一日500リクエストまで。
執筆時点では、3つめのGoogle Assistant APIの利用がお手軽に思えましたので、今回はこちらを試してみます。

ここからは、Raspberry Piが通信する相手となるサーバーの準備を行います。非常に長い手順となりますので、一つずつ確実に実行していきましょう。

なお、以下の手順は「Introduction to the Google Assistant Library」の解説に基づいています。

まず、Raspberry PiのChromiumブラウザを起動してGoogleにアクセスし、お持ちのGoogleアカウントでログインしておきましょう。下図の「ログイン」ボタンから行えます。


すると、下図のようなログイン画面が現れますので、IDとパスワードを入力してログインします。


ログインが済んだら、Google Assistant APIを用いるためのプロジェクトを作成していきましょう。まず、Chromiumブラウザで「Welcome to Actions on Google」のリンクをクリックして移動してください。

リンク先に下図のようなボタンがありますので、クリックしてプロジェクトを作成します。


ここでは、プロジェクトの名前を「Google Assistantのテスト」の意味で「gatest」とし、デフォルト言語を「Japanese」に、国を「Japan」にしましょう。特に、言語をJapaneseにしないと、Google Assitant APIは日本語を認識してくれません。

記入と設定が終わったら「CREATE PROJECT」をクリックします。


プロジェクト作成が終わったら、Chromium上のタブは閉じずにそのまま残しておきます。

そして、そのまま今度は「Google Assistant API」のリンクをクリックしてください。

このとき、下図の赤い四角に記されているように、作成したプロジェクト「gatest」が選択されていることを確認してください。

選択されていない場合、まずその部分をクリックしてプロジェクト「gatest」を見つけて選択します(現れるまでに時間がかかることがあります)。その後、そのタブを一旦閉じ、もう一度上「Google Assistant API」のリンクをクリックすると、下図の状態が実現すると思います。

そして、そのまま「有効にする」ボタンをクリックします。


次に、「OAuth consent screen」のリンクをクリックします。すると下記のようなページが開きますので、「メールアドレスを選択してください」の部分をクリックします。選択肢として、お使いのGoogleのメールアドレスが現れますので、それを選択します。

そして、ページ下部にある「保存」ボタンをクリックします。


ここまでの作業が終わったら、今度は「デバイスモデルの登録」の作業に移ります。

さきほど閉じずに残しておいたタブに戻り、一番下にある下記のボタンをクリックします。


すると、次のような画面に遷移しますので、「REGISTER MODEL」をクリックします。


現れたウインドウに、例えば下記のように記入します。上の2つは
  • Assistant SDK light
  • Assistant SDK developer
です。3つ目の「Light」は選択式となっています。

これらは、本来は何を入力しても良いのですが、チュートリアルページのサンプルに合わせてこのような内容にしています。

なお、ここで表示されている「Device Model id」の内容は、以下で多用しますので、コピーしてテキストエディタなどに記して控えておきましょう。


記入やメモが終わったら「REGISTER MODEL」ボタンをクリックします。

すると、遷移した先のページで以下のようなボタンが現れますので、クリックしましょう。


すると、「client_secret_XXXXXXXXXXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json」という形式の、非常に長い名前のファイルがダウンロードされます。保存先はデフォルトで /home/pi/Downloads となります。

このファイルを、Downloadsディレクトリからホームディレクトリ (/home/pi) に移動しておきましょう。

ターミナルを起動し、下記のコマンドを実行してください。なお、先頭の「$」はコマンドの先頭を表す記号ですので、コマンドには含めないでください。また、最後の「.」(ピリオド)にも意味はありますので、忘れずにコマンドに含めてください。
$ mv Downloads/*.json .
拡張子 json のファイルを、Downloadsディレクトリからカレントディレクトリ (.) に移動しています。以下、コマンドはブラウザ上でコピーし、ターミナルへ貼り付けて実行することを推奨します。

ここまで終わったらブラウザで「NEXT」ボタンをクリックします。

次に、traitsを保存(SAVE TRAITS)するためのページに遷移しますが、ここでは「SKIP」をクリックしてスキップします。

最終的に、下記のようにModel IDが確定します。先ほどメモしたModel IDが再掲されていますね。


Model IDとともに、Project IDも以下で必要となりますので、ここで調べてメモしておきましょう。Chromiumブラウザでリソース管理ページに移動すると、先ほど作成したgatestに対する Project ID(プロジェクトID)が表示されていますのでこれもメモしておきましょう。

恐らく、Model IDとProject IDは先頭が 「gatest-xxxxx」の形式で共通となっていることでしょう。

次に、Chromiumブラウザで「アクティビティ管理」のページに移動し、下記の4点の設定を確認してください。
  • 「ウェブとアプリのアクティビティ」を有効(青色)に
  • 「Chrome の閲覧履歴と Google サービスを使用するウェブサイトやアプリでのアクティビティを含める」にチェック
  • 「端末情報」を有効(青色)に
  • 「音声アクティビティ」を有効(青色)に
以上でサーバーの設定は完了です。しかし、そのほかの設定はまだまだ続きます。

5. 様々なソフトウェアのインストール

まず、ターミナルを起動し、下記の5つのコマンドを順に実行してください。なお、先頭の「$」はコマンドの先頭を表す記号ですので、コマンドには含めないでください。 長いコマンドが続きますので、ブラウザ上でコマンドをCtrl-cでコピーし、ターミナル上の「編集」→「貼り付け」によりコマンドを貼り付けて実行することを推奨します。
$ sudo apt update
$ sudo apt install python3-dev python3-venv
$ python3 -m venv env
$ env/bin/python -m pip install --upgrade pip setuptools
$ source env/bin/activate
3つめのコマンドでは、/home/pi/env にpython3の仮想実行環境を用意しています。Google Assistant SDKのチュートリアルでは、仮想環境でのインストールと実行を行っていますので、本ページでもそれに従います。

また、最後のコマンド「source env/bin/activate」では作成した仮想環境を優先して利用するよう設定しています。それにより、コマンドプロンプトは下記のように変化します。
(env) pi@raspberrypi:~ $
本ページではこれを以下のように略記し、これの後に記されたコマンドは仮想環境で実行すべきコマンドであるものとします。
(env) $
さて、Pythonの仮想環境において下記の4つのコマンドを引き続き実行していきます。これまで通りコマンドのコピーと貼り付けにより実行しましょう。実行すべきコマンドは「$」の後ろの部分のみですので注意してください。
(env) $ sudo apt install portaudio19-dev libffi-dev libssl-dev
(env) $ python -m pip install --upgrade google-assistant-library==1.0.0
(env) $ python -m pip install --upgrade google-assistant-sdk[samples]
(env) $ python -m pip install --upgrade google-auth-oauthlib[tool]
以上でツールのインストールは完了です。次は引き続きツールとサーバーの連携作業となります。途中で作業をやめず、一気に最後まで進めた方が良いです。

6. サーバーとの連携の設定

引き続き、下記のコマンドをターミナルで実行するのですが、これはこのまま実行してもうまくいきません。
(env) $ google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --scope https://www.googleapis.com/auth/gcm --save --headless --client-secrets /path/to/client_secret_client-id.json
最後の「/path/to/client_secret_client-id.json」の部分を、皆さんが「サーバー側の準備」でダウンロードしたファイル「client_secret_XXXXXXXXXXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json」で読み替えて実行しないといけないのです。

それを、下記の手順で実現しましょう。まず、上のコマンドをから末尾の「/path/to/client_secret_client-id.json」を省いた下記のコマンドをコピーし、ターミナルに貼り付けましょう。
(env) $ google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --scope https://www.googleapis.com/auth/gcm --save --headless --client-secrets 
そして、そのコマンドの末尾にスペースを入力し、さらに「client」まで書きます。すなわち、下記のようになります。
(env) $ google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --scope https://www.googleapis.com/auth/gcm --save --headless --client-secrets client
そこで、キーボードの[TAB]キーを押します。すると、ファイル名が補完され、下記のようにコマンドが完成します。そこでEnterを押して実行します。
                
(env) $ google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --scope https://www.googleapis.com/auth/gcm --save --headless --client-secrets client_secret_XXXXXXXXXXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json
なお、ファイル名の「XXXXXXXXXXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」の部分は人により異なりますので、「XXXXXXXXXXXX-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx」をそのままコピーしても正しい実行結果は得られませんので注意してください。 また、[TAB]キーを押したときにファイル名が補完されるのは、「サーバー側の準備」で、
  • ファイルをDownloadsディレクトリに保存したこと
  • 保存したファイルをDownloadsディレクトリからホームディレクトリに移動したこと
の二点が正しく行われたときのみですので良く確認してください。

基本的に本ページの内容は一つでも正しく実行されていない項目があればうまくいかないとお考え下さい。

さて、コマンドが正しく実行されると、下図のように、ブラウザでアクセスすべきアドレスが現れます(図を拡大表示して形式を良く観察してください)。


このアドレスの部分(「https://」から「consent」まで)をマウスでなぞり、ターミナル上で「編集」→「コピー」を選択してコピーします(Ctrl-cでコピーしようとするとコマンドが終了してしまいますので注意しましょう)。

そして、chromiumブラウザのアドレス欄にコピーされたアドレスを貼り付けてEnterし、ブラウザでページを移動します。

すると、アカウントの選択が求められた後、さらに下図のようにブラウザ上にgatestからのリクエストが表示されますので、許可ボタンをクリックしてください。


すると、ブラウザで下記のように長いコードが表示されますので、マウスでなぞってCtrl-cでコピーしてください。


そして、先ほどのターミナルに戻り、「編集」→「貼り付け」で貼り付け、Enterキーを押します。

すると、下図のように「credentials saved: /home/pi/.config/google-oauthlib-tool/credentials.json」と表示されて処理が完了します。


7. 動作確認

以上で動作確認を行う準備が整いました。 まず、動作確認および音声認識用のファイルをダウンロードします。ダウンロードは通常環境と仮想環境のどちらで行っても構いません。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/controlTV-ga.py
次に、動作確認を行います。もしこの時点でPythonの仮想環境に入っていなければ、次のコマンドで仮想環境に入ります。
$ source env/bin/activate
そして、実行すべきコマンドは下記になります。斜体部は上で確認した皆さんのProject IDとModel IDで置き換えて実行してください。
$(env) $ python controlTV-ga.py --project_id project-id --device_model_id model-id
実行したら、マイクに向かって「オーケー グーグル」と話しかけてください。


認識されれば上図のように「ON_CONVERSATION_TURN_STARTED」と表示されますので、そのまま「今何時?」などと話しかけてみてください。スピーカーから返答が出力されるはずです。

なお、AndroidスマートフォンやiPhoneなどをお持ちの場合、Googleのアプリケーションで「設定」→「設定」→「Assistant SDK light」とたどり、以下のように「アカウントに基づく情報」をオンにすると、「私は誰?」や「今日の予定」など、アカウントの個人情報に基づく質問にも答えてくれるようになります。なお、Googleのアプリケーションは、Android用iPhone/iPad用があります。





最後に、このサンプルプログラムを終了するには、キーボードでCtrlキーを押しながらcキーを押してください(Ctrl-cといいます)。

8. 音声でのテレビの操作

この状態で、冒頭の動画のように「オーケーグーグル、テレビつけて」などとマイクに話かけると、 テレビをオンにする信号赤外線LEDより出力される、という流れです。

ダウンロードしたプログラム controlTV-ga.py のうち、テレビの操作に関わる部分を一部抜きだしたのが以下の部分になります。
    if event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED:
        s = event.args['text']
        print(s)
        if 'テレビ' in s and  ('つけて' in s or 'オン' in s):
            args = ['irsend', '-#', '1', 'SEND_ONCE', 'TV', 'power']
            try:
                subprocess.Popen(args)
            except OSError:
                print('command not found.')
            assistant.stop_conversation()
        elif 'テレビ' in s and  ('消して' in s or 'オフ' in s):
            args = ['irsend', '-#', '1', 'SEND_ONCE', 'TV', 'power']
            try:
                subprocess.Popen(args)
            except OSError:
                print('command not found.')
            assistant.stop_conversation()
        elif 'NHK 教育' in s or 'Eテレ' in s:
            args = ['irsend', '-#', '1', 'SEND_ONCE', 'TV', 'ch2']
            try:
                subprocess.Popen(args)
            except OSError:
                print('command not found.')
            assistant.stop_conversation()
少し解説しましょう。

この部分は、「オーケーグーグル」と話しかけた後に発した言葉が「Google Assistant API」により音声認識された後の処理を表しています。「s」という変数に音声認識結果が格納されています。

そこで、「if 'テレビ' in s」と書くと、「文字列 s に'テレビ'という語句が含まれていたら」という条件となります。

さらに、and (なおかつ)、or (または)を組み合わせて条件を作成しています。

結果的に「'テレビ' in s and ('つけて' in s or 'オン' in s)」という条件で、
「sに’テレビ'と'つけて'が含まれている場合」または「sに’テレビ'と'オン'が含まれている場合」にテレビの電源がつくという動作になります。
条件文を作る際のカッコ「()」の使い方にも注意しましょう。

なお、テレビの電源を入れるための信号は、「irsend -# 1 SEND_ONCE TV power」という命令を実行することで行っています。この命令を有効にする方法は本書5章で解説されていますので、参照してください。赤外線LEDを用いた回路の作成方法も解説しています。

それらの準備を済ませると、冒頭の動画のように音声に応じてテレビを操作することができます。

なお、音声認識の結果に基づいてテレビを操作した後、「assistant.stop_conversation()」という命令を実行してGoogle Assistantとの会話をそこで打ち切っています。そうしないと、テレビへの命令を受け付けるたびにGoogle Assistantが「申し訳ありません、お役に立てそうにありません」と音声を発してしまうためです。

9. Google Assistantでのテレビ操作についての注意

以上のように、Google Assistantによる音声認識結果を直接if文で判定させることでテレビの制御を行いました。

実は、Google Assistantには、家電などのオンオフを制御する公式な方法があります。例えば、こちらのページではRaspberry Piに接続したLEDをオンオフする方法が解説されています。

しかし、この方法を用いてテレビのチャンネルを変える方法が思いつかなかったので、本ページでは音声認識結果を直接if文で判定する方法を取りました。

10. 仮想環境上のPythonを利用する際の注意

なお、本ページではGoogle Assistant SDKの公式ページで行われているのに合わせ、Python3の仮想環境を作成してその上でプログラムを実行しました。

この方法を用いると、デフォルトでは電子工作用のライブラリを用いることができません。本書ではGPIOにアクセスするためにRPi.GPIOというライブラリを用いましたが、そのままではそれを利用することができないのです。

既存ライブラリをPythonの仮想環境でも利用可能にするには、設定が必要です。
設定ファイル /home/pi/env/pyvenv.cfg 内にある
include-system-site-packages = false
という行を
include-system-site-packages = true
に変更して保存すると、既存ライブラリが仮想環境でも利用可能になります。

11. Google Assistant APIの利用制限について

Google Assistant APIは無制限に使えるわけではなく、
  • 1日当たり500リクエスト
  • 100秒当たり100リクエスト
という制限があります。ですから、何度も会話をしているとその制限を超え、結果が返ってこないことがあり得ます。

どの程度Google Assistant APIを利用しているかを確認する方法を以下に記します。

リソースの管理」ページに行き、プロジェクト名をクリックします。 現れるページの左のカラムで「割り当て」をクリックします。 そして、現れた右の表示エリアで「Google Assistant API」を見つけてクリックすると、利用状況のグラフを見ることができます。

「太平洋時間(PT)の午前 0 時にリセット」と書かれておりますので、 その時間が過ぎればリクエスト数がリセットされてまた利用可能になるはずです。

13. Raspberry Pi起動時にプログラムを自動起動する

ここで紹介したcontrolTV-ga.pyは、実行するためのコマンドがやや複雑でした。

このコマンドをシンプルに実行できるようにし、さらにRaspberry Pi起動時に自動で実行する方法を解説します。

まず、コマンドをシンプルにする方法です。ターミナルを起動し、以下の2つのコマンドを順に実行しましょう。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/start-assistant.sh
$ chmod 755 start-assistant.sh
以後実行するのはstart-assistant.shというコマンドになるのですが、その前にこのファイルの中身を編集する必要があります。まず、下記のコマンドを実行することでテキストエディタleafpadでstart-assistant.shを編集用に開きます。
$ leafpad start-assistant.sh
このファイルの中身は下記のようになっています。
#!/bin/bash

source /home/pi/env/bin/activate

# set if you are inside firewall
#export http_proxy=http://(proxy server):(port)/
#export https_proxy=http://(proxy server):(port)/
#export ftp_proxy=http://(proxy server):(port)/

python /home/pi/controlTV-ga.py --project_id gatest-xxxxx --device_model_id gatest-xxxxx-assistant-sdk-light-xxxxxx
この最後の行の「gatest-xxxxx」と「gatest-xxxxx-assistant-sdk-light-xxxxxx」の部分を皆さんが使っているモデルのIDに変更してください。この行が、いつも実行しているcontrolTV-ga.pyを実行しているコマンドが記されていることはわかるでしょう。
さらに、controlTV-ga.pyの位置がユーザpiのホームディレクトリ (/home/pi) であることを前提とされていることにも注意してください。
変更が終わったら、ファイルを上書き保存してleafpadを閉じましょう。
その後ターミナルを起動して、下記のコマンドを実行しましょう。これは仮想環境に入らずに実行して構いません。このファイル内部で仮想環境の利用設定とcontrolTV-ga.pyの実行が両方行われるので、プログラムの実行が容易になるというわけです。
$ ./start-assistant.sh
ここまでが実現できたら、Raspberry Pi起動時の自動実行が下記のように実現されます。
まず、下記のコマンドを実行してください。必要なファイルをダウンロードし、適切な場所に配置しています。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/assistant.service
$ sudo mv assistant.service /etc/systemd/system
その後、下記のコマンドを実行すると、再起動時にcontrolTV-ga.pyが自動で起動します(ターミナルの表示がないのでわかりにくいですが…)。
$ sudo systemctl enable assistant
上記の動画では、Raspberry Pi本体にキーボードとマウスが取り付けられていませんが、これはこの自動起動が行われていたからです。

自動起動を無効にするには下記のコマンドを実行します。
$ sudo systemctl disable assistant


2017年3月20日月曜日

Raspberry Pi + Open JTalkによる音声合成で天気予報付き温度計に喋らせる

0. はじめに

前回の記事「Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する」では、Raspberry Piで音声認識を行いました。

今回はその逆、ということで音声合成を行ってみましょう。音声合成システムとしてはOpen JTalkを用います。

題材としては、本書第4章で作成した「天気予報付き温度計」に、「現在〇〇度」や「今日(明日)の天気は○○」のように音声合成により話す機能を追加してみます。

作成した「喋る天気予報付き温度計」の様子は下図のようになります。見やすいよう大きいLCDを使っていますが、回路自体は書籍第4章とほぼ同じです。唯一異なるのが、温度計に喋らせるためのタクトスイッチを1つ追加した点です。

喋る天気予報付き温度計

この喋る天気予報付温度計の動作の様子を示したのが下記の動画です。温度計以外にも、音声認識と組み合わせて「音声認識の結果をそのままオウム返しで喋る」というデモンストレーションの様子も示しています。



1. Raspberry Piでのスピーカー(イヤフォン)の利用

まずはRaspberry Piで音を鳴らせるようにしましょう。主に下記の3つの方法があります。
  1. Raspberry Pi本体のミニジャックにスピーカーやイヤフォンを接続する方法
  2. Raspberry PiとつながったHDMIディスプレイにスピーカーやイヤフォンを接続する方法
  3. USB接続のサウンドカードを利用する方法
このうち、Raspberry Pi本体のミニジャックから音声を出力したい方は、OSのバージョンによって異なる設定が必要です。
2020-12-02以降の Raspberry Pi OS をお使いの方は、下記の設定によりミニジャックによる音声出力を設定してください。なお、raspi-config コマンドを実行中は、キーボードの Esc キーが「戻る」に対応しますので、困ったら Esc キーを何度か押してみると良いでしょう。
  1. ターミナルで「 sudo raspi-config 」コマンドを実行し、設定画面を開く
  2. キーボードの「Enter」キーを押し、「1 System Options」に入る
  3. キーボードの「↓」キーを一回押し、「S2 Audio」にフォーカスを合わせる
  4. キーボードの「Enter」キーを押し、「S2 Audio」の設定画面に入る
  5. キーボードの「↓」キーを一回押し、「1 Headphones」にフォーカスを合わせる
  6. キーボードの「Enter」キーを押し、「1 Headphones」を選択する
  7. キーボードの「TAB」キー二回を押し、「Finish」にフォーカスを合わせる
  8. キーボードの「Enter」キーを押し、raspi-config の設定画面を終了する
また、ミニジャックを利用したい方で 2020年2月までの Raspbian をお使いの方は、 ターミナルを起動して以下コマンドを実行してミニジャックを有効にしておきましょう。その他の場合の方はここでは何もせず先に進みます。
$ amixer cset numid=3 1

なお、USB接続のサウンドカードとしては以下の3種のみ動作確認をしました。

2. Open JTalkのインストール

まず音声合成システムOpen JTalkのインストール方法を紹介します。

ターミナルを起動して下記のコマンドを順に実行し、必要なソフトウェアをネットワークからインストールします。もちろんRaspberry Piがインターネットに接続されている必要があります。
$ sudo apt update
$ sudo apt install open-jtalk open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001
2 つ目のコマンド実行後に「続行しますか? [y/n]」などと聞かれた場合は、キーボードで「y」をタイプしてEnterキーを押して下さい。これらのインストールにより、「男性の声」での音声合成が可能になります。

さらに、女性の声で音声合成する際に必要になるファイルもインストールしておきましょう。こちらより、MMDAgent_Example-1.8.zipをブラウザでダウンロードしておきます。バージョンが新しくなっている場合、新しいもののダウンロードで問題ないと思います。 ダウンロードされたファイルはDownloadsディレクトリ(フォルダ)(/home/pi/Downloads)に格納されますので、本書p.322の図A-1のようにファイルマネージャを用いてユーザーpiのホーム(/home/pi)に移動しましょう。
その後、ターミナルで下記のコマンドを順に実行します。なお、MMDAgentの新しいバージョンがダウンロードされた場合、バージョン番号の部分(「1.8」)を適切に読み替えてください。
$ unzip MMDAgent_Example-1.8.zip
$ sudo cp -r  MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/

最後に、実際にOpen JTalkを呼び出して発話をさせるプログラム (スクリプトといいます) をダウンロードして実行可能にしましょう。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/speech.sh
$ chmod a+x speech.sh
$ sudo mv speech.sh /usr/local/bin
以上でRaspberry Piに音声合成させるためのソフトウェアの準備ができました。

3. Open JTalkの動作確認

Open JTalkで音声合成を行う際、USB接続のサウンドカードを用いていないならば、そのまま実行が可能です。ターミナルを起動し、例えば下記のようなコマンドを実行します。
$ speech.sh ラズベリーパイ
「ラズベリーパイ」の部分は前回の記事「Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する」でインストールしたMozcにより日本語入力を行い実現します。 うまくいけば、男性の声で「ラズベリーパイ」と発声されます。想像がつくように、「ラズベリーパイ」の部分を変更すれば、合成される音声もかわりますので試してみて下さい。また、音声の指示に半角空白を含ませたい場合、「speech.sh 'ラズベリー パイ'」のように「'」でくくる必要があります。

なお、ここまでで音が正しく鳴るのは以下の2つの方法のどちらかで音声を聞いているでしょう。
  • Raspberry Pi本体のミニジャックにスピーカーやイヤフォンを接続する方法(2020年2月までのOSをご利用の場合)
  • Raspberry PiとつながったHDMIディスプレイにスピーカーやイヤフォンを接続する方法(OSのバージョンによらない)
それ以外の方法を用いている方は、プログラム実行前にプログラムの変更が必要です。ターミナルを起動し、実行スクリプトを編集用に管理者権限のleafpadで開きます。
$ sudo leafpad /usr/local/bin/speech.sh
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ sudo mousepad /usr/local/bin/speech.sh
その中で下記の部分を見つけてください。
# for the device on Raspberry Pi
aplay -q $TMPVOICE

# for USB sound card (old Raspbian)
#  or earphone jack on Raspberrry Pi (Raspberry Pi OS 2020-05-27 or later)
#aplay -D plughw:1,0 -q $TMPVOICE

# for USB sound card (Raspberry Pi OS 2020-05-27 or later)
#aplay -D plughw:2,0 -q $TMPVOICE
「aplay」と書かれた行が3行ありますが、1つめが有効になっており、2つ目と3つ目は先頭に「#」があるため無効になっています。

ここで、「Raspberry Pi本体のミニジャックにスピーカーやイヤフォンを接続する方法(2020年5月以降のOSをご利用の場合)」および 「USB接続のサウンドカードを利用する方法(2020年2月までのOSをご利用の場合)」 を用いている場合は、 下記のように2番目のaplayのみを有効に変更してください。
# for the device on Raspberry Pi
#aplay -q $TMPVOICE

# for USB sound card (old Raspbian)
#  or earphone jack on Raspberrry Pi (Raspberry Pi OS 2020-05-27 or later)
aplay -D plughw:1,0 -q $TMPVOICE

# for USB sound card (Raspberry Pi OS 2020-05-27 or later)
#aplay -D plughw:2,0 -q $TMPVOICE
すなわち、1つ目のaplayに「#」をつけて無効化し、2つ目のaplayから「#」を削除して有効化するわけです。「#」は半角文字で記述しましょう。

また、「USB接続のサウンドカードを利用する方法(2020年5月以降のOSをご利用の場合)」を用いている場合は、 下記のように3番目のaplayのみを有効に変更してください。
# for the device on Raspberry Pi
#aplay -q $TMPVOICE

# for USB sound card (old Raspbian)
#  or earphone jack on Raspberrry Pi (Raspberry Pi OS 2020-05-27 or later)
#aplay -D plughw:1,0 -q $TMPVOICE

# for USB sound card (Raspberry Pi OS 2020-05-27 or later)
aplay -D plughw:2,0 -q $TMPVOICE
自分に該当する方法での編集が終わったらファイルを保存してleafpadを閉じて構いません。この編集が終わったら、 ターミナルで「speech.sh ラズベリーパイ」などと実行することで、それぞれの場合でも音声合成が行えるはずです。

なお、ボリュームの変更方法にもいくつかのパターンがあります。speech.sh を変更せずに済んだ方は、デスクトップの右上のアイコンでボリュームを変更できます。

一方、「Raspberry Pi本体のミニジャックにスピーカーやイヤフォンを接続する方法(2020年5月以降のOSをご利用の場合)」を用いている方は、次のコマンドが使えます。「70%」の部分を0~100%で変化させて適切なボリュームを見つけてください。
amixer -q -c1 cset numid=1 70%
また、「USB接続のサウンドカードを利用する方法(2020年2月までのOSをご利用の場合)」、ターミナルで例えば下記のようなコマンドを実行します。「10」の部分は0%~100%で表されたボリュームなので、適切な数値で読み替えます。特にイヤフォンを用いている場合、小さな値から試すのが無難です。
$ amixer -c 1 sset 'Speaker' 10
最後に、「USB接続のサウンドカードを利用する方法(2020年5月以降のOSをご利用の場合)」、ターミナルで例えば下記のようなコマンドを実行します。「10」の部分は0%~100%で表されたボリュームなので、適切な数値で読み替えます。特にイヤフォンを用いている場合、小さな値から試すのが無難です。
$ amixer -c 2 sset 'Speaker' 10


4. 音声の変更(お好みで)

デフォルトでは男性の声で音声合成されますが、これを女性の声に変更したい場合は、下記の手順に従ってください。 まず、ターミナルで下記のコマンドを実行し、実行スクリプトspeech.shを編集用に管理者権限のleafpadで開きます。
$ sudo leafpad /usr/local/bin/speech.sh
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ sudo mousepad /usr/local/bin/speech.sh
その中で、下記の「HTSVOICE」で始まる部分に着目しましょう。
HTSVOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_happy.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_angry.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_bashful.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_normal.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_sad.htsvoice
この部分では、音声合成する際に用いる音声を選択しています。先頭に「#」がついている行はコメント文となっており、無効な行です。「#」がついていない 1 行のみが有効となっており、これが男性の声だったというわけです。

有効とする行を変更し(「#」のつかない有効な行を1行だけとする)、leafpadで保存してから、もう一度「speech.sh ラズベリーパイ」などと実行してみましょう。
その際、leafpadを起動したターミナルとは別に新たにターミナルを開いて実行するのが良いでしょう。声がかわるのが分かるはずです。なお、女性の声に変更するためには、インストール時にMMDAgent_Example-1.8.zipをダウンロードして、必要なファイルを適切な位置にコピーしていることが必要ですのでご注意ください。

5. 応用1:天気予報機能付き温度計に喋らせる

さて、以上により、Open JTalkとそれを呼び出すスクリプトにより音声合成ができることがわかりました。あとはこの機能をどう活用するかですが、まずは本書第4章で作成した「天気予報付き温度計」に発話機能を追加してみましょう。対象となるのは、本書第4章でこの温度計を作成した方となります。温度計とLCDの両方がI2C通信を行うものを用いますので、I2Cがあらかじめ有効にされている必要があります。

まず、必要な回路は下記のようになります。本書と同様にLCDとして横8文字x縦2文字のAQM0802を用いる場合は下図のようになります。GPIO 23に接続したタクトスイッチが一つ増えているだけです。ただし、最近はAQM0802を用いる際に、特殊な対処法が必要になることが多いです。本書補足ページ「本書発売後の追加情報」の「完成品のLCDを購入しても認識されない場合の暫定的な対処法」をご覧ください。


また、同じく「本書発売後の追加情報」の「4章全般:利用できるLCDについて」で紹介したストロベリーリナックスの横16文字x縦2文字のLCDを用いる場合の回路図はこちらです。LCDのピン配置が異なるだけで、基本的には同じ回路です。


これらの回路を動かすためのプログラムをダウンロードするため、ターミナルで下記のコマンドを実行してください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/speech-weather.py
このプログラムは、書籍と同じくLCDとしてAQM0802を用いる場合のプログラムとなっています。 ストロベリーリナックスの横16文字x縦2文字のLCDを用いる場合、プログラムを少し変更する必要があります。そのためには、このプログラムを編集用にleafpadで開きます。ターミナルで下記のコマンドを実行しましょう。
$ leafpad speech-weather.py
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ mousepad speech-weather.py
プログラム中で、211行目にある下記の部分に着目します。横16文字のLCDを用いる場合はこのうちchars_per_lineの数値を8から16に変更してください。また、contrastの値は、文字が薄い場合に40程度に変更してください。変更が終わったら保存してleafpadを閉じて構いません。
contrast = 32 # 0から63のコントラスト。通常は32、文字が薄いときは40を推奨
chars_per_line = 8  # LCDの横方向の文字数
display_lines = 2   # LCDの行数
さて、プログラムを実行するには、ターミナル上で、プログラムspeech-weatherがあるディレクトリ(フォルダ)にて下記のコマンドを実行します。Python3 のみサポートしています。
$ python3 speech-weather.py
ただし、 「OpenWeatherの天気予報データをLCDに表示する」にもとづき、123行目の API_KEY の部分を、自分の API Key に置き換える必要があります。
key = 'API_KEY'
適切に動作すると、冒頭の動画のように「左のタクトスイッチで温度計のモード切替」、「右のタクトスイッチで音声合成」が実現されるはずです。

このプログラム中で動作のポイントとなっているのは、81行目から始まる下記の部分です。modeというのは温度計の4つのモードを決める変数なのですが、その値に応じて、話す内容を場合分けしているのが見て取れます。
    elif channel==23:
        if mode==0:
            s = '今日の天気は'+weather_kanji
            args = ['speech.sh', s]
            try:
                subprocess.Popen(args)
            except OSError:
                print('no speech.sh')
        elif mode==1 or mode==3:
            s = '現在'+'{0:.1f}'.format(temperature)+'度'
            args = ['speech.sh', s]
            try:
                subprocess.Popen(args)
            except OSError:
                print('no speech.sh')
        elif mode==2:
            s = '明日の天気は'+weather2_kanji
            args = ['speech.sh', s]
            try:
                subprocess.Popen(args)
            except OSError:
                print('no speech.sh')


6. 応用2:音声認識と組み合わせてオウム返しで喋らせる

最後に、音声認識と組み合わせ、音声認識の結果をオウム返しで喋る、という例を試してみましょう。こちらも冒頭の動画にて例示されていましたね。 この例は音声認識にかなりの計算パワーを必要としますので、Raspberry Pi 3を用いることを推奨します。Raspberry Pi 3を用いても、認識から発声まで5秒程度の時間がかかることがあることが動画から見て取れるでしょう。 さらに、ここから先の内容は「Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する」を一通り終えた方を対象とします。

まず、音声認識を行う上で、どの辞書を用いるかに注意しておきましょう。「Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する」を終えた状態では「テレビ操作用の辞書」を用いる設定になっています。 一方、冒頭の動画では「Juliusのディクテーションキットのデフォルトの辞書」を用いました。どの辞書を用いても実行は可能ですが、もしデフォルトの辞書に戻したいならば、まずターミナルを起動して下記のように設定ファイルをleafpadで編集用に開きます。
$ leafpad dictation-kit-v4.4/main.jconf
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ mousepad dictation-kit-v4.4/main.jconf
この中で、辞書選択部分を下記のように「model/lang_m/bccwj.60k.htkdic」を用いるようにするのでした(先頭に「#」がない行のみが有効です)。
## 単語辞書ファイル
##
-v model/lang_m/bccwj.60k.htkdic
#-v ../remocon.dic
編集が終わったら保存してleafpadを閉じます。これにより、デフォルトの辞書が用いられるようになりました(ちなみに、自分でカスタマイズした辞書の方が単語数が少ないので認識速度は速いです)。

次に、必要なプログラムをダウンロードするため、ターミナルを起動して下記のコマンドを実行します。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/recog-speech.py
このプログラムを実行するためには、事前に音声認識エンジンJuliusをモジュールモードで起動しておく必要があります。 そのためには、ターミナルを起動して下記のコマンドを実行するのでした。
$ julius -C dictation-kit-v4.4/main.jconf -C dictation-kit-v4.4/am-gmm.jconf -demo -module
その状態で、もう一枚別のターミナルを起動し、その上で下記のコマンドでプログラムを実行します。
$ python recog-speech.py
あとはマイクに向かって話せば冒頭の動画のような動作が実現します。動画にも注釈がありますが、音声合成の結果の音声をマイクが拾わないように注意してください。そうしないと、「認識」→「合成」→「認識」→「合成」→…が無限に繰り返されます(この現象の原因に気づくまでちょっと悩みました)

さて、このプログラムrecog-speech.pyのポイントは、38行目から始まる下記の部分です。 認識された語は「''」→「語1」→「語2」→…「語n」→「'。'」という順で届くのですが、これをひとまとめに 「語1語2…語n。」と結合してからspeech.shに渡しています。
# 認識された単語wordの中に、u('...') という文字列が含まれるかどうかを
# チェックし、文字列に応じたアクションを記述します。
# u('...')でくくるのは、python2とpython3の互換性を保つためです。

if word != u(''):
    recognized_word += word

if u('。') in word: 
    print(recognized_word)
    args = ['speech.sh', recognized_word]
    subprocess.Popen(args).wait()
    recognized_word = u('')
以上、お疲れさまでした。

2017年3月15日水曜日

Raspberry Pi + Julius + LIRC により家電製品を音声認識で操作する

 0. はじめに

本書の第5章において、LIRC(Linux Infrared Remote Control) というソフトウェアを用いてRaspberry Piをテレビなどの家電製品のリモコンにする方法を紹介しました。

さらに、本ページの追加コンテンツ「Raspberry Pi上のLIRCでエアコンを操作する」において、書籍では触れられなかったエアコンの操作方法も紹介しました。

これらにおいて、家電製品へ命令を送信する方法として下記の3つを用いました。
  1. ターミナル上でのirsendコマンドによる送信
  2. タクトスイッチによる送信
  3. WebIOPiによるブラウザ経由の送信
本ページではこれらの3つに加え、「音声による命令の送信」の方法を紹介します。音声認識のためには音声認識エンジンであるJuliusを用います。

応用的なコンテンツとなりますので、本書第5章の「ターミナル上でのirsendコマンドによる送信(5.4.1章)」までの演習を終え、内容を理解している方を対象とします。

また、用いるOSはNOOBS 2.3.0以降に含まれるRaspbianとします。少なくとも、OSがJessieやStretch(NOOBS 1.4.2以降)であることを前提とした解説がありますので、なるべく新しいバージョンのOSを用いてください。

家電製品の操作に興味がないという方でも、本ページを読むことで「音声認識の結果を取得してその結果をもとに何らかのアクションを起こす」方法を知ることができます。

なお、本ページのJuliusを用いる方法は音声認識機能をRaspberry Pi本体上のOSに追加する方法ですが、インターネット(クラウド)上の音声認識機能を用いる例として「Raspberry Pi + Google Assistant API + LIRCによりテレビを音声認識で操作する」も追加しましたので、お好みの方をお試しください。

Raspberry PiにサンワプライのUSBマイクを接続した様子

また、リモコンで操作できるLED照明に対して本ページの内容を適用したデモンストレーションの動画が下記になります。


1. 音声認識を行うために必要な物品

音声認識を行うためにはマイクが必要ですが、Raspberry Piにはマイクを接続する端子がありません。マイクをRaspberry Piに接続するためには、外部機器を購入する必要があります。

本ページでは、マイクを接続するためのデバイスとして下記の5つを試しました。これ以外のデバイスはサポート対象外としますのでご了承ください。

[USBマイク系]
[USBサウンドデバイス系]
どれもUSBデバイスであり、USB端子に接続すれば使えるようになります。

ただし、USBサウンドデバイスを用いる場合、さらにミニジャック接続のPC用マイクも必要となります。これはどのようなマイクでも問題ないと思いますが、例えば
のようなものがあります。

2. 日本語入力メソッド mozc のインストール

以下の手順において、Raspberry Pi 上で日本語を入力しなければならない箇所があります。Raspberry Pi用のOSであるRaspbian(Jessie以降)で日本語を入力するため、Googleが開発した日本語入力システムMozcをインストールします。「Google日本語入力」のもととなったソフトウェアです。

LXTerminalを起動し、下記の 2 つのコマンドを順に実行することで、ネットワークからMozcをダウンロードしてインストールします。なお、2 つ目のコマンドの実行後に「続行しますか? [y/n]」などと聞かれた場合は、キーボードで「y」をタイプしてEnterキーを押して下さい。
$ sudo apt update
$ sudo apt install ibus-mozc
なお、1 つ目のコマンドは、インストールできるアプリケーションのリストを更新するためのものです。1 分程度かかる場合あります。2 つ目のコマンドで、Mozcをインストールしています。

インストールが終った後はまずRaspberry Piを再起動します。その後、下図のように「US」と書かれた部分をマウスでクリックし「日本語 - Mozc」を選択してください。あとは「半角/全角」キーで日本語入力のオンオフを切り替えられます。

この際、キーボードの設定が英語キーボードに戻って「半角/全角」キーが効かなくなっていることがありますので、そのような場合はもう一度キーボードの設定をし直しましよう。

日本語入力の動作チェックとして、ブラウザでGoogleなどにアクセスし、日本語で検索してみると良いでしょう。

3. 音声認識エンジンJuliusのインストール

ここからは、Raspberry Pi上で音声認識を行うために用いるソフトウェアであるJuliusのインストール方法を解説します。

まず、こちらより julius-4.4.2.tar.gz をダウンロードしてください。ブラウザでダウンロードすると、デフォルトでユーザーpiのDownloadsディレクトリ(フォルダ)にダウンロードされます。

さらに、こちらより、Juliusディクテーションキットの最新版である dictation-kit-v4.4.zip をダウンロードしてください。こちらは400MB以上のサイズがあり、解凍するとさらに800MBの容量が必要となります。事前にRaspberry PiのOSを格納したSDカードの容量に余裕があることを確認した方がよいでしょう。

ターミナルを起動し、dfコマンドに-kオプションを付けて実行することで、SDカードの残り容量をKB単位で知ることができます。
$ df -k
ファイルシス   1K-ブロック    使用 使用可 使用% マウント位置
/dev/root          6154176 5625008 193504   97% /
(以下略)
上記の表示は、8GBのSDカードを用いてJuliusのインスト―ルを全て終えた状態でのSDカードの容量を示しています。使用率97%、残り容量193.504MBであることを示しますので、ほぼギリギリです。ですので、Juliusを用いる際は16GB以上のSDカードを用いることを推奨します。なお、2018年1月に確認したところ、この時期のRaspbianを用いると8GBのSDカードでは容量が足りませんでしたので、16GB以上のSDカードが必須だとお考え下さい。

さて、ダウンロードが終わったら、 julius-4.4.2.tar.gz および dictation-kit-v4.4.zip をDownloadsディレクトリ(フォルダ)(/home/pi/Downloads)からユーザーpiのホーム(/home/pi)に移動しましょう。本書p.322の図A-1のようにファイルマネージャを用いるのが良いでしょう。

次に、ターミナルを起動して下記のコマンドを順に実行し、Juliusのインストールに必要なライブラリをインストールします。
$ sudo apt update
$ sudo apt install libasound2-dev
上記のコマンドが終わったら、以下のコマンドでJuliusをインストールします。
$ tar zxf julius-4.4.2.tar.gz
$ cd julius-4.4.2
$ ./configure --enable-words-int --with-mictype=alsa
$ make
$ sudo make install
次に、 dictation-kit-v4.4.zip を下記のコマンドでユーザpiのホームに解凍します。これは解凍のみで構いません。
$ cd
$ unzip dictation-kit-v4.4.zip
以上でJuliusの準備は終了ですが、最後に、導入したマイクをJuliusで用いるための設定を行います。下記のコマンドにより、環境設定ファイル .bashrc テキストエディタ leafpadで開きましょう。
$ cd
$ leafpad .bashrc
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ cd
$ mousepad .bashrc
このファイルには様々な設定が書き込まれていますが、それを壊さないように注意しながら、ファイル末尾に下記の1行を追加しましょう。
export ALSADEV=plughw:1,0
なお、2020年5月27日(2020-05-27)以降のOSを利用している場合は、追加する行は以下の行としてください。
export ALSADEV=plughw:2,0
書き込みが終わったらファイルを保存してからleafpadを閉じます。

以上が終わったら、Raspberry Piを一旦再起動しましょう。再起動の前に、(もし接続していなかったら)Raspberry PiにUSB経由のマイクデバイスを接続しておきましょう。

4. Juliusの動作確認

インストールしたJuliusの動作チェックをしてみましょう。ターミナルを起動し、下記のコマンドを実行します。
$ julius -C dictation-kit-v4.4/main.jconf -C dictation-kit-v4.4/am-gmm.jconf -demo
長い命令ですので、本書p.344で解説した「TABキーによるファイル名の補完」をうまく活用しないと入力は大変です。それも難しい場合、上記コマンドのコピー&貼り付けで実行しても良いでしょう(個人的には、TABキーによる補完をマスターすることをお勧めします)。

また、juliusコマンドに与えるオプションは、用いるディクテーションキットのバージョンに依存します。本ページではv4.4を用いた解説になっていますのでご注意ください。

実行して数秒待つと、ターミナルに下記の表示が現れます。
(省略)
Stat: adin_alsa: device name from ALSADEV: "plughw:1,0"
Stat: capture audio at 16000Hz
Stat: adin_alsa: latency set to 32 msec (chunk = 512 bytes)
Stat: "plughw:1,0": Device [USB PnP Audio Device] device USB Audio [USB Audio] subdevice #0
STAT: AD-in thread created
<<< please speak >>>
「<<< please speak >>>」の表示が出たら、マイクに向かって何か話してください。認識結果がターミナルに表示されます。下記は、マイクに向かって「こんにちは」、「ラズベリーパイ」と話したときの表示です。
(省略)
pass1_best:  こんにちは 。
sentence1:  こんにちは 。
pass1_best:  ラズベリー 杯 、
sentence1:  ラズベリー パイ 。
<<< please speak >>>
認識に失敗することもありますが、必ずしも認識率が高いわけではありませんので、あまり気にしなくても良いでしょう。

認識動作を終了したい場合、ターミナル上で「Ctrl-c」(Ctrlキーを押しながらcキーを押す)を実行してJuliusを終了してください。

なお、マイクの感度を調整したい場合、下記のコマンドをあらかじめターミナルで実行しておきます。「50」の部分は0%~100%の感度に相当する数字なので、適切な数字をいれます。100に近付く程感度が高いはずです。
$ amixer -c 1 sset 'Mic' 50
2020年5月27日(2020-05-27)以降のOSを利用している場合は、マイクの感度調節命令は下記としてください。
$ amixer -c 2 sset 'Mic' 50

5. Juliusの辞書作成

ここまでで、Raspberry PiとJuliusを用いた音声認識が実現しました。これを本書第5章の内容と組み合わせ、テレビのリモコン操作を音声で行ってみましょう。マイクに向かって「電源」と話すと、テレビの電源のオン・オフが切り替る、などの動作です。

そのためには、まず音声認識の認識率を向上させる必要があります。デフォルトでなぜ誤認識が多いかというと、Juliusが提示する認識語の候補が多すぎるからです。Juliusが認識に用いる辞書を、テレビ操作に必要な語句のみに限定すると、(当然ながら)認識率が向上します。今回の目的にはこれで十分ですので、試してみましょう。

まず、テレビの操作に必要な語句を、下記の通りとします。左が表記、右が読みとします。

電源 でんげん
入力 にゅうりょく
いち
さん
よん
ろく
なな
はち
きゅう
10 じゅう
11 じゅういち
12 じゅうに
ボリュームアップ ぼりゅーむあっぷ
ボリュームダウン ぼりゅーむだうん
チャンネルアップ ちゃんねるあっぷ
チャンネルダウン ちゃんねるだうん

この語句のみを記した辞書を作成するため、まずは上記の内容を記したテキストファイルを作成する必要があります。名前は任意ですが、ここでは remocon.yomi としましょう。

下記のコマンドでremocon.yomiファイルを新規作成します。
$ leafpad remocon.yomi
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ mousepad remocon.yomi
なお、leafpadを用いない方のために記しておくと、このファイルはUTF-8の文字コードで記述する必要があります。 このファイルには、下記のように「表記」と「読み」を列挙します。
電源 でんげん
入力 にゅうりょく
1 いち
2 に
3 さん
4 よん
5 ご
6 ろく
7 なな
8 はち
9 きゅう
10 じゅう
11 じゅういち
12 じゅうに
ボリュームアップ ぼりゅーむあっぷ
ボリュームダウン ぼりゅーむだうん
チャンネルアップ ちゃんねるあっぷ
チャンネルダウン ちゃんねるだうん
上記の内容を記述する際には注意がいくつかあります。

まず、「表記」と「読み」の間は「半角空白」か「TAB」などを区切り文字として記入します。 ただし、「半角空白」を区切り文字として用いる方針にすると、間違って「全角空白」を入力してしまうことがあり、トラブルの元です。 そのため、個人的には「TAB」を区切り文字とすることを推奨します。 具体的には例えば1行目の記述において下記のようにキーボードのTABキーを入力する、ということです。
電源[TABキー]でんげん
次に、上記の内容を記入する際、空行があってはいけません。典型的には、末尾に空行を入れてしまうことはしばしば起こりますので、注意しましょう。

記入が終わったら、ファイルを保存してleafpadを閉じます。

保存した remocon.yomi を元に、辞書ファイルを作成します。まずは、その際に用いる補助コマンドをダウンロードしましょう。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/addsil.pl
$ chmod a+x addsil.pl
上記2コマンドの実行が終わったら、下記のコマンドにより、読みファイル remocon.yomi から辞書ファイル remocon.dic を作成します。
$ iconv -f utf-8 -t euc-jp remocon.yomi | ./julius-4.4.2/gramtools/yomi2voca/yomi2voca.pl  | iconv -f euc-jp  -t utf-8 | ./addsil.pl > remocon.dic
非常に長いコマンドなので、コピー&貼り付けで実行するのが無難でしょう。具体的には「remocon.yomi の文字コードをEUP-JPに変換」→「yomi2voca.pl コマンドにより読みをローマ字に変換」→「文字コードをUTF-8に変換」 →「ファイル先頭に休止状態の情報2行を追加」→「remocon.dic を保存」という流れで処理が実行されます。

念のため、 remocon.dic の中身を確認しておきましょう。下記のコマンドで中身をleafpadで確認できます。UTF-8で記述されています。
$ leafpad remocon.dic
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ mousepad remocon.dic
中身は下記のようになっているはずです。
<s>     []      silB
</s>    [。]    silE
電源    d e N g e N
入力    ny u u ry o k u
1      i ch i
2      n i
3      s a N
4      y o N
5      g o
6      r o k u
7      n a n a
8      h a ch i
9      ky u u
10    j u u
11    j u u i ch i
12    j u u n i
ボリュームアップ        b o ry u: m u a q p u
ボリュームダウン        b o ry u: m u d a u N
チャンネルアップ        ch a N n e r u a q p u
チャンネルダウン        ch a N n e r u d a u N
読みがローマ字に変換されているのが yomi2voca.pl の効果、先頭2行が追加されているのが addsil.pl の効果です。確認が終わったらleafpadを閉じて構いません。

なお、完成済みの辞書ファイル remocon.dic を下記のコマンドでダウンロードできますので、作成が面倒だという方はご活用ください。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/remocon.dic
なお、既に remocon.dic が存在する場所で上記コマンドを実行すると、ダウンロードされたファイルの名前は remocon.dic.1 などと自動的に変更されますのでご注意ください。

以上でテレビ操作用の辞書ファイルを用意できましたので、Juliusがこれを用いるよう変更してみましょう。そのためには、julius を実行する時に利用する設定ファイル dictation-kit-v4.4/main.jconf を編集する必要があります。

下記のコマンドで dictation-kit-v4.4/main.jconf を leafpadにより開きましょう。
$ leafpad dictation-kit-v4.4/main.jconf
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ mousepad dictation-kit-v4.4/main.jconf
この中に、辞書ファイルを指定している下記の場所があるので見つけてください。
## 単語辞書ファイル
##
-v model/lang_m/bccwj.60k.htkdic
これを下記のように編集してください。元からあった辞書指定行に「#」を追記して無効化し、新たに一行追加するわけです。全て半角で記述するよう注意しましょう。
## 単語辞書ファイル
##
#-v model/lang_m/bccwj.60k.htkdic
-v ../remocon.dic
なお、ここでの解説は、ディクテーションキットが /home/pi/dictation-kit-v4.4 に、作成した辞書ファイルが /home/pi/remocon.dic にあることを前提としています。追記した行にある「../」とは「一つ上の階層のディレクトリ(フォルダ)」を意味します。 ついでに、 dictation-kit-v4.4/main.jconf でもう一か所変更しておきましょう。下記の場所を見つけてください。
-rejectshort 800 # 指定ミリ秒以下の長さの入力を棄却する
これは、800ミリ秒以下の入力を無視する設定なのですが、この設定のままだと「に」や「ご」などのように短い音声の認識に失敗することが多くなります。 そこで、下記のようにこの数値を400に変更しましょう。半角で記述するよう注意しましょう。
-rejectshort 400 # 指定ミリ秒以下の長さの入力を棄却する
以上の変更が終わったら保存をしてからleafpadを閉じましょう。 そして、上と同様のjuliusコマンドで音声認識を試してみましょう。今度は上で列挙したテレビ操作用の単語しか認識されないはずです。 

下記は「でんげん」、「いち」、「ぼりゅーむあっぷ」と話したときの様子です。警告(WARNING)は出ていますが、正しく認識されていることがわかります。
pass1_best:  電源 。
WARNING: 00 _default: hypothesis stack exhausted, terminate search now
sentence1:  電源 。
pass1_best:  1 。
WARNING: 00 _default: hypothesis stack exhausted, terminate search now
sentence1:  1 。
pass1_best:  ボリュームアップ
WARNING: 00 _default: hypothesis stack exhausted, terminate search now
sentence1:  ボリュームアップ 。

6. moduleモードでのJuliusの実行

ここまででJuliusによる音声認識が実現できましたが、これを実現しているのは、配布されたファイルをインストールしてできたjuliusコマンドでした。

しかし、我々が実現したいのは、音声認識の結果を自分で作成したプログラムから利用することです。それができて初めて、「『電源』という音声が認識されたら、テレビの電源をオンするための命令を送信する」などの動作が可能になるのです。

そのためには、Juliusをmoduleモードと呼ばれるモードで動作させる必要があります。moduleモードの概念図を示したのが下図です。左側に描かれたmoduleモードで動作するjuliusがサーバーとなり、右側に描かれたクライアント(jcontrolコマンドや自作プログラム)からの認識の問い合わせに応答する、という仕組みです。


イメージをつかむために、moduleモードの動作を試してみましょう。まず、下記のコマンドでjuliusをmoduleモードで起動しましょう。これまでとの違いは、末尾に「-module」がつくだけです。
$ julius -C dictation-kit-v4.4/main.jconf -C dictation-kit-v4.4/am-gmm.jconf -demo -module
しばらく待つと、ターミナルに下記の表示が現れます。juliusがサーバーとして動作し、クライアントからの接続を待ち受けていることが記されています。
(省略)
Stat: server-client: socket ready as server
///////////////////////////////
///  Module mode ready
///  waiting client at 10500
///////////////////////////////
moduleモードのjuliusが動作するターミナルはそのままにしておき、 もう一つ別のターミナルを起動しましょう。そこで下記のコマンドを実行します。jcontrolはjuliusに付属するコマンドで、moduleモードのjuliusに接続し結果を表示することができます。「localhost」とはjuliusが動作しているRaspberry Piを表わすホスト名です。
$ jcontrol localhost
すると、そのターミナル上では下記のように表示されます。
connecting to localhost:10500...done
> <STARTPROC/>
> <INPUT STATUS="LISTEN" TIME="1489549742"/>
一方、moduleモードのjuliusが動作するターミナル上の表示は下記のように変化します。
(省略)
STAT: AD-in thread created
その状態で、これまで通りマイクに何か話してみましょう。先ほどと同様「でんげん」、
「いち」、「ぼりゅーむあっぷ」と話したとき、jcontrolコマンドを実行したターミナルには、例えば下記のように表示されます。
(省略)
>   <SHYPO RANK="1" SCORE="-2573.104004">
>     <WHYPO WORD="" CLASSID="<s>" PHONE="silB" CM="0.958"/>
>     <WHYPO WORD="電源" CLASSID="電源" PHONE="d e N g e N" CM="0.978"/>
>     <WHYPO WORD="。" CLASSID="</s>" PHONE="silE" CM="1.000"/>
>   </SHYPO>
(省略)
>   <SHYPO RANK="1" SCORE="-1995.357666">
>     <WHYPO WORD="" CLASSID="<s>" PHONE="silB" CM="0.927"/>
>     <WHYPO WORD="" CLASSID="1" PHONE="i ch i" CM="0.864"/>
>     <WHYPO WORD="。" CLASSID="</s>" PHONE="silE" CM="1.000"/>
>   </SHYPO>
(省略)
>   <SHYPO RANK="1" SCORE="-2783.654053">
>     <WHYPO WORD="" CLASSID="<s>" PHONE="silB" CM="0.953"/>
>     <WHYPO WORD="ボリュームアップ" CLASSID="ボリュームアップ" PHONE="b o ry u: m u a q p u" CM="0.967"/>
>     <WHYPO WORD="。" CLASSID="</s>" PHONE="silE" CM="1.000"/>
>   </SHYPO>
これまでとは形式がかなり異なりますが、「電源」、「1」、「ボリュームアップ」が認識されていることは見て取れるでしょう。 

jcontrolを終了するには、jcontrolを実行しているターミナルで「Ctrl-c」を実行します。そうすると、moduleモードのjuliusは再びクライアントからの接続を待機する状態に戻ります。 moduleモードのjuliusを終了するには、やはりそのターミナルで「Ctrl-c」を実行してください。

さて、以上の動作確認により、目指すべきことがわかりました。jcontrolのようにmoduleモードのjuliusに接続し、得られた認識結果に応じてアクションを起こすプログラムを書けば良いのです。

そのためのPythonプログラムを作成してみましたので、ダウンロードして試してみましょう。下記のコマンドで recog-TV.py をダウンロードできます。
$ wget https://raw.githubusercontent.com/neuralassembly/raspi/master/recog-TV.py
これを実行するため、あらかじめmoduleモードのjuliusを起動しておきます

そして、別のターミナルを開き、下記のコマンドでrecog-TV.pyを実行します。
$ python recog-TV.py
実行後にすぐ「socket error」と表示される場合はmoduleモードのjuliusが起動されていませんので注意してください。

エラーがでなければ、マイクに向かって話しかけてみましょう。

LIRCを既にインストール済みだが起動されていない方ならば下記のように表示されるでしょうし、
電源
irsend: could not connect to socket
irsend: No such file or directory
1
irsend: could not connect to socket
irsend: No such file or directory
ボリュームアップ
irsend: could not connect to socket
irsend: No such file or directory
LIRCをインストールされていない方ならば下記のように表示されるでしょう。
電源
command not found.
1
command not found.
ボリュームアップ
command not found.
いずれにせよ、音声認識結果「電源」、「1」、「ボリュームアップ」が表示されていることがわかります。 

このプログラム recog-TV.py は、音声が認識されたときに、テレビを操作する信号を出力するように作られています。 その準備ができていれば上記のエラーメッセージは出ないのですが、LIRCが起動していないときやLIRCがインストールされていない場合には 上記のようなメッセージが現れるというわけです。

LIRCの準備は本書第5章の5.4.1までを終えれば完了します。すなわち、「irsend SEND_ONCE TV power」などのコマンドでテレビが操作できるようになれば良いわけです。その状態で「moduleモードのjulius」および「recog-TV.py」が適切に起動されれば、エラーがでず、音声によりテレビを操作できるようになります。

もちろん、音声認識の結果をテレビの操作以外に用いることもできます。例えば、認識された言葉を合成音声でRaspnberry Piに発話させることも可能です。 その方法を、本ページの続編的な内容である「Raspberry Pi + Open JTalkによる音声合成で天気予報付き温度計に喋らせる」にて紹介しておりますので、是非ご覧ください。

7. 若干のコードの解説

サンプルファイル recog-TV.py を全て解説するのは難しいですが、このプログラムを
改造したいという方のために重要な部分のみを解説します。 

ポイントとなるのは以下の部分です。Web上での見やすさのためインデント(字下げ)は一部省略します。
# 認識された単語wordの中に、u('...') という文字列が含まれるかどうかを
# チェックし、文字列に応じたアクションを記述します。
# u('...')でくくるのは、python2とpython3の互換性を保つためです。
# 「対象となる文字が含まれているか」を調べていますので、
# 先に「『1』が含まれるか」をチェックすると
# 10~12がすべて「1」と判定されてしまいます。
# そのため、10~12のチェックを先に行っています。

if u('10') in word:
    print(word)
    args = ['irsend', '-#', '1', 'SEND_ONCE', 'TV', 'ch10']
    try:
        subprocess.Popen(args)
    except OSError:
        print('command not found.')
elif u('11') in word:
    print(word)
    args = ['irsend', '-#', '1', 'SEND_ONCE', 'TV', 'ch11']
    try:
        subprocess.Popen(args)
    except OSError:
        print('command not found.')
(以下略)
この部分ではwordという変数の中に、認識された語句が格納されています。それをif~elif文により分岐させ、認識された語句ごとに実行する処理を変えている、というわけです。

この部分をうまく書き換えることで、処理を色々と変えることができるでしょう。

8. 自動起動

最後に、ここで試したmoduleモードのjuliusとrecog-TV.pyを、Raspberry Pi起動時に自動で実行する方法を記しておきましょう。

自動起動用のファイル/etc/rc.localにコマンドを追記します。まず、管理者権限のleafpadで/etc/rc.localを編集用に開きます。
$ sudo leafpad /etc/rc.local
なお、NOOBS 3.2.1以降ではテキストエディタとしてleafpadではなくmousepadを用います。
$ sudo mousepad /etc/rc.local
そして、末尾にある「exit 0」の手前に下記の3つのコマンドを記述し、保存します。
ALSADEV=plughw:1,0 julius -C /home/pi/dictation-kit-v4.4/main.jconf -C /home/pi/dictation-kit-v4.4/am-gmm.jconf -demo -module &

sleep 10

python /home/pi/recog-TV.py &

exit 0
ポイントは、下記の通りです。
  • .bashrcに書いたALSADEVについての設定は/etc/rc.localでは読み込まれないので、直に書く
  • main.jconfやam-gmm.jconfは「dictation-kit-v4.4/main.jconf」のような相対パスではなく「/home/pi/dictation-kit-v4.4/main.jconf」のような絶対パスで書く
  • juliusの起動が終了するのを待つため、sleep文を挿入する
  • recog-TV.pyも絶対パスで書く
保存したらleafpadを終了し、Raspberry Piを再起動してみてください。自動で音声入力を受け付けるモードになっているはずです。

 以上、お疲れさまでした。