1. YOLOを使えるようにする
以下のコマンドを実行してライブラリをインストールする。
python -m pip install ultralytics
yoloを実行するためのコードは次のようになる。
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
result = model.predict(source=0 , show=True)
このファイルと同じ階層にultralyticsを置くだけで物体検出が行える。
また今回はモデルをyolov8n.ptにしているがnだけでなくモデルの小さい順にn,s,m,l,xがある。
ちなみに私のパソコンのスペックだとl,xを使ったときに大幅な遅延があった。
2. 座標を取り出してArduinoに送信する
from ultralytics import YOLO
import cv2
from ultralytics.utils.plotting import Annotator
import serial
import time
まずは必要なライブラリインポートする。持っていないものは
python -m pip install モジュール名
これでインストールできる。
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
ser = serial.Serial("COM6", 9600, timeout=0.1)
time.sleep(2)
各種設定を行う
一番上は先ほども出たmodelの選択である。
その下はカメラの設定、シリアル通信の設定を行い2秒間プログラムを停止(PCとArduinoが準備する時間を確保するため)させている。
ポート番号はArduinoIDEから確認できる
while True:
_, img = cap.read()
results = model.predict(img)
カメラからの画像を取り込んで物体検出を実行しその結果をresultに格納している。
for r in results:
annotator = Annotator(img)
boxes = r.boxes
for box in boxes:
b = box.xyxy[0] # get box coordinates in (left, top, right, bottom) format
c = box.cls
if int(c) == 0:
annotator.box_label(b, model.names[int(c)])
data = f"{(b[0]+b[2])/2},{(b[1]+b[3])/2}\n"
ser.write(data.encode())
検出した物体の座標とクラス(何の物体なのか)がboxesに格納されているためそれぞれをb,cに代入しておく。
このとき座標データは矩形の左下と右上のx,y座標が分かるので
(x[左]+x[右])/2 , (y[下]+y[上])/2
それぞれこのような式で中心の座標に変換する。
dataではこの計算をしつつ二つのデータを','で区切っている。
あとはser.writeでデータを送信するだけである。
コードの全体は以下のようになっている。
from ultralytics import YOLO
import cv2
from ultralytics.utils.plotting import Annotator
import serial
import time
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
ser = serial.Serial("COM6", 9600, timeout=0.1)
time.sleep(2)
while True:
_, img = cap.read()
results = model.predict(img)
for r in results:
annotator = Annotator(img)
boxes = r.boxes
for box in boxes:
b = box.xyxy[0] # get box coordinates in (left, top, right, bottom) format
c = box.cls
if int(c) == 0:
annotator.box_label(b, model.names[int(c)])
data = f"{(b[0]+b[2])/2},{(b[1]+b[3])/2}\n"
ser.write(data.encode())
print(f"Object {model.names[int(c)]}: X={(b[0]+b[2])/2}, Y={(b[1]+b[3])/2}")
img = annotator.result()
cv2.imshow('YOLO V8 Detection', img)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
ser.close()
今回は受信したx座標をもとにしてサーボモータを回している。
受信したデータを','と'改行'で区切ってx,y座標を取り出してint型に変換している。
このx座標を範囲変換で0~180までの角度としている。あとはモータを回すだけである。
#include <Servo.h>
String data;
long value1;
long value2;
Servo myservo;
int angle = 90;
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
myservo.attach(12);
myservo.write(90);
}
void loop() {
if(Serial.available()){
data = Serial.readStringUntil(',');
value1 = data.toInt();
data = Serial.readStringUntil('\n');
value2 = data.toInt();
}
angle = map(value1, 0, 640, 0, 180);
myservo.write(angle);
delay(100);
}