OpenCV-basierte Zeilenfolge

Jetzt sehr beliebte Kurse zum Erstellen von Autopiloten für Autos. Dieser Nano-Grad von Udacity ist wahrscheinlich die bekannteste Option.

Viele Menschen lernen daraus und veröffentlichen ihre Entscheidungen. Auch ich konnte nicht vorbeikommen und wegtragen.

Der Unterschied besteht darin, dass der Kurs die Entwicklung eines Algorithmus auf der Grundlage der bereitgestellten Daten beinhaltet und ich alles für meinen Roboter getan habe .

Die erste Aufgabe, der sich die Kursteilnehmer beim Studium des Computer Vision stellen müssen, besteht darin, der Linie auf der Straße zu folgen. Zu diesem Thema wurden viele Artikel verfasst, hier einige der detailliertesten:


Alle von ihnen sind recht einfach und das Arbeitsschema ist auf mehrere Punkte reduziert:


Ich klebte weißes Klebeband auf den Boden und machte mich an die Arbeit.



In den oben genannten Arbeiten bestand Ihre Aufgabe darin, die gelbe Linie zu finden, sodass sie mit HLS- und HSV-Farben arbeiteten. Da meine Linie nur weiß war, beschloss ich, mich nicht darum zu kümmern und mich auf einen Schwarz-Weiß-Filter zu beschränken.

Geometrie


Die Probleme mit der Geometrie begannen sofort. Für die Schüler auf den Bildern geht der Streifen mit dem Pfeil in den Horizont. Und dennoch sind viele Linien darauf zu erkennen, die die Autoren kombinieren mussten. Ihre Linien waren jedoch gut gerichtet und es gab keine Trümmer auf den Bildern.

Ich habe ein ganz anderes Bild. Die Geometrie des Klebebandstreifens war alles andere als gerade. Blendung auf dem Boden erzeugte Geräusche.

Nach dem Auftragen von Canny ist Folgendes passiert:



Und die Hough-Linien waren wie folgt:

Bild

Durch die Stärkung der Kriterien konnte Müll ausgeschlossen werden, aber fast alle auf dem Streifen gefundenen Linien verschwanden. Sich auf so winzige Segmente zu verlassen, wäre dumm.



Im Allgemeinen waren die Ergebnisse äußerst instabil, und es kam mir der Gedanke, einen anderen Ansatz zu wählen.

Anstelle von Linien begann ich nach Konturen zu suchen. Nachdem wir davon ausgegangen waren, dass der größte Stromkreis das Isolierband ist, haben wir es geschafft, den Müll loszuwerden. (Dann stellte sich heraus, dass die große weiße Fußleiste mehr Platz im Rahmen einnahm als das Isolierband. Ich musste sie mit einem Sofakissen abdecken.)
Wenn wir das minimale Rechteck nehmen, das die Kontur begrenzt, ist die mittlere Längslinie sehr gut für die Rolle des Bewegungsvektors geeignet.



Das Licht


Das zweite Problem war die Beleuchtung. Ich habe sehr erfolgreich eine Seite der Spur in den Schatten des Sofas gelegt und es war völlig unmöglich, Fotos der gesamten Spur mit den gleichen Einstellungen zu verarbeiten. Infolgedessen musste ich einen dynamischen Cutoff für einen Schwarzweißfilter implementieren. Der Algorithmus lautet wie folgt: Wenn nach dem Anwenden des Filters im Bild zu viel Weiß (mehr als 10%) vorhanden ist, sollte der Schwellenwert angehoben werden. Wenn zu wenig (weniger als 3%) - weglassen. Die Praxis hat gezeigt, dass es im Durchschnitt für 3-4 Iterationen möglich ist, den optimalen Cutoff zu finden.

Magische Zahlen werden in einer separaten Konfiguration platziert (siehe unten). Sie können mit ihnen auf der Suche nach dem Optimum spielen.

def balance_pic(image): global T ret = None direction = 0 for i in range(0, tconf.th_iterations): rc, gray = cv.threshold(image, T, 255, 0) crop = Roi.crop_roi(gray) nwh = cv.countNonZero(crop) perc = int(100 * nwh / Roi.get_area()) logging.debug(("balance attempt", i, T, perc)) if perc > tconf.white_max: if T > tconf.threshold_max: break if direction == -1: ret = crop break T += 10 direction = 1 elif perc < tconf.white_min: if T < tconf.threshold_min: break if direction == 1: ret = crop break T -= 10 direction = -1 else: ret = crop break return ret 

Nach Anpassung der Bildverarbeitung war es möglich, zur eigentlichen Bewegung überzugehen. Der Algorithmus war wie folgt:

  • 0,5 Sekunden geradeaus fahren
  • mach ein Foto
  • finde den Vektor
  • Wenn der Anfang des Vektors relativ zur Bildmitte verschoben ist, rollen wir leicht
  • Wenn der Neigungswinkel des Vektors mehr als nötig von der Vertikalen abweicht, lenken wir in die richtige Richtung
  • Wenn es plötzlich passiert ist, dass der Streifen aus dem Rahmen verschwunden ist, gehen wir davon aus, dass wir eine Kurve gefahren sind und beginnen, uns in Richtung der letzten Lenkung oder Neigung des Vektors im vorherigen Schritt zu drehen

Verkürzte Version des Codes (Full - on Github ):

 def check_shift_turn(angle, shift): turn_state = 0 if angle < tconf.turn_angle or angle > 180 - tconf.turn_angle: turn_state = np.sign(90 - angle) shift_state = 0 if abs(shift) > tconf.shift_max: shift_state = np.sign(shift) return turn_state, shift_state def get_turn(turn_state, shift_state): turn_dir = 0 turn_val = 0 if shift_state != 0: turn_dir = shift_state turn_val = tconf.shift_step if shift_state != turn_state else tconf.turn_step elif turn_state != 0: turn_dir = turn_state turn_val = tconf.turn_step return turn_dir, turn_val def follow(iterations): tanq.set_motors("ff") try: last_turn = 0 last_angle = 0 for i in range(0, iterations): a, shift = get_vector() if a is None: if last_turn != 0: a, shift = find_line(last_turn) if a is None: break elif last_angle != 0: logging.debug(("Looking for line by angle", last_angle)) turn(np.sign(90 - last_angle), tconf.turn_step) continue else: break turn_state, shift_state = check_shift_turn(a, shift) turn_dir, turn_val = get_turn(turn_state, shift_state) if turn_dir != 0: turn(turn_dir, turn_val) last_turn = turn_dir else: time.sleep(tconf.straight_run) last_turn = 0 last_angle = a finally: tanq.set_motors("ss") 

Ergebnisse


Ungleichmäßig, aber sicher kriecht der Panzer entlang der Flugbahn:



Und hier ist ein GIF aus den Debugging-Grafiken:



Algorithmuseinstellungen


 ## Picture settings # initial grayscale threshold threshold = 120 # max grayscale threshold threshold_max = 180 #min grayscale threshold threshold_min = 40 # iterations to find balanced threshold th_iterations = 10 # min % of white in roi white_min=3 # max % of white in roi white_max=12 ## Driving settings # line angle to make a turn turn_angle = 45 # line shift to make an adjustment shift_max = 20 # turning time of shift adjustment shift_step = 0.125 # turning time of turn turn_step = 0.25 # time of straight run straight_run = 0.5 # attempts to find the line if lost find_turn_attempts = 5 # turn step to find the line if lost find_turn_step = 0.2 # max # of iterations of the whole tracking max_steps = 100 

Github- Code.

Source: https://habr.com/ru/post/de426675/


All Articles