#!/usr/bin/env python
import tensorflow as tf
import cv2
import sys
import numpy as np
from core.yolo_tiny import YOLOv3_tiny
import picamera
import io
import RPi.GPIO as GPIO
import wiringpi
#学習クラス
def load_class_names():
#学習クラスのリスト
_CLASS_NAMES_FILE = './data/coco.names'
#Returns a list of string corresonding to class names and it's length
with open(_CLASS_NAMES_FILE, 'r') as f:
class_names = f.read().splitlines()
return class_names, len(class_names)
#サーボモータの設定
def getServoDutyHw(id, val):
val_min = 0
val_max = 4095
# デューティ比0%を0、100%を1024として数値を入力
# 50Hz(周期20ms)、デューティ比3.5%: 3.5*1024/100=約36
servo_min = 36
# 50Hz(周期20ms)、デューティ比10%: 10*1024/100=約102
servo_max = 102
if id==1:
servo_min = 53
servo_max = 85
duty = int((servo_min - servo_max)*(val - val_min)/(val_max - val_min) + servo_max)
if duty > servo_max:
duty = servo_max
if duty < servo_min:
duty = servo_min
return duty
def main():
#各種初期設定------------------
#カメラの解像度
CAMERA_WIDTH = 512
CAMERA_HEIGHT = 384
#GPIOの設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
GPIO.output(4, GPIO.LOW)
#サーボモータで使用するピンNo.
PWM0 = 19
PWM1 = 18
#サーボモータへの入力の初期値
input_x = 2048
input_y = 2048
# wiringPi によるハードウェアPWM
# GPIO名で番号を指定する
wiringpi.wiringPiSetupGpio()
# 左右方向のPWM出力を指定
wiringpi.pinMode(PWM0, wiringpi.GPIO.PWM_OUTPUT)
# 上下方向のPWM出力を指定
wiringpi.pinMode(PWM1, wiringpi.GPIO.PWM_OUTPUT)
# 周波数を固定するための設定
wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS)
# 50 Hz。18750/(周波数) の計算値に近い整数
wiringpi.pwmSetClock(375)
# PWMのピン番号とデフォルトのパルス幅をデューティ100%を1024として指定。
# ここでは6.75%に対応する69を指定
wiringpi.pwmWrite(PWM0, 69)
wiringpi.pwmWrite(PWM1, 69)
#----------------------------------
class_names, n_classes = load_class_names()
iou_threshold = 0.1
confidence_threshold = 0.25
model = YOLOv3_tiny(n_classes=n_classes,
iou_threshold=iou_threshold,
confidence_threshold=confidence_threshold)
inputs = tf.placeholder(tf.float32, [1, *model.input_size, 3])
detections = model(inputs)
saver = tf.train.Saver(tf.global_variables(scope=model.scope))
with tf.Session() as sess:
saver.restore(sess, './weights/model-tiny.ckpt')
stream = io.BytesIO()
#cap = cv2.VideoCapture(0)
cap = picamera.PiCamera()
#カメラ解像度の設定 ※この値を変えたらutils_busters.pyの値も変更する
cap.resolution = (CAMERA_WIDTH, CAMERA_HEIGHT)
n = 0 #初期位置に戻すためのカウント
#---------------------ここから while ループ---------------------
while True:
#カメラ撮影
cap.capture(stream, format='jpeg')
#numpy型に変換
data = np.frombuffer(stream.getvalue(), dtype=np.uint8)
#opencv型に変換
frame = cv2.imdecode(data, 1)
frame_size = (frame.shape[1], frame.shape[0])
resized_frame = cv2.resize(frame, dsize=tuple((x) for x in model.input_size[::-1]),
interpolation=cv2.INTER_NEAREST)
result = sess.run(detections, feed_dict={inputs: [resized_frame]})
#撮影画像の表示
cv2.imshow('frame', frame)
boxes_dict = result[0]
resize_factor = (frame_size[0] / model.input_size[1], frame_size[1] / model.input_size[0])
n = n + 1
for cls in range(len(class_names)):
boxes = boxes_dict[cls]
#ターゲットを「バナナ」に
if np.size(boxes) != 0 and class_names[cls] == "banana":
#初期位置戻しリセット
n=0
for box in boxes:
xy = box[:4]
xy = [int(xy[i] * resize_factor[i % 2]) for i in range(4)]
#ターゲットの中心(水平方向)
target_x = int((xy[0]+xy[2])/2)
#ターゲットの中心(垂直方向)
target_y = int((xy[1]+xy[3])/2)
#水平方向のカメラ取込画素のの中心値
img_center_x = CAMERA_WIDTH/2
#垂直方向のカメラ取込画素のの中心値img_center_y = CAMERA_HEIGHT/2
#ターゲットとカメラの中心ズレ(水平方向)
dx = img_center_x - target_x
#ターゲットとカメラの中心ズレ(垂直方向)dy = img_center_y - target_y
#モータ入力値の更新。4は想定通り動くか確認して決めた実験値。
input_x = input_x - 4 * dx
#モータ入力値の更新。4は想定通り動くか確認して決めた実験値。
input_y = input_y - 4 * dy
#モータが回りすぎないようににリミッタ設定
if input_x > 4095:
input_x = 4095
if input_x < 0:
input_x = 0
if input_y > 2048:
input_y = 2048
if input_y < 1500:
input_y = 1500
duty0 = getServoDutyHw(0, input_x)
duty1 = getServoDutyHw(0, input_y)
print("ターゲット発見")
print("ターゲット中心:" + str(target_x) + ", " + str(target_y))
print("画像中心:" + str(img_center_x)+ ", " + str(img_center_y))
print("中心ズレ:" + str(dx) + ", " + str(dy))
#dxが大きい時
if abs(dx) > 20:
#dxもdyが大きい時は両方のモータ を動かす
if abs(dy) > 15:
#水平モータ駆動
wiringpi.pwmWrite(PWM0, duty0)
#垂直モータ駆動
wiringpi.pwmWrite(PWM1, duty1)
#dxが大きい時は水平方向のみのモータ を動かす
else:
#水平モータ駆動
wiringpi.pwmWrite(PWM0, duty0)
#dxが小さい時
else:
#dyが大きい時は垂直方向のモータ を動かす
if abs(dy) > 15:
#垂直モータ駆動
wiringpi.pwmWrite(PWM1, duty1)
#ターゲットが鳥でない場合
else:
#10回検知しなかったら初期位置に戻す
if n == 10:
wiringpi.pwmWrite(PWM0, 69)
wiringpi.pwmWrite(PWM1, 69)
elif n > 10:
#nを11でストップ
n=11
#カメラのリセット
stream.seek(0)
#「q」を押すと,while ループをブレイク (画面表示の停止へ)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
#---------------------ここまで while ループ---------------------
#画面表示の停止
#カメラのリセット
stream.seek(0)
#カメラキャプチャを停止
cap.close()
#画面ウインドを消去
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
|