ラズパイを音声でシャットダウン

ラズパイを音声でシャットダウン

車にラズパイを持ち込んで使いたいけど
シャットダウンするときに困りそうなので
音声でシャットダウンできるようにする

bluetooth ボタンも考えたけど
ハズレをひくと買い直しになるし紛失を考えると
音声でシャットダウンし
シガーソケットから給電すればエンジンをかければ電源がはいる

音声認識エンジンJulius をまずはいれる

Raspberry Pi×JuliusとPythonでスマートスピーカー風にカメラを操作

Raspberry PiとJuliusで特定の単語を認識させる

を参考に

mkdir julius
cd julius/
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/

でGit で取得し展開したファイルに移動

sudo apt-get install libasound2-dev libesd0-dev libsndfile1-dev

で必要なライブラリのインストール

Raspberry pi3B+でjuliusを動かせるようになるまでの覚書き(2019.3.10現在)

にあるように
RaspberryPi3b+の最新カーネルでは
snd-pcm-ossモジュールが含まれていないので

sudo apt-get install osspd-alsa
sudo apt-get  install libasound2-dev

でサウンドドライバをインストール

./configure --with-mictype=alsa
make
sudo make install

でコンパイルしてインストール

次に音声認識パッケージの
ディクテーションキットの取得

cd ../
mkdir julius-kit
cd julius-kit/
wget https://osdn.net/dl/julius/dictation-kit-v4.4.zip
unzip dictation-kit-v4.4.zip 

で取得

さらに必要なライブラリのインストール

sudo apt-get install alsa-utils sox libsox-fmt-all

次にマイクの設定

 arecord -l

の結果が

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

これを元に
ALSADEV設定で使用デバイスを指定

vim ~/.profile 

で設定ファイルを開き

export ALSADEV=hw:2

を最終行へ追記

hw;にはカード番号を記述

source ~/.profile 

で設定反映

あとは起動して実験

cd  ~/julius/julius-kit/dictation-kit-v4.4
julius -C main.jconf -C am-gmm.jconf -demo

起動はするけど精度が低すぎて使い物にならない

このため辞書ファイルを作成する

日本語入力ができるように mozc をインストール

sudo apt-get install fcitx-mozc

次に辞書ファイルの作成

cd ~/julius/
mkdir dict
cd dict/
sudo vim hello.yomi

でファイルを作成

おはよう おはよう
こんにちわ こんにちわ

として保存

左に認識させる言葉
右にはよみかたをひらがなで記述して
.yomi という拡張子で保存

区切りのスペースを半角にしないとバグるので注意

これを元に音素ファイルを作成する
このときに文字コードをUTF8 から EUC-JP に変換するので

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

これでファイルが作成される
拡張子は
.phone となる

内容は

おはよう	o h a y o u
こんにちわ	k o N n i ch i w a

となる

次に構文ファイルの作成
これで
認識する文章の構成を定義している

sudo vim hello.grammar

でファイルを作成

S : NS_B HELLO NS_E
HELLO OHAYOU
HELLO KONNICHIWA

として保存

S : NS_B HELLO NS_E

NS_Bが文章の開始
NS_Eが文章の終了
という意味

HELLO OHAYOU
HELLO KONNICHIWA
の部分は
.phone ファイルの読みを大文字にしたもの

あとは語彙ファイルの作成
これはJulius に認識させたい言葉を定義するもので
拡張子は
.voca になる

sudo cp hello.phone hello.voca

でファイルをコピーし編集

% OHAYOU
おはよう o h a y o u
% KONNICHIWA
こんにちは k o N n i ch i w a
% NS_B
[s] silB
% NS_E
[/s] silE

というようにする

次に辞書ファイルへ変換

cd ~/julius/julius-4.4.2.1/gramtools/mkdfa/
mkdfa.pl ~/julius/dict/hello

を実行したがエラー

/home/pi/julius/dict/hello.grammar has 3 rules
/home/pi/julius/dict/hello.voca    has 4 categories and 4 words
---
Now parsing grammar file
Error:       parse error
Error: cannot open "/home/pi/julius/dict/hello.dfa.tmp"
---
no .dfa or .dict file generated

となる

このため
julius 辞書 自作
で検索

ラズパイ4日目①:Juliusで独自辞書を作成する

をみたところ

grammar ファイルの区切りで 
:
で区切っていないので修正

cd julius/dict/
sudo vim hello.grammar

でファイル編集

S : NS_B HELLO NS_E
HELLO : OHAYOU
HELLO : KONNICHIWA

として保存

mkdfa.pl ~/julius/dict/hello

を実行すると
.dfa
.term
.dict
ファイルが生成される

これで独自辞書ができたので
音声認識をするため

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

を実行

これで辞書ファイルへ登録した
おはよう
こんにちわ
だけは認識するようになるが
それ以外は表示されない

次に辞書の追加

Raspberry Pi×JuliusとPythonでスマートスピーカー風にカメラを操作

を参考に
.yomi ファイルを編集

おはよう おはよう
こんにちわ こんにちわ
電源オフ でんげんおふ

として保存

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

を実行

sudo vim hello.grammar

でファイルを編集

S : NS_B HELLO NS_E
HELLO : OHAYOU
HELLO : KONNICHIWA
HELLO : DENGENNOHU

として保存

sudo vim hello.voca 

でファイルを編集

% OHAYOU
おはよう o h a y o u
% KONNICHIWA
こんにちは k o N n i ch i w a
% DENGENNOHU
電源オフ d e N g e N o f u
% NS_B
[s] silB
% NS_E
[/s] silE

として保存

mkdfa.pl ~/julius/dict/hello

で辞書ファイル作成

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


電源オフ
と認識されるのがわかる

次にモジュールモードでJulius の起動

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

これで Julius がサーバとなり
python プログラムとの通信待ちになる

次に

sudo vim speech.py

でファイルを区制

ラズパイと音声認識でLチカ

も参考に

いくつかコードも調べてみた

# -*- coding: utf-8 -*-

はpython3 なら記載不要

なおインストールしたままの状態だと

python --version

で調べると
Python 2.7.16

Python で文頭に記載する文字コードの「アレ」の名称(なんちゃら UTF-8 みたいなやつ)

を参考に

ファイルの内容は

# -*- coding: utf-8 -*-
import socket

host = 'localhost'   # Raspberry PiのIPアドレス
port = 10500         # juliusの待ち受けポート

# パソコンからTCP/IPで、自分PCのjuliusサーバに接続
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))

res = ''
while True:
 # 音声認識の区切りである「改行+.」がくるまで待つ
 while (res.find('\n.') == -1):
  # Juliusから取得した値を格納していく
  res += sock.recv(1024)

 # 音声XMLデータから、<WORD>を抽出して音声テキスト文に連結する
 word =''
 for line in res.split('\n'):
  # Juliusから取得した値から認識文字列の行を探す
  index = line.find('WORD=')

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

  # 「電源オフ」という文字列を認識したら...
  if word == '電源オフ':
   print("電源オフ")
  res =''

この後に

sudo python speech.py

とすると
マイクで
電源オフ
と話しかけると
電源オフ
と表示される

これで音声の認識はできたので
次に
python で linux コマンドの実行

これは subprocess モジュールを使うことでできる

Python: subprocessでOSコマンドを実行する

Pythonでシェルコマンドを実行する

Pythonからシェルコマンドを実行!subprocessでサブプロセスを実行する方法まとめ

を参考に

使い方は
subprocess.run([“実行したいコマンド”,”オプションなど”,…])
でOK

今回はshutdown コマンドを使うので

Raspberry Piの電源をブラウザからOFF

を参考に

# -*- coding: utf-8 -*-
import socket
import subprocess
cmd = "sudo shutdown -h now"

host = 'localhost'   # Raspberry PiのIPアドレス
port = 10500         # juliusの待ち受けポート

# パソコンからTCP/IPで、自分PCのjuliusサーバに接続
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))

res = ''
while True:
 # 音声認識の区切りである「改行+.」がくるまで待つ
 while (res.find('\n.') == -1):
  # Juliusから取得した値を格納していく
  res += sock.recv(1024)

 # 音声XMLデータから、<WORD>を抽出して音声テキスト文に連結する
 word =''
 for line in res.split('\n'):
  # Juliusから取得した値から認識文字列の行を探す
  index = line.find('WORD=')

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

  # 「電源オフ」という文字列を認識したら...
  if word == '電源オフ':
    print("電源オフ")
    subprocess.call(cmd, shell=True)
  res =''

として保存

再度

sudo python speech.py 

を実行し
マイクに電源オフと話すと
電源オフと表示された後にラズパイの電源が落ちる