LoginSignup
2
1

YOLOで検出した物体の座標をシリアル通信でArduinoに送る

Posted at

1. YOLOを使えるようにする

以下のコマンドを実行してライブラリをインストールする。

python -m pip install ultralytics

yoloを実行するためのコードは次のようになる。

YOLO_Test.py
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に送信する

YOLO_Arduino.py
from ultralytics import YOLO
import cv2
from ultralytics.utils.plotting import Annotator
import serial
import time

まずは必要なライブラリインポートする。持っていないものは

python -m pip install モジュール名

これでインストールできる。

YOLO_Arduino.py
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から確認できる

YOLO_Arduino.py
while True:
    _, img = cap.read()
    results = model.predict(img)

カメラからの画像を取り込んで物体検出を実行しその結果をresultに格納している。

YOLO_Arduino.py
    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でデータを送信するだけである。

コードの全体は以下のようになっている。

YOLO_Arduino.py
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までの角度としている。あとはモータを回すだけである。

YOLO_servo.ino
#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);
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1