気の向くままに辿るIT/ICT/IoT
webzoit.net
IoT・電子工作

Raspberry Pi/ESP8266・ESP32/Julius/Open JTalkでスマートスピーカーを作る

ウェブ造ホーム前へ次へ
サイト内検索
カスタム検索
Raspberry Piって?

Raspberry Pi/ESP8266・ESP32/Julius/Open JTalkでスマートスピーカーを作る

Raspberry Pi/ESP8266・ESP32/Julius/Open JTalkでスマートスピーカーを作る

2018/10/01

 Raspberry Pi、ESP8266/ESP-WROOM-02/ESP-WROOM-32/ESP32、音声認識エンジンJulius、デフォルトでは日本語の音声合成Open JTalkでスマートスピーカーを自作してみるページ。

2018/11/21

 後述のように執筆時点では、サーバとしているもの以外、ラズパイを持っていなかった為、ラズパイ/Raspbianの代わりにPC/Debianで検証しました。

 が、いざ、Rapsberry Pi 3 B+を買ってDebian環境からJuliusディレクトリをコピー(scp)して実装してみるとラズパイならでは、Raspbian Jessie/Stretchならでは、ラズパイ3B+ならではの違いがあり、代替・追加作業が必要となりました。

 具体的には、以下で他は、同じ。

  1. sudo apt install binutils-arm-none-eabi
  2. sudo apt install osspd-alsa libasound2-dev
    cd julius
    ./configure --with-mictype=alsa
    make
    以後の手順[sudo modprobe snd-pcm-oss]は実行しない
    参考リンク:raspi最新カーネルでjuliusを動かす
  3. julius/mkgshmm/mkgshmmの配置・確認
    make install
  4. 2項に関連し、環境変数ALSADEVのみ設定(AUDIODEVは設定不要)
    ちなみに[/etc/modprobe.d/alsa-base.conf]の順序変更設定も不要
  5. 2項に関連し、aplay *.wav(-Dオプション不要/Open JTalkスクリプト内)
2018/12/07

 マイクでも想定外の状況に遭遇しました。

 検証に使った2つあるUSB接続のWebカメラ内蔵マイクは、パソコンと内蔵スピーカーや簡易ヘッドセットのイヤホン、3.5mmミニジャック接続の100均スピーカーでは、音質はクリアに録音・再生でき、ラズパイではこれら組み合わせで僅かにノイズものりつつも気にならないレベルで正常に再生できました。

 ですが、オーディオ・サウンドカードとUSBサウンドアダプタ音量調整付きUSBサウンドアダプタにある2つのUSBサウンドアダプタと先のWebカメラに付属の簡易ヘッドセットのマイク、同ヘッドセットのイヤホンや3.5mmミニジャック接続の100均スピーカーとの組み合わせだと、何れもラズパイUSBポートに直挿しでもUSB延長ケーブルを介してもマイク入力時のノイズが激しすぎて話になりませんでした。

 また、ラズパイに元々入っている(/usr/share/sound/alsa/の)音源は、簡易ヘッドセットのイヤホンや100均スピーカーをUSBサウンドアダプタに挿しても、ラズパイの3.5mmミニジャックに挿しても音質はクリアなので少なくともUSBサウンドアダプタとスピーカーやイヤホンの組み合わせに起因するものではないと考えてよさそう。

 となるとマイク入力においてラズパイUSBポート付近の何かに影響を受けているようですが、それでもWebカメラ内蔵マイクは十二分に許容範囲内であり、ならば簡易ヘッドセットのマイクが原因かと思いきや、『2つのWebカメラ』のリンク先の流れでDebian/Fedora/NetBSDでの通話テストでは、簡易ヘッドセットはマイク・イヤホンともに、またWebカメラと合わせて正常に使えたので、性能というか、あるとしたらノイズへの耐性の違い...?はたまた、USBサウンドアダプタに起因?

 そもそも、マイクは、別途購入した3.5mmミニプラグのものを、スピーカーもできれば手持ちの100均の3.5mmミニジャックを予定し、USBサウンドアダプタを介すこと前提で想定していたのですが...。

 ただ、マイクが届いてみると4極プラグでパソコンでは完璧に機能したのですが、3.5mmミニジャック(及びプラグ)が先端からLEFT/RIGHT/GND/映像の4極らしきラズパイでは、プラグから分岐しているスピーカー出力用ジャックは機能するも肝心のマイクは残念ながら機能しませんでした。

 と思ったら、3極である簡易ヘッドセットのマイクをラズパイの3.5mmミニジャックに挿してみたところ、録音できない...、そもそもラズパイの3.5mmミニジャックは映像とあることもあって、マイク入力に対応していないのか...?

 これらのことからするとラズパイについては、マイクはUSB接続のものが良さげ、とすると、これを別途調達するなり、Webカメラ内蔵マイクを使うなりといった計画変更が必要になりそう。

 尤も100均のスピーカーも、音が小さすぎ、PulseAudioで最大(153%)にしてみたところ、そこそこで音量調節せずに使うならよいかなと思わなくもありませんが、やはりアンプ?でもかまさないといけないかとは思っています。

2018/12/20

 ラジオ機能追加に伴い、試しにここで使った手持ちのLogicool Z120BWを接続してみたら、やっぱり、ある程度、ちゃんとしたスピーカーじゃないと...と思いました。

2018/12/20

 しばらく使ってみたら、スクリプト内で書くと実行時から更新されないということに気づくドジっぷり...日付時刻は、今回使ったWheather Hacks含め、Web APIから取得したものを使うか、dateコマンドから加工したスクリプト使った方がよさ気。

 何れもスマホやタブレット、パソコンなどからWiFi操作で壁や家具越しにも操作可能なESPモジュールによる自作IR・赤外線リモコンでリモコン対応家電や自作スマートプラグ(スマートコンセント)に挿した非リモコン家電を日本語の音声でWiFi越しにON/OFF操作(スマートリモコ化)できるのは、もちろんのこと、内蔵時計(localtime)、天気APIとも連携し、日付、時刻、今日の天気、明日の天気、明日の(最高/最低)気温も日本語で聞けば教えてくれる、まさにスマートスピーカーは、公開されている情報の組み合わせで比較的簡単にできました。

 Amazon EchoにおけるAlexaの日本語版発売が、2017年11月と考えると、そういう意味では、そこそこ最新っぽい(AI使ってないけど?ん?Juliusで使ってるHMM/Hidden Markov Model/隠れマルコフモデルもAI?)。

 自作IoTガジェットで非IoTな自宅がスマートホームに...。

Echo Plus (エコープラス)、 スマートホームハブ内蔵 - スマートスピーカー with Alexa、ブラック

Echo Spot (エコースポット) - スマートスピーカー with Alexa、ブラック

Echo (エコー) - スマートスピーカー with Alexa、チャコール (ファブリック)

Echo Dot (エコードット) - スマートスピーカー with Alexa、ブラック

【Amazon Alexa認定取得製品】 TP-Link WiFi スマートプラグ 遠隔操作 直差しコンセント Echo シリーズ Googleホーム対応 音声コントロール コンパクト ハブ不要 3年保証 HS105

ラトックシステム スマート家電コントローラ スマホで家電コントロール [Works with Alexa認定製品] RS-WFIREX3

LinkJapan eRemote IoTリモコン 家でも外からでもいつでもスマホで自宅の家電を操作 【Works with Alexa認定製品】

LinkJapan eRemote mini IoTリモコン 家でも外からでもいつでもスマホで自宅の家電を操作【Works with Alexa認定製品】MINI

Nature Remo

スマートリモコン Nature Remo mini【Amazon Echo/Google Home対応】

OHM リモコンセント OCR-05

電機器具専用 リモコンコンセント [品番]07-8251 OCR-05W

 一方、ESPチップ・モジュールでも設定次第で外からも操作可能だし、esp8266-alexa-wemo-emulatorを作って下さった強者もおり、おかげでAlexa定型アクションでも使えますが、ここでは、定型アクションも備えるスマートスピーカーを自作します。

スマートスピーカー用ハードウェアにRaspberry Pi

 さて、自作スマートスピーカーのメインとなるハードウェアは、Raspberry Pi。

 Raspberry Piのモデルに合わせた出力のUSB充電器、microSDカードやUSBメモリ、microUSB-USB Aケーブルあたりは必要。

 また、スマートスピーカーと言えば、マイクとスピーカーは必須。

 どんなものでも良いですが、ただ、ラズパイにオーディオジャックは一つしかなく、スピーカーは、オーディオプラグのみか、USB+オーディオプラグのものしかないでしょうから、自ずとマイクはUSB接続のものを選ぶことになるでしょう。

 あ、USBサウンドアダプタを使えば、マイクとスピーカーがどっちもオーディオジャック出しでも同時にUSBに変換することもできるかもしれませんが。

2018/12/07

 冒頭に追記した通り、ラズパイで使う限りにおいては、少なくともマイクはUSB接続のものを調達すべきな模様。

 スマートスピーカーの自由度を上げるなら有線より無線、ラズパイ3B/3B+は内蔵してるけど、ラズパイ2 Bなら無線LANドングル、有線環境はあるけど無線環境自体ないなら有線ルータにつないでアクセスポイントにする用の無線LANルータも。

 あると便利なのは、個別スイッチ付き電源タップとか、電圧・電流チェッカーあたり。

Raspberry Pi2 Model B ボード&ケースセット (Standard, Clear)-Physical Computing Lab

Raspberry Pi 3 Model B+ スターターセット BASIC

Transcend microSDHCカード 4GB Class4 TS4GUSDHC4

Transcend microSDカード 2GB TS2GUSD

シリコンパワー USBメモリ 32GB USB2.0 キャップ式 永久保証 Ultima U02シリーズ ブラック SP032GBUF2U02V1K

Anker 24W 2ポート USB急速充電器 【急速充電 / iPhone&Android対応 / 折畳式プラグ搭載】 (ホワイト)

ELECOM タブレット用USB2.0ケーブル A-microB 2A出力 0.8m ブラック TB-AMB2A08BK

ルートアール USB 簡易電圧・電流チェッカー ストレート型 3.4V~8.0V,0A~3A クリア RT-USBVA2C (Rev.2)

iBUFFALO ツメの折れないLANケーブル UTP Cat6a ストレート フラットタイプ 1m ブラック BSLS6AFU10BK

BUFFALO エアステーション 11n対応 11g/b USB2.0用 無線LAN子機 親機・子機同時モード対応 WLI-UC-GNM2S

ELPA エルパ スイッチ付タップ 6個口 2m WLS-N62EB(W)

エレコム WiFi ルーター 無線LAN WRC-F300NF 11n 300Mbps 1LDK1階建向け 接続推奨3台

iGOKUピンマイク コンピュータ用マイク 無指向性USBミニクリップマイク マイクロフォン PC用マイクMacbook、インタビュー、Skype、オーディオビデオレコーディング、ポッドキャストなど対応 1.5m延長コード付き 一年間品質保証

iBUFFALO PC用スピーカー USB電源 ホワイト BSSP29UWH

スマートリモコン用ハードウェアにESP8266

 今回、ESPモジュールについては、スマートリモコンとして赤外線LEDから信号を発信するだけということもあり、ESP-01を使いましたが、これでできるということは、上位機種のESP-02...、ESP-12...、ESP-WROOM-02、ESP32などESP-xxでも、これら開発ボードでもできますが、ESPシリーズの内、日本の技適を通っているのは、いまのところ、ESP-WROOM-02/ESP-WROOM-32(とこれらを搭載した開発ボード)のみ。

 ただし、Aliexpress等、海外の通販で買う場合、ESP32でも技適を通っていない(通る前の古い?)ものもあるので技適マークの確認は必要。

Rasbee ESP8266 ESP-01 LWIP AP+STA シリアル WIFI無線モジュール

HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード

waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送

waves ESP-WROOM-32 + 開発基盤 セット 国内発送

waves ESP8266 WiFiモジュール(技適取得済み) ESP-WROOM-02 キット 赤基盤

[スイッチサイエンス] ESP-WROOM-02ピッチ変換済みモジュール《シンプル版》

[スイッチサイエンス] ESP-WROOM-02ピッチ変換済みモジュール《フル版》

HiLetgo 2個セット ESP8285 ESP-M2 CH340開発ボード WIFIシリアルポートモジュール CH340 ESP8266に対応

HiLetgo® 2個セット ESP8266 NodeMCU LUA CP2102 ESP-12E モノのインターネット開発ボード ESP8266 無線受信発信モジュール WIFIモジュール Arduinoに適用 [並行輸入品]

HiLetgo OTA WeMos D1 CH340 WiFi 開発ボード ESP8266 ESP-12F Arduino IDE UNO R3に対応 

Rasbee WeMos D1 R3 簡易デザイン WiFi 開発ボード NODEMCU LUA ESP8266 Arduino UNO 適用 1個 [並行輸入品]

Rasbee OTA WeMos D1 CH340 WiFi 開発ボード ESP8266 ESP-12F For IDE UNO R3 Arduino用 1個 [並行輸入品]

Rasbee アップグレード版 WeMos D1 R2 WiFi UNO 開発板 ESP8266 Arduino 1個 [並行輸入品]

前提

 USB充電器、USBケーブル、microSDカードやUSBメモリなどラズパイ一式の他、マイクとスピーカーを用意、これらが使える状態にあること。

 OSもインストール済みの利用可能なラズパイにホスト名.localでアクセスできるようにOSに応じてAvahiかBonjour、音声認識用にJulius、音声合成用にOpen JTalkをインストールや必要に応じてgit cloneかダウンロード・展開し、動作確認してあること。

 と言っても自身は、現在、サーバに使っているもの以外にラズパイを持っていないので、とりあえず、パソコン上でRaspbianのベースでもあるDebian(Linux)で代用しましたが。

 ラズパイとは別に、ここでは、Arduino IDEを使うのでパソコンにおいてESP8266を使う場合、Arduino IDEの[ツール] => [ボード]から[Generic ESP8266 Module]を選択、ESPにスケッチをアップロードできる状態であること。

 ESP32を使う場合には、Arduino IDEの[ツール] => [ボード]から[espressif/arduino-esp32]を選択、ESPにスケッチをアップロードできる状態であること。

 何れにしてもフラッシュメモリ上にファイルを置きたい場合には、SPIFFSを使う準備をしておくこと。

 また、スクリプトやプログラム言語は、何でも良いですが、ここでは、PerlスクリプトとPython(2.7)スクリプトを使ったので、これらがインストールされていること。

 参考までに自身が今回使用したOSは、Debian Stretch amd64、Arduino IDEのバージョンは、1.8.7。

概算

 基本、ラズパイ一式の価格プラスアルファといった価格構成になると思われ、持ち運びを考慮するとWiFi必須、処理速度からしてRaspberry Pi 2B/3B/3B+が無難、2B+WiFiドングルか、WiFi内蔵3B/3B+か、何れにするにしてもラズパイケース、USB充電器、microSDカードかUSBメモリ、USBケーブル等々一式でAmazon相場で1万円前後かと。

 ESPモジュールは、ESP32開発ボードならAmazon相場で1500円前後、今回、映像関連機能をも見据えて?マイク内蔵USBカメラを使いましたが、マイク、スピーカー、その他諸々、Amazon&Amazonマーケットプレイス&100円ショップ相場で一部電子部品などは単価割したとして約2000〜3000円程度?

 締めて1万4千〜1万5千円前後くらいかと。

 Aliexpress相場だとこれの7割くらい〜半額程度といったところか。

 あんまり深く考えていませんが、一から買い揃えるとなると、やはり、どうやっても量産された市販のスマートスピーカーを買ったほうが安いよね、きっと、しかも、より機能豊富だったり。

2019/01/10

 あ、家電操作を含めるとスマートリモコンも買わないといけないのか...すると更に1.5〜2倍くらいだとして3万円前後になる?

 そうだとすると自作は価格メリットもあるか...。

 それに実際使ってみると自作のメリットは意外と多いかも。

 Google AsistantやAmazon Alexaなどもラズパイに実装できるため、ラズパイに注目すると焦点がズレる可能性があり、スマートスピーカーについては、音声認識・音声合成がローカル上のJulius・Open JTalkかクラウド上のAIかの比較といえるかも。

  • スマートスピーカー・スマートリモコンやスマートコンセント(全て自作)を合わせると市販品と遜色ないか安価な可能性
  • ラズパイとESPモジュールなどハードウェアを除き、ソフトウェアは全て無料のフリーソフトウェアを選択可能
    • その場合、クラウドのような使用回数や時間制限超過による課金もなく、何を気にすることなく安心して使える
    • Web APIやRSSを利用した機能拡張が可能
    • API/RSS以外にも自身で作ることさえできれば機能拡張は、自由自在で柔軟に対応可能(AIも例外ではない)
      • ちなみにJulius自体は、DNN(Deep Neural Network≒Deep learning)にも対応済み
  • 自作スマートスピーカー機能はパソコンにも搭載可能なのでUSBマイクとUSBスピーカーさえあれば、PCの電源を入れてある間は、その部屋では、(別途)ハードウェアとしてのスマートスピーカを用意する必要はない
  • ウェイクワードや操作文言・フレーズの制約はなく自由に設定可能
  • クラウドを使わない仕様にすれば、余計なセキュリティリスクの可能性を回避できる
  • あえて買い物機能を搭載しない限り、子どもがダダをこねて発した音声など誤操作で不要なものを買ってしまったというような事態を回避できる
  • 家電操作にESPモジュール回路は必要も別途市販スマートリモコン機器を買う必要はない
    • 家電数台なら市販スマートリモコンより、ESPモジュール回路の方が、安価に調達できる可能性あり
    • 市販スマートリモコンは本体から信号を出力する為、その周辺の赤外線リモコン家電対象ですが、ESPモジュール回路なら部屋やフロアが違っても操作可能
    • もちろん市販品 or 自作スマートコンセントを使えば、非赤外線リモコン家電操作も可能
    • ESPモジュール回路なら音声操作だけでなく、WiFiなどでつながり、同一ネットワーク上にあるパソコン・スマホ・タブレット上のブラウザから操作することも可能
  • 筐体や構成部品の制約はない為、マイクやスピーカーの選定、マイク感度含め、試行錯誤しつつも思い通りにできる

 どっちにしても誤操作については、考慮しておく必要がありますが、在宅時は対処できるとして外出時に電源をOFFにするという運用をすれば問題ないでしょう。

 一方、スマートスピーカーに限らず、外からの家電操作を行なうことも不可能ではないし、スマートロックとカメラによる確認は便利かもと思うものの、特段必要性を感じないという以前に疑問符もあり、現時点では、実装していません。

  • 家にはたいてい誰かしら人がいて、そもそも外から家電操作する必要がない可能性(見守りカメラを除く)
  • 転倒時、電源OFF機能があっても、そもそも転倒の可能性がある家電や火元や事故の原因になり得る家電を外出先から操作するのは、怖い為、対象から除外する必要がある
  • エアコンの運転は、外窓や室内ドアなどの開閉状態によっては、無駄になる可能性
  • エアコンの運転状態(自動・除湿・冷房・暖房、温度設定)など実際の運転状態の確認方法は?(真夏に暖房・真冬に冷房とか怖すぎる)
  • 明示的に電源ONしたテレビ・ステレオ・ラジオなどの音量確認には、監視カメラ内蔵マイクを使う?
  • 誤動作で電源ONしたテレビ・ステレオ・ラジオなどの音量が大きすぎて近所迷惑になる、無駄な電源ONの可能性の確実な排除方法は?
  • セキュリティリスク
    • 勝手に操作されるリスク
    • 巡り巡ってクラウドや自宅回線へ侵入されるリスク
    • GPSを使う機能の場合、居場所を特定されるリスク(自宅付近にいるのか、いないのか...いないなら泥棒...とか)
    • 見守りカメラや監視カメラを悪用されるリスク(誰かに盗聴されていたり、覗かれていたりしたら最悪)
    • ...etc.

 この中の一部については、不在時のタイマー起動にもあてはまるものも。

ESPモジュール関連の準備

 冒頭の自作IRリモコン、自作スマートコンセントを必要に応じて用意します。

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <Arduino.h>
#include <FS.h>
 
const char* path_root  = "/index.html";
 
const char *ssid = "ssid";
const char *password = "password";
 
#define BUFFER_SIZE 16384
uint8_t buf[BUFFER_SIZE];
 
ESP8266WebServer server ( 80 );
IRsend irsend(2);
#define SOFTAP_SSID "XXXXX"
#define SOFTAP_PW "YYYYY"
 
boolean readHTML() {
 File htmlFile = SPIFFS.open(path_root, "r");
 if (!htmlFile) {
  Serial.println("Failed to open index.html");
  return false;
 }
 size_t size = htmlFile.size();
 if (size >= BUFFER_SIZE) {
  Serial.print("File Size Error:");
  Serial.println((int)size);
 } else {
  Serial.print("File Size OK:");
  Serial.println((int)size);
 }
 htmlFile.read(buf, size);
 htmlFile.close();
 return true;
}
 
void handleRoot() {
 Serial.println("Access");
 char temp[100];
 int sec = millis() / 1000;
 int min = sec / 60;
 int hr = min / 60;
 
 snprintf ( temp, 100, "", hr, min % 60, sec % 60 );
 server.send(200, "text/html", (char *)buf);
}
 
void tv_on_off() {
 Serial.println("Power");
 irsend.sendPanasonic(0x555A,0x555AF148688B);
 delay(10);
 irsend.sendPanasonic(0x555A,0x555AF148688B);
 delay(2000);
 server.send(200, "text/html", "Power ON/OFF");
}
// ...必要に応じ、関数追加
 
void handleNotFound() {
 
 String message = "File Not Found\n\n";
 message += "URI: ";
 message += server.uri();
 message += "\nMethod: ";
 message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
 message += "\nArguments: ";
 message += server.args();
 message += "\n";
 
 for ( uint8_t i = 0; i < server.args(); i++ ) {
  message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
 }
 server.send ( 404, "text/plain", message );
}
 
void setup() {
 Serial.begin(115200);
 
 SPIFFS.begin();
 if (!readHTML()) {
  Serial.println("Read HTML error!!");
 }
 
 WiFi.begin(ssid, password);
 irsend.begin();
 Serial.println("");
 // AP+STAモードの設定
 WiFi.mode(WIFI_AP_STA);
 // WiFi.mode(WIFI_STA);
 // APとして振る舞うためのSSIDとPW情報
 WiFi.softAP(SOFTAP_SSID, SOFTAP_PW);
 Serial.print("Connecting to ");
 Serial.println(SOFTAP_SSID);
 Serial.println("----------");
 
 //wait for connection
 while ( WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
 }
 Serial.println("");
 Serial.print("Connected to ");
 Serial.println(ssid);
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
 
 if (!MDNS.begin("esptv")) {
  Serial.println("Error setting up MDNS responder!");
  while (1) {
   delay(1000);
  }
 }
 Serial.println("mDNS responder started");
 
 server.on("/", handleRoot);
 server.on("/TV/Power", tv_on_off);
// ...必要に応じ、アクセス時の処理を追加
 
 server.onNotFound(handleNotFound);
 
 server.begin();
 Serial.println("HTTP server started");
 
 // Add service to MDNS-SD
 MDNS.addService("http", "tcp", 80);
}
 
void loop() {
 server.handleClient();
}
 

 今回使ったラフスケッチは、これ。

 仮にこのまま使う場合、少なくとも宅内・社内無線LAN用のssid/password、ESPモジュール・アクセスポイント用のSOFTAP_SSID/SOFTAP_PWは、実際の環境に合わせて設定する必要があります。

 信号出力ピンは、スケッチにIRsend irsend(2);とあるようにGPIO2を使いました。

 とりあえず、テレビ(検証したのはSHARP AQUOS)のON/OFF用だけ書きましたが、エアコンでもファンでも何でも必要に応じて追記すればよいし、個別に作るなら、GPIOピンは1本で足り、併用するなら、その数に応じてESPモジュールを選ぶと良いでしょう。

 ちなみに声で操作もできますが、例えば、http://esptv.local/TV/Powerのようにブラウザからアクセスするだけで機能するし、SPIFFS関連コードも一部書いてはあるも実装していませんが、handleルートにアクセスしたらindex.htmlを展開すれば、声でもブラウザからでもON/OFF以外の各種操作やESPリモコン回路を併用するなら他機器への操作を併用もできるでしょう。

 どっちでも機能するのは、(Web|USB)カメラなどを使って赤外線発射の動作確認する際にも便利。

Open JTalkの準備

$ echo "あい藍アイi" | open_jtalk -m /usr/share/hts-voice/mei/mei_normal.htsvoice -ow output.wav -x /var/lib/mecab/dic/open-jtalk/naist-jdic && aplay output.wav && rm output.wav
$ echo "一二三" | open_jtalk -m /usr/share/hts-voice/mei/mei_normal.htsvoice -ow output.wav -x /var/lib/mecab/dic/open-jtalk/naist-jdic && aplay output.wav && rm output.wav
$ echo "24500" | open_jtalk -m /usr/share/hts-voice/mei/mei_normal.htsvoice -ow output.wav -x /var/lib/mecab/dic/open-jtalk/naist-jdic && aplay output.wav && rm output.wav
$ ...
$

 Open JTalkは、日本語を標準とするテキストを音声に変換し、読み上げてくれるソフトウェアであり、デフォルトで漢字、ひらがな、カタカナ、アルファベット、算用数字、漢数字、また、日付時刻など意味のある漢字数字混じりの並びの文は、それに合わせて相応に読んでくれます。

 より最新に近いLinuxのリポジトリや、公式サイトなどから、より最新のOpen JTalkパッケージを取得した場合、冒頭や後段のリンク先に書いた通り、インストールや展開するだけで簡単に試してみることができます。

 より具体的には、端末(コンソール・ターミナル)上で必要に応じたオプション付きのopen_jtalkコマンドにテキストをechoしてパイプ経由で渡したり、テキストファイルをcatやリダイレクトしたりするだけで日本語で音読してくれます(読み上げてくれます)。

$ cat jsay
#!/bin/sh
WAV_FILE=/home/xxx/sound/jsay_${RANDOM}.wav
#cd /usr/share/hts-voice/nitech-jp-atr503-m001
#cd /usr/share/hts-voice/mei/mei_happy
#cd /usr/share/hts-voice/mei/mei_normal
cd /usr/share/hts-voice/mei/
echo "$1" | open_jtalk \
-x /var/lib/mecab/dic/open-jtalk/naist-jdic \
-m mei_happy.htsvoice \
-ow $WAV_FILE && \
aplay --quiet $WAV_FILE
rm -f $WAV_FILE
$

 Open JTalkに関しては、後述のPerlスクリプトから利用できるようにスクリプトを作成、実行権限を与えておきます。

 スクリプトは何でも良いですが、ここでは、Raspberry Piを使ってスマホ・音声で家電を制御するのbashスクリプトを使わせて頂きました(自身はシェバンを#!/bin/shとしました)。

 ただ、中間にあったオプションがことごとくエラーとなり、調べても今ひとつよくわからなかったため、省きました。

 また、メイさんの音声ファイルの格納ディレクトリ構成が変わったようなので修正したついでに、声色は、normalではなく、happyなメイさん(mei_happy.htsvoice)にお願いすることにしました。

 これで入力された単語や文章を元気はつらつなメイさんが、しゃべってくれるようになります。

Juliusの準備

 Juliusは、標準では、日本語の音声をテキストに変換してくれるソフトウェア。

 Juliusは、より最新に近いLinuxのリポジトリや、公式サイトなどから、より最新のJuliusパッケージを取得した場合、冒頭や後段のリンク先に書いた通り、インストールするだけで簡単に使ってみることができます。

$ julius -C config_file -C am-gmm.jconf -module
...

 端末(コンソール・ターミナル)上で必要に応じたオプション付き(例えば、最も簡潔なものの1つは、dictation kitのディレクトリに移動し、[julius -C config_file]のようにする)のjuliusコマンドにモジュールモードでの起動を指定する-moduleオプションを付けて([julius -C config_file -module])サーバとして待機させておきます。

<sil>           []              silB
<sil>           []              silE
            []              sp
スタンバイ     [スタンバイ]    s u t a N b a i
ニュートラル    [ニュートラル]  n u t o r a r u
照明起動        [照明起動]      sh o u m e i k i d o u
ライト点ける    [照明起動]      r a i t o t u k e r u
ライト点けて    [照明起動]      r a i t o t u k e t e
電気点けて      [照明起動]      d e N k i t u k e t e
照明停止        [照明停止]      sh o u m e i t e i sh i
ライト消す      [照明停止]      r a i t o k e s u
ライト消して    [照明停止]      r a i t o k e s i t e
電気消して      [照明停止]      d e N k i k e s i t e
暖房起動        [暖房起動]      d a N b o u k i d o u
暖房つける      [暖房起動]      d a N b o u t u k e r u
暖房つけて      [暖房起動]      d a N b o u t u k e t e
暖房停止        [暖房停止]      d a N b o u t e i sh i
暖房止めて      [暖房停止]      d a N b o u t o m e t e
暖房消す        [暖房停止]      d a N b o u k e s u
暖房消して      [暖房停止]      d a N b o u k e s i t e
冷房起動        [冷房起動]      r e: b o: k i d o u
冷房つける      [冷房起動]      r e: b o: t u k e r u
冷房つけて      [冷房起動]      r e: b o: t u k e t e
冷房停止        [冷房停止]      r e: b o: t e i sh i
冷房消す        [冷房停止]      r e: b o: k e s u
冷房消して      [冷房停止]      r e: b o: k e sh i t e
冷房止めて      [冷房停止]      r e: b o: t o m e t e
除湿して        [除湿起動]      j o s i t u sh i t e
除湿消して      [除湿停止]      j o sh i t u k e sh i t e
除湿止めて      [除湿停止]      j o sh i t u t o m e t e
エアコン消して  [エアコン停止]  e a k o N k e s i t e
テレビつけて    [テレビ起動]    t e r e b i t u k e t e
テレビ消して    [テレビ停止]    t e r e b i k e s i t e
今何時          [時刻報告]      i m a n a N j i
今日何日        [日付報告]      ky o n a N n i t i
今日何曜日      [曜日報告]      ky o n a N y o: b i
今日の天気は    [今日天気]      ky o n o t e N k i w a
今日の天気      [今日天気]      ky o n o t e N k i
明日の天気は    [明日天気]      a sh i t a n o t e N k i w a
明日の天気      [明日天気]      a sh i t a n o t e N k i
今日の気温は    [今日気温]      ky o n o k i o N w a
今日の気温      [今日気温]      ky o n o k i o N
明日の気温は    [明日気温]      a sh i t a n o k i o N w a
明日の気温      [明日気温]      a sh i t a n o k i o N

 ただ、そのconfig_file内で指定する辞書については、標準のものも使えますが、今回のようなリモコン操作など使いみち(使う言葉)が限定されればされるほど自作した方が格段に認識率があがるので、自身の用途に応じて作成する必要があるでしょう。

 今回は、これまた、先のjsayスクリプトでお世話になったリンク先から拝借したものをベースに追加する恰好でこんな辞書を作ってみました。

 dictation kitの場合、おおまかに最小限で言うと命令に当たる日本語のフレーズと、ちょっとした作法に基づいて、これの読みとなるアルファベットの並びを一行ずつ書いたファイルのエンコードをeuc-jpに変換したもの。

 「ちょっとした作法」というのは、無音部分を表わすらしき、<sil>行は、とりあえず、そのまま書いておく、少なくとも母音と子音との間は半角スペースで区切る、大文字の[N]は[ん]を表わす、[:](コロン)は、音をのばすときに使うなど(半角スペース区切りは、先の決まりごと以外の部分でも使え、ここでは基本半角スペース区切りを多用しています)。

 ここでは、3列ありますが、2列めは、1列めやこれに準ずる音声が発せられたとき、Juliusがテキスト表示する際の文字列となり、後述のPerlスクリプトの条件分岐では、この2列めのフレーズで判定しています。

 ちなみに「冷房」については、後述のPerlスクリプトの方で冷房の条件分岐を書き忘れ、認識しないな...というボケをかまして、ちょっとハマり、[r e i b o u]、[r e: b o:]はどっちでも、良かったはずですが、認識しないのと勘違い...[r e I]としたら、[I]がエラーではじかれたりしつつ、書き忘れに気づき、その時書いてあった[r e: b o:]とすることにした経緯があります。

 ただ、詳細は割愛しますが、別途スクリプトで判定する文字列をどうしたらよいのかはさておき、辞書としては、grammer kitで.grammarと.vocaファイルを作成、mkdfa.plで変換し、.dfa/.dict/.termファイルを作成、併用する方が、「今日」とか「明日」とか、「つけて」「けして」など重複するものをまとめられるため、スマートだし、こうした用途の場合、本来の使い分けとして想定されているのは、この方法である模様。

$ iconv -f utf-8 -t euc-jp word.list.utf8 > word.list.eucjp
$

 先で例示した辞書は、エンコーディングがUTF-8ですが、このようにeuc-jpにエンコード・変換したファイルを辞書として指定しないとエラーになったり、原因不明と無駄にあたふたする羽目になったりします。

 ちなみにgrammar kitの方の.vocaは、SJISだったりして、ちょっと、ややこしい...。

$ cat config_file
-w mysmartspeaker.eucjp
-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 oss
-rejectshort 600
-charconv euc-jp utf8
-lv 1500
$

 例えば、dictation kitのconfig_fileにおいて辞書は、-wオプション付きで指定します。

 この中でmysmartspeaker.eucjp以外の言語モデルや音響モデルは、Julius標準のものを使っているだけ。

$ julius -C config_file -C am-gmm.jconf -module
...

 このように実行した状態で自マシン上の他の端末(や設定によっては他のマシン上の端末)から相応の手順を書いたスクリプトでアクセスし、音声を発すると他の端末やマシンに結果をテキストで返してくれます(というのが、モジュールモード)。

 オウム返しでは、芸がない(ボイスチェンジャーか遠方に音声を届けるくらいしか使いみちを思いつかない)が、発声した言葉に応答してくれます(そのようにスクリプトに書くことができる)となると格段に発想が広がる...いや、そうでもない...か?

#voicerecieve.pl
#!/usr/bin/env perl
use utf8;
#use strict;
use warnings;
 
use 5.10.0;
 
use Encode;
use IO::Socket;
 
use LWP::Simple;
use LWP::UserAgent;
use HTTP::Request::Common( "GET" );
 
# 接続先情報にJuliusサーバを指定する
my $socket = IO::Socket::INET->new(
 PeerAddr => 'localhost', # 接続先
 PeerPort => 10500,  # Port 番号
 Proto => 'tcp',  # Protocol
 TimeOut => 5    # タイムアウト時間
);
 
die("Could not create socket: $!") unless($socket);
 
# テレビON/OFFサーバー
local $esp_tv_server='http://esptv.local'
 
# ローカルタイム設定
local ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
$year += 1900;
$mon++;
local @wdays = qw/日 月 火 水 木 金 土/;
 
# 待機モードのループ
while(1){
 my $msg = $socket->getline();
 my ($word, $cm) = &get_parameter($msg);
 
 # 誤認識による誤作動防止のための合言葉を判定
 # 認識の信憑性もCM値で確認する
 if($word eq "スタンバイ" && $cm >= 0.8){
  system("/home/xxx/sound/jsay アクティブモードを開始します");
 
  eval{
   local $SIG{ALRM} = sub { die "timeout" };
 
   # タイムアウトする時間(秒)の設定
   my $timer = 30;
 
   # タイムアウト処理-開始-
   alarm($timer);
 
   # 音声コマンドの受付のループ
   while(1){
    my $msg = $socket->getline();
    my ($word, $cm) = &get_parameter($msg);
 
    # 認識の信憑性が一定である場合はコマンドを識別し実行する
    if($cm >= 0.8){
     given($word){
      when("ニュートラル"){
       system("/home/xxx/sound/jsay アクティブモードを終了します");
       last;
      }
      when("照明起動"){
       system("/home/xxx/sound/jsay 照明を起動します");
#       system("sudo bto_ir_cmd -e -t 022000E70C976800000000000000000000000000000000000000000000000000000000");
      }
      when("照明停止"){
       system("/home/xxx/sound/jsay 照明を停止します");
#       system("sudo bto_ir_cmd -e -t 022000E70C8B7400000000000000000000000000000000000000000000000000000000");
      }
      when("暖房起動"){
       system("/home/xxx/sound/jsay 暖房を起動します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043422EDE230068000001000055000000000000000000000000000000");
      }
      when("暖房停止"){
       system("/home/xxx/sound/jsay 暖房を停止します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043412EDE030068000001000052000000000000000000000000000000");
      }
      when("冷房起動"){
       system("/home/xxx/sound/jsay 冷房を起動します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043422EDE230068000001000055000000000000000000000000000000");
      }
      when("冷房停止"){
       system("/home/xxx/sound/jsay 冷房を停止します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043412EDE030068000001000052000000000000000000000000000000");
      }
      when("除湿起動"){
       system("/home/xxx/sound/jsay じょしつを起動します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043422EDE230068000001000055000000000000000000000000000000");
      }
      when("除湿停止"){
       system("/home/xxx/sound/jsay ジョシツを停止します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043412EDE030068000001000052000000000000000000000000000000");
      }
      when("エアコン停止"){
       system("/home/xxx/sound/jsay エアコンを停止します");
#       system("sudo bto_ir_cmd -e -t 0188004000148043412EDE030068000001000052000000000000000000000000000000");
      }
      when("テレビ起動"){
       system("/home/xxx/sound/jsay テレビをつけます");
      print "$esp_tv_server/TV/Power ON\n";
 
      #### インスタンスの生成
      my $ua = new LWP::UserAgent;
      $ua->timeout( 10 );
 
      #### 要求条件を生成
      my $req = GET( "$esp_tv_server/TV/Power" );
      my $res = $ua->request( $req );
      print $res->as_string;
      }
      when("テレビ停止"){
       system("/home/xxx/sound/jsay テレビを消します");
      print "$esp_tv_server/TV/Power OFF\n";
 
      #### インスタンスの生成
      my $ua = new LWP::UserAgent;
      $ua->timeout( 10 );
 
      #### 要求条件を生成
      my $req = GET( "$esp_tv_server/TV/Power" );
      my $res = $ua->request( $req );
      }
      when("時刻報告"){
       print "時刻は、 $hour 時 $min 分 $sec 秒です。";
       my $speechtime = sprintf("時刻は、%04d時%02d分%02d秒です。", $hour ,$min ,$sec);
 
       system("/home/xxx/sound/jsay $speechtime");
      }
      when("日付報告"){
       print "日付は、 $year 年 $mon 月 $mday 日です。";
       my $speechdate = sprintf("日付は、%04d年%02d月%2d日です。", $year ,$mon ,$mday);
       system("/home/xxx/sound/jsay $speechdate");
      }
      when("曜日報告"){
       print "$wdays[$wday]曜日です。";
       my $speechwday = sprintf("%s曜日です。", $wdays[$wday]);
       system("/home/xxx/sound/jsay $speechwday");
      }
      when("今日天気"){
       system("/home/xxx/sound/today_weather.py");
      }
      when("明日天気"){
       system("/home/xxx/sound/tomorrow_weather.py");
      }
      when("今日気温"){
       system("/home/xxx/sound/jsay 今日の気温は、");
      }
      when("明日気温"){
       system("/home/xxx/sound/tomorrow_temperature.py");
      }
     }
    }
   }
 
   # タイムアウト処理-終了-
   alarm(0);
  };
 
  if($@){
   print $@ . "\n";
   system("/home/xxx/sound/jsay ディアクティベートモードになります");
  }
 }
}
 
# 渡されたXMLにUTF-8フラグを付けてWORDとCMを取得する関数
sub get_parameter(){
 my $msg = shift;
 
 my $text = decode_utf8($msg);
 
 if($text =~ /.+WORD="(\S+)".+CM="(\S+)"/){
  return ($1, $2);
 }else{
  return ("", 0);
 }
}
 

 今回、Perlを使った、そのスクリプトがこれ(voicerecieve.pl)でベースは、bashスクリプトjsayやJulius辞書でもお世話になったリンク先のPerlスクリプトを、ESPモジュールへHTTPアクセスするにあたっては、Perl/Webアクセスから拝借しました。

 また、天気APIから情報を得るスクリプトについては、livedoorの天気予報API『Weather Hacks』を使ったというPythonで書かれたtalk_weather.pyをhttp://raspi.seesaa.net/article/415530289.htmlから拝借。

 入力に応じた分岐条件を追記・編集してもよかったのかもしれませんが、Python初心者の自身は、用途ごとにスクリプトファイルを分割し、ベースと成るPerlスクリプトから、それらPythonスクリプトを直接呼び出す方法をとりました。

 このpythonスクリプト内では、奇しくも発話用にjsayという同じ名前のスクリプトを指定していますが、登録済み実行パスに存在するコマンドとして書いてあるようなので、そうでない場合は、環境に応じてjsayスクリプトのパスを合わせておく必要があります。

 日付と時刻は、天気予報APIからも取得できることを後で知ることになりますが、先にPerlでは、最も古い部類と思われるlocaltime()関数から取得する方法をとっていたため、その値を加工して使いました。

$ cat ~/script/res_date.sh
#!/bin/sh
nowdate=`date | awk '{print $1 $2 $3}' | sed -e "s/$/です/"`
$HOME/sound/jsay $nowdate
$ cat ~/script/res_day.sh
#!/bin/sh
nowday=`date | awk '{print $4}' | sed -e "s/$/です/"`
$HOME/sound/jsay $nowday
$ cat ~/script/res_time.sh
#!/bin/sh
nowtime=`date | awk '{print $5}' | sed -e "s/:/時/" | sed -e "s/:/分/" | sed -e "s/$/秒です/"`
$HOME/sound/jsay $nowtime
$ chmod +x ~/script/res_date.sh
$ chmod +x ~/script/res_day.sh
$ chmod +x ~/script/res_time.sh
2018/12/20

 前述の通り、スクリプト内でlocaltime()を使ったところで、これだと実行時から更新されない為、Web APIから取得するか、オリジナルスクリプトで対応する必要がありました。

 ここでは、日付、曜日、時間用にdate、awksedjsayスクリプトを使ったshellスクリプトをそれぞれ作って自作スマートスピーカー用perlスクリプトvoicerecieve.plに反映させることにしました。

 ただし、時・分・秒共にちょうど(00)の際は、読み飛ばされるので、気になる場合は、別途処理を要します。

 今回作った一連の回路では、家電の操作・制御をより遠くから行なうことができるようにWiFiを介して操作するものを想定し、実際、そのようにしました。

 ベースとさせて頂いたPerlスクリプトでは、音声に反応してラズパイから赤外線信号が届く範囲で操作する前提で直接信号出力するように書かれています。

 一方、今回は、プログラムを書き込むことができるWiFiモジュールであるESP8266/ESP32にリモコンとなる赤外線LEDを回路として組んである(というほどでもないが)ため、壁や家具・家電、何ならフロアを越えてもWiFi信号が届く範囲で利用可能であり、このPerlスクリプトからは、WebサーバでもあるESPの特定のパスにアクセスさせることにしました(そうすることでESP側で赤外線信号を発信するようにしました)。

 このサンプルスクリプトのリモコン操作に関しては、テレビのON/OFFの部分しか編集しておらず、他の分岐も一部追記はしたものの、発信信号を含めたコピペでしかありません。

 尚、Perlスクリプト冒頭でJuliusのアクセス先を'localhost'としたので今回は、自マシンの他の端末からモジュールモードで起動したJuliusにアクセスすることになります。

 とは言え、コンセント接続、電源ONボタンでラズパイのOSが起動、マイクは構成ファイル上で環境変数に登録するなどしておき、無線LANに接続、cron登録されたJulius、Perlスクリプトを順次起動、準備完了LED点灯...といった完成品らしきものを作る場合でも自己完結させるなら'localhost'でよいでしょう。

実行

 半完成品状態の今回は、実行する場合、一部順不同な部分もあるも概ね以下のような手順となります。(JuliusやOpen JTalkについての設定詳細は、前述、もしくは後段関連リンクの各リンク先参照。)

  1. 宅内・社内の無線LANにラズパイやESPが接続可能な状態であることを確認
  2. ESPモジュールを当該無線LANルータやアクセスポイントに接続・確認
  3. ラズパイを起動、同じネットワークドメイン上にあることを確認
  4. ラズパイに接続したマイクが認識され、音声入力が行えることを確認
  5. マイクが、Juliusから使える状態になっていることを確認(事前に環境変数に設定か引数で指定)
  6. Juliusをモジュールモードで起動
  7. (他)端末から先のPerlスクリプトを実行
  8. マイクから辞書にある[スタンバイ]と発声
  9. Juliusから[アクティブモードに入ります]との応答を確認
  10. マイクから辞書にある任意の命令を発声
  11. Juliusからこれに応じた応答があれば、そのとおり、実行される
  12. タイムアウト時間が経過すると[ディアクティベートモードになります]とアナウンスされる
  13. 再度、操作する場合は、8に戻る

 終了する場合は、どちらも端末から起動した場合は、[Ctrl]+[C]。

 場合によっては、スクリプト側は、ps aux | grep するなどしてkillせざるを得ないこともあるかも。

 ここでは、操作の合言葉として元のスクリプトのフレーズ「スタンバイ」を使わせて頂いていますが、「ヘイ、Siri ...」、「OK、グーグル ...」、「アレクサ ...」のように、それっぽくするのも簡単で、辞書とスクリプトの当該箇所を変更するだけ。

 逆に言えば、誤認識がなく、ちゃんと認識されるフレーズであれば、他に制約なくウェイクワードとすることができます。

実行結果

 先のサンプルスクリプトにおいて、家電については「テレビをつけて」「テレビを消して」という声に反応して「テレビをつけます」「テレビをけします」という女声メイさんの日本語音声による応答とともに既に電源が入ってついているか、リモコンで電源を切ってあるテレビのON/OFF操作ができます。(リモコン信号は、テレビに限らず、各電気製品のメーカーや機種のリモコンに合わせたものに変更する必要があります。)

 また、「今日何日」「今日何曜日」「今何時」とか、「今日の天気」「明日の天気」「明日の気温」にも日本語で答えてくれます。

 もちろん、「照明」/「冷房」/「暖房」/「除湿」+「つけて」/「かけて」/「して」/「起動」/「消して」/「消す」/「停止」などと既に辞書登録、Perlスクリプトに追記してあるものも、ESP回路を併用したり、別個に作ったりしつつ、それに合わせたコマンドなどをPerlスクリプトに追記するだけで使えるようになります。

 更なる機能追加もできますが、どれだけ無線でぶら下げられるかは、WiFiルーターやアクセスポイントの性能次第な部分もある?

 ちなみに、びっくりするほど読みが正確なOpen JTalkも漢字の「除湿」を「じょしめ」と読んでしまいますが、スクリプトjsayに渡すときに「じょしつ」や「ジョシツ」とひらがなやカタカナにすれば、とりあえず、回避できます。

感想

 スマートスピーカーを買ってみようと思うほど興味はなかった自身も基本、完成品の組み合わせで手を加えたのは、ほんの少しながら、自作できてみると迂闊にもちょっと感激。

注意

 とりあえずHTTPアクセスした部分は、HTTPSアクセスにする他、あらゆる面でセキュリティは注意の上にも注意が必要でしょう。

ぎょっ

  ラズパイ、Julius、Open JTalkのみならず、コンピュータビジョンやAIも取り込んでいるらしき、OpenCVやTensorFlow/Kerasまで使ったスマートスピーカーを公開している方がいた...。

おっ!?

  やっぱり買うには少し高いな。スマートスピーカーの作り方教えちゃいますってコレもなかなかおもしろいかも。

  ラズパイ+Google AIYで日本語対応スマートスピーカーRaspberry Piで日本語Alexaの方がスマートか。

  いや待てよ?Raspberry pi3でAIスピーカーをガッツリ自作が、すごいか。

機能追加・修正

2018/12/22

 ちなみにメインのノートPCにも自作スマートスピーカー機能を入れてみました。

2018/12/23

 ラズパイはまだもノートパソコンで自作スマートスピーカーの自動起動をやってみたら、思いの外、ハマりましたが、結果、/etc/rc.local等々を使う方法でできました。(詳細はリンク先参照)

2018/12/27

 ノートか、ラズパイか、何れのスマートスピーカーで使うか、使えるかは、まだわかりませんが、侮れないらしきダイソー300円スピーカーを買ってみたら確かに良品でした。(詳細はリンク先参照)

2018/12/28
Yahoo!ニュース読み上げ機能追加
RSSから取得のテキストとOpen JTalkラッパスクリプトによるpythonスクリプト
2019/01/04

 自作ラズパイスマートスピーカー自動起動設定完了、ノート同様にはできず、systemdで実装。

 今、自作スマートスピーカーは、リビングダイニング、キッチン用にしてあります。

 とりあえず、スマートスピーカ構成品の筐体として少し大きめのダンボール(Amazon発注品が入っていた片開きタイプ WxDxH:330x225x120mm)に入れてあります。

 ダンボール内には、転倒防止、保護クッション、遮熱?として滑り止めマットを敷き、USBメモリブートで冷却ファン付きケース入り、各所にヒートシンク装着のラズパイ3B+、スピーカLogicool Z120BWを並べ、後述の理由からマイクとしてマイク内蔵WebカメラELECOM UCAM-220FEは外出しにしてあります。

 スピーカーとマイクが反対(スピーカーの背面にマイクが来る)方向になるよう冷蔵庫の上にスピーカー、マイクが側面になるよう横向き?にし、キッチン側にマイクが来るように置いてあります。

 冷蔵庫は壁を背に上方、前面、側面は開放状態、冷蔵庫正面から見て右方向がキッチン、左方向がLD。

 音が篭もらないようスピーカーホーン部と前面にあるボリュームボタン部のダンボールは切り欠き、できる限り音声認識感度を広くとれるよう試行錯誤した結果、マイクとしてのWebカメラは、箱に入れず、冷蔵庫側面にぶら下げてあります。

 比較的室内の反響が良いためか、リビングでテレビがついていて、キッチンで換気扇が動いている程度の環境でウェイクワードを発する場合、それなりに声を張れば、3m程度離れても音声操作可能となっています。

 ただ、マイクと一定の距離があるとは言え、スピーカー音量を一定以上上げると操作音声を認識できず、反応しないし、相応のボリュームにしておき、マイクと反対になるスピーカー側から音声操作できることもある一方、原因不明も何かの折には、極力マイク付近に近寄らないと音声操作できないこともあります。

 少し甘めの評価かもしれませんが、それでも実用的と言ってよいレベルと認識しています。

 こうした設置場所の場合、例えば、献立考え中、調理中や洗い物中、食器の出し入れ中、掃き掃除や拭き掃除中、部屋、バス、トイレ、玄関に行く・戻る途中、もの思いにふけりつつ、家電操作したり、ニュースや天気、ラジオを聴くために音声操作できます。

 前述の通り、PCにもスマートスピーカ機能を入れていますが、記事やプログラム作成中、調べものなどネット閲覧中等々、キーボード、マウス操作中でも、ながら音声操作できます。

 もちろん、PCを起動すれば、SSHやVNCでラズパイスマートスピーカにアクセスし、新機能を搭載したり、編集したりと自由自在。

 当初、そんなもん要らないでしょと思っていた自身ですら、実際使ってみると、こうしたあらゆるシーンで何かしながら音声操作できることに利便性を感じ、とても重宝しています。

 試行錯誤中はそうもいきませんが、ある程度、一定の機能性を保持できるよう調整できた後は、時に感度がよくないことがあるとややストレスが溜まることもないわけでもありませんが、そこは自作した満足感も手伝ってか、かわいいものだと思えます。

 自作、自作と言っても様々な素晴らしい出来合いのものを組み合わせたに過ぎませんが。

2019/01/08

 ラズパイ起動/再起動/シャットダウン物理ボタンを追加。

 モニタやマウス・キーボードのないIoTデバイスとしてのラズパイ用に、別途PCを起動しアクセスしなくても起動、再起動、シャットダウンできる物理ボタンがあると便利。

 例えば、メインスクリプトは起動したのにJuliusの起動に失敗した(スマートスピーカーがうまく応答しないといった)時などには、ラズパイに専用の再起動ボタンがあると、コンセントの挿抜やスイッチON/OFFで電源を切るのは乱暴なのでシャットダウンボタンがあると、シャットダウンしてみたけど、やっぱり起動という時、コンセントの挿抜・スイッチOFF => ONするまでもなく、シャットダウンと同じボタンを押すことで起動できると...など。

 併せてラズパイACアダプタをスイッチ付きコンセントに挿し、シャットダウン後、コンセントのスイッチでOFF(電源断)できるようにしました。

 もちろん、スイッチ付きコンセントのスイッチONでもラズパイは起動します。

2019/01/24

 ラズパイスマートスピーカーでUPnP/DLNAサーバ上のメディア再生機能を追加。

 例えば、手持ちの音楽CDなどをリッピングしてサーバにアップしておけば、自分だけのオリジナルの音楽ライブラリを任意の音声操作で再生・停止できます。

 もちろん、メディアサーバのストレージが許す限り、音源追加は自由にできます。

 自身の場合、他サーバ機能含め、メディアサーバもラズパイ、現在、ストレージは、HDD 2TB。

 今は、「音楽かけて」と呼びかけると「CDコレクションを再生します」と応答後、再生が始まる(シャッフルし、再生する度に曲順が変わる)ようにしてあります。

2019/01/30

 ラジオ再生において、これまでの、らじる★らじるを含むradikoやサイマルラジオ各局の他、IcecastストリームからJazz、Classicに加え、Blues放送局を追加、呼びかけワードは、まんま「ブルース」。

 気がつけば、言いよどみがあり、記憶が曖昧だったのでjsayスクリプトで単に"睦月・如月・弥生..."といった旧暦の月を読ませるold_month.shを作成、追加、コールワードは、「旧暦の月」。

 尚、Open JTalkの読み上げにおいて「文月」の「ふづき」は、「ふみづき」と併せ、これも正しいと思われるので漢字のままも、「神無月」は「かみなづき」と読み、間違いではない気もしますが、一応、これだけ平仮名で「かんなづき」としました。

 これらを反映させるため、何れも応答スクリプト、Julius辞書への追記、Juliusオリジナル辞書ファイルエンコーディングをeucjpへ変換、systemctlでstop/disable/daemon-reload/enable/start。

 ちなみに先日、テレビでスマートホーム三昧の方が出ていてGoogle Homeの音声もチラッと流れていましたが、それを聞いた限りにおいては、明らかにOpen JTalkの方が、イントネーションに違和感がないように感じました。

2019/02/04

 仕組みからして元々、定型アクションを登録・実行できることを明記。

2019/02/05

 自作ラズパイスマートスピーカーでテレビを音声操作する方法の概要を追記。

2019/02/06

 ほぼ全て既存機能ながら以下の機能のページを起こしました。

天気予報読み上げ
Web APIから取得のテキストとOpen JTalkラッパスクリプトによるpythonスクリプト
日付時刻読み上げ
date/awk/sedコマンドとOpen JTalkラッパスクリプトによるshellスクリプト
干支・十二支読み上げ
テキストとOpen JTalkラッパスクリプトによるshellスクリプト
旧暦の月読み上げ
テキストとOpen JTalkラッパスクリプトによるshellスクリプト

備考

2018/10/05

 Chainerも入れたりしたけど、TensorFlow、Kerasを使ってみるべく、pyenv、virtualenvなどと比較検討した結果、検討時点で想定はしていたことではありますが、anaconda3を介したら、Pythonバージョンが上がってシステム側もPython3を強制使用するようになってしまう関係で自作スマートスピーカーにおいて呼び出していたPython2ベースのスクリプトでエラーになり、スクリプトをPython3ベースに修正する必要がありました(anacondaを削除すれば、強制されないため、これを削除するっていう手もありますが...)。

2018/10/07

 結局、Dockerを使うことにし、anacondaを削除したのでホストOS上のPythonバージョンに影響はなく、問題は解決。

2018/11/21

 冒頭追記のようにRaspberry Pi 3 Model B+を買って実装、3B/3B+ならではのUSBブートしているわけですが、スマートスピーカー機能とは別にどういう訳か、人気があるらしきWeb|USBカメラLogicool C270をラズパイに挿しておくとブートするはずのUSBが起動しないという事象に遭遇。

 試しにUSBサウンドアダプタ、もう1つ手持ちのUSBカメラELECOM UCAM C0220FE(C0220FEWH)、更には、レスキュー用Live USBを挿してみましたが、何れも正常にラズパイブート用USBからRaspbianが起動することを確認済み。

 なぜだ...例えば、C270の消費電流が他に比べ、一時的にでも相当に高くなり、電流不足に陥っているのか...?、はたまた、他のUSB機器との違いとしてはC270には、ケーブル上にフェライトコアが付いていますが、これの影響があるのか?ちなみにUSB延長ケーブルを介してみても変わらない...。

 尚、C270もラズパイ起動後に挿せば、内蔵マイクも使えるし、以前、ラズパイ2Bでmotionを試したときもカメラとして使えたはず...。

 ん?2Bならいけるのか?と現在サーバとして使っているRaspberry Pi 2 Model Bで試してみるとC270をつないでおいてもラズパイが起動する...。

 ということは、フェライトコアの可能性は消えたと考えてよいだろう、すると3B+との相性...か、なんら異常は見られず、機能していますが、手持ちの3B+の不具合か...、高性能になり消費電力が高くなった3B+に加え、C270が他より高い、結果ブートに影響...という可能性もなくもないか...?

 幸い、これとは別にラズパイと古いパソコン周辺機器を組み合わせてパソコン化を検証してみる為、Aliexpressではありながらも違う店にRaspberry Pi 3 B+を発注してあるので届き次第、試してみようと思います。

 後日、試してみましたが同じでした...、となるとRaspberry Pi 3 Model B+とUSB HDDブートでも触れたように電源容量関係かもしれません。

ウェブ造ホーム前へ次へ