Rollstuhlsteuerung per Eye-Tracking

Beschreibung:

Eine Kamerabrille, die einem querschnittsgelähmten Menschen per Eye-Tracking die Rollstuhlsteuerung ermöglicht. Durch die Bewegung der Augenlinse wird der Elektrorollstuhl in Blickrichtung gesteuert. Zum An- und Ausschalten befindet sich an den Griffen des Rollstuhls ein Taster, welcher von der Begleitperson bedient wird. Dieser Taster bewirkt das Starten und Stoppen des Rollstuhls. Die größte Schwierigkeit des Eye-Trackings sind unterschiedliche Lichtverhältnisse, daher ist zur Einstellung der Helligkeit ein Regler vorhanden. Dieser wird ebenfalls von der Begleitperson bedient.

Bauteile:

• Raspberry Pi 3
• Raspicam
• DC Motoren
• 12V Batterie
• Powerbank
• Taster
• Potentiometer
• ADC
• Brille mit Kamerahalterung
• H-Brücke

Fritzing:

Code-Ausschnitte:

eye_tracking.py
import cv2
import numpy as np
 
 
def get_position(x_value, brightness):
    cap = cv2.VideoCapture(0)#Bildquelle; bei RP normalerweise index 0 
    cap.set(11, 200)# kontrast
 
    while True:
        cap.set(10, brightness.value)#70 mittelwert, min 60, max 80
        ret, frame = cap.read() #bildinformationen lesen
        if ret is False:
            break
 
        roi = frame[100:500, 170:850]#Bildausschnitt definieren
        #roi = frame[269: 795, 537: 1416]
 
        #zur Suche der Koordinaten, wird das Bild in schwarz/weiß umgewandelt
        rows, cols, _ = roi.shape
        gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
        gray_roi = cv2.GaussianBlur(gray_roi, (7, 7), 0)
 
        #mit Hile von openCV koordinaten der Pupille ermitteln
        _, threshold = cv2.threshold(gray_roi, 3, 255, cv2.THRESH_BINARY_INV)
        contours,_ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        contours = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
 
        for cnt in contours:
            (x, y, w, h) = cv2.boundingRect(cnt)#Koordinaten zur Weiterverarbeitung in einzelnen Variablen speichern
 
            x_value.value = int(x) #x koordinate der Pupille an die Steuerung uebergeben
 
            #zeichnen der vertikalen und horizontalen
            #cv2.drawContours(roi, [cnt], -1, (0, 0, 255), 3)
            cv2.rectangle(roi, (x, y), (x + w, y + h), (255, 0, 0), 2)
            cv2.line(roi, (x + int(w/2), 0), (x + int(w/2), rows), (0, 255, 0), 2)
            cv2.line(roi, (0, y + int(h/2)), (cols, y + int(h/2)), (0, 255, 0), 2)
            break
 
        #cv2.imshow("Threshold", threshold)
        #cv2.imshow("gray roi", gray_roi)
        cv2.imshow("Roi", roi)
        #anzeigen des Eyetrackings
 
        key = cv2.waitKey(30)
        if key == 27:
            break
Steuerung.py
import Eye_tracking as et
import Taster
from time import sleep
import multiprocessing as mp
from ctypes import c_bool
import Rover
import Poti
import RPi.GPIO as GPIO
 
try:        
    x = mp.Value('i', 0) # x koordinate der Pupille 
    taster_value = mp.Value(c_bool, False) #Ein/ Aus schalten der Motoren
    v = mp.Array('i', 2) # geschwindigkeit; index 0 = links/ index 1 = rechts
    brightness = mp.Value('i', 70)
 
    #initialisieren der Prozesse
    tracking = mp.Process(target=et.get_position, args=(x, brightness))
    taster = mp.Process(target=Taster.switch_OnOff, args=(taster_value,))
    rover = mp.Process(target=Rover.rover, args=(v,))
    poti = mp.Process(target=Poti.get_value, args=(brightness,))
 
    poti.start()
    tracking.start()
    taster.start()
 
 
    #Motoren mit v=0 starten
    v[0] = 0
    v[1] = 0
    rover.start()
 
    #"falsche" koordinaten mit maximalen x werten filtern
    #50 160 290
    #max wert rechts: 50
    #max wert links: 290
 
    while True:
        while taster_value.value == True: #sobald start/stop-knopf gedrückt wurde, fahre los:
            if(x.value > 0 and x.value > 50 and x.value < 290): #Wenn Position der Pupille erkannt wird
                if(x.value < 140):
                   #fahre nach rechts
                    v[0] = 50
                    v[1] = 25
                    #print("rechts")
                elif(x.value > 180):
                    # fahre nach links
                    v[0] = 25
                    v[1] = 50
                    #print("links")
                elif(x.value > 140 and x.value < 180):
                    #fahre geradeaus
                    v[0] = 50
                    v[1] = 50
                    print("gerade")
            else:#anhalten wenn Pupille nicht erkannt wird
                v[0] = 0
                v[1] = 0
        #anhalten wenn start/stop-knopf betätigt wird
        v[0] = 0
        v[1] = 0
except KeyboardInterrupt:
    print("Programm beenden..")
    poti.terminate()
    taster.terminate()
    tracking.terminate()
    rover.terminate()
    GPIO.cleanup()
Poti.py
import spidev
from time import sleep
import RPi.GPIO as gpio
 
gpio.setmode(gpio.BCM)
gpio.setup(18, gpio.OUT)
gpio.output(18, gpio.HIGH) #stromversorgung des Potis
 
spi = spidev.SpiDev()      # SPI-Bus initialisieren
spi.open(0,0)              # SPI Bus 0, Client 0 oeffnen
spi.max_speed_hz=1000000   # Max. Geschw. begrenzen fuer stabiles Auslesen
 
def readMCP3008(channel):# auslesen des Signals auf entsprechendem Channel
  adc=spi.xfer2([1,(8+channel)<<4,0])
  wert = ((adc[1]&3) << 8) + adc[2]
  return wert
 
def get_value(value):
    while True: #frage zweimal pro Sekunde den Wert des Reglers ab
        v=readMCP3008(0) # A/D Wandler auf Channel 0 auslesen; v entspricht einer Zahl zwischen 0 und 1023
        p = int((v/10.23)/5 + 60)# umrechnen in Prozent (v/1023/100) und umrechnung in Helligkeitswert zwischen 60 und 80
        value.value = p #Helligkeitswert an Steuerung übergeben
        sleep (0.5)

Anmerkung:

Die Motoren werden per PWM gesteuert. Anleitungen dazu sind im Wiki vorhanden.
Die Methode erwartet ein Array v. v[0] = Geschwindigkeit linker Motor; v[1] = Geschwindigkeit rechter Motor
Im Array v ist der Wert für die Steuerung per PWM gespeichert.
Anleitungen für den Taster befinden sich ebenfalls im Wiki. Hierbei ist ein Pulldown Widerstand zu verwenden
Darüber hinaus sollte der Taster entprellt werden