Tuesday 29 December 2020

Mengganti Konsol Game Mobil Dengan Stir Dari Kardus

membuat kontrol game mobil pc dengan kardus - Ini adalah projek kecil-kecilan yang saya bikin dalam rangka belajar OpenCV dan python. Sedikit cerita latar belakang, awalnya saya menemukan postingan dari orang luar negeri di grup facebook machine learning internasional. Disitu terlihat bahwa dia bisa mengendalikan game mobil dengan stir yang dideteksi oleh kamera. Saya coba download source codenya, ternyata gak dapet. Akhirnya saya coba buat sendiri dan inilah hasilnya.


Bagaimana Saya Membuatnya?

Cara kerja dari projek ini cukup simpel. AI dari kamera hanya perlu mendeteksi warna dari titik tertentu pada stir dari kardus. Setelah warna yang dimaksud terdeteksi, saya mengambil titik koordinat dari titik tengah warna tersebut. Kemudian dari titik koordinat tersebut, bisa didapatkan sudut lingkaran. Sudut inilah yang dikonversi ke simulator/game.

Berikut ini langkah-langkah pembuatannya.

1. Import library yang dibutuhkan. Dalam hal ini, kita membutuhkan:

- cv2 (pip install opencv-python)
Untuk mendeteksi warna pada kamera.

- math
Untuk menggunakan fungsi matematika dengan mudah.

- socketio
Untuk mengirim data ke server game.

my_project/steering.py

import cv2 as cv
import math
import socketio

2.  Siapkan variabel dan koneksikan ke socket.

my_project/steering.py

cap = cv.VideoCapture(0)
pointsList = [0, 0]

sio = socketio.Client()

@sio.on('connect')
def connect():
   print("connect")

sio.connect('http://localhost:4567')

3. Siapkan fungsi yang dibutuhkan.

my_project/steering.py
def getAngle(pointsList):
    pt1, pt2 = pointsList
    angR = math.atan2(-(pt1[0] - pt2[0]), (pt1[1] - pt2[1]))
    angD = round(math.degrees(angR))
    return angD

def centroid(contour, mask):
    for c in contour:
        area = cv.contourArea(c)
        if area > 200:
            M = cv.moments(mask)
            cX = int(M["m10"] / M["m00"])
            cY = int(M["m01"] / M["m00"])
            return [cX, cY]

def trainingColor(hsv, low, upp, pt):
    mask = cv.inRange(hsv, low, upp)
    contour, hierarchy = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    center = centroid(contour, mask)
    if center != None: pointsList[pt] = center
    # else: pointsList[pt] = [0, 0]

def drawFrame(frame):
    cv.circle(frame, tuple(pointsList[0]), 5, (255, 0, 0), -1)
    cv.circle(frame, tuple(pointsList[1]), 5, (0, 255, 0), -1)
    cv.line(frame, tuple(pointsList[0]), (pointsList[0][0], pointsList[0][1]-100), (0,0,255), 2)
    cv.line(frame, tuple(pointsList[1]), tuple(pointsList[0]), (0,0,255), 2)
    cv.putText(frame, str(pointsList[0]), (pointsList[0][0]-100, pointsList[0][1]), cv.FONT_HERSHEY_COMPLEX, 0.5, (255,0,0),2)
    cv.putText(frame, str(pointsList[1]), (pointsList[1][0]+50, pointsList[1][1]), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,255,0),2)

Penjelasan masing-masing fungsi:
- trainingColor
Mendeteksi warna dengan angka HSV tertentu.

- centroid
Mengambil koordinat titik tengah pada warna yang terdeteksi.

- drawFrame
Membuat garis, titik, dan keterangan pada titik koordinat.

- getAngle
Mengambil nilai sudut lingkaran.

4. Inti aplikasi
Disini, kita akan menampilkan kamera dan membuat pendeteksi sudut lingkaran dengan fungsi yang telah kita buat.

my_project/steering.py
while True:
    _, frame = cap.read()
    frame = cv.flip(frame, 1)
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)

    # Origin Color
    trainingColor(hsv, (0, 98, 171), (73, 255, 255), 0)
    
    # Point Color
    trainingColor(hsv, (21, 77, 81), (81, 255, 255), 1)

    if pointsList[0] != 0 and pointsList[1] != 0:
        drawFrame(frame)
        angle = getAngle(pointsList)
        sio.emit('steering', angle/180/0.5)
        
    cv.imshow("frame", frame)

    key = cv.waitKey(1)
    if key == 27: # ESC BUTTON
        break

5. Socketio standby

my_project/steering.py
sio.wait()

6. Mengirim socketio ke server simulator.
Dalam hal ini, saya menggunakan simulator dari udacity (https://github.com/udacity/self-driving-car-sim). Silahkan download dan jalankan programnya.


7. Masukan hasil dari steering.py ke server udacity steering simulator dengan membuat file baru.

my_project/drive.py

import socketio
import eventlet
import eventlet.wsgi
from flask import Flask

sio = socketio.Server()
app = Flask(__name__)

@sio.on('telemetry')
def telemetry(sid, data):
    if data:
        pass
    else:
        # NOTE: DON'T EDIT THIS.
        sio.emit('manual', data={}, skip_sid=True)

@sio.on('connect')
def connect(sid, environ):
    print("connect ", sid)

@sio.on('steering')
def steering(sid, data):
    send_control(data, 0.4)

def send_control(steering_angle, throttle):
    sio.emit(
        "steer",
        data={
            'steering_angle': steering_angle.__str__(),
            'throttle': throttle.__str__()
        },
        skip_sid=True)


if __name__ == '__main__':
    # wrap Flask application with engineio's middleware
    app = socketio.Middleware(sio, app)

    # deploy as an eventlet WSGI server
    eventlet.wsgi.server(eventlet.listen(('', 4567)), app)

8. Jalankan kedua file. (steering.py untuk menjalankan aplikasi deteksi pergerakan stir, drive.py untuk meneruskan hasil dari steering.py ke udacity car simulator)

$ python drive.py
$ python steering.py

Untuk mendapatkan kode program secara keseluruhan, silahkan download source code disini.

Berikut ini langkah-langkah dan penjelasan teknis lebih rinci tentang cara kerja projek ini.

















Oke, itulah pembahasan tentang projek sederhana saya. Semoga bermanfaat! Terima kasih!

0 komentar

Post a Comment

Hai, Mohon Komentar Yang Relevan Dan Tidak OOT!