Raspberry Pi でライザップミクを作る

https://instagram.com/p/5l34vHJ35V/

ライザップミク

 Raspberry Piで音声認識・音声合成する記事を以前書きました。

 これにちょいとハードウェアとプログラムを付け加えてライザップするミクさんをつくってみました。

完成動画

 動画は以下のような感じ。

 ニコニコ動画で公開しているのは、ライザップの効果音に以下のニコニコ動画でしか使えない素材を使っているので仕方なくというところです。


必要なもの

 購入品をまとめておきます。

Raspberry Pi 2 Model B

Raspberry Pi 2 Model B

Raspberry Pi 2


SANWA SUPPLY MM-MCUSB16 USBマイクロホン

SANWA SUPPLY MM-MCUSB16 USBマイクロホン

USBマイク


LOGICOOL ステレオスピーカー Z120BW

LOGICOOL ステレオスピーカー Z120BW

アクティブスピーカ


激安サーボモータ(SG90)


 ミクさんの人形は、北海道の新千歳空港の初音ミクタウンのガチャガチャでゲットしました。どうも限定みたいなので、気合のある人は買いに行ってください。サーボモータで動かせるくらいのものだったら何でもよいので適当に用意してモータの軸にくっつけて下さい。
何か紙にミクさんの絵を描いて貼り付けるだけでもよいと思います。


 その他のSDカード、電源、ディスプレイケーブル等の必要品は、以下のセットアップの記事を参考に各自必要なものを買ってください。
karaage.hatenadiary.jp

ハードウェアセットアップ

 以下のとおり接続します

  • USBマイクをRaspberry PiのUSBポートに接続
  • アクティブスピーカのオーディオコネクタをRaspberry Piのヘッドフォン端子に接続。電源はRaspberry PiのUSBポートからとりましょう
  • 激安サーボモータをRaspberry PiのGPIOコネクタに接続。具体的には以下のような感じ

黒:GND(6pinとか)
赤:Vcc(2pinか4pin)
オレンジ:(12pin)

ソフトウェアセットアップ

 以下ソフト関係のセットアップ。ここからはLinuxのコマンドやプログラム(宇宙語)が続くので興味ある方のみ続きをクリックください。

音声認識辞書作成

 エディタで辞書ファイルを作成します。

$ vi recog.yomi

 ファイルの中身は以下としてください。以下の例だと「みく」と認識すると「C1」というコマンドに対応。「こんにちは」と認識すると「C2」というコマンドに対応するようになります。

C1      みく
C2      こんにちは

 あとは、辞書を変換して移動

$ iconv -f utf8 -t eucjp recog.yomi | yomi2voca.pl > recog.dic
$ mv recog.dic ./julius-kits/dictation-kit-v4.2.3

WiringPiセットアップ

 音声に反応して、サーボモータを介してミクさんが動くようにします。サーボモータを動かすために、WiringPiを使ってRaspberry PiのGPIO端子を制御するので、WiringPiをセットアップします。

 WiringPiのインストールのため以下コマンド実行

$ git clone git://git.drogon.net/wiringPi
$ cd WiringPi
$ ./build


 次にpythonでWiringPiを使うため、以下コマンドでWiringPi2-Pythonのインストール

$ cd
$ sudo apt-get update
$ sudo apt-get install python-dev python-setuptools
$ git clone https://github.com/Gadgetoid/WiringPi2-Python.git
$ cd WiringPi2-Python
$ sudo python setup.py install

 WiringPiのピン配置は以下参照。プログラムでwiringPiSetup()という関数を使用して設定すると以下のピン配置になります。wiringPiSetupGpio()だといわゆる普通(?)のRaspberry Piのピン配置になるようです。

raspi.tv

mikusan.py

 メインプログラムとなるmikusan.pyというスクリプトを作成

$ vi mikusan.py

mikusan.pyの中身は以下ね

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
#import serial
import xml.etree.ElementTree as ET
import subprocess
import time
import wiringpi2 as wiringpi

host = 'localhost'
port = 10500
 
# port setting
SERVO = 4

wiringpi.wiringPiSetup()
wiringpi.pinMode(SERVO,1)
wiringpi.softPwmCreate(SERVO,0,100) # Setup PWM using Pin, Initial Value and Range parameters

def miku():
    # music
    cmd ="aplay -Dhw:1,0 /home/pi/rz.wav&"
    subprocess.call(cmd, shell=True)

    # action
    wiringpi.delay(500)
    for time in range(0,2):
        for brightness in range(0,100): # Going from 0 to 100 will give us full off to full on
	    wiringpi.softPwmWrite(SERVO,brightness) # Change PWM duty cycle
	    wiringpi.delay(10) # Delay for 0.2 seconds
        for brightness in reversed(range(0,100)):
    	    wiringpi.softPwmWrite(SERVO,brightness)
	    wiringpi.delay(10)
    for time in range(0,3):
        for brightness in range(0,100): # Going from 0 to 100 will give us full off to full on
	    wiringpi.softPwmWrite(SERVO,brightness) # Change PWM duty cycle
	    wiringpi.delay(5) # Delay for 0.2 seconds
        for brightness in reversed(range(0,100)):
	    wiringpi.softPwmWrite(SERVO,brightness)
            wiringpi.delay(5)
    wiringpi.delay(600)
    for time in range(0,2):
        for brightness in range(0,100): # Going from 0 to 100 will give us full off to full on
	    wiringpi.softPwmWrite(SERVO,brightness) # Change PWM duty cycle
	    wiringpi.delay(10) # Delay for 0.2 seconds
        for brightness in reversed(range(0,100)):
	    wiringpi.softPwmWrite(SERVO,brightness)
	    wiringpi.delay(10)
    for time in range(0,3):
        for brightness in range(0,100): # Going from 0 to 100 will give us full off to full on
    	    wiringpi.softPwmWrite(SERVO,brightness) # Change PWM duty cycle
            wiringpi.delay(5) # Delay for 0.2 seconds
        for brightness in reversed(range(0,100)):
            wiringpi.softPwmWrite(SERVO,brightness)
	    wiringpi.delay(5)

def hello():
    # voice
    cmd ="/home/pi/soft/aquestalkpi/AquesTalkPi こんにちは: | aplay -Dhw:1,0&"
    subprocess.call(cmd, shell=True)

    # action
    for time in range(0,2):
        for brightness in range(0,80): # Going from 0 to 100 will give us full off to full on
            wiringpi.softPwmWrite(SERVO,brightness) # Change PWM duty cycle
	    wiringpi.delay(2) # Delay for 0.2 seconds
        for brightness in reversed(range(0,80)):
	    wiringpi.softPwmWrite(SERVO,brightness)
	    wiringpi.delay(2)

clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsock.connect((host, port))

sf = clientsock.makefile('rb')

while True:
    line = sf.readline().decode('utf-8')
    if line.find('WHYPO') != -1:
        print line
        if line.find(u'C1') != -1:
            print("miku")
            miku()
        elif line.find(u'C2') != -1:
            print("hello")
            hello()

 スクリプトに実行権限与えて、/usr/local/bin/ 以下に移動

$ chmod 755 mikusan.py
$ sudo cp mikusan.py /usr/local/bin/

mikusan.sh

 次にmikusan.shというプログラムを作成。juliusを起動してから mikusan.pyを立ち上げるというだけのプログラム。

$ vi mikusan.sh

 中身は以下ね。

#!/bin/sh
julius -C /home/pi/julius-kits/dictation-kit-v4.2.3/recog.jconf &
sleep 5 
mikusan.py &

exit 0

 めっちゃ手抜きです…本当はjuliusの正常起動フラグをみてから次のプログラムを起動するべきとは思うのですが適当にスリープを入れて対処というダメダメプログラム。2行目のrecog.jconfのファイルの場所は自分の環境に合わせて適宜変更してあげてください。

 実行権限与えて /usr/local/bin/ 以下に移動

$ chmod 755 mikusan.sh
$ sudo cp mikusan.sh /usr/local/bin/

mikusan自動起動

 最後に、自動起動させるためのプログラム。その名もmikusan を作成

$ vi mikusan

 中身は以下

### BEGIN INIT INFO
# Provides: mikusan
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start Mikusan at boot time
# Description: Start Mikusan at boot time
### END INIT INFO
#! /bin/sh
# /etc/init.d/mikusan
DAEMON=/usr/local/bin/mikusan.sh
case "$1" in
  start)
        echo -n "Starting Mikusan"
        sudo $DAEMON
        ;;
  stop)
        echo -n "Stopping Mikusan"
        sudo killall julius
	sudo killall python
        ;;
esac
exit 0

 実行権限与えて /etc/init.d/ 以下に移動

$ chmod 755 mikusan
$ sudo cp mikusan /etc/init.d/

 プログラムはこれでOK。

ソフト動作確認

 続いて順に動作確認。まずは「mikusan.py」が正しく動くか。

$ julius -C 
$ sudo mikusan.py

 これで「ミク」とか話しかけてみましょう。動いたらもうできたも同然。うまく動かなかったら、残念でしたね。そういうこともあります。「mikusan.py」とかハードの接続チェックしてみましょう。それでもダメだったら私が何か誤記しているのかもしれないです。大変申し訳ないことです。

 無事動いたの確認できたら一旦停止しましょう。

$ killall julius
$ sudo killall python

 次に「mikusan.sh」実行して一つのコマンドでjulius起動と「mikusan.py」起動して無事動くこと確認

$ sudo mikusan.sh

 多分これは動くでしょ。ダメだったら再起動とかしてみるとひょっとしたらうまくいくかも。うまくいったら、また一旦停止

$ killall julius
$ sudo killall python

 サービス起動の確認。これで起動すればOK。

$ sudo /etc/init.d/mikusan start

 stop(停止)は、実は手抜きスクリプトなのでできません…ごめんちゃい、誰か直して。


 あとは、以下のコマンドを実行して自動起動の設定をします。

$ sudo update-rc.d mikusan defaults

 この後、再起動すると、プログラムが自動で起動してミクさんが声に反応してくれるようになります。これで完了です。お疲れ様でした。自動起動を止めたくなったら以下コマンドを実行してください。

$ sudo update-rc.d mikusan remove

まとめ

 音声認識、最初に参考にしたサイトのプログラムがうまく動かなかったりして意外に難しかったです。正直もうちょっとスマートなプログラムにしたい気もしますが、今回は諦めました。誰かもっと良い例教えてくれると嬉しいです。
 試した感じは、結構正しく認識するなという印象ですが、ざわざわしているところだとどうなるかは不明です。家とかなら普通に使えそうな印象を持ちました。フリーでこれだけできちゃうのは凄いですね。リモコン制御とかやったら便利そうな気もするけど、おおよそ結果が見えているのとそれだけのためにRaspberry Piの電源入れっぱなしはちょっとイマイチな気もするのでちょっと迷い中です。なんかもっと役に立たない電波なことをしたいなという気がします。

関連記事