Autonome Spurführung mit einem JetRacer ROS AI Robot

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
Abb. 1: JetRacer ROS AI Roboter von Waveshare
Autor: Jonas Michael Beisler
Modul: Projektarbeit, MBP-B-2-6.05
Starttermin: 19.02.2024
Abgabetermin: 14.02.2025
Prüfungsform: Modulabschlussprüfung als Hausarbeit (Praxisbericht als HSHL Wiki Artikel Pflege)
Betreuer: Prof. Dr.-Ing. Schneider, Tel. 806
Mitarbeiter: Marc Ebmeyer, Tel. 847

Einführung

Der JetRacer ist ein Modellrennwagen im Maßstab 1:10. Hierbei handelt es sich um ein leistungsstarkes intelligentes KI-Modellfahrzeug, das speziell für Studierende entwickelt wurde, die den Umgang mit dem Robot Operation System (ROS) erlernen möchten. Das System besteht aus zwei Steuereinheiten. Den Host-Controller bildet das JETSON-NANO-DEV-KIT-A und als Sub-Controller wird ein Raspberry Pi RP2040-Mikrocontroller verwendet. Die Hauptplatine integriert die OLED, den Servomotor-Antriebsschaltkreis, den Batterieschutzschaltkreis, einen Audioausgangsschaltkreis usw., das lästige Löten des Schaltkreises erspart, das Entladen und Laden unterstützt und kein wiederholtes Entfernen des Akkus erfordert. Der verbauter IMU-Lagesensor und DC-Encodermotor regelt die Geschwindigkeit über einen PID-Regler und kann einen Radkilometerzähler ausgeben. Die Software ist mit dem Open-Source-Projektprogramm NVIDIA JetRacer kompatibel und unterstützt AI Deep Learning, SLAM-Mapping und -Navigation, visuelle OpenCV-Verarbeitung, intelligente Sprachinteraktion und andere Funktionen.

Getting started

Lesen Sie zum Einstieg diese Artikel

Tabelle 2: Checkliste
LOP Status Bemerkung
Sicherheitseinweisung für die Labore von Marc Ebmeyer
Schlüsselübergabe für die Labore von Marc Ebmeyer
SVN-Zugang bereitstellen user: Jonas_Beisler
Vereinbarung wöchentlicher Meetings
Wiki-Zugang bereitstellen user: Jonas_Beisler


Einleitung

Abb. 2: Bild der Strecke 1
Abb. 3: Bild der Strecke 2
Abb.4: Gantt Diagramm der ursprünglichen Zeitplanung
Abb.5: Gantt Diagramm des tatsächlichen Ablaufs

Vorstellung des Labors Autonome_Systeme

Die Projektarbeit wurde im Labor Autonome Systeme unter Aufsicht von Herrn Professor Schneider und Herrn Ebmeyer durchgeführt. In dem Labor werden normalerweise Lehrveranstaltungen in den Bereichen Elektrotechnik und Systementwicklung durchgeführt. Außerdem werden dort auch viele Projekte ähnlich zu dieser Projektarbeit durchgeführt, einige Themen, die behandelt werden, sind zum Beispiel Autonomes Fahren oder Spurführung.

Aufgabenstellung

Anforderungen

Das Projekt erfordert Vorwissen in den nachfolgenden Themengebieten. Sollten Sie die Anforderungen nicht erfüllen, müssen Sie sich diese Kenntnisse anhand im Rahmen der Arbeit anhand von Literatur/Online-Kursen selbst aneignen.

Anforderungen an die wissenschaftliche Arbeit

Konkrete Aufgabengebiete

  • Einarbeitung in den JetRacer
Zuerst sollte der JetRacer Roboter Bausatz zusammen gebaut werden und dann sollten die grundlegenden Fähigkeiten des Roboters ausprobiert werden. Im Laufe dieser Aufgaben sollte dann ein grundlegendes Verständnis aufgebaut werden, welches durch eigene Ideen dann zu einem neuen Endergebnis führt.
  • Ansteuerung des Antriebs und der Lenkung
Danach sollte der Roboter manuell gesteuert werden können, hierfür gibt es mehrere Möglichkeiten, die später bei den Ergebnissen dargestellt werden.
  • Einlesen der Sensoren
Nachdem der Roboter manuell gesteuert werden kann, sollten die verschiedenen Sensoren, die eine autonome Steuerung erlauben, nacheinander ausgetestet und wenn nötig kalibriert werden.
  • Autonome Spurführung in der rechten Fahrspur mit SLAM LiDAR Mapping
Als Endziel wurde festgelegt, das der Roboter die im Labor aufgemalte Strecke (siehe Abb.2 und Abb.3) abfahren soll. Dies sollte anhand von automatisch gesetzten Navigationspunkten erfolgen. Als automatisch wird in diesem Falle festgelegt, dass alle benötigten Punkte gesetzt werden, sobald ein bestimmtes Skript im JetRacer ausgeführt wird. Dieses Skript kann ausgeführt werden, sobald der Roboter und die VM beide im Navigationsmodus laufen.
  • Systemtest
Nachdem die ersten Schritte für das Erreichen des Endziels umgesetzt wurden, sollte das komplette System ausgetestet werden, um mögliche Schwachstellen zu finden.
  • Optimierung
Anhand des Systemtests sollten dann benötigte Optimierungen oder Änderungen im Algorithmus getroffen werden.
  • Dokumentation der Vorgehensweise im HSHL-Wiki
Am Ende sollten dann alle Erkenntnisse und Ergebnisse im HSHL-Wiki dokumentiert und festgehalten werden, sodass nachfolgende Projekte bzw. Studenten von diesen profitieren können.

Zeitplanung/Projektplan

Die Zeitplanung des Projektes wurde mit sogenannten Gantt Diagrammen dargestellt. In Abb.4 ist die ursprüngliche Planung dargestellt. In Abb.5 sieht man dann die im Nachhinein überarbeitet Planung, welche den Ablauf des Projektes genauer darstellt.

Übersicht Inhalt

Der Inhalt der Projektarbeit kann in sechs Abschnitten zusammengefasst werden. Im Abschnitt "Theoretische Grundlagen" werden zuerst die benötigten Systeme bzw. Grundkenntnisse zusammengefasst und vorgestellt. Danach wird im Kapitel "Möglichkeiten der Spurführung" beschrieben, wie das Endziel der Projektarbeit erreicht werden könnte und wieso sich für welche Methode entschieden wurde. Im Abschnitt "Darstellung der Ergebnisse" wird dann dargestellt, welche Anforderungen erfüllt wurden und was genau bei den einzelnen Schritten als Ergebnis oder Erkenntnis herausgekommen ist. Dann wird im Kapitel "Zusammenfassung und Ausblick" noch einmal zusammengefasst, wie die Projektarbeit abgelaufen ist und was in Zukunft noch an Möglichkeiten umgesetzt werden könnte. Zuletzt werden dann im "Quellenverzeichnis" und im "Anhang" erst die einzelnen Literaturquellen zusammengefasst und dann die Ablageorte für die verschiedenen relevanten Dateien dargestellt.

Theoretische Grundlagen

  • Software
In diesem Projekt wurden verschiedene Software Aspekte benötigt, um die Aufgabenstellung umzusetzen. Die beiden wichtigsten Aspekte waren dabei die Programmiersprache Python sowie das sogenannte Robot Operating System (ROS). Die mitgelieferten Skripte auf dem JetRacer waren in Python verfasst und das neu angelegte Skript wurde ebenfalls in Python umgesetzt, dementsprechend wurde ein Verständnis von Python sowie allgemeiner Programmierung benötigt. Das ROS war relevant für die Kommunikation des Roboters, also der Sensoren im Auto, und den darüberliegenden Softwaresystemen. Diese Systeme waren zum einen das NVIDIA JetPack SDK sowie ein Ubuntu Linux System. Das Linux System war das grundlegende Level, auf dem der Roboter sowie die virtuelle Maschine liefen und das SDK wäre relevant gewesen für Bildverarbeitung oder maschinelles Lernen.
  • Hardware
Die wichtigste Hardware in diesem Projekt waren die verschiedenen Systeme des JetRacers. Diese umfassen das Jetson Nano Modul, welches als zentrale Steuerungseinheit fungiert, sowie die verschiedenen Sensoren und Aktuatoren die die Umgebung des Roboters aufnehmen oder den Roboter antreiben. Ebenfalls hat der Roboter mehrere Kommunikationsmodule mit welchen er sich mit dem WLAN, per Bluetooth oder per Ethernet mit anderen Systemen verbinden kann.
Ebenfalls relevante Hardware war eine virtuelle Maschine, die sich mit dem Roboter verbunden hat und auf welcher die Daten visualisiert werden konnten. Die relevanten Komponenten dieses Systems waren dann die jeweiligen Komponenten die zum Rechner gehörten, auf dem die VM lief.
Die letzte Komponente die benötigt wird, ist ein Netzwerk über das der Roboter und die VM kommunizieren können. In diesem Projekt war es das "Autonome Systeme" WLAN welches im Labor empfänglich ist.
  • LiDAR
LiDAR steht für "Light detection and ranging" und bezeichnet ein System, welches einen Laserstrahl nutzt, um Abstände zu messen. Genauer gesagt scannt ein Laser 360 Grad um seinen Ursprung herum und detektiert außerdem Rückstreuungen die wieder beim Ursprung eintreffen. Über die Dauer, die es benötigt, bis eine Rückstreuung eintrifft kann dann die Distanz zu Objekten in der Umgebung berechnet werden. Im Bereich der Robotik bzw. des autonomen Fahrens wird die LiDAR Technologie genutzt, um Karten der Umgebung herzustellen sowie Objekte und Hindernisse während der Fahrt zu erkennen. [1]
  • Odometrie
Als Odometrie wird das Verfahren bezeichnet mit welchem man die ungefähre Position und Orientierung eines mobilen Systems schätzt. Dieses Verfahren beruht normalerweise darauf, das der Antrieb des Systems überwacht wird, und man so auf Entfernungen, die zurückgelegt wurden, schließt. Im Falle des JetRacers werden also die Radumdrehungen mitgezählt und über den bekannten Radumfang werden dann die zurückgelegten Strecken berechnet. Ebenfalls kann auf die Orientierung geschlossen werden, wenn man die Winkel der Achseinstellung mit einbezieht.
Allerdings ist dieses System alleine anfällig für Fehler und wird deswegen meist in Verbindung mit anderen Systemen für die Position des Roboters verwendet. Die Fehler in der Berechnung der Odometrie entstehen zum Beispiel durch eine Orientierung der Räder welche das Auto leicht nach links oder rechts anstatt geradeaus fahren lassen. Oder aber der Untergrund ist rutschig, was dazu führt das bei einer gezählten Radumdrehung nicht wirklich eine ganze Umdrehung nach vorne vollzogen wurde. [2]
  • Kamera
Abb.6: Move_Base Übersicht
Im JetRacer ist ein sogenanntes "IMX219" Kamera-Modul verbaut. Mit diesem Modul können verschiedene Video Algorithmen durchgeführt werden. Die Daten werden dafür von der sogenannten "Multimedia API" verarbeitet und somit sind verschiedene Methoden wie zum Beispiel Form- oder Gesichtserkennung möglich. In dieser Projektarbeit wird die Kamera allerdings nach anfänglichem ausprobieren und kalibrieren nicht weiter genutzt. [3]
  • Robot Operating System (ROS)
ROS, also das Robot Operating System, ist ein Gerüst welches auf verschiedenen Betriebssystemen für private oder Industrieroboter genutzt werden kann. Es fasst dabei verschiedenste benötigte Teile zusammen, zum Beispiel dient es dazu Nachrichten zwischen verschiedenen Teilsystemen hin und her zu schicken oder aber Pakete von Gerätetreibern zusammenzufassen und zur Verfügung zu stellen. Die Version ROS2 ist dabei eine neuere Version, welche dazu dient die guten Aspekte von ROS1 beizubehalten aber in anderen Aspekten neuere bessere Vorgänge einzubringen. ROS2 zielt zum Beispiel auf Echtzeitfähigkeit sowie Benutzerkomfort ab. [4]
Der in dieser Projektarbeit wahrscheinlich wichtigste Aspekt von ROS ist der Pfadplaner, intern genannt "move_base". Er vereint die Daten von verschiedenen Sensoren sowie die ihm zugewiesenen Ziele und berechnet daraus einen lokalen bzw. globalen Pfad. Dieser Pfad wird dann an die Bewegungs-Kontrolle weitergeleitet. Eine Übersicht dieses Systems ist in Abb. 6 dargestellt. [5]

Möglichkeiten der Spurführung

Kamera & KI

Eine Möglichkeit der Strecke im Labor zu folgen wäre per Kamera und KI Training umsetzbar gewesen. Dafür hätte man zuerst eine KI anhand von eigens erstellten Daten trainieren müssen. Also zum Beispiel die Strecke abfilmen, und dann die benötigten Steuerungsbefehle manuell eingeben und diese Informationen dann der KI zur Verfügung stellen.
In dieser Projektarbeit wurde sich allerdings gegen das Verwenden der Kamera und einer Künstlichen Intelligenz entschieden. Diese Entscheidung basierte darauf das wenig Vorwissen in diesen Bereichen existierte, allerdings gab es Vorwissen in der allgemeinen Programmierung sowie dem Umgang mit Koordinaten, deswegen wurde dann auch die zweite Möglichkeit der programmierten Koordinaten als Lösungsmethode gewählt.

Programmierte Koordinaten

Um die Strecke per Koordinaten abzufahren wurden mehrere Schritte/Abläufe benötigt. Zuerst musste ein Python Skript erstellt werden, indem die einzelnen Schritte abgearbeitet werden. Dieses Skript musste dann die Markierungen die der Strecke entsprechen enthalten und sich auch um die Orientierung kümmern. Die einzelnen benötigten Code Segmente sind im Nachfolgenden beschrieben.

Erstellen eigener Python Skripte Vorhandene Skripte umbauen

Als Vorbild wurde das sogenannte "Multipoint Navigation Skript" genutzt. In diesem war der Code, der für das Markieren der Punkte im Visualisierungstool RViz und das Setzen der Zielpunkte für den Racer benötigt wird, vorhanden. Dieser Code wurde dann zuerst so abgeändert das mehrere Markierungen in RViz nicht nur bei einem Mausklick, sondern direkt beim Ausführen des Skriptes gesetzt werden. Allerdings benötigte dieser Schritt ausführlicheren Publisher und Subscriber Code als im Skript ursprünglich vorhanden war.
  • ROS Publisher & Subscriber
Die sogenannten Publisher und Subscriber sind zwei der wichtigsten Systeme, die es in ROS gibt. Sie sind dafür zuständig Daten, wie z.B. Informationen über Marker oder Odometrie, zwischen den verschiedenen Nodes hin und her zu schicken.
Der "Publisher" ist ein Teilknoten, oder sogar ganzer Knoten, welcher Daten versendet. Diese Daten werden in ein sogenanntes "Topic", also quasi einen Kanal mit bestimmter Frequenz, gesendet. Dabei ist es egal, ob ein anderer Knoten diesem Kanal zuhört oder nicht, der Publisher sendet immer Daten hinein.
Der "Subscriber" ist dann ein Teilknoten, oder ganzer Knoten, der an diesem Kanal interessiert ist. Das besondere dabei ist das sich der Publisher und der Subscriber gegenseitig nicht kennen, sondern einfach nur beide etwas mit dem Kanal zu tun haben. Der ROS Master merkt sich auf beiden Seiten den jeweiligen Kanal, der sie betrifft, und die Informationen werden dann weitergegeben.
Da mehrere Publisher in einen einzelnen Kanal senden können, und auch mehrere Subscriber einem Kanal zuhören können, ist es wichtig, dass die jeweiligen Datentypen der gewünschten Informationen bei der Initialisierung der Knoten übereinstimmen.
Publisher und Subscriber können in einem Python Skript wiefolgt initialisiert werden:
# Subscribe to the status of reaching the target point 
self.sub_goal_result = rospy.Subscriber('/move_base/result', MoveBaseActionResult, self.goal_result_callback)

# Subscribe to the initial pose topic
self.sub_initialpose = rospy.Subscriber('/initialpose', PoseWithCovarianceStamped, self.initialpose_callback)

Einbauen der benötigten Funktionen

  • Automatische Marker
Im erstellten Skript werden die Koordinaten der zu befahrenden Strecke zuerst in einem Array gesammelt. Dabei wird jeder zu markierender Punkt als eigenes Array im Format [X-Koordinate, Y-Koordinate] angelegt und dann in einem Gesamtarray gesammelt.
marker0 = [4.047, 0]
marker1 = [7.078, 0.02]
marker2 = [8.429, 1.128]
marker3 = [8.761, 3.032]
marker4 = [8.163, 5.253]
marker5 = [7.643, 5.38]
marker6 = [5.033, 5.052]
marker7 = [4.003, 4.222]
marker8 = [2.897, 2.374]
marker9 = [3.386, 1.226]
marker10 = [5.101, 1.705]
marker11 = [5.721, 3.513]
marker12 = [5.033, 5.052]
marker13 = [4.971, 6.913]
marker14 = [0.5, 6.462]
marker15 = [-0.65, 4.98]
marker16 = [-0.501, 4.029]
marker17 = [-0.51, 2.805]
marker18 = [-1.168, 1.74]
marker19 = [-0.92, 0.498]
marker20 = [0, 0]
		
coordinates = [marker0, marker1, marker2, marker3, marker4, marker5, marker6, marker7, marker8, marker9, marker10, marker11, marker12, marker13, marker14, marker15, marker16, marker17, marker18, marker19, marker20]
Danach wird dieses Array in einer For-Schleife durchgearbeitet und für jedes der Koordinatenpaare wird eine Markierung angelegt und dem sogenannten "markerArray" zugefügt. Jede dieser Markierungen hat ebenfalls verschiedene Parameter die gesetzt werden. Diese Parameter sind zum Beispiel die Farbe oder Größe der Markierung. Ebenfalls können hier "Header" oder "Text" der Markierung gesetzt werden.
for x in coordinates:
# Create a marker object
marker = Marker()
marker.header.frame_id = 'map'
# Character format
marker.type = marker.TEXT_VIEW_FACING
# marker model
marker.action = marker.ADD
# the size of the marker
marker.scale.x = 0.6
marker.scale.y = 0.6
marker.scale.z = 0.6
# marker ColorRGBA
marker.color.r = 1
marker.color.g = 0
marker.color.b = 0
marker.color.a = 1
# marker position XYZ
markX = x[0]
markY = x[1]
markZ = 0

marker.pose.position.x = markX
marker.pose.position.y = markY
marker.pose.position.z = markZ
# marker text
marker.text = str(self.count)
self.markerArray.markers.append(marker)
self.count +=1

# Set the id of markers
id = 0
for m in self.markerArray.markers:
	m.id = id
	print('marker: ' + str(m.id) + ' ' + str(m.pose.position.x))
	id += 1
  • Anfahren der Zielpunkte bzw. Abfahrend der Strecke
Der erste Zielpunkt wird gesetzt bzw. veröffentlicht, sobald das gesamte "markerArray" gefüllt ist. Dieses setzten, ist fest im Code einprogrammiert, sodass das Abfahren der Strecke initial startet. Danach greift die sogenannte "goalCallback" Methode.
if self.count == 0:
	# Publish target point
	self.PubTargetPoint(coordinates[0][0],coordinates[0][1])
    self.index+=1
Sobald der Roboter dann den ersten Zielpunkt erreicht hat, wählt die unten dargestellte Methode anhand der IDs der Markierungen den nächsten Zielpunkt aus. Die Koordinaten des Punktes werden dabei aus den Parametern der Markierung herausgezogen und an die sogenannte "pubTargetPoint" Methode weitergegeben.
def goal_result_callback(self, msg):
	print('Goal Result started')
	if self.count == 0: return
       	print ("Get the status of reaching the target point!!!")
       	# Reach the target point
        if msg.status.status == 3:
            	self.try_again = 1
           	 #  This round of cruise is completed, restart cruise
            	if self.index == self.count:
                	print ('Reach the target point ' + str(self.index - 1) + '.')
                	self.index = 0
                	x = self.markerArray.markers[self.index].pose.position.x
                	y = self.markerArray.markers[self.index].pose.position.y
                	self.PubTargetPoint(x, y)
                	# Cruise to the next point
                	self.index += 1
            	# Cruise the remaining points of the round
            	elif self.index < self.count:
                	print ('Reach the target point ' + str(self.index - 1) + '.')
                	x = self.markerArray.markers[self.index].pose.position.x
                	y = self.markerArray.markers[self.index].pose.position.y
                	self.PubTargetPoint(x, y)
                	# Cruise to the next point
                	self.index += 1
        # Did not reach the target point
        else :
            	rospy.logwarn('Can not reach the target point ' + str(self.index - 1) + '.')
            	# Try again to reach the unreached target point
            	if self.try_again == 1:
                	rospy.logwarn('trying reach the target point ' + str(self.index - 1) + ' again!')
                	x = self.markerArray.markers[self.index - 1].pose.position.x
                	y = self.markerArray.markers[self.index - 1].pose.position.y
                	self.PubTargetPoint(x, y)
                	# It is not allowed to try again to reach the unreached target point
                	self.try_again = 0
            	# Continue to the next target point
            	elif self.index < len(self.markerArray.markers):
                	rospy.logwarn('try reach the target point ' + str(self.index - 1) + ' failed! reach next point.')
                	# If this round of cruise has been completed, the setting starts from the beginning
                	if self.index == self.count: self.index = 0
                	x = self.markerArray.markers[self.index].pose.position.x
                	y = self.markerArray.markers[self.index].pose.position.y
                	self.PubTargetPoint(x, y)
                	# Cruise to the next point
                	self.index += 1
                	# Allow another attempt to reach the unreached target point
                	self.try_again = 1
  • Orientierung an den Zielpunkten
Die Orientierung an den Zielpunkten war eines der größten Probleme in der Erstellung des finalen Skriptes. Der Roboter konnte zwar früh die gewollten Koordinaten anfahren, aber er hatte Probleme mit seiner Orientierung.
Der Roboter hat sich an jedem der gesetzten Zielpunkte immer so ausgerichtet wie er in seiner Startposition ausgerichtet war. Selbst wenn er sich z.B. nur um 90 Grad hätte drehen müssen und dann ein Stück vorwärtsfahren müssen, um den nächsten Punkt zu erreichen, hat der Roboter immer große Bögen gefahren, um am Zielpunkt wieder in die gleiche Richtung zu schauen wie vorher.
Die Lösung für dieses Problem waren dann die sogenannten Quaternionen und eine Formel zur Berechnung des Anfahrtswinkels.
  • Quaternionen
Abb. 7: Quaternion Grundform [6]
Allgemein versteht man unter Quaternionen der Mathematik eine Erweiterung der reellen Zahlen, welche durch mehrere Werte und Koeffizienten (siehe Abb.7) einen Startpunkt und eine Orientierung für einen "Vektor" angeben kann.
Im Gebiet der Robotik werden deshalb Quaternionen für genau diesen Umstand benutzt, sie geben die genauen Koordinaten des Roboters so wie seine Ausrichtung an. Ebenfalls kann eine gewünschte weitere Position, z.B. ein Ort der angefahren werden soll, mit einem Quaternion angegeben werden. Hierbei stehen die Koordinaten dann für die Koordinaten des Punktes und die Orientierung für die Orientierung die der Roboter am Zielort haben soll.
  • Anfahrtswinkel Formel
Um im Code Quaternionen nutzen zu können wurde nach einiger Recherche eine Formel gefunden welche die derzeitige Position des Roboters sowie die Position des Zielortes verrechnet und somit einen natürlichen Winkel für die Anfahrt herausfindet.
Aber, um diese Formel nutzen zu können mussten erstmal die genauen Koordinaten des Roboters per Odometrie Subscriber herausgefunden werden. Sobald der Subscriber gestartet ist, werden die Variablen welche die aktuellen Roboterkoordinaten enthalten aktualisiert und können dann genutzt werden.
Aufgrund dieser Koordinaten kann dann der Anfahrtswinkel anhand der Formel ausgerechnet und in ein Quaternion eingesetzt werden. Danach wird dann eine Pose erstellt die das Quaternion sowie die X- und die Y-Koordinate, welche der Methode übergeben wurden, enthält. Diese Pose kann dann als neuer Zielpunkt veröffentlicht werden.
Mithilfe der unten gezeigten Anpassung des Codes sollte der Roboter nun eine möglichst natürliche Orientierung an den Zielpunkten einnehmen und somit auch wie erwartet durch Kurven fahren können.
self.sub_Odometry = rospy.Subscriber('/odom', Odometry, self.odometry)
		
angle_to_goal=math.atan2(y-self.testY,  x-self.testX)
q_angle = quaternion_from_euler(0, 0, angle_to_goal)
q = Quaternion(*q_angle)
poseTest = Pose(Point(x, y, 0.000), q)
self.sub_Odometry.unregister()

Darstellung der Ergebnisse

Einarbeitung

Zu Beginn der Projektarbeit wurde sich anhand von Tutorials auf der Website des Vertreibers des JetRacers in eben diesen eingearbeitet. Die Grundlagen sind in den nächsten Punkten wiedergegeben.
  • Montage
Die Montage war ein relativ selbsterklärender Prozess, der aufgrund der Konzipierung des Systems als Baukasten gut durchführbar war.
Die einzigen Veränderungen an der Montage waren das die Schraubenlöcher für den Servo tiefer gefeilt worden, und kleine Gummiringe zur Dämpfung der Räder eingebaut wurden.
  • Bewegung
Ebenfalls konnte schnell ein Stand erreicht werden, bei dem der Roboter frei gesteuert werden konnte. Dabei gab es drei verschiedene Modi:
1. Der Roboter konnte per einzelnen Eingaben in einer sogenannten "Publishing Node" gesteuert werden.
2. Ebenfalls konnte eine Tastatur benutzt werden um die verschiedenen Richtungen oder Orientierungen anzusteuern.
3. Oder es konnte ein Spiele-Kontroller benutzt werden um den Roboter frei steuern zu können.
  • LIDAR
Die LIDAR Funktion konnte ebenfalls schnell gestartet und getestet werden. Mit ihr ließen sich leicht Hindernisse erkennen und Karten erstellen.
  • Odometrie
Um einen reibungslosen Ablauf der verschiedenen Bewegungssysteme zu gewährleisten wurden ebenfalls einige Odometrie Tests durchgeführt, wie z.B. zu überprüfen, dass der Roboter nicht auf einer Geraden schief fährt oder das er wirklich einen Meter fährt, wenn er aufgefordert wird einen Meter zu fahren.
  • Kamera
Ebenfalls wurde die Kamera überprüft und mithilfe eines Schachbrettmusters kalibriert.

Die Tutorials zu den verschiedenen Punkten können auf der Hauptseite des JetRacers eingesehen werden

Raum Kartographieren

Abb. 8: Karte des Raums
Damit der Roboter sich im Raum orientieren kann wird eine Karte benötigt. Diese wurde per LIDAR bzw. SLAM Mapping erstellt (siehe SLAM Mapping). Wichtig dabei war das der Mapping-Algorithmus dort gestartet wurde, wo das Auto später für das Abfahren der Strecke starten wird. Dadurch wird die Karte mit dem korrekten Fokus angezeigt, und der Ursprung liegt gleichzeitig im Startpunkt.
Die gespeicherte Karte wird im Navigationsmodus automatisch ausgewählt und genutzt.
In der Karte (siehe Abb.8) sind in hellgrau die freien Flächen, und in Schwarz die erfassten Hindernisse dargestellt. Die beiden Säulen von Punkten an beiden Seiten des Raumes sind z.B. Tischbeine.

Testen & Verbessern des Skriptes

Das Skript für die Streckenabfahrt wurde im gesamten Zeitraum kontinuierlich entwickelt und verbessert. Bei jedem der oben genannten Zwischenschritten gab es kleinere Probleme, die aus dem Weg geschafft werden mussten. Entweder geschah dies durch Recherche der jeweiligen Probleme oder durch Änderung der Herangehensweise an bestimmten Stellen.
Einige Problemfälle waren zum Beispiel:
1. Nach der Montage des Roboters ließ er sich nicht hochfahren. Dies lag daran, dass das Expansion-Board, welches die Batterien hält, fehlerhaft war. Nachdem es ausgetauscht wurde ließ der Roboter sich hochfahren. Allerdings konnte man ihn immer noch nicht vernünftig steuern, und auch die LIDAR Funktion ging nicht. Dies lag an einem fehlerhaften Nano Module, nachdem dieses ausgetauscht wurde lief der Roboter.
2. Der Roboter fuhr zu Beginn des Skriptes nicht los, um den ersten gesetzten Zielpunkt zu erreichen. Dies wurde behoben, indem alle Marker in einem Array zusammengefügt wurden und dann erst der Aufruf zum Anfahren des ersten Punktes durchgeführt wird. Dies führt dazu das das System nicht mit zu vielen Aufgaben überlastet wird.
3. Ebenfalls in diesem Teilgebiet gab es das Problem, das der Roboter den letzten Zielpunkt zuerst anfahren wollte, anstatt dem ersten. Dies lag daran, dass eine Zeit lang jeder Marker der angelegt wurde auch direkt als Zielpunkt gesetzt wurde, dies führte dann dazu das der zuletzt angelegte Marker, also der letzte Zielpunkt angefahren wurde. Dieses Problem wurde ebenfalls durch die neue Logik mit dem Array gelöst, da die Marker dadurch erst dann zum Zielpunkt gemacht wurden, wenn der vorherige Marker erreicht war.
4. Der Roboter wollte an jedem Punkt die gleiche Orientierung wie am Startpunkt einnehmen. Auf der Geraden war das kein Problem, in Kurven führte dies jedoch zu einem unnatürlichen Fahrverhalten. Theoretisch wurde dieses Problem durch die oben im Unterpunkt "Orientierung an den Zielpunkten" beschriebenen Änderungen behoben.
5. Die selber angelegten Python Skripte konnten zuerst nicht ausgeführt werden. Dies lag daran, dass die Berechtigungen an den Dateien nicht korrekt gesetzt waren. Mit dem Befehl chmod +x XYZ.py, wobei XYZ.py durch den jeweiligen Skriptnamen ersetzt werden muss.
Abb.9: RViz mit Karte & Markierungen

Finale Vorführung des Ziels

  • Navigationsmodus starten
Damit der Roboter die Strecke abfahren kann, muss er zuerst im Navigationsmodus sein, dafür muss am Roboter im Terminal erst der ROS Hauptknoten und die Navigationssysteme gestartet werden.
Dies geschieht über die Befehle roscore und roslaunch jetracer nav.launch, welche in zwei verschiedenen Terminals ausgeführt werden müssen.
Dafür kann entweder das NoMachine Programm genutzt werden, um von außerhalb auf den Roboter zuzugreifen, oder man steckt die benötigten Peripheriegeräte an.
Danach muss dann noch innerhalb der virtuellen Maschine im Terminal der Befehl roslaunch jetracer view_nav.launch eingegeben werden. Das sorgt dafür das sich das RViz Tool öffnet, in welchem man die Karte und die Position des Roboters sieht.
  • Skript ausführen
Jetzt muss der Roboter an seine gewünschte Startposition, hier also den Start/Ziel Strich der Strecke, gesetzt werden. Danach kann dann per NoMachine Zugriff in einem weiteren Terminal auf dem Roboter das, in diesem Falle streckeFahren.py benannte, Skript ausgeführt werden.
Der Befehl hierfür lautet rosrun jetracer streckeFahren.py
Nach einer kurzen Verzögerung werden dann die Markierungen, die der Strecke entsprechen im RViz Tool erscheinen (siehe Abb.9) und der Roboter fährt los.
  • Video
Ein Video des Abfahrens der Strecke kann auf Panopto eingesehen werden.

Bewertung des finalen Ergebnisses

Das finale Ergebnis kann das gesetzte Endziel nicht komplett erfüllen. Der Roboter fährt die verschiedenen gesetzten Punkte der Strecke ab, aber er verlässt sehr häufig die rechte Fahrspur beziehungsweise die Fahrbahn komplett. Dies liegt daran, dass der Roboter bei seiner Pfadplanung die Grenzen der Strecke außer Acht lässt da er sie nicht kennt, er weiß nur etwas von den Punkten, die er anfahren soll. Diese Pfadplanung wäre ein kleineres Problem, wenn der Roboter wie geplant seine Orientierung, beziehungsweise seinen Anfahrtswinkel, anhand der oben erwähnten Quaternionen mehr wie ein Mensch setzen würde. Allerdings hatte diese Implementierung im Code nicht die gewünschte Wirkung. Somit bleibt der Fakt das das Endergebnis nicht komplett mit dem gesetztem Ziel übereinstimmt. Dennoch wurde eine zufriedenstellende Grundlage geschaffen.

Zusammenfassung und Ausblick

Fazit

Als Fazit kann gesagt werden, das eine Grundlage zur weiteren Entwicklung gelegt wurde. Ebenfalls wurden die Anforderungen an die Projektarbeit zum großen Teil erfüllt.
  • Es wurden alle grundlegenden Funktionen des JetRacers ausgetestet und falls nötig kalibriert. Ebenfalls wurden die verschiedenen Bewegungsarten angesteuert und ausprobiert.
  • Danach wurde erfolgreich ein eigenes Pythonskript erstellt welches zur gezielten Bewegung bzw. dem gezielten Anfahren von Markierungen genutzt werden kann.
  • Schlussendlich wurde dann ebenfalls durch Testen und Optimieren die Notwendigkeit von Quaternionen zum Setzen der Orientierung erkannt und umgesetzt.
Allerdings wurde nicht das komplette Ziel erreicht, da der Roboter eigentlich ähnlich einem Menschen durch die Strecke fahren sollte. Dies wurde zwar durch die Quaternionen im Code umgesetzt, aber wirkt sich beim wirklichen Abfahren nicht aus. Dies könnte verschiedene Gründe haben, entweder wird die Orientierung im Pfadplaner im Hintergrund überschrieben oder die Formel für die Berechnung des Anfahrtswinkels enthält noch nicht alle Parameter. Dieser Umstand müsste also bei einem anschließendem Projekt zuerst gelöst werden.

Ausblick

Ein Problem des aktuellen Standes ist allerdings das dieses Skript nur fest eingetragene Koordinaten abfahren kann, ein Ändern der Strecke würde also relativ mühsam Koordinate für Koordinate eingetragen werden müssen. Eine mögliche Weiterentwicklung wäre also vielleicht das automatische Erstellen der Koordinaten durch eine darauf trainierte KI.
Diese KI müsste darauf trainiert werden das sie Streckenbilder oder anderen Input zu Kurven oder Geraden aufnimmt und diese in passenden Code umwandelt. Der Code könnte zum Beispiel ein Array mit den Koordinaten im [x, y] Format sein. Dieser Code Schnipsel könnte dann einfach in das Pythonskript eingefügt werden. Für das Training der KI müsste allerdings noch einiges an Know-How zusammengetragen werden.
Ebenfalls könnte man auch eine Kombination aus den beiden oben erwähnten Möglichkeiten der Spurführung in Betracht ziehen. Also über eine KI, welche darauf trainiert ist anhand von Kamerabildern der Strecke zu folgen, eine Verbesserung des Fahrverhaltens zu generieren. Diese KI könnte zum Beispiel für das konkrete Fahrverhalten zwischen den gesetzten Markierungen zuständig sein, und somit die Feinmechanik der Steuerung verbessern.
Ein weiteres Gebiet, welches noch Weiterentwicklung benötigt, ist das Fahrverhalten bzw. die gesamte Odometrie des Roboters. Hier müssten vielleicht noch einmal die Einzelteile wie Achsen und Räder neu oder besser angebaut werden und das gesamte System neu kalibriert werden. Dies würde dazu führen das der Roboter geradliniger und genauer fahren kann.

Quellenverzeichnis

Anhang

Repositorium

Links zu wichtigen Dateien

Tabelle 1: Links
Dateiname Link Bemerkung
Finale Skript "streckeFahren.py" [[1]]
"multipointNavigation.py" [[2]]
Screencapture Video des Ausführen des Skriptes /
Video der finalen Vorführung /
Virtualbox Image [[3]] Der Inhalt der Zip kann aus dem Oracle VM Programm heraus geöffnet werden
JetRacer Image [[4]] Diese komprimierten Dateien sind wahrscheinlich nicht mehr benutzbar da die Linux geschützten Ordner das hochladen nicht mitgemacht haben
unverändertes JetRacer Image [[5]] Dieses Image kann auf eine SD-Karte gezogen werden von der dann gebootet werden kann. Danach muss nur das streckeFahren.py Skript in den Ordner /catkin_ws/src/jetracer_ros/scripts/ abgelegt werden
Karten Dateien [[6]] Die .pgm Datei ist dabei eine Visualisierung der Karte und die .csv Datei sollte die Grauwerte für jeden Pixel in tabellarischer Form enthalten. Beim Übertragen durch ein Python Skript sind aber vielleicht Fehler entstanden. Die benutzbare Datei für zukünftiger Roboter ist also die .pgm Datei.'

→ zurück zum Hauptartikel: Studentische Arbeiten