プログラム解説「demo_face1.py」

・demo_face1.pyはべゼリーが顔を認識したらセリフを言うプログラムです。騒がしい場所に置く場合など、音声認識が使えないときに適しています。


モジュールの読み込み
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 顔認識デモ
# for Bezelie Edgar
# for Raspberry Pi
# by Jun Toyoda (Team Bezelie)
# from Aug15th2017

from datetime import datetime      # 現在時刻取得
from random import randint         # 乱数の発生
from time import sleep             # ウェイト処理
import subprocess                  # 外部プロセスを実行するモジュール
import json                        # jsonファイルを扱うモジュール
import csv                         # CSVファイルを扱うモジュール
import sys                         # python終了sys.exit()のために必要
import picamera                    # カメラ用モジュール
import picamera.array              # カメラ用モジュール
import cv2                         # Open CVモジュール    
import bezelie                     # べゼリー専用サーボ制御モジュール

csvFile   = "/home/pi/bezelie/chatDialog.csv"          # 対話リスト
jsonFile  = "/home/pi/bezelie/edgar/data_chat.json"    # 設定ファイル
ttsFile   = "/home/pi/bezelie/edgar/exec_openJTalk.sh" # 音声合成
debugFile = "/home/pi/bezelie/debug.txt"               # debug用

# 設定ファイルの読み込み
f = open (jsonFile,'r')
jDict = json.load(f)
name = jDict['data0'][0]['name']       # べゼリーの別名。
user = jDict['data0'][0]['user']       # ユーザーのニックネーム。
# mic = jDict['data0'][0]['mic']         # マイク感度。62が最大値。
vol = jDict['data0'][0]['vol']         # スピーカー音量。

# 変数の初期化
alarmStop = False   # アラームのスヌーズ機能(非搭載)
waitTime = 5        # autoモードでの会話の間隔

# OpenCV
cascade_path =  "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # 顔認識xml
cascade = cv2.CascadeClassifier(cascade_path)

# 関数
def timeCheck(): # 活動時間内かどうかのチェック
  f = open (jsonFile,'r')
  jDict = json.load(f)
  awake1Start = jDict['data1'][0]['awake1Start']
  awake1End = jDict['data1'][0]['awake1End']
  awake2Start = jDict['data1'][0]['awake2Start']
  awake2End  = jDict['data1'][0]['awake2End']
  t = datetime.now()
  if   int(t.hour) >  int(awake1Start[0:2]) and int(t.hour) <    int(awake1End[0:2]):
    flag = True
  elif int(t.hour) == int(awake1Start[0:2]) and int(t.minute) >= int(awake1Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake1End[0:2])   and int(t.minute) <= int(awake1End[3:5]):
    flag = True
  elif int(t.hour) >  int(awake2Start[0:2]) and int(t.hour) <    int(awake2End[0:2]):
    flag = True
  elif int(t.hour) == int(awake2Start[0:2]) and int(t.minute) >= int(awake2Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake2End[0:2])   and int(t.minute) <= int(awake2End[3:5]):
    flag = True
  else:
    flag = False # It is not Active Time
  return flag

def replyMessage(keyWord):        # 対話
  data = []                       # 対話ファイル(csv)を変数dataに読み込む
  with open(csvFile, 'rb') as f:  # csvFileをオープン
    for i in csv.reader(f):       # ファイルから1行ずつiに読み込む
      data.append(i)              # dataに追加

  data1 = []                      # dataから質問内容がキーワードに一致している行をdata1として抜き出す
  for index,i in enumerate(data): # index=連番
    if unicode(i[0], 'utf-8')==keyWord:  # i[0]はstrなのでutf-8に変換して比較する必要がある
      j = randint(1,100)          # 1から100までの乱数を発生させる
      data1.append(i+[j]+[index]) # data1=質問内容,返答,乱数,連番のリスト

  if data1 == []:                 # data1が空っぽだったらランダムで返す
    for index,i in enumerate(data):
      j = randint(1,100)
      data1.append(i+[j]+[index])

  maxNum = 0                      # 複数の候補からランダムで選出。data1から欄数値が最大なものを選ぶ
  for i in data1:
    if i[2] > maxNum:
      maxNum = i[2]
      ansNum = i[3]

  # 設定ファイルの読み込み
  f = open (jsonFile,'r')
  jDict = json.load(f)
  # mic = jDict['data0'][0]['mic']         # マイク感度の設定。
  vol = jDict['data0'][0]['vol']         # スピーカー音量。

  bez.moveRnd()
  subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量
  subprocess.call("sh "+ttsFile+" "+data[ansNum][1], shell=True)
  bez.stop()

def debug_message(message):
  print message
#  writeFile(message)
# pass
#  sys.stdout.write(message)

def writeFile(text): # デバッグファイル出力機能
  f = open (debugFile,'r')
  textBefore = ""
  for row in f:
    textBefore = textBefore + row
  f.close()
  f = open (debugFile,'w')
  f.write(textBefore + text + "\n")
  f.close()

# サーボの初期化
bez = bezelie.Control()                 # べゼリー操作インスタンスの生成
bez.moveCenter()                        # サーボの回転位置をトリム値に合わせる

# メインループ
def main():
  try:
    subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True)      # スピーカー音量
    subprocess.call("sh "+ttsFile+" "+u"顔認識モード", shell=True)
    stageAngle = 0           # ステージの初期角度
    stageDelta = 5           # ループごとにステージを回転させる角度
    stageSpeed = 8           # ループごとにステージを回転させる速度
    bez.moveHead(15)
    with picamera.PiCamera() as camera:                         # Open Pi-Camera as camera
      with picamera.array.PiRGBArray(camera) as stream:         # Open Video Stream from Pi-Camera as stream
        camera.resolution = (640, 480)                          # Display Resolution
        # camera.resolution = (1280, 720)                       # Display Resolution
        # camera.resolution = (1920, 1080)                      # Display Resolution
        camera.hflip = True                                     # Vertical Flip 
        camera.vflip = True                                     # Horizontal Flip
        while True:
          if timeCheck(): # 活動時間だったら動く
            camera.capture(stream, 'bgr', use_video_port=True)    # Capture the Video Stream
            gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # Convert BGR to Grayscale
            facerect = cascade.detectMultiScale(gray,             # Find face from gray
              scaleFactor=1.9,                                    # 1.1 - 1.9 :the bigger the quicker & less acurate 
              minNeighbors=3,                                     # 3 - 6 : the smaller the more easy to detect
              minSize=(100,120),                                   # Minimam face size 
              maxSize=(640,480))                                  # Maximam face size
            if len(facerect) > 0:
              for rect in facerect:
                cv2.rectangle(stream.array,                       # Draw a red rectangle at face place 
                  tuple(rect[0:2]),                               # Upper Left
                  tuple(rect[0:2]+rect[2:4]),                     # Lower Right
                  (0,0,255), thickness=2)                         # Color and thickness
              replyMessage(u"顔認識")
            # cv2.imshow('frame', stream.array)                     # 画面に表示したい場合はコメント外してください
            if cv2.waitKey(1) & 0xFF == ord('q'):                 # Quit operation
              break
            stream.seek(0)                                        # Reset the stream
            stream.truncate()
            stageAngle = stageAngle + stageDelta
            if stageAngle > 25 or stageAngle < -25:
              stageDelta = stageDelta*(-1)
            bez.moveStage(stageAngle,stageSpeed)
          else:           # 活動時間外は動作しない
            subprocess.call('amixer cset numid=1 60% -q', shell=True)      # スピーカー音量を調整
            subprocess.call("sh "+ttsFile+" "+"活動時間外です", shell=True)
            print "活動時間外なので発声・動作しません"
            sleep (600)   # 10分待機
            subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量を戻す
        cv2.destroyAllWindows()

  except KeyboardInterrupt: # CTRL+Cで終了
    debug_message('keyboard interrupted')
    bez.moveCenter()
    bez.stop()
    sys.exit(0)

if __name__ == "__main__":
  debug_message('---------- started ----------')
  main()
  sys.exit(0)

・ラズパイカメラ用モジュールpycameraと、画像処理モジュールOpenCVをimportしています。

初期設定
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 顔認識デモ
# for Bezelie Edgar
# for Raspberry Pi
# by Jun Toyoda (Team Bezelie)
# from Aug15th2017

from datetime import datetime      # 現在時刻取得
from random import randint         # 乱数の発生
from time import sleep             # ウェイト処理
import subprocess                  # 外部プロセスを実行するモジュール
import json                        # jsonファイルを扱うモジュール
import csv                         # CSVファイルを扱うモジュール
import sys                         # python終了sys.exit()のために必要
import picamera                    # カメラ用モジュール
import picamera.array              # カメラ用モジュール
import cv2                         # Open CVモジュール    
import bezelie                     # べゼリー専用サーボ制御モジュール

csvFile   = "/home/pi/bezelie/chatDialog.csv"          # 対話リスト
jsonFile  = "/home/pi/bezelie/edgar/data_chat.json"    # 設定ファイル
ttsFile   = "/home/pi/bezelie/edgar/exec_openJTalk.sh" # 音声合成
debugFile = "/home/pi/bezelie/debug.txt"               # debug用

# 設定ファイルの読み込み
f = open (jsonFile,'r')
jDict = json.load(f)
name = jDict['data0'][0]['name']       # べゼリーの別名。
user = jDict['data0'][0]['user']       # ユーザーのニックネーム。
# mic = jDict['data0'][0]['mic']         # マイク感度。62が最大値。
vol = jDict['data0'][0]['vol']         # スピーカー音量。

# 変数の初期化
alarmStop = False   # アラームのスヌーズ機能(非搭載)
waitTime = 5        # autoモードでの会話の間隔

# OpenCV
cascade_path =  "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # 顔認識xml
cascade = cv2.CascadeClassifier(cascade_path)

# 関数
def timeCheck(): # 活動時間内かどうかのチェック
  f = open (jsonFile,'r')
  jDict = json.load(f)
  awake1Start = jDict['data1'][0]['awake1Start']
  awake1End = jDict['data1'][0]['awake1End']
  awake2Start = jDict['data1'][0]['awake2Start']
  awake2End  = jDict['data1'][0]['awake2End']
  t = datetime.now()
  if   int(t.hour) >  int(awake1Start[0:2]) and int(t.hour) <    int(awake1End[0:2]):
    flag = True
  elif int(t.hour) == int(awake1Start[0:2]) and int(t.minute) >= int(awake1Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake1End[0:2])   and int(t.minute) <= int(awake1End[3:5]):
    flag = True
  elif int(t.hour) >  int(awake2Start[0:2]) and int(t.hour) <    int(awake2End[0:2]):
    flag = True
  elif int(t.hour) == int(awake2Start[0:2]) and int(t.minute) >= int(awake2Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake2End[0:2])   and int(t.minute) <= int(awake2End[3:5]):
    flag = True
  else:
    flag = False # It is not Active Time
  return flag

def replyMessage(keyWord):        # 対話
  data = []                       # 対話ファイル(csv)を変数dataに読み込む
  with open(csvFile, 'rb') as f:  # csvFileをオープン
    for i in csv.reader(f):       # ファイルから1行ずつiに読み込む
      data.append(i)              # dataに追加

  data1 = []                      # dataから質問内容がキーワードに一致している行をdata1として抜き出す
  for index,i in enumerate(data): # index=連番
    if unicode(i[0], 'utf-8')==keyWord:  # i[0]はstrなのでutf-8に変換して比較する必要がある
      j = randint(1,100)          # 1から100までの乱数を発生させる
      data1.append(i+[j]+[index]) # data1=質問内容,返答,乱数,連番のリスト

  if data1 == []:                 # data1が空っぽだったらランダムで返す
    for index,i in enumerate(data):
      j = randint(1,100)
      data1.append(i+[j]+[index])

  maxNum = 0                      # 複数の候補からランダムで選出。data1から欄数値が最大なものを選ぶ
  for i in data1:
    if i[2] > maxNum:
      maxNum = i[2]
      ansNum = i[3]

  # 設定ファイルの読み込み
  f = open (jsonFile,'r')
  jDict = json.load(f)
  # mic = jDict['data0'][0]['mic']         # マイク感度の設定。
  vol = jDict['data0'][0]['vol']         # スピーカー音量。

  bez.moveRnd()
  subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量
  subprocess.call("sh "+ttsFile+" "+data[ansNum][1], shell=True)
  bez.stop()

def debug_message(message):
  print message
#  writeFile(message)
# pass
#  sys.stdout.write(message)

def writeFile(text): # デバッグファイル出力機能
  f = open (debugFile,'r')
  textBefore = ""
  for row in f:
    textBefore = textBefore + row
  f.close()
  f = open (debugFile,'w')
  f.write(textBefore + text + "\n")
  f.close()

# サーボの初期化
bez = bezelie.Control()                 # べゼリー操作インスタンスの生成
bez.moveCenter()                        # サーボの回転位置をトリム値に合わせる

# メインループ
def main():
  try:
    subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True)      # スピーカー音量
    subprocess.call("sh "+ttsFile+" "+u"顔認識モード", shell=True)
    stageAngle = 0           # ステージの初期角度
    stageDelta = 5           # ループごとにステージを回転させる角度
    stageSpeed = 8           # ループごとにステージを回転させる速度
    bez.moveHead(15)
    with picamera.PiCamera() as camera:                         # Open Pi-Camera as camera
      with picamera.array.PiRGBArray(camera) as stream:         # Open Video Stream from Pi-Camera as stream
        camera.resolution = (640, 480)                          # Display Resolution
        # camera.resolution = (1280, 720)                       # Display Resolution
        # camera.resolution = (1920, 1080)                      # Display Resolution
        camera.hflip = True                                     # Vertical Flip 
        camera.vflip = True                                     # Horizontal Flip
        while True:
          if timeCheck(): # 活動時間だったら動く
            camera.capture(stream, 'bgr', use_video_port=True)    # Capture the Video Stream
            gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # Convert BGR to Grayscale
            facerect = cascade.detectMultiScale(gray,             # Find face from gray
              scaleFactor=1.9,                                    # 1.1 - 1.9 :the bigger the quicker & less acurate 
              minNeighbors=3,                                     # 3 - 6 : the smaller the more easy to detect
              minSize=(100,120),                                   # Minimam face size 
              maxSize=(640,480))                                  # Maximam face size
            if len(facerect) > 0:
              for rect in facerect:
                cv2.rectangle(stream.array,                       # Draw a red rectangle at face place 
                  tuple(rect[0:2]),                               # Upper Left
                  tuple(rect[0:2]+rect[2:4]),                     # Lower Right
                  (0,0,255), thickness=2)                         # Color and thickness
              replyMessage(u"顔認識")
            # cv2.imshow('frame', stream.array)                     # 画面に表示したい場合はコメント外してください
            if cv2.waitKey(1) & 0xFF == ord('q'):                 # Quit operation
              break
            stream.seek(0)                                        # Reset the stream
            stream.truncate()
            stageAngle = stageAngle + stageDelta
            if stageAngle > 25 or stageAngle < -25:
              stageDelta = stageDelta*(-1)
            bez.moveStage(stageAngle,stageSpeed)
          else:           # 活動時間外は動作しない
            subprocess.call('amixer cset numid=1 60% -q', shell=True)      # スピーカー音量を調整
            subprocess.call("sh "+ttsFile+" "+"活動時間外です", shell=True)
            print "活動時間外なので発声・動作しません"
            sleep (600)   # 10分待機
            subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量を戻す
        cv2.destroyAllWindows()

  except KeyboardInterrupt: # CTRL+Cで終了
    debug_message('keyboard interrupted')
    bez.moveCenter()
    bez.stop()
    sys.exit(0)

if __name__ == "__main__":
  debug_message('---------- started ----------')
  main()
  sys.exit(0)

・39~40行目で顔を認識するための定義ファイルを読み込んでいます。

関数定義
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 顔認識デモ
# for Bezelie Edgar
# for Raspberry Pi
# by Jun Toyoda (Team Bezelie)
# from Aug15th2017

from datetime import datetime      # 現在時刻取得
from random import randint         # 乱数の発生
from time import sleep             # ウェイト処理
import subprocess                  # 外部プロセスを実行するモジュール
import json                        # jsonファイルを扱うモジュール
import csv                         # CSVファイルを扱うモジュール
import sys                         # python終了sys.exit()のために必要
import picamera                    # カメラ用モジュール
import picamera.array              # カメラ用モジュール
import cv2                         # Open CVモジュール    
import bezelie                     # べゼリー専用サーボ制御モジュール

csvFile   = "/home/pi/bezelie/chatDialog.csv"          # 対話リスト
jsonFile  = "/home/pi/bezelie/edgar/data_chat.json"    # 設定ファイル
ttsFile   = "/home/pi/bezelie/edgar/exec_openJTalk.sh" # 音声合成
debugFile = "/home/pi/bezelie/debug.txt"               # debug用

# 設定ファイルの読み込み
f = open (jsonFile,'r')
jDict = json.load(f)
name = jDict['data0'][0]['name']       # べゼリーの別名。
user = jDict['data0'][0]['user']       # ユーザーのニックネーム。
# mic = jDict['data0'][0]['mic']         # マイク感度。62が最大値。
vol = jDict['data0'][0]['vol']         # スピーカー音量。

# 変数の初期化
alarmStop = False   # アラームのスヌーズ機能(非搭載)
waitTime = 5        # autoモードでの会話の間隔

# OpenCV
cascade_path =  "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # 顔認識xml
cascade = cv2.CascadeClassifier(cascade_path)

# 関数
def timeCheck(): # 活動時間内かどうかのチェック
  f = open (jsonFile,'r')
  jDict = json.load(f)
  awake1Start = jDict['data1'][0]['awake1Start']
  awake1End = jDict['data1'][0]['awake1End']
  awake2Start = jDict['data1'][0]['awake2Start']
  awake2End  = jDict['data1'][0]['awake2End']
  t = datetime.now()
  if   int(t.hour) >  int(awake1Start[0:2]) and int(t.hour) <    int(awake1End[0:2]):
    flag = True
  elif int(t.hour) == int(awake1Start[0:2]) and int(t.minute) >= int(awake1Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake1End[0:2])   and int(t.minute) <= int(awake1End[3:5]):
    flag = True
  elif int(t.hour) >  int(awake2Start[0:2]) and int(t.hour) <    int(awake2End[0:2]):
    flag = True
  elif int(t.hour) == int(awake2Start[0:2]) and int(t.minute) >= int(awake2Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake2End[0:2])   and int(t.minute) <= int(awake2End[3:5]):
    flag = True
  else:
    flag = False # It is not Active Time
  return flag

def replyMessage(keyWord):        # 対話
  data = []                       # 対話ファイル(csv)を変数dataに読み込む
  with open(csvFile, 'rb') as f:  # csvFileをオープン
    for i in csv.reader(f):       # ファイルから1行ずつiに読み込む
      data.append(i)              # dataに追加

  data1 = []                      # dataから質問内容がキーワードに一致している行をdata1として抜き出す
  for index,i in enumerate(data): # index=連番
    if unicode(i[0], 'utf-8')==keyWord:  # i[0]はstrなのでutf-8に変換して比較する必要がある
      j = randint(1,100)          # 1から100までの乱数を発生させる
      data1.append(i+[j]+[index]) # data1=質問内容,返答,乱数,連番のリスト

  if data1 == []:                 # data1が空っぽだったらランダムで返す
    for index,i in enumerate(data):
      j = randint(1,100)
      data1.append(i+[j]+[index])

  maxNum = 0                      # 複数の候補からランダムで選出。data1から欄数値が最大なものを選ぶ
  for i in data1:
    if i[2] > maxNum:
      maxNum = i[2]
      ansNum = i[3]

  # 設定ファイルの読み込み
  f = open (jsonFile,'r')
  jDict = json.load(f)
  # mic = jDict['data0'][0]['mic']         # マイク感度の設定。
  vol = jDict['data0'][0]['vol']         # スピーカー音量。

  bez.moveRnd()
  subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量
  subprocess.call("sh "+ttsFile+" "+data[ansNum][1], shell=True)
  bez.stop()

def debug_message(message):
  print message
#  writeFile(message)
# pass
#  sys.stdout.write(message)

def writeFile(text): # デバッグファイル出力機能
  f = open (debugFile,'r')
  textBefore = ""
  for row in f:
    textBefore = textBefore + row
  f.close()
  f = open (debugFile,'w')
  f.write(textBefore + text + "\n")
  f.close()

# サーボの初期化
bez = bezelie.Control()                 # べゼリー操作インスタンスの生成
bez.moveCenter()                        # サーボの回転位置をトリム値に合わせる

# メインループ
def main():
  try:
    subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True)      # スピーカー音量
    subprocess.call("sh "+ttsFile+" "+u"顔認識モード", shell=True)
    stageAngle = 0           # ステージの初期角度
    stageDelta = 5           # ループごとにステージを回転させる角度
    stageSpeed = 8           # ループごとにステージを回転させる速度
    bez.moveHead(15)
    with picamera.PiCamera() as camera:                         # Open Pi-Camera as camera
      with picamera.array.PiRGBArray(camera) as stream:         # Open Video Stream from Pi-Camera as stream
        camera.resolution = (640, 480)                          # Display Resolution
        # camera.resolution = (1280, 720)                       # Display Resolution
        # camera.resolution = (1920, 1080)                      # Display Resolution
        camera.hflip = True                                     # Vertical Flip 
        camera.vflip = True                                     # Horizontal Flip
        while True:
          if timeCheck(): # 活動時間だったら動く
            camera.capture(stream, 'bgr', use_video_port=True)    # Capture the Video Stream
            gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # Convert BGR to Grayscale
            facerect = cascade.detectMultiScale(gray,             # Find face from gray
              scaleFactor=1.9,                                    # 1.1 - 1.9 :the bigger the quicker & less acurate 
              minNeighbors=3,                                     # 3 - 6 : the smaller the more easy to detect
              minSize=(100,120),                                   # Minimam face size 
              maxSize=(640,480))                                  # Maximam face size
            if len(facerect) > 0:
              for rect in facerect:
                cv2.rectangle(stream.array,                       # Draw a red rectangle at face place 
                  tuple(rect[0:2]),                               # Upper Left
                  tuple(rect[0:2]+rect[2:4]),                     # Lower Right
                  (0,0,255), thickness=2)                         # Color and thickness
              replyMessage(u"顔認識")
            # cv2.imshow('frame', stream.array)                     # 画面に表示したい場合はコメント外してください
            if cv2.waitKey(1) & 0xFF == ord('q'):                 # Quit operation
              break
            stream.seek(0)                                        # Reset the stream
            stream.truncate()
            stageAngle = stageAngle + stageDelta
            if stageAngle > 25 or stageAngle < -25:
              stageDelta = stageDelta*(-1)
            bez.moveStage(stageAngle,stageSpeed)
          else:           # 活動時間外は動作しない
            subprocess.call('amixer cset numid=1 60% -q', shell=True)      # スピーカー音量を調整
            subprocess.call("sh "+ttsFile+" "+"活動時間外です", shell=True)
            print "活動時間外なので発声・動作しません"
            sleep (600)   # 10分待機
            subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量を戻す
        cv2.destroyAllWindows()

  except KeyboardInterrupt: # CTRL+Cで終了
    debug_message('keyboard interrupted')
    bez.moveCenter()
    bez.stop()
    sys.exit(0)

if __name__ == "__main__":
  debug_message('---------- started ----------')
  main()
  sys.exit(0)

 

メインループ
#!/usr/bin/python
# -*- coding: utf-8 -*-
# 顔認識デモ
# for Bezelie Edgar
# for Raspberry Pi
# by Jun Toyoda (Team Bezelie)
# from Aug15th2017

from datetime import datetime      # 現在時刻取得
from random import randint         # 乱数の発生
from time import sleep             # ウェイト処理
import subprocess                  # 外部プロセスを実行するモジュール
import json                        # jsonファイルを扱うモジュール
import csv                         # CSVファイルを扱うモジュール
import sys                         # python終了sys.exit()のために必要
import picamera                    # カメラ用モジュール
import picamera.array              # カメラ用モジュール
import cv2                         # Open CVモジュール    
import bezelie                     # べゼリー専用サーボ制御モジュール

csvFile   = "/home/pi/bezelie/chatDialog.csv"          # 対話リスト
jsonFile  = "/home/pi/bezelie/edgar/data_chat.json"    # 設定ファイル
ttsFile   = "/home/pi/bezelie/edgar/exec_openJTalk.sh" # 音声合成
debugFile = "/home/pi/bezelie/debug.txt"               # debug用

# 設定ファイルの読み込み
f = open (jsonFile,'r')
jDict = json.load(f)
name = jDict['data0'][0]['name']       # べゼリーの別名。
user = jDict['data0'][0]['user']       # ユーザーのニックネーム。
# mic = jDict['data0'][0]['mic']         # マイク感度。62が最大値。
vol = jDict['data0'][0]['vol']         # スピーカー音量。

# 変数の初期化
alarmStop = False   # アラームのスヌーズ機能(非搭載)
waitTime = 5        # autoモードでの会話の間隔

# OpenCV
cascade_path =  "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # 顔認識xml
cascade = cv2.CascadeClassifier(cascade_path)

# 関数
def timeCheck(): # 活動時間内かどうかのチェック
  f = open (jsonFile,'r')
  jDict = json.load(f)
  awake1Start = jDict['data1'][0]['awake1Start']
  awake1End = jDict['data1'][0]['awake1End']
  awake2Start = jDict['data1'][0]['awake2Start']
  awake2End  = jDict['data1'][0]['awake2End']
  t = datetime.now()
  if   int(t.hour) >  int(awake1Start[0:2]) and int(t.hour) <    int(awake1End[0:2]):
    flag = True
  elif int(t.hour) == int(awake1Start[0:2]) and int(t.minute) >= int(awake1Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake1End[0:2])   and int(t.minute) <= int(awake1End[3:5]):
    flag = True
  elif int(t.hour) >  int(awake2Start[0:2]) and int(t.hour) <    int(awake2End[0:2]):
    flag = True
  elif int(t.hour) == int(awake2Start[0:2]) and int(t.minute) >= int(awake2Start[3:5]):
    flag = True
  elif int(t.hour) == int(awake2End[0:2])   and int(t.minute) <= int(awake2End[3:5]):
    flag = True
  else:
    flag = False # It is not Active Time
  return flag

def replyMessage(keyWord):        # 対話
  data = []                       # 対話ファイル(csv)を変数dataに読み込む
  with open(csvFile, 'rb') as f:  # csvFileをオープン
    for i in csv.reader(f):       # ファイルから1行ずつiに読み込む
      data.append(i)              # dataに追加

  data1 = []                      # dataから質問内容がキーワードに一致している行をdata1として抜き出す
  for index,i in enumerate(data): # index=連番
    if unicode(i[0], 'utf-8')==keyWord:  # i[0]はstrなのでutf-8に変換して比較する必要がある
      j = randint(1,100)          # 1から100までの乱数を発生させる
      data1.append(i+[j]+[index]) # data1=質問内容,返答,乱数,連番のリスト

  if data1 == []:                 # data1が空っぽだったらランダムで返す
    for index,i in enumerate(data):
      j = randint(1,100)
      data1.append(i+[j]+[index])

  maxNum = 0                      # 複数の候補からランダムで選出。data1から欄数値が最大なものを選ぶ
  for i in data1:
    if i[2] > maxNum:
      maxNum = i[2]
      ansNum = i[3]

  # 設定ファイルの読み込み
  f = open (jsonFile,'r')
  jDict = json.load(f)
  # mic = jDict['data0'][0]['mic']         # マイク感度の設定。
  vol = jDict['data0'][0]['vol']         # スピーカー音量。

  bez.moveRnd()
  subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量
  subprocess.call("sh "+ttsFile+" "+data[ansNum][1], shell=True)
  bez.stop()

def debug_message(message):
  print message
#  writeFile(message)
# pass
#  sys.stdout.write(message)

def writeFile(text): # デバッグファイル出力機能
  f = open (debugFile,'r')
  textBefore = ""
  for row in f:
    textBefore = textBefore + row
  f.close()
  f = open (debugFile,'w')
  f.write(textBefore + text + "\n")
  f.close()

# サーボの初期化
bez = bezelie.Control()                 # べゼリー操作インスタンスの生成
bez.moveCenter()                        # サーボの回転位置をトリム値に合わせる

# メインループ
def main():
  try:
    subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True)      # スピーカー音量
    subprocess.call("sh "+ttsFile+" "+u"顔認識モード", shell=True)
    stageAngle = 0           # ステージの初期角度
    stageDelta = 5           # ループごとにステージを回転させる角度
    stageSpeed = 8           # ループごとにステージを回転させる速度
    bez.moveHead(15)
    with picamera.PiCamera() as camera:                         # Open Pi-Camera as camera
      with picamera.array.PiRGBArray(camera) as stream:         # Open Video Stream from Pi-Camera as stream
        camera.resolution = (640, 480)                          # Display Resolution
        # camera.resolution = (1280, 720)                       # Display Resolution
        # camera.resolution = (1920, 1080)                      # Display Resolution
        camera.hflip = True                                     # Vertical Flip 
        camera.vflip = True                                     # Horizontal Flip
        while True:
          if timeCheck(): # 活動時間だったら動く
            camera.capture(stream, 'bgr', use_video_port=True)    # Capture the Video Stream
            gray = cv2.cvtColor(stream.array, cv2.COLOR_BGR2GRAY) # Convert BGR to Grayscale
            facerect = cascade.detectMultiScale(gray,             # Find face from gray
              scaleFactor=1.9,                                    # 1.1 - 1.9 :the bigger the quicker & less acurate 
              minNeighbors=3,                                     # 3 - 6 : the smaller the more easy to detect
              minSize=(100,120),                                   # Minimam face size 
              maxSize=(640,480))                                  # Maximam face size
            if len(facerect) > 0:
              for rect in facerect:
                cv2.rectangle(stream.array,                       # Draw a red rectangle at face place 
                  tuple(rect[0:2]),                               # Upper Left
                  tuple(rect[0:2]+rect[2:4]),                     # Lower Right
                  (0,0,255), thickness=2)                         # Color and thickness
              replyMessage(u"顔認識")
            # cv2.imshow('frame', stream.array)                     # 画面に表示したい場合はコメント外してください
            if cv2.waitKey(1) & 0xFF == ord('q'):                 # Quit operation
              break
            stream.seek(0)                                        # Reset the stream
            stream.truncate()
            stageAngle = stageAngle + stageDelta
            if stageAngle > 25 or stageAngle < -25:
              stageDelta = stageDelta*(-1)
            bez.moveStage(stageAngle,stageSpeed)
          else:           # 活動時間外は動作しない
            subprocess.call('amixer cset numid=1 60% -q', shell=True)      # スピーカー音量を調整
            subprocess.call("sh "+ttsFile+" "+"活動時間外です", shell=True)
            print "活動時間外なので発声・動作しません"
            sleep (600)   # 10分待機
            subprocess.call('amixer cset numid=1 '+vol+'% -q', shell=True) # スピーカー音量を戻す
        cv2.destroyAllWindows()

  except KeyboardInterrupt: # CTRL+Cで終了
    debug_message('keyboard interrupted')
    bez.moveCenter()
    bez.stop()
    sys.exit(0)

if __name__ == "__main__":
  debug_message('---------- started ----------')
  main()
  sys.exit(0)

・127~128行目でステージを回転させる速度を設定しています。
・132行目の表示サイズは、お使いのディスプレイの解像度に合わせて調整してください。
・認識する顔のサイズや認識精度と速度のバランスは142~145行目で調整できます。