「Codama」ボードを使った音声認識によるデバイス制御実験（Part-3：音声によるLED・サーボモータの制御例）

ここでは、定番のLEDやサーボモータの制御を音声により行ってみます。
![codama_11_1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134874/ae1a795d-70d7-0a37-53d8-c0b609f0ce20.jpeg)

## ■　構成
[Part-1](https://qiita.com/wanic/items/a8cfd5bdda62b301ce12)では、認識結果を取得しRedis D/Bに保存処理を行い、[Part-2](https://qiita.com/wanic/items/11963ac57a6e1d412c74)では、Redis D/Bに保存された認識結果を読み込みWindows Appを起動することを行いました。
Part-3では、発話により定番のデバイスであるＬＥＤ及びサーボモータを動かす試みを行います。
![codama_11_2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134874/af13344c-c97d-682f-cc54-de5323bfbdf9.jpeg)

## ■　用意したもの
* ラズパイ ３B+
* １６チャンネル　サーボモータドライバ（ＰＣＡ９６８５）×１
* サーボモータ（ＳＧ９０）×４
* トランジスタ（２ＳＣ１８１５）×３
* ＬＥＤ×３（赤・緑・青）
* 抵抗（100Ω×３、2.2KΩ×３）
* 5V電源×２（マイクロＵＳＢ）
  
※　ラズパイのＧＰＩＯ端子に直接ＬＥＤやサーボモータを接続する場合、採れる電流値に制限があるので気をつける必要があります。　ＧＰＩＯから採れる電流は全体で、せいぜい５０ｍA程度なので別電源での駆動が必要です。　また、電源はスイッチ付きのものを採用するととっても便利です。
※　トランジスタは手持ちのものを使いました。　動作の確認をするだけなので明るい方が良いと思い、ＬＥＤの電流制限抵抗は寿命を考えずに100Ω程度にしました。

## ■　接続図
以上のものを図のように接続しました。
![codama_12.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134874/acbce404-e88f-993d-9b48-b4e1dc1ac85e.jpeg)

補助的に、

* [プロトタイプ基板](https://www.switch-science.com/catalog/3945/)
* [電源用マイクロＵＳＢコネクタＤＩＰ化キット](http://akizukidenshi.com/catalog/g/gK-10972/)

などを使うと、作りやすいかと思います。

## ■　動作を考える
実験ですので、この段階では複雑な動作をさせる必要は無いのですが、それではつまらないので、単純かつ多少面白みのある動作を考えます。

* 色指定によるＬＥＤの点灯・消灯  
点滅はプログラムが複雑になるので止めました。
* 角度及び増分指定によるサーボモータの駆動  
旗の上下やカメラマウントのパン・チルトの微小動作を行います。
* どの言葉に反応するかは、下方にあるリストの辞書を見てもらえれば分かるかと思います。

実際の動作はビデオを参照願います。
Youtube：[「Codama」ボードを使った音声によるデバイス制御実験](https://www.youtube.com/watch?v=MBEKwfJghEw)

## ■　Part-3のまとめ

* 今回の実験を通して、はっきり発話し正しい音声認識結果が得られれば、任意のデバイスを制御できることを確認できた。
* 個人レベルでも高精度な音声認識が使えるようであるので、後はどうプログラムや電子・電気回路を考えるかの段階に入ったようである。
* しかしながら、何度も書いているように反応が鈍いのが残念である。　５Gが使えるようになるまで、当面はリアルタイム性を気にしないターゲットで応用を考えるしかないのか？
* 形態素解析により、辞書の圧縮及び精度の向上を確認することが出来たが、発話内容に数字が混じっている場合、形態素解析処理前後で何らかの文字列処理が必要となるようなので今後の課題とする。  
（例えば、「あと１０．５度右向いて」など）

次回の「おまけ」では、今回使ったカメラ画像と認識結果をブラウザ上でモニタする例を紹介します。

## ■　参考資料
* [PCA9685で複数サーボモーターをコントロール](https://qiita.com/tokyokuma/items/a3906809badbb704d91e)

## ■　プログラム構成

基本的な考え方は、[Part-2](https://qiita.com/wanic/items/11963ac57a6e1d412c74)の「音声によるPC Appの起動」と同じです。　異なるのは、辞書及び動作内容ですので、細かな解説は省略します。

* define.py
* appcont.py
* AppControl.py

### ■　起動方法

* [Part-1](https://qiita.com/wanic/items/a8cfd5bdda62b301ce12)で作成した「Codama」をハットしたラズパイで「main.py」を起動する。
* 今回作成したラズパイで、「AppControl.py」を起動します。
* ウェイク・アップ・ワードを認識させた後に、発話して動作させます。

## ■　プログラムコード

```python:define.py
# -------------------------------------
# 定数の定義
#   使い方：
#      from define import Const
#      c = Const()    : 最初の方で記述する
# -------------------------------------
class Const:
    ON = 1
    OFF = 0

    # ---------------------
    # Servo Parameter difinition
    # Adafruit PCA9685 channel
    SG90_Left = 0
    SG90_Right = 2
    SG90_Pan = 4
    SG90_Tilt = 6

    Abs_move = 0        # Absolute move
    Inc_move = 1        # Incremental move

    Right_min = 0
    Right_mid = 45
    Right_max = 90

    Left_min = 90
    Left_mid = 135
    Left_max = 180

    Pan_min = 60        # 45
    Pan_mid = 90
    Pan_max = 120       # 135

    Tilt_min = 60       # 30 上向き角度の上限
    Tilt_mid = 90
    Tilt_max = 130      # 105 下向き角度の上限

    Dlt_th = 5          # 微小回転：５度

    # ---------------------
    # GPIO LED Port definition
    LED_BLUE = 19
    LED_GREEN = 26
    LED_RED = 21
```
```python:appcont.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
from __future__ import division
import time
import RPi.GPIO as GPIO
import Adafruit_PCA9685
from define import Const

# 定数の定義
c = Const()

pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq( 60)

class DeviceControl:
    # -------------------------------
    # SG90のパラメータ
    # -------------------------------
    _sg90_min = 160    # -->  : 0 degree
    _sg90_max = 630    # <--  : 180 degree
    _sg90_unit = ( _sg90_max - _sg90_min) / 180
    
    # -------------------------------
    # GPIOの初期化
    # -------------------------------
    def __init__( self):
        self.Right_cpos = c.Right_min
        self.Left_cpos = c.Left_max
        self.Pan_cpos = c.Pan_mid
        self.Tilt_cpos = c.Tilt_mid

        self.servo_init()
        GPIO.setmode( GPIO.BCM)
        GPIO.setup( c.LED_RED, GPIO.OUT)
        GPIO.setup( c.LED_GREEN, GPIO.OUT)
        GPIO.setup( c.LED_BLUE, GPIO.OUT)

        GPIO.output( c.LED_RED, GPIO.LOW)
        GPIO.output( c.LED_GREEN, GPIO.LOW)
        GPIO.output( c.LED_BLUE, GPIO.LOW)
        
    # -------------------------------
    # サーボモータ位置の初期化
    # -------------------------------
    def servo_init( self):
        pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( c.Right_min))
        time.sleep( 0.1)
        pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( c.Left_max))
        time.sleep( 0.1)
        pwm.set_pwm( c.SG90_Pan, 0, self.conv_unit( c.Pan_mid))
        time.sleep( 0.1)
        pwm.set_pwm( c.SG90_Tilt, 0, self.conv_unit( c.Tilt_mid))
        time.sleep( 0.1)
    
    # -------------------------------
    # 正面から見て右のサーボモータ（回転角度を90°に制限）
    # 　右水平位置　：　0°
    # 　中央　　　　：　45°
    # 　左回り垂直　：　90°
    # -------------------------------
    def Right_Servo( self, arg):
        th = arg[ 'prm1']
        kd = arg[ 'prm2']
        if kd == c.Inc_move:
            cpos = self.Right_cpos + th
            if ( cpos >= c.Right_min) and ( cpos <= c.Right_max):
                self.Right_cpos = cpos
        else:
            self.Right_cpos = th

        pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))

    # -------------------------------
    # 正面から見て左のサーボモータ（回転角度を90°に制限）
    # 　左水平位置　：　180°
    # 　中央　　　　：　135°
    # 　右回り垂直　：　90°
    # -------------------------------
    def Left_Servo( self, arg):
        th = arg[ 'prm1']
        kd = arg[ 'prm2']
        if kd == c.Inc_move:
            cpos = self.Left_cpos + th
            if ( cpos >= c.Left_min) and ( cpos <= c.Left_max):
                self.Left_cpos = cpos
        else:
            self.Left_cpos = th

        pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))

    # -------------------------------
    # 左右回転サーボモータ（回転角度を90°に制限）
    # 　正面から見て右端　：　45°
    # 　中央　　　　　　　：　90°
    # 　正面から見て左端　：　135°
    # -------------------------------
    def Pan_Servo( self, arg):
        th = arg[ 'prm1']
        kd = arg[ 'prm2']
        if kd == c.Inc_move:
            cpos = self.Pan_cpos + th
            if ( cpos >= c.Pan_min) and ( cpos <= c.Pan_max):
                self.Pan_cpos = cpos
        else:
            self.Pan_cpos = th
        pwm.set_pwm( c.SG90_Pan, 0, self.conv_unit( self.Pan_cpos))

    # -------------------------------
    # 上下回転サーボモータ（回転角度を75°に制限）
    # 　下端　：　30°
    # 　中央　：　90°
    # 　上端　：　105°
    # -------------------------------
    def Tilt_Servo( self, arg):
        th = arg[ 'prm1']
        kd = arg[ 'prm2']
        if kd == c.Inc_move:
            cpos = self.Tilt_cpos + th
            if ( cpos >= c.Tilt_min) and ( cpos <= c.Tilt_max):
                self.Tilt_cpos = cpos
        else:
            self.Tilt_cpos = th
        pwm.set_pwm( c.SG90_Tilt, 0, self.conv_unit( self.Tilt_cpos))

    # -------------------------------
    # Pan/Tileサーボモータを中央
    # -------------------------------
    def Center_Pos( self, arg):
        self.Tilt_cpos = c.Tilt_mid
        pwm.set_pwm( c.SG90_Tilt, 0, self.conv_unit( self.Tilt_cpos))
        self.Pan_cpos = c.Pan_mid
        pwm.set_pwm( c.SG90_Pan, 0, self.conv_unit( self.Pan_cpos))

    # -------------------------------
    # 角度をサーボ指令単位に変換
    # -------------------------------
    def conv_unit( self, th):
        return int( th * self._sg90_unit + self._sg90_min)

    # -------------------------------
    # 緑のLED
    # -------------------------------
    def Green_LED( self, arg):
        sw = arg[ 'prm1']
        if sw == c.ON:
            GPIO.output( c.LED_GREEN, GPIO.HIGH)
        else:
            GPIO.output( c.LED_GREEN, GPIO.LOW)

    # -------------------------------
    # 赤のLED
    # -------------------------------
    def Red_LED( self, arg):
        sw = arg[ 'prm1']
        if sw == c.ON:
            GPIO.output( c.LED_RED, GPIO.HIGH)
        else:
            GPIO.output( c.LED_RED, GPIO.LOW)

    # -------------------------------
    # 青のLED
    # -------------------------------
    def Blue_LED( self, arg):
        sw = arg[ 'prm1']
        if sw == c.ON:
            GPIO.output( c.LED_BLUE, GPIO.HIGH)
        else:
            GPIO.output( c.LED_BLUE, GPIO.LOW)

    # -------------------------------
    # 赤・緑・青のLED点灯/消灯
    # -------------------------------
    def LED_all( self, arg):
        sw = arg[ 'prm1']
        if sw == c.ON:
            GPIO.output( c.LED_RED, GPIO.HIGH)
            GPIO.output( c.LED_GREEN, GPIO.HIGH)
            GPIO.output( c.LED_BLUE, GPIO.HIGH)
        else:
            GPIO.output( c.LED_RED, GPIO.LOW)
            GPIO.output( c.LED_GREEN, GPIO.LOW)
            GPIO.output( c.LED_BLUE, GPIO.LOW)

    # -------------------------------
    # 赤・緑・青のLEDフラッシュ
    # -------------------------------
    def LED_flash( self):
        time.sleep( 0.35)
        GPIO.output( c.LED_RED, GPIO.HIGH)
        GPIO.output( c.LED_GREEN, GPIO.HIGH)
        GPIO.output( c.LED_BLUE, GPIO.HIGH)
        time.sleep( 0.05)
        GPIO.output( c.LED_RED, GPIO.LOW)
        GPIO.output( c.LED_GREEN, GPIO.LOW)
        GPIO.output( c.LED_BLUE, GPIO.LOW)
        #time.sleep( 0.3)

    # -------------------------------
    # 万歳・バンザーイ
    # -------------------------------
    def Banzai( self, arg):
        for i in range( 0, 2):
            # LED点灯
            prm = { "prm1": c.ON}
            self.LED_all( prm)

            # 左右サーボ
            for th1, th2 in zip( range( 0, 95, 5), range( 180, 85, -5)):
                self.Right_cpos = th1
                pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))
                self.Left_cpos = th2
                pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))
                time.sleep( 0.03)

            # LED消灯
            prm = { "prm1": c.OFF}
            self.LED_all( prm)

            # 左右サーボ
            for th1, th2 in zip( range( 90, -5, -5), range( 90, 185, 5)):
                self.Right_cpos = th1
                pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))
                self.Left_cpos = th2
                pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))
                time.sleep( 0.03)

        # サーボ位置の初期化
        time.sleep( 1)
        self.servo_init()

    # -------------------------------
    # 三々七拍子
    # -------------------------------
    def Sansan( self, arg):
        # 右サーボ ＆ LED
        angle = [ 0, 30, 60, 90]
        for th in angle:
            self.Right_cpos = th
            pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))
            self.LED_flash()

        # 左サーボ ＆ LED
        angle = [ 180, 150, 120, 90]
        for th in angle:
            self.Left_cpos = th
            pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))
            self.LED_flash()

        # 左・右・Panサーボ ＆ LED
        angle1 = [ 0, 90, 0, 90, 0]
        angle2 = [ 180, 90, 180, 90, 180]
        #angle3 = [ 90, 45, 90, 135, 90]
        angle3 = [ 90, 60, 90, 120, 90]
        for th1, th2, th3 in zip( angle1, angle2, angle3):
            self.Right_cpos = th1
            pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))
            self.Left_cpos = th2
            pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))
            self.Pan_cpos = th3
            pwm.set_pwm( c.SG90_Pan, 0, self.conv_unit( self.Pan_cpos))
            self.LED_flash()

        # 左・右・Tiltサーボ ＆ LED
        angle1 = [ 30, 60, 90]
        angle2 = [ 150, 120, 90]
        #angle3 = [ 30, 105, 90]
        angle3 = [ 60, 130, 90]
        for th1, th2, th3 in zip( angle1, angle2, angle3):
            self.Right_cpos = th1
            pwm.set_pwm( c.SG90_Right, 0, self.conv_unit( self.Right_cpos))
            self.Left_cpos = th2
            pwm.set_pwm( c.SG90_Left, 0, self.conv_unit( self.Left_cpos))
            self.Tilt_cpos = th3
            pwm.set_pwm( c.SG90_Tilt, 0, self.conv_unit( self.Tilt_cpos))
            self.LED_flash()

        # サーボ位置の初期化
        time.sleep( 1)
        self.servo_init()
    # -------------------------------
    # GPIOの初期化
    # -------------------------------
    def Led_clear( self):
        GPIO.cleanup()
```

```python:AppControl.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import MeCab
import redis
from define import Const
from devcont import DeviceControl

# ------------------
# 定数の定義クラス
# ------------------
c = Const()
# ------------------
# デバイスコントロールクラス
# ------------------
dv = DeviceControl()
# ------------------
# MeCab
# ------------------
mcab = MeCab.Tagger( "-Ochasen")    # 認識結果をカタカナで使いたいため
# ------------------
# コールバックの実行
# ------------------
def handler( func, *args):
    return func( *args)
# ------------------
# Redis
# ------------------
hostname = '192.168.xxx.xxx'
pool = redis.ConnectionPool( host = hostname, port = 6379, db = 0)
r = redis.StrictRedis( connection_pool = pool)
prec_id = None

# ------------------
# 認識パターン辞書
# ------------------
Pat_dic = [
    # 赤のＬＥＤつけて
    { "Keyword": [ 'アカ',  'ＬＥＤ', 'ツケ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.ON}},
    # 赤いＬＥＤつけて
    { "Keyword": [ 'アカイ','ＬＥＤ', 'ツケ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.ON}},
    # 赤のＬＥＤ消して
    { "Keyword": [ 'アカ', 'ＬＥＤ', 'ケシ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},
    # 赤いＬＥＤ消して
    { "Keyword": [ 'アカイ', 'ＬＥＤ', 'ケシ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},
    # 赤のＬＥＤ化して <- 「消して」が「化して」と変換される場合がある
    { "Keyword": [ 'アカ', 'ＬＥＤ', 'カ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},
    # 赤のＬＥＤ決して <- 「消して」が「決して」と変換される場合がある
    { "Keyword": [ 'アカ', 'ＬＥＤ', 'ケッシテ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},
    # 赤いＬＥＤ化して <- 「消して」が「化して」と変換される場合がある
    { "Keyword": [ 'アカイ', 'ＬＥＤ', 'カ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},
    # 赤いＬＥＤ決して <- 「消して」が「決して」と変換される場合がある
    { "Keyword": [ 'アカイ', 'ＬＥＤ', 'ケッシテ'], "Func": dv.Red_LED,   "Arg": { "prm1": c.OFF}},

    # 青のＬＥＤつけて
    { "Keyword": [ 'アオ', 'ＬＥＤ', 'ツケ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.ON}},
    # 青いＬＥＤつけて
    { "Keyword": [ 'アオイ', 'ＬＥＤ', 'ツケ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.ON}},
    # 青のＬＥＤ消して
    { "Keyword": [ 'アオ', 'ＬＥＤ', 'ケシ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},
    # 青いＬＥＤ消して
    { "Keyword": [ 'アオイ', 'ＬＥＤ', 'ケシ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},
    # 青のＬＥＤ化して <- 「消して」が「化して」と変換される場合がある
    { "Keyword": [ 'アオ', 'ＬＥＤ', 'カ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},
    # 青のＬＥＤ決して <- 「消して」が「決して」と変換される場合がある
    { "Keyword": [ 'アオ', 'ＬＥＤ', 'ケッシテ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},
    # 青いＬＥＤ化して <- 「消して」が「化して」と変換される場合がある
    { "Keyword": [ 'アオイ', 'ＬＥＤ', 'カ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},
    # 青いＬＥＤ決して <- 「消して」が「決して」と変換される場合がある
    { "Keyword": [ 'アオイ', 'ＬＥＤ', 'ケッシテ'], "Func": dv.Blue_LED,  "Arg": { "prm1": c.OFF}},

    # 緑のＬＥＤつけて
    { "Keyword": [ 'ミドリ', 'ＬＥＤ', 'ツケ'], "Func": dv.Green_LED, "Arg": { "prm1": c.ON}},
    # 緑のＬＥＤ消して <- ちゃんと変換されている
    { "Keyword": [ 'ミドリ', 'ＬＥＤ', 'ケシ'], "Func": dv.Green_LED, "Arg": { "prm1": c.OFF}},
    # 緑のＬＥＤ化して <- 念のため
    { "Keyword": [ 'ミドリ', 'ＬＥＤ', 'カ'], "Func": dv.Green_LED, "Arg": { "prm1": c.OFF}},
    # 緑のＬＥＤ決して <- 「消して」が「決して」と変換される場合がある
    { "Keyword": [ 'ミドリ', 'ＬＥＤ', 'ケッシテ'], "Func": dv.Green_LED, "Arg": { "prm1": c.OFF}},

    # ＬＥＤ全部点けて
    { "Keyword": [ 'ＬＥＤ', 'ゼンブ', 'ツケ'], "Func": dv.LED_all, "Arg": { "prm1": c.ON}},
    # ＬＥＤ全部消して
    { "Keyword": [ 'ＬＥＤ', 'ゼンブ', 'ケシ'], "Func": dv.LED_all, "Arg": { "prm1": c.OFF}},

    # 右向いて
    { "Keyword": [ 'ミギ', 'ムイ'],   "Func": dv.Pan_Servo, "Arg": { "prm1": c.Pan_min, "prm2": c.Abs_move}},
    # 左向いて
    { "Keyword": [ 'ヒダリ', 'ムイ'],   "Func": dv.Pan_Servo, "Arg": { "prm1": c.Pan_max, "prm2": c.Abs_move}},

    # もう少し右
    { "Keyword": [ 'モウスコシ', 'ミギ'], "Func": dv.Pan_Servo, "Arg": { "prm1":  -c.Dlt_th, "prm2": c.Inc_move}},
    # もう少し左
    { "Keyword": [ 'モウスコシ', 'ヒダリ'], "Func": dv.Pan_Servo, "Arg": { "prm1": c.Dlt_th, "prm2": c.Inc_move}},

    # 上むいて
    { "Keyword": [ 'ウエ', 'ムイ'], "Func": dv.Tilt_Servo, "Arg": { "prm1": c.Tilt_min, "prm2": c.Abs_move}},
    # 下向いて　<- 「下向」＋「い」＋「て」に変換されてしまう
    { "Keyword": [ 'ゲコウ', 'イ', 'テ'], "Func": dv.Tilt_Servo, "Arg": { "prm1": c.Tilt_max, "prm2": c.Abs_move}},
    # 下向いて <- 念のため
    { "Keyword": [ 'シタ', 'ムイ'], "Func": dv.Tilt_Servo, "Arg": { "prm1": c.Tilt_max, "prm2": c.Abs_move}},

    # もう少し上
    { "Keyword": [ 'モウスコシ', 'ウエ'], "Func": dv.Tilt_Servo, "Arg": { "prm1": -c.Dlt_th, "prm2": c.Inc_move}},
    # もう少しした
    { "Keyword": [ 'モウスコシ', 'シ', 'タ'], "Func": dv.Tilt_Servo, "Arg": { "prm1":  c.Dlt_th, "prm2": c.Inc_move}},

    # 正面向いて
    { "Keyword": [ 'ショウメン', 'ムイ'], "Func": dv.Center_Pos, "Arg": { "prm1": 0}},
    { "Keyword": [ 'チュウオウ', 'ムイ'], "Func": dv.Center_Pos, "Arg": { "prm1": 0}},

    # 赤の旗を上げて
    { "Keyword": [ 'アカ', 'ハタ', 'アゲ'], "Func": dv.Right_Servo, "Arg": { "prm1": c.Right_mid, "prm2": c.Abs_move}},
    # 赤い旗を上げて
    { "Keyword": [ 'アカイ', 'ハタ', 'アゲ'], "Func": dv.Right_Servo, "Arg": { "prm1": c.Right_mid, "prm2": c.Abs_move}},

    # 赤の旗下げて
    { "Keyword": [ 'アカ', 'ハタ', 'サゲ'], "Func": dv.Right_Servo, "Arg": { "prm1": c.Right_min, "prm2": c.Abs_move}},
    # 赤い旗下げて
    { "Keyword": [ 'アカイ', 'ハタ', 'サゲ'], "Func": dv.Right_Servo, "Arg": { "prm1": c.Right_min, "prm2": c.Abs_move}},

    # 白の旗を上げて
    { "Keyword": [ 'シロ', 'ハタ', 'アゲ'], "Func": dv.Left_Servo, "Arg": { "prm1": c.Left_mid, "prm2": c.Abs_move}},
    # 白い旗を上げて
    { "Keyword": [ 'シロイ', 'ハタ', 'アゲ'], "Func": dv.Left_Servo, "Arg": { "prm1": c.Left_mid, "prm2": c.Abs_move}},

    # 白の旗下げて
    { "Keyword": [ 'シロ', 'ハタ', 'サゲ'], "Func": dv.Left_Servo, "Arg": { "prm1": c.Left_max, "prm2": c.Abs_move}},
    # 白い旗下げて
    { "Keyword": [ 'シロイ', 'ハタ', 'サゲ'], "Func": dv.Left_Servo, "Arg": { "prm1": c.Left_max, "prm2": c.Abs_move}},

    # 三々七拍子
    { "Keyword": [ 'サン', 'サン', 'ナナ', 'ヒョウシ'], "Func": dv.Sansan, "Arg": { "prm1": 0}},

    # 万歳・ばんざーい
    { "Keyword": [ 'バンザイ'], "Func": dv.Banzai, "Arg": { "prm1": 0}},
    { "Keyword": [ 'バンザーイ'], "Func": dv.Banzai, "Arg": { "prm1": 0}},
]

# ------------------
# 認識パターンの検索
# ------------------
def find_action( ss):
    # バグ対策：空でパースする必要があるらしい？
    mcab.parse( '')

    chasenText = mcab.parse( ss)
    chasenList = chasenText.split( '\n')

    # カタカナの読みリスト作成
    # カタカナにすることにより、変換ノイズを除去　（例：「上げる／揚げる／あげる」など）
    yomi = []
    for ll in chasenList:
        parts = ll.split( '\t')
        # ２番目が「カタカナの読み」
        if ( len( parts) > 1) and ( parts[ 0] != "EOS"):
            yomi.append( parts[ 1])

    # 認識文字列のchasen配列を集合に変換
    yomi_set = set( yomi)

    for x in Pat_dic:
        # Keyword配列を集合に変換
        pat_set = set( x[ "Keyword"])

        # Keywordが認識結果に含まれるか？（部分集合か？）
        if pat_set <= yomi_set:
            handler( x[ 'Func'], x[ 'Arg'])
            break;
# ------------------

# ------------------
# 音声認識入力によるデバイス制御
# ------------------
try:
    while True:
        if r.exists( "Rec_id"):
            rec_id = r.get( "Rec_id")
            # 認識結果が更新されたか？
            if rec_id != prec_id:
                rst = r.get( "Result")
                if rst is not None:
                    # byte型を文字列型に変換
                    result = rst.decode( 'utf-8')
                    # デバイス制御の検索と実行
                    find_action( result)
                # --------
                prec_id = rec_id
# ----------

except KeyboardInterrupt:
    pass

dv.Led_clear()

```