空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

ロボットとお話しよう2(音声でラジコン操作)

robonchu.hatenablog.com

の続き

[Raspberry Pi]USBマイクと音声認識ソフトJuliusを使って音声認識を試す(2) : 工作と競馬

[Raspberry Pi]USBマイクと音声認識ソフトJuliusを使って音声認識を試す(3) ~ フルカラーLEDを音声で操作 ~ : 工作と競馬

をとても参考にさせていただきました。 感謝です。

目標

声を使ってラジコンを操作

準備

dictation-kit-v4.3.1-linuxからmodelファイルをコピーしてこれから作業するフォルダにおく

手順

  1. teleop.dic(utf-8で作成)ファイルの作成
    右   m i g i
    左   h i d a r i
    前   m a e
    後ろ    u sh i r o
    停止    t e i sh i
    
  2. juliusの設定ファイルのteleop.jconfの作成
    -w teleop.dic       #単語辞書ファイル
    #-v model/lang_m/bccwj.60k.htkdic  #N-gram、または文法用の単語辞書ファイルを指定$
    -h model/phone_m/jnas-tri-3k16-gid.binhmm #使用するHMM定義ファイル
    -hlist model/phone_m/logicalTri   #HMMlistファイルを指定する
    -n 5        #n個の文仮説数が見つかるまで検索を行う
    -output 1     #見つかったN-best候補のうち、結果として出力する個数
    -input mic #-demo     #マイク使用
    -zmeanframe
    -rejectshort 600  #検出された入力が閾値以下なら棄却
    #-charconv euc-jp utf8 #入出力エンコード指定(内部euc-jp, 出力utf-8)
    -lv 1000    #入力の振幅レベルの閾値(0~32767)
    
  3. juliusを起動するシェルスクリプトrun_teleop.shの作成
    #! /bin/sh
    export ALSADEV=“plughw:1,0”
    julius -C teleop.jconf -demo -input alsa -nostrip -module > /dev/null &
    echo $!
    sleep 3
    
  4. juliusから送られるコマンドを受け取りarduinoに送る

以下com_julius_arduino.pyを作成。 これを実行すると音声入力に対し、cmd_velがpubされる。

#!/usr/bin/python
# coding:utf-8

import rospy
from sensor_msgs.msg import Joy
from geometry_msgs.msg import Twist

import socket
import subprocess
import xml.etree.ElementTree as ET
 
HOST = "localhost"
PORT = 10500

teleop_cmd = [
    u"右" , \
    u"左" , \
    u"前" , \
    u"後ろ" , \
    u"停止" 
    ]

class RosSet(object):
    def __init__(self):
        rospy.init_node('voice_twist')
        self.twist_pub = rospy.Publisher('cmd_vel', Twist, queue_size=100)
        self.twist = Twist() 

    def sendCmd(self, cmdName):
        if cmdName==u"前":
            self.twist.linear.x = 10
            self.twist.angular.z = 0
        elif cmdName==u"後ろ":
            self.twist.linear.x = -10
            self.twist.angular.z = 0
        elif cmdName==u"右":
            self.twist.linear.x = 0
            self.twist.angular.z = -10
        elif cmdName==u"左":
            self.twist.linear.x = 0
            self.twist.angular.z = 10
        elif cmdName==u"停止":
            self.twist.linear.x = 0
            self.twist.angular.z = 0 
        rospy.loginfo(self.twist)
        self.twist_pub.publish(self.twist)

def main():
    rosset = RosSet()
    p = subprocess.Popen(["sh run_teleop.sh"], stdout=subprocess.PIPE, shell=True)
    pid = p.stdout.read() # juliusのプロセスIDを取得
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((HOST, PORT))
    try:
        data = ""
        while 1:
            if "</RECOGOUT>\n." in data:
                root = ET.fromstring('<?xml version="1.0"?>\n' + data[data.find("<RECOGOUT>"):].replace("\n.", ""))
                for whypo in root.findall("./SHYPO/WHYPO"):
                    if whypo.get("WORD") in teleop_cmd:
                        # 判別した言葉を表示する
                        print whypo.get("WORD")
                        rosset.sendCmd(whypo.get("WORD"))
                    else:
                        print "Unknown"
                data = ""
            else:
                try:
                    data = data + client.recv(1024)
                except:
                    print "ps kill."
                    p.kill() #
                    subprocess.call(["kill " + pid], shell=True) 
                    client.close()
                    break
    
    except KeyboardInterrupt:
        print "KeyboardInterrupt occured."
        p.kill() #
        subprocess.call(["kill " + pid], shell=True)
        client.close()
   
if __name__ == "__main__":
    main()

参考:

www.feijoa.jp