====== 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: ====
{{:eyetracking.jpg|}}
==== Code-Ausschnitte: ====
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
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()
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**