RaspberryPi4で音声認識ボットを作るハンズオン 【ラズパイ+Julius+Open JTalk】

13 min read読了の目安(約12400字

こんにちは、ニホンザルとネコが大好きな @minako-ph です 🐱

最近こんなのを作ってみたので、忘れないうちにこの時の知見をまとめつつ
皆さんにも気軽にラズパイに挑戦してもらえるようハンズオン形式でまとめてみます🐱!

https://twitter.com/minako__ph/status/1373964368266731523

何が出来るのか

今回の記事ではオープンソースの音声認識エンジン「julius」と
オープンソースの音声合成エンジン「Open JTalk」を使って、
以下のツイートの様に「おはよう・おやすみ」を認識して
日本語で返事をしてくれるお友達( 音声認識bot )を作ります🍓

https://twitter.com/minako__ph/status/1372144431302860816

この記事の概要

  • RaspberryPi4でマイクのセットアップ
  • 音声認識エンジン「julius」のセットアップ
  • 独自辞書データを作成してjuliusに指定した単語を認識させる
  • juliusをモジュールモードで起動し音声認識サーバーとして使用する
  • 音声合成エンジン「Open JTalk」のセットアップをしてラズパイに日本語を喋らせる
  • juliusとOpen JTalkを組み合わせて音声認識ボットへ

事前準備と用意するもの

用意するもの

  • RaspberryPi4
  • USBマイク
  • スピーカー

今回の記事で私が使用しているマイクとスピーカーは以下のものです!

https://www.amazon.co.jp/dp/B073QPLKRN/ref=cm_sw_r_tw_dp_ESZ94ZC82PBSV7NGJ9VC
https://www.daisonet.com/product/4549131578874

事前準備

  • OSのセットアップ
    • インターネットに繋がる状態まで
  • (推奨)SSHで開発ができる環境

2021.03のアップデートでOSのセットアップが非常に簡単になりました🚀 参考↓

https://dev.classmethod.jp/articles/raspberry-pi-imager-v1-6-update/

マイクのセットアップ

まずはマイクのセットアップから始めます。

USBマイクをラズパイに挿した状態で以下のコマンドを叩き
自分の挿しているUSBマイクが一覧に出てくることを確認する

# USB接続されてるデバイス一覧の表示
lsusb

次に以下のコマンドでデバイスに割り振られてる番号の確認します。

arecord -l

# アウトプット
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

上記のアウトプットだとカード1デバイス0であることがわかります。

カード番号とデバイス番号が確認できたら、
ラズパイを起動する度に音声入力デバイスとして認識してもらえる様に設定を記述してあげます。

vimなどを使い /etc/profile に以下の行を追加してください。

# vimなどを使用してファイルを開く
vi /etc/profile

# ファイルを開いたら以下の1行を追記するf
export ALSADEV="1:0"
# ↑ ALSADEV="{カード番号}:{デバイス番号}"

上記の設定をすぐに使用できるようにターミナルでも一回叩いておきましょう。

export ALSADEV="1:0"

マイクの設定は以上で完了です!🎉

「julius」のセットアップ

次に、オープンソースの音声認識エンジン「julius」のセットアップを行います。

Juliusは、京都大学・名古屋工業大学が中心になって開発を行っている C言語で書かれたオープンソースの機械学習済みの音響モデルを搭載した汎用大語彙連続音声認識エンジンです。

任意の作業場所(今回は~/juliusというディレクトリ)を作成してください。

# ディレクトリの作成
mkdir ~/julius
# 作成したディレクトリへ移動
cd ~/julius

JuliusのソースコードをGithubからダウンロード&解凍し、
ソースコード本体が格納されているディレクトリへ移動します。

# ダウンロード
wget https://github.com/julius-speech/julius/archive/v4.4.2.1.tar.gz
# 解凍
tar xvzf v4.4.2.1.tar.gz
# 移動
cd julius-4.4.2.1

今回の作業で必要なライブラリを入れます。

# 一応アップデートしておく
sudo apt-get update
sudo apt-get upgrade

# ライブラリのインストール
sudo apt-get install libasound2-dev libesd0-dev libsndfile1-dev

次にコンパイルを行います。

./configure --with-mictype=alsa

make
sudo make install

以上でJuliusのセットアップは完了です!🎉

次に「ディクティーションキット」という日本語のディクテーション(自動口述筆記)に必要な
最小限のモデルとJuliusの実行バイナリが含まれている音声認識パッケージのセットアップを行います。

最初に作ったjuliusの作業ディレクトリ(今回は~/julius)の配下にディクティーションキットの作業ディレクトリを作成&移動します

# julius作業ディレクトリに戻る
cd ~/julius

# ディクティーションキット作業場所を作成
mkdir julius-kit

# 作業場所へ移動
cd julius-kit

ディクティーションキットをダウンロード&解凍し、移動します。

# ディクティーションキットのダウンロード
wget https://osdn.net/dl/julius/dictation-kit-v4.4.zip

# 解凍
unzip dictation-kit-v4.4.zip

# 移動
cd dicration-kit-v4.4

音声認識ができる状態になったか実際に試してみましょう。
ディクティーションキットのデモスクリプトを起動します。

# 音声認識デモの起動
julius -C main.jconf -C am-gmm.jconf -demo

# アウトプット
...
()
...
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 Sound Device] device USB Audio [USB Audio] subdevice #0
STAT: AD-in thread created
<<< please speak >>>

上記のように <<< please speak >>> と出てきたら準備完了です。
実際にマイクに向かって話しかけてみましょう 🗣

# アウトプット
<<< please speak >>>Warning: strip: sample 0-21 has zero value, stripped
pass1_best:  この 日 は           
sentence1:  ある 日 七 。
pass1_best:  ああ 、<input rejected by short input>
pass1_best:  生 へ 。              
sentence1:  何 万 円 。
<<< please speak >>>

音声は認識できているようですが、精度は高くなさそうですね。
(ちなみに、こんにちは・名前と発言しています...)

マイクの感度が悪い場合は、マイクの音量を上げましょう。

# マイクのカード番号を確認
arecord -l

# アウトプット
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
  
# 音量を100%に設定
amixer -D hw:1 sset Mic 100%
# ↑ amixer -D hw:{カード番号} sset Mic 100%

次のセクションで認識して欲しい単語の独自辞書データ作成して精度を高めましょう!

独自辞書データを作る

先程のデモのように、音声は認識するもの言葉の認識までは難しそうでした。
しかし、認識対象とする単語とその読み(音素列表記)を定義した単語辞書から
独自辞書データを作成することで Juliusはその単語を認識できるようになります。

独自辞書データを作成するには以下の4種類のファイルが必要になります。

  • xxxx.yomi 読みファイル
  • xxxx.phone 音素ファイル
  • xxxx.grammar 構文ファイル
  • xxxx.voca 語彙ファイル

それでは実際に作成していきましょう。
juliusの作業ディレクトリ(今回は~/julius)配下に独自辞書データ作業ディレクトリを作成しましょう。

# dictというディレクトリを作成
mkdir ~/julius/dict
# 移動
cd ~/julius/dict

まずは読みファイル(xxxx.yomi)から作成していきます。
任意のエディタ(コマンドはvim使用)で bot.yomi ファイルを作成・編集します。

# ファイルの作成&エディタでオープン
vim bot.yomi

ファイルの中身は以下のように記載します

次に音素ファイル(xxxx.phone)を作成します。
音素ファイルはJuliusに付属されてる yomi2voca.pl を使用することで出力することが出来ます。
以下のコマンドを叩き音素ファイルを出力しましょう。

iconv -f utf8 -t eucjp ~/julius/dict/bot.yomi | ~/julius/julius-4.4.2.1/gramtools/yomi2voca/yomi2voca.pl | iconv -f eucjp -t utf8 > ~/julius/dict/bot.phone

以下のような bot.phone ファイルが生成されていたら成功です。

実は 最新版のJuliusではUTF8に統一された為 EUC-JPに変換する必要はないのですが、今回使用しているJuliusのバージョンでは文字コードをEUC-JPにする必要がある為 最後に変換してから出力しています。

次に構文ファイル(xxxx.grammar)を作成します。
任意のエディタで bot.grammar ファイルを作成・編集します。

# ファイルの作成&エディタでオープン
vim bot.grammar

ファイルの中身を以下のように記載します。

この構文ファイルは、Sを開始記号として,1行につき1つの書き換え規則を記述しています。

そして、終端記号 すなわち左辺に現れなかったシンボルは単語カテゴリとなります。
NS_BNS_Eはそれぞれ文頭および文末の「無音区間」に対応する単語カテゴリとなり、必ず書く必要があります。

次に作成する語彙ファイルで各カテゴリに属する単語を定義していきます。

それでは語彙ファイル(xxxx.voca)を作成していきましょう。

まずは、先ほど作成した音素ファイルをコピーします

# コピー
cp bot.phone bot.voca
# 編集
vi bot.voca

ファイルの中身を以下のように編集します。

行頭が%で始まる行は構文ファイルで定義した単語カテゴリです。

NS_BNS_Eには、それぞれ文頭・文末の無音に対応する無音音響モデル
( Julius の標準音響モデルではsilB, silE )を、割り当ててます。

独自辞書の生成に必要なファイルが作成できたので、Juliusの形式である
オートマトン(dfaファイル)と単語辞書(dictファイル)に変換していきましょう。

Julius付属のコンパイラmkdfa.plを用いて変換していきます。

#コンパイラのあるディレクトリに移動
cd ~/julius/julius-4.4.2.1/gramtools/mkdfa
# コンパイルの実行
mkdfa.pl ~/julius/dict/bot

成功すると ~/julius/dict 内にdfaファイルdictファイル
そして単語カテゴリ番号と実際のカテゴリ名の対応が記載されたtermファイル
計3ファイルが出力され 最終的に以下のような構成になります。

https://github.com/minako-ph/raspi_speechbot_dict

以上で独自辞書の作成は完了です!🎉

実際に認識が出来ることを確認してみましょう

julius -C ~/julius/julius-kit/dictation-kit-v4.4/am-gmm.jconf -nostrip -gram ~/julius/dict/bot -input mic

<<< please speak >>> と出てきたら話しかけてみましょう。

<<< please speak >>>
...(略)...
sentence1:  [s]おはよう[/s]
...(略)...
sentence1:  [s]おやすみ[/s] 

上記のように辞書に登録した単語が認識されていることが確認できると思います💪✨

juliusを音声認識サーバーとして使用する

先ほど作成した辞書データを用いてjuliusを音声認識サーバーとして使用してみましょう。

juliusをモジュールモードで起動するとクライアントとの接続が可能となり
認識した音声をソケット経由で、XML形式のテキストメッセージとして返します。

返されたメッセージをプログラムと連携する事で任意の処理を行うことが可能になります。
今回はPythonで認識した音声データを受け取ってみましょう!

まずはプログラムを格納する作業ディレクトリ(今回は~/speechbot)とファイルの用意をします。

# ディレクトリの作成
mkdir ~/speechbot
# 移動
cd ~/speechbot
# ファイルの作成
touch bot.py

作成した bot.py の中身を以下のように編集します。

上記のプログラムで行っていることは主に以下の4つです。

  • 音声認識サーバーへの接続
  • 受け取ったXMLから認識文字列の検索
  • 認識文字列が存在していたら文字列の抽出
  • コンソールへの表示

それでは実際に音声認識サーバーを起動してプログラムを動かしてみましょう。
まずは以下のコマンドでJuliusを音声認識サーバーとして起動します。

# 音声認識サーバーの起動
julius -C ~/julius/julius-kit/dictation-kit-v4.4/am-gmm.jconf -nostrip -gram ~/julius/dict/bot -input mic -module

# 出力
...(略)...
Stat: server-client: socket ready as server
///////////////////////////////
///  Module mode ready
///  waiting client at 10500
///////////////////////////////

上記のように出力されると準備完了です!

次に別のターミナルを開きプログラムを起動します。

# プログラムの起動
python ~/speechbot/bot.py

プログラムを起動すると音声認識サーバーを起動しているターミナルで
以下のような出力がされプログラムから接続されたことが確認できるかと思います!

# 出力
///  Stat: server-client: connect from 127.0.0.1

クライアントからの接続を受けると,Julius は音声認識可能な状態となります。
それではプログラムを起動したターミナルに戻り実際に話しかけてみましょう。

# 出力
🐛 word:おはよう
🐛 word:おはよう[/s]
🐛 word:おやすみ
🐛 word:おやすみ[/s]
🐛 word:おはよう
🐛 word:おはよう[/s]

上記のように出力され認識した音声をプログラムで受け取れている事が確認できるかと思います!
これで音声認識サーバーとプログラムの接続は成功です🎉

次にラズパイが返事を音声で返せるよう音声合成エンジンのセットアップをしていきます
音声認識サーバーとプログラムは一旦終了させましょう。

音声合成エンジン「Open JTalk」のセットアップ

入力された日本語テキストに基づいて自由な音声を生成するHMMテキスト音声合成システム
「Open JTalk」のセットアップをしていきましょう。

Open JTalkをインストールします

sudo apt-get install open-jtalk open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001

次に音声を再生するスピーカーの確認をしましょう。

# デバイスの確認
aplay -l

# 出力
**** ハードウェアデバイス PLAYBACK のリスト ****
カード 0: Headphones [bcm2835 Headphones], デバイス 0: bcm2835 Headphones [bcm2835 Headphones]
  サブデバイス: 8/8
  サブデバイス #0: subdevice #0
  サブデバイス #1: subdevice #1
  サブデバイス #2: subdevice #2
  サブデバイス #3: subdevice #3
  サブデバイス #4: subdevice #4
  サブデバイス #5: subdevice #5
  サブデバイス #6: subdevice #6
  サブデバイス #7: subdevice #7

上記のアウトプットだとカード1デバイス0であることがわかります。

カード番号とデバイス番号が確認できたら、実際に音が流れるか以下のコマンドで確認しましょう。

# サンプル音声の再生
aplay -Dhw:0,0 /usr/share/sounds/alsa/Front_Center.wav
# ↑ -Dhw:{カード番号},{デバイス番号}

# 音が小さい場合はミキサーを起動して調節
alsamixer

音声が流れたらスピーカーの確認は完了です✨

次に音声合成を行い再生するプログラムの用意をします。
プログラムを格納する作業ディレクトリ(今回は~/speechbot)にJTalk用のスクリプトを作成します。

touch ~/speechbot/jtalk.py

jtalk.pyの中身は以下のように変更してください。

上記のファイルを実行して正常に再生されることを確認しましょう。

python ~/speechbot/jtalk.py

これでプログラムから合成音声を生成して再生出来る事が確認できました!

しかし、現在は男性の声で再生されているかと思います。
メイちゃんの音声ファイルをダウンロードして使用する音声を変更してみましょう。

音声ファイル(MMDagent)のダウンロードと配置を行います。

# ダウンロード
wget https://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/MMDAgent_Example-1.7/MMDAgent_Example-1.7.zip --no-check-certificate

# 解凍
unzip MMDAgent_Example-1.8.zip

# 配置
sudo cp -R ./MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/

そしたら先ほど作成した jtalk.py の音声指定部分をメイちゃんの音声に置き換えてみます

jtalk.py
- htsvoice=['-m','/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice']
+ htsvoice=['-m','/usr/share/hts-voice/mei/mei_bashful.htsvoice']

置き換えたら再生して確認してみましょう

python ~/speechbot/jtalk.py

女性の音声になってることが確認できるかと思います!

今回は mei_bashful を指定していますが、他にも
mei_angrymei_happymei_normalmei_sad
などがあるので、是非他の音声も試してみてください💪

いよいよラストステップ!
セットアップしたOpen JTalkを認識した音声の処理プログラムに組み込んで
音声認識をしたら返事ができるようにしましょう!

juliusとOpenJTalkを組み合わせて音声認識ボットへ

いよいよラストステップ。
認識音声の処理プログラム(bot.py)で全ステップで作成した
音声合成を行い再生するプログラム(jtalk.py)を呼び出します。

先ほど作成した ~/speechbot/bot.py に以下の変更を行ってください。

bot.py
# -*- coding: utf-8 -*-
import socket
import time
import picamera
import subprocess
+ import jtalk
import random

...(略)...

        # 認識文字列があったら...
        if index != -1:
            # 認識文字列部分だけを抜き取る
            line = line[index + 6 : line.find('"', index + 6)]
            # 文字列の開始記号以外を格納していく
            if line != '[s]':
                word = word + line
                print('🐛 word:' + word)

+        # 文字列を認識したら...
+        if word == 'おはよう':
+            morning_word = [u'おはよう', u'おはまる', u'おはぴよ', u'おっはー']
+            jtalk.jtalk(random.choice(morning_word))
+        elif word == 'おやすみ':
+            jtalk.jtalk(u'おやすみまる')
        res = ''

認識した単語がおはようおやすみかを判別してお返事させてます。
( 言葉は好きな単語を使用して下さい!笑 )

それでは実行してみましょう。

# 音声認識エンジンをモジュールサーバーとして起動
julius -C ~/julius/julius-kit/dictation-kit-v4.4/am-gmm.jconf -nostrip -gram ~/julius/dict/bot -input mic -module

# (別のターミナルで)プログラムの起動
python ~/speechbot/bot.py

この状態で話しかけてみると、冒頭に載せたツイートの動画のように
お返事をしてくれることが確認できるかと思います!

これでひとまずシンプルな音声認識ボットの完成です🎉
本当にお疲れ様でした👏👏👏👏!

今回の記事で出来上がったソースコード

  • Juliusの独自辞書データ

https://github.com/minako-ph/raspi_speechbot_dict
  • ~/speechbot/bot.py
  • ~/speechbot/jtalk.py

おわりに

今回作った音声認識ボットのプログラムを改良することでいろんな事ができます!
私の場合は以下のような機能を追加して、今現在も毎日使ってます⭐️

  • おはよう
    • 天気の読み上げ
    • Googleカレンダーの読み上げ
    • Trelloから今日のタスクの読み上げ
    • 睡眠タイムトラッキングの終了
  • おやすみ
    • 睡眠タイムトラッキングの開始

皆さんもぜひ挑戦してみて下さい!

また今回のハンズオンで上手くいかなかない部分などが発生しましたら気軽にコメント
もしくは @minako-ph までDMして下さい🐱

最後まで読んでくださりありがとうございました!

参考

今回参考にさせて頂いた記事はこちらにまとめてます🙇🏻‍♂️ ありがとうございました!

https://www.notion.so/minakoph/RaspberryPi-4-bot-LED-ada628336e3141c0bd88d3153fe56cd5