はじめに
カメラにうつりこんだ人の顔を検出し、その顔の方向にカメラが向くようにしてみた。
- まずはサーボの上にiPhoneを乗せた。(多少の工作が必要)
- iPhoneのカメラ画像をWi-Fi経由でPCに送信。
- PC内で画像内の顔を検出し、XBee経由でArduinoにサーボを動かす命令を送信。
- Arduinoはサーボを動かすだけ。
iPhone→PCは「EpocCam」(無料版)を使用。
Wi-FiとXBeeの両方を使って無線化している。
Wi-Fiは2.4GHz, 5GHzどちらを使っても今のところXBeeとの干渉問題は起きていない。
環境
Windows10 Pro NVIDIA GeForce GTX1080 Python 3.7.7 Arduino IDE 1.8.12 Arduino Uno R3
バージョン確認(pip freeze)
インストールが必要なのは「mxnet-cu101]と「gluoncv」と「opencv-python」と「pyserial」のみ。
pip install https://repo.mxnet.io/dist/python/cu101/mxnet_cu101-1.6.0-py2.py3-none-win_amd64.whl pip install gluoncv pip install opencv-python pip install pyserial
その他は勝手についてくる。
certifi==2020.4.5.1 chardet==3.0.4 cycler==0.10.0 gluoncv==0.7.0 graphviz==0.8.4 idna==2.6 kiwisolver==1.2.0 matplotlib==3.2.1 mxnet-cu101 @ https://repo.mxnet.io/dist/python/cu101/mxnet_cu101-1.6.0-py2.py3-none-win_amd64.whl numpy==1.16.6 opencv-python==4.2.0.34 Pillow==7.1.2 portalocker==1.7.0 pyparsing==2.4.7 pyserial==3.4 python-dateutil==2.8.1 pywin32==227 requests==2.18.4 scipy==1.4.1 six==1.14.0 tqdm==4.46.0 urllib3==1.22
Pythonコード
import mxnet as mx import gluoncv import serial, time import cv2 ctx = mx.gpu() # Load the model classes = ['face'] net = gluoncv.model_zoo.get_model('ssd_512_mobilenet1.0_custom', classes=classes, pretrained=False, root='./model') net.load_parameters('ssd_512_mobilenet1.0_face.params') net.collect_params().reset_ctx(ctx) # Compile the model for faster speed net.hybridize() # Load the webcam handler cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # letting the camera autofocus time.sleep(1) ser =serial.Serial("COM4", 9600) time.sleep(1.5) #初期設定 servo_angle = 90 while(True): # Load frame from the camera ret, frame = cap.read() if ret==False: break # Image pre-processing frame = mx.nd.array(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)).astype('uint8') rgb_nd, frame = gluoncv.data.transforms.presets.ssd.transform_test(frame, short=480) # Run frame through network class_IDs, scores, bounding_boxes = net(rgb_nd.as_in_context(ctx)) if scores[0][0] > 0.6: x_min = bounding_boxes[0][0][0] y_min = bounding_boxes[0][0][1] x_max = bounding_boxes[0][0][2] y_max = bounding_boxes[0][0][3] if x_min > 320: servo_angle += 5 if x_max < 320: servo_angle -= 5 servo_angle = 170 if servo_angle > 170 else servo_angle servo_angle = 10 if servo_angle < 10 else servo_angle #bufferがゼロになるまで待つ finished = False while not finished: finished = (ser.out_waiting == 0) send_data = servo_angle.to_bytes(1, 'big') ser.write(send_data) ser.write((255).to_bytes(1, 'big')) # Display the result img = gluoncv.utils.viz.cv_plot_bbox(frame, bounding_boxes[0], scores[0], class_IDs[0], class_names=net.classes, thresh=0.6) gluoncv.utils.viz.cv_plot_image(img) time.sleep(0.2) # escを押したら終了 if cv2.waitKey(1) == 27: break ser.close() cap.release() cv2.destroyAllWindows()
Arduinoスケッチ
#include <Servo.h> Servo myServo; int var = 90; int dummy; void setup() { myServo.attach(9); myServo.write(var); Serial.begin(9600); } void loop() { if(Serial.available()>1){ var = Serial.read(); myServo.write(var); dummy = Serial.read(); delay(100); } }
dummyデータを読み込むことで命令完了をPC側に知らせている。