Robotics Frameworks - Exam.pdf

Robotics Frameworks - Exam
Robotics Frameworks - Exam Aufgabe 1) In einem ROS (Robot Operating System) basierten Projekt hast Du verschiedene Kommunikationsmechanismen zur Verfügung, um die Interaktion zwischen den Knoten zu steuern. Es gibt drei Hauptarten von Kommunikationsmechanismen: Topics: Die Topics ermöglichen eine asynchrone und unidirektionale Kommunikation im Publizieren-Abonnieren-Muster. Services: Diensten nutz...

© StudySmarter 2024, all rights reserved.

Robotics Frameworks - Exam

Aufgabe 1)

In einem ROS (Robot Operating System) basierten Projekt hast Du verschiedene Kommunikationsmechanismen zur Verfügung, um die Interaktion zwischen den Knoten zu steuern. Es gibt drei Hauptarten von Kommunikationsmechanismen:

  • Topics: Die Topics ermöglichen eine asynchrone und unidirektionale Kommunikation im Publizieren-Abonnieren-Muster.
  • Services: Diensten nutzen ein Anfrage-Antwort-Muster für eine synchrone und bidirektionale Kommunikation. Dienste werden oft durch das \texttt{rosservice}-Werkzeug erstellt und manipuliert.
  • Actions: Actions sind für langlaufende Prozesse entworfen, die Zwischenergebnisse liefern müssen. Sie nutzen das Zielgerichtete Aufträge-Modell und implementieren Rückmeldungsmechanismen. Enthalten sind typischerweise der \texttt{SimpleActionClient} und der \texttt{SimpleActionServer}.

a)

Erläutere, wie Du in einem ROS-Projekt einen Publisher und einen Subscriber mithilfe von Topics implementierst. Vergiss nicht, auch relevante Code-Snippets aufzulisten.

Lösung:

Um in einem ROS-Projekt einen Publisher und einen Subscriber mithilfe von Topics zu implementieren, musst Du die folgenden Schritte beachten:

  • Erstellen eines Publishers: Ein Publisher sendet Nachrichten zu einem bestimmten Topic. Hier ist ein Beispiel, wie das in Python gemacht wird:
#!/usr/bin/env python3import rospyfrom std_msgs.msg import String# Initialize the ROS noderospy.init_node('my_publisher')# Create a Publisher object that will publish messages to the 'chatter' topicpub = rospy.Publisher('chatter', String, queue_size=10)rate = rospy.Rate(1) # 1 Hzwhile not rospy.is_shutdown():    msg = 'Hello, ROS!'    rospy.loginfo(f'Publishing: {msg}')    pub.publish(msg)    rate.sleep()if __name__ == '__main__':    try:        talker()    except rospy.ROSInterruptException:        pass
  • In diesem Beispiel erstellt der Publisher Nachrichten vom Typ String und veröffentlicht sie auf dem Topic chatter. Mit rospy.Rate(1) wird festgelegt, dass der Publisher einmal pro Sekunde eine Nachricht sendet.
  • Erstellen eines Subscribers: Ein Subscriber empfängt Nachrichten von einem bestimmten Topic. Hier ist ein Beispiel, wie das in Python gemacht wird:
#!/usr/bin/env python3import rospyfrom std_msgs.msg import String# Callback function that gets called when a new message is receiveddef callback(data):    rospy.loginfo(f'I heard: {data.data}')# Initialize the ROS noderospy.init_node('my_subscriber')# Create a Subscriber object that will listen to the 'chatter' topicsub = rospy.Subscriber('chatter', String, callback)rospy.spin()if __name__ == '__main__':    try:        listener()    except rospy.ROSInterruptException:        pass
  • In diesem Beispiel erstellt der Subscriber einen Listener, der Nachrichten vom Typ String auf dem Topic chatter empfängt. Bei jedem Empfang einer Nachricht ruft er die Callback-Funktion callback auf.

Zusammenfassend definierst Du den Publisher und den Subscriber zunächst mithilfe der Methoden rospy.Publisher und rospy.Subscriber. Sie kommunizieren über ein gemeinsames Topic, in diesem Fall chatter. Der abonniert und die erhält Nachrichten asynchron, was die Leistungsstärke der ROS Topics ausmacht.

b)

Erkläre den Unterschied zwischen Services und Actions in ROS. Nenne dabei zwei spezifische Anwendungsfälle, in denen es sinnvoll ist, jeweils den einen oder den anderen Mechanismus zu verwenden.

Lösung:

In einem ROS-Projekt gibt es zwei wesentliche Kommunikationsmechanismen, die für spezifische Anforderungen verwendet werden können: Services und Actions. Hier sind die Hauptunterschiede sowie typische Anwendungsfälle für beide Mechanismen:

  • Services:Services verwenden ein Anfrage-Antwort-Muster, das durch eine synchrone und bidirektionale Kommunikation gekennzeichnet ist. Wenn ein Knoten einen Service aufruft, sendet er eine Anfrage und wartet, bis er eine Antwort erhält, bevor er fortfährt. Dies ist nützlich für Aufgaben, die sofortige Ergebnisse erfordern und bei denen keine Zwischenstatusberichte notwendig sind.
  • Anwendungsfall für Services:
    • Datenabfrage: Ein Beispiel wäre, wenn ein Knoten aktuelle Sensordaten benötigt. Er könnte einen Service aufrufen, der die aktuellen Daten vom Sensor zurückgibt.
    • Einfaches Auslösen von Aktionen: Wenn Du eine Aktion wie das Öffnen oder Schließen eines Roboterarms ausführen möchtest, könnte ein Service verwendet werden, um dies auszulösen, da die Aktion selbst kurz ist und keine Zwischenberichte oder lange Laufzeiten erfordert.
  • Actions:Actions sind für langlaufende Prozesse ausgelegt, bei denen Zwischenergebnisse oder Feedback notwendig sind. Actions verwenden das Zielgerichtete Aufträge-Modell und liefern Feedback während des gesamten Prozesses. Dies ist besonders nützlich für komplexe Aufgaben, die über längere Zeiträume laufen und deren Fortschritt überwacht werden muss.
  • Anwendungsfall für Actions:
    • Navigation: Wenn ein Roboter eine Navigation zu einem Zielpunkt durchführt, kann eine Action verwendet werden, um den Navigationsprozess zu starten. Während der Roboter navigiert, können Zwischenupdates gesendet werden, um den Fortschritt zu überwachen und den Prozess anzupassen.
    • Bildverarbeitung: Bei Aufgaben wie der Bilderkennung oder dem Verarbeiten großer Bilddaten kann eine Action verwendet werden, um den Prozess zu starten. Regelmäßiges Feedback kann dazu verwendet werden, den Fortschritt anzuzeigen und eventuell erforderliche Anpassungen vorzunehmen.

Zusammenfassend lässt sich sagen:

  • Services: Verwende Services für kurzfristige, einmalige Aufgaben, die sofortige Ergebnisse ohne Zwischenfeedback erfordern.
  • Actions: Verwende Actions für langfristige Aufgaben, bei denen Zwischenberichte und Feedback erforderlich sind, um den Fortschritt zu überwachen und wenn nötig, anzupassen.

c)

Gegeben sei ein Szenario, in dem ein unbemanntes Fahrzeug einen weiten Entfernungspfad absuchen soll. Der Pfad muss dynamisch während der Ausführung geändert werden können, und das Fahrzeug soll Statusupdates über dessen Fortschritt und eventuelle Hindernisse übermitteln. Definiere einen ROS-Action-Client und -Server, um diese Anforderungen zu erfüllen. Implementiere die entsprechenden Python-Code-Snippets.

Lösung:

Für das gegebene Szenario, in dem ein unbemanntes Fahrzeug einen weiten Entfernungspfad absuchen soll und dynamische Updates gesendet werden müssen, eignet sich der ROS-Action-Mechanismus hervorragend. Dieser ermöglicht es, kontinuierliche Statusupdates und Fortschrittsmeldungen zu senden, während das Fahrzeug den Pfad absucht.

  • Definieren des ROS-Action-Servers: Der Action-Server ist für den eigentlichen Pfadsuchalgorithmus und die Handhabung von Statusupdates verantwortlich. Unten findest Du ein Beispiel für die Implementierung eines einfachen Action-Servers in Python:
#!/usr/bin/env python3import rospyimport actionlibfrom robotics_msgs.msg import SearchPathAction, SearchPathFeedback, SearchPathResultclass SearchPathActionServer:    def __init__(self):        self.server = actionlib.SimpleActionServer('search_path', SearchPathAction, self.execute, False)        self.server.start()    def execute(self, goal):        feedback = SearchPathFeedback()        result = SearchPathResult()        success = True        r = rospy.Rate(1) # 1 Hz        rospy.loginfo('Search Path Action: Executing path...')        for i in range(1, 11):            # Simulate path searching and obstacle detection            if self.server.is_preempt_requested():                rospy.loginfo('Search Path Action: Preempted')                self.server.set_preempted()                success = False                break            feedback.current_status = f'Searching... {i*10}% complete'            self.server.publish_feedback(feedback)            rospy.loginfo(feedback.current_status)            r.sleep()        if success:            result.final_status = 'Path successfully searched!'            rospy.loginfo('Search Path Action: Succeeded')            self.server.set_succeeded(result)if __name__ == '__main__':    rospy.init_node('search_path_server')    server = SearchPathActionServer()    rospy.spin()
  • Definieren des ROS-Action-Clients: Der Action-Client sendet die Suchanfragen an den Server und überwacht den Fortschritt. Unten findest Du ein Beispiel in Python:
#!/usr/bin/env python3import rospyimport actionlibfrom robotics_msgs.msg import SearchPathAction, SearchPathGoaldef feedback_cb(feedback):    rospy.loginfo(f'Feedback received: {feedback.current_status}')def active_cb():    rospy.loginfo('Search Path Action: Goal just went active')def done_cb(state, result):    if state == actionlib.GoalStatus.SUCCEEDED:        rospy.loginfo(f'Search Path Action: Finished with result: {result.final_status}')    else:        rospy.loginfo('Search Path Action: Failed with state: {state}')if __name__ == '__main__':    rospy.init_node('search_path_client')    client = actionlib.SimpleActionClient('search_path', SearchPathAction)    client.wait_for_server()    goal = SearchPathGoal()    rospy.loginfo('Sending Search Path Goal')    client.send_goal(goal, done_cb, active_cb, feedback_cb)    client.wait_for_result()

Die Python-Code-Snippets veranschaulichen:

  • Der Action-Server (SearchPathActionServer) führt die Pfadsuche aus und sendet Feedback an den Client. Es ist konfiguriert, um den Fortschritt alle zehn Sekunden zu melden und auf Anfragen zur Beendigung der Aktion zu reagieren.
  • Der Action-Client sendet ein Ziel an den Server und wartet auf dessen Ergebnisse. Dabei werden Feedback-Updates und Statusmeldungen empfangen, die zur Laufzeit verarbeitet und protokolliert werden.

Um die Action-Nachrichtentypen zu definieren, musst Du eine Datei namens SearchPath.action im Verzeichnis action des entsprechenden Pakets erstellen. Eine einfache Definition könnte folgendermaßen aussehen:

# Goal definestandard_goal_request# Feedbackmsg current_status# Resultmsg final_status

Diese Komponenten stellen die Grundlage für eine robuste Vorwärts- und Rückwärtskommunikation zwischen Knoten in ROS-Umgebungen dar und ermöglichen flexible Reaktionsmöglichkeiten auf dynamische Änderungen während der Ausführung des Pfadsuchprozesses.

Aufgabe 2)

Betrachten wir eine zweidimensionale Karte zur Bewegungsplanung eines robotischen Systems. Die Karte besteht aus Freiräumen, die ohne Hindernisse sind, und Hindernissen, die nicht durchdrungen werden können. Gegeben ist ein Startpunkt S und ein Zielpunkt G. Der Roboter muss mittels A*, RRT und RRT* Algorithmen den optimalen Pfad von S nach G finden.

a)

Teilaufgabe 1: Implementiere den A* Algorithmus zur Pfadplanung auf der gegebenen Karte. Verwende eine geeignete Heuristikfunktion für den zweidimensionalen Raum. Erkläre die Kostenfunktion, die Du verwendest, und zeige anhand eines Beispiels, wie der Algorithmus funktioniert.

Lösung:

Teilaufgabe 1: Implementiere den A* Algorithmus zur Pfadplanung auf der gegebenen Karte. Verwende eine geeignete Heuristikfunktion für den zweidimensionalen Raum. Erkläre die Kostenfunktion, die Du verwendest, und zeige anhand eines Beispiels, wie der Algorithmus funktioniert.

Implementierung des A* Algorithmus

Der A* Algorithmus ist ein bewährter Algorithmus zur Pfadplanung, der die Vorteile von Uniform-Cost Search und Greedy Best-First Search kombiniert. Er verwendet eine Kostenfunktion, die aus zwei Teilen besteht: den tatsächlichen Kosten vom Startknoten zum aktuellen Knoten und einer Heuristik, die die geschätzten Kosten vom aktuellen Knoten zum Zielknoten angibt.

  • g(n): Die tatsächlichen Kosten, um vom Startknoten S zum aktuellen Knoten n zu gelangen.
  • h(n): Eine Heuristikfunktion, die die geschätzten Kosten vom aktuellen Knoten n zum Zielknoten G angibt. Für einen zweidimensionalen Raum ist die euklidische Distanz oft eine geeignete Heuristik:

\(h(n) = \sqrt{(x_{n} - x_{G})^2 + (y_{n} - y_{G})^2}\) mit \((x_{n}, y_{n})\) -- Koordinaten des aktuellen Knotens \((x_{G}, y_{G})\) -- Koordinaten des Zielknotens

Die kombinierte Kostenfunktion lautet:

\(f(n) = g(n) + h(n)\)

Implementierung des A* Algorithmus in Python:

import heapqimport mathclass AStar:    def __init__(self, start, goal, grid):        self.start = start        self.goal = goal        self.grid = grid        self.open_list = []        self.closed_list = set()        self.came_from = {}        self.g_score = {start: 0}        self.f_score = {start: self.heuristic(start, goal)}        heapq.heappush(self.open_list, (self.f_score[start], start))    def heuristic(self, a, b):        return math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)    def neighbors(self, node):        neighbor_nodes = []        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]        for direction in directions:            neighbor = (node[0] + direction[0], node[1] + direction[1])            if 0 <= neighbor[0] < len(self.grid) and 0 <= neighbor[1] < len(self.grid[0]) and self.grid[neighbor[0]][neighbor[1]] == 0:                neighbor_nodes.append(neighbor)        return neighbor_nodes    def reconstruct_path(self, current_node):        path = []        while current_node in self.came_from:            path.append(current_node)            current_node = self.came_from[current_node]        path.append(self.start)        return path[::-1]    def search(self):        while self.open_list:            _, current = heapq.heappop(self.open_list)            if current == self.goal:                return self.reconstruct_path(current)            self.closed_list.add(current)            for neighbor in self.neighbors(current):                if neighbor in self.closed_list:                    continue                tentative_g_score = self.g_score[current] + 1                if neighbor not in self.g_score or tentative_g_score < self.g_score[neighbor]:                    self.came_from[neighbor] = current                    self.g_score[neighbor] = tentative_g_score                    self.f_score[neighbor] = self.g_score[neighbor] + self.heuristic(neighbor, self.goal)                    if neighbor not in [i[1] for i in self.open_list]:                        heapq.heappush(self.open_list, (self.f_score[neighbor], neighbor))        return None# Beispiel für die Anwendungstart = (0, 0)goal = (3, 3)grid = [    [0, 0, 1, 0],    [0, 0, 1, 0],    [0, 0, 0, 0],    [1, 1, 0, 0]]astar = AStar(start, goal, grid)path = astar.search()print(path)

Erklärung des Beispiels:

  • Startpunkt: (0, 0)
  • Zielpunkt: (3, 3)
  • Karte: Ein 4x4 Raster mit einigen Hindernissen (1 bedeutet, dass auf dieser Kachel ein Hindernis ist)

Der Algorithmus durchläuft die Knoten, bewertet ihre Kosten und baut schließlich den optimalen Pfad vom Start zum Ziel auf.

b)

Teilaufgabe 2: Implementiere den RRT Algorithmus zur Pfadplanung für dieselbe Karte. Zeige den Schritt-für-Schritt-Wachstumsprozess des Baums und beschreibe, wie zufällige Punkte zu einer effektiven Baumexpansion beitragen. Diskutiere die Herausforderungen, die mit der Verwendung von RRT in hochdimensionalen Räumen verbunden sind.

Lösung:

Teilaufgabe 2: Implementiere den RRT Algorithmus zur Pfadplanung für dieselbe Karte. Zeige den Schritt-für-Schritt-Wachstumsprozess des Baums und beschreibe, wie zufällige Punkte zu einer effektiven Baumexpansion beitragen. Diskutiere die Herausforderungen, die mit der Verwendung von RRT in hochdimensionalen Räumen verbunden sind.

Implementierung des RRT Algorithmus

Der Rapidly-Exploring Random Tree (RRT) Algorithmus ist ein algorithmisches Verfahren zur Pfadplanung, das auf einer zufälligen Probennahme im Konfigurationsraum basiert. Es wächst einen Baum von der Startkonfiguration aus durch schrittweise Erweiterung in die Richtung zufällig gezogener Punkte.

Schritt-für-Schritt-Wachstumsprozess:

  1. Startknoten: Beginne mit einem Baum, der nur den Startknoten S enthält.
  2. Zufällige Punktprobe: Wähle einen zufälligen Punkt im Konfigurationsraum aus.
  3. Nachbar des Baums finden: Finde den nächstgelegenen Knoten im Baum zu diesem zufälligen Punkt.
  4. Knoten erweitern: Erweitere den Baum vom nächstgelegenen Knoten ausgehend in Richtung des zufälligen Punkts. Wenn das Hindernisfrei ist, füge den neuen Knoten und die Verbindung zum Baum hinzu.
  5. Zielüberprüfung: Wiederhole den Vorgang, bis der Baum das Ziel G erreicht hat.

Python Implementierung

import randomimport mathclass RRT:    def __init__(self, start, goal, grid, max_iter=1000):        self.start = start        self.goal = goal        self.grid = grid        self.max_iter = max_iter        self.tree = {start: None}        self.path = []    def get_random_point(self):        x = random.randint(0, len(self.grid) - 1)        y = random.randint(0, len(self.grid[0]) - 1)        return (x, y)    def is_free(self, point):        return self.grid[point[0]][point[1]] == 0    def distance(self, p1, p2):        return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)    def get_nearest(self, point):        nearest_point = None        min_dist = float('inf')        for node in self.tree:            dist = self.distance(node, point)            if dist < min_dist:                min_dist = dist                nearest_point = node        return nearest_point    def step_towards(self, nearest, random_point, step_size=1):        theta = math.atan2(random_point[1] - nearest[1], random_point[0] - nearest[0])        new_point = (int(nearest[0] + step_size * math.cos(theta)), int(nearest[1] + step_size * math.sin(theta)))        if self.is_free(new_point) and new_point not in self.tree:            return new_point        return None    def build_tree(self):        for _ in range(self.max_iter):            random_point = self.get_random_point()            nearest = self.get_nearest(random_point)            new_point = self.step_towards(nearest, random_point)            if new_point:                self.tree[new_point] = nearest                if new_point == self.goal:                    self.path = self.reconstruct_path(new_point)                    return self.path        return None    def reconstruct_path(self, end):        path = [end]        while self.tree[end]:            end = self.tree[end]            path.append(end)        path.reverse()        return path# Beispiel für die Anwendungstart = (0, 0)goal = (3, 3)grid = [    [0, 0, 1, 0],    [0, 0, 1, 0],    [0, 0, 0, 0],    [1, 1, 0, 0]]rrt = RRT(start, goal, grid)path = rrt.build_tree()print(path)

Erklärung des Beispiels:

  • Startpunkt: (0, 0)
  • Zielpunkt: (3, 3)
  • Karte: Ein 4x4 Raster mit einigen Hindernissen (1 bedeutet, dass auf dieser Kachel ein Hindernis ist)

Der Algorithmus wächst einen Baum von Startknoten aus durch zufällige Probennahme und schrittweise Erweiterung. Dabei werden Hindernisse berücksichtigt, um eine effektive Baumexpansion zu ermöglichen.

Herausforderungen bei der Verwendung von RRT in hochdimensionalen Räumen:

  • Komplexität: Die Anzahl der Knoten im Baum kann exponentiell mit der Anzahl der Dimensionen zunehmen, was zu einer hohen Rechenkomplexität führt.
  • Effizienz: In hochdimensionalen Räumen kann die Wahrscheinlichkeit, geeignete Wege zu finden, verringert sein. Die zufällige Probennahme kann ineffektiv werden.
  • Kollisionserkennung: Die Kollisionserkennung kann in hochdimensionalen Räumen aufwändiger und rechnerisch intensiver sein.

c)

Teilaufgabe 3: Implementiere den RRT* Algorithmus. Beschreibe detailliert, wie der RRT*-Algorithmus die Pfadoptimierung im Vergleich zum normalen RRT verwirklicht. Führe eine mathematische Analyse der Kostenfunktion durch und erkläre, warum RRT* als asymptotisch optimal gilt.

Lösung:

Teilaufgabe 3: Implementiere den RRT* Algorithmus. Beschreibe detailliert, wie der RRT*-Algorithmus die Pfadoptimierung im Vergleich zum normalen RRT verwirklicht. Führe eine mathematische Analyse der Kostenfunktion durch und erkläre, warum RRT* als asymptotisch optimal gilt.

Implementierung des RRT* Algorithmus

Der RRT* Algorithmus ist eine Erweiterung des RRT (Rapidly-Exploring Random Tree) Algorithmus. Während RRT einen zufälligen Baum generiert, um einen Pfad zu finden, optimiert RRT* diesen Pfad weiter, um die Gesamtkosten zu minimieren. Der Hauptunterschied zwischen RRT und RRT* liegt darin, dass RRT* bei jeder Erweiterung des Baums den neuen Knoten und dessen Nachbarn überprüft und den Pfad so optimiert, dass die Kosten minimiert werden.

Schritte zur Optimierung in RRT*:

  • Zufällige Punktprobe: Wähle einen zufälligen Punkt im Konfigurationsraum aus.
  • Nachbar des Baums finden: Finde den nächstgelegenen Knoten im Baum zu diesem zufälligen Punkt.
  • Knoten erweitern: Erweitere den Baum vom nächstgelegenen Knoten ausgehend in Richtung des zufälligen Punkts. Wenn das hindernisfrei ist, füge den neuen Knoten und die Verbindung zum Baum hinzu.
  • Nachbarnprüfung: Bestimme in einem bestimmten Umkreis die Nachbarknoten des neuen Knotens.
  • Pfadrekonfiguration: Berechne die Kosten von allen möglichen Verbindungen zu den Nachbarknoten und wähle diejenige mit den geringsten Kosten. Aktualisiere gegebenenfalls die Elternknoten.

Mathematische Analyse der Kostenfunktion:

Die Kostenfunktion im RRT* Algorithmus optimiert wie folgt:

  • Kostenfunktion:\(C(x)\) gibt die Kosten des Pfades vom Startknoten \(S\) zu einem Knoten \(x\) an. Es ist die Summe aller Übergangskosten:\[ \text{Kosten}(Pfad) = \sum \text{Übergangskosten} \]
  • Heuristik: Die zugrunde liegende Idee ist, diese Kostenfunktion zu minimieren:\[ \text{C}_{\text{opt}}(x) \rightarrow \text{kleinste mögliche Kosten} \]

Implementierung des RRT* Algorithmus in Python:

import randomimport mathclass RRTStar:    def __init__(self, start, goal, grid, max_iter=1000, r=1.5):        self.start = start        self.goal = goal        self.grid = grid        self.max_iter = max_iter        self.r = r        self.tree = {start: None}        self.costs = {start: 0}        self.path = []    def get_random_point(self):        x = random.randint(0, len(self.grid) - 1)        y = random.randint(0, len(self.grid[0]) - 1)        return (x, y)    def is_free(self, point):        return self.grid[point[0]][point[1]] == 0    def distance(self, p1, p2):        return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)    def get_nearest(self, point):        nearest_point = None        min_dist = float('inf')        for node in self.tree:            dist = self.distance(node, point)            if dist < min_dist:                min_dist = dist                nearest_point = node        return nearest_point    def get_neighbors(self, new_point):        neighbors = []        for node in self.tree:            if self.distance(node, new_point) <= self.r:                neighbors.append(node)        return neighbors    def step_towards(self, nearest, random_point, step_size=1):        theta = math.atan2(random_point[1] - nearest[1], random_point[0] - nearest[0])        new_point = (int(nearest[0] + step_size * math.cos(theta)), int(nearest[1] + step_size * math.sin(theta)))        if self.is_free(new_point) and new_point not in self.tree:            return new_point        return None    def rewire(self, new_point, neighbors):        min_cost = self.costs[self.tree[new_point]] + self.distance(new_point, self.tree[new_point])        min_node = self.tree[new_point]        for neighbor in neighbors:            cost = self.costs[neighbor] + self.distance(new_point, neighbor)            if cost < min_cost:                min_cost = cost                min_node = neighbor        self.tree[new_point] = min_node        self.costs[new_point] = min_cost    def build_tree(self):        for _ in range(self.max_iter):            random_point = self.get_random_point()            nearest = self.get_nearest(random_point)            new_point = self.step_towards(nearest, random_point)            if new_point:                self.tree[new_point] = nearest                self.costs[new_point] = self.costs[nearest] + self.distance(new_point, nearest)                if self.distance(new_point, self.goal) <= self.r:                    self.tree[self.goal] = new_point                    self.costs[self.goal] = self.costs[new_point] + self.distance(new_point, self.goal)                    self.path = self.reconstruct_path(self.goal)                    return self.path                neighbors = self.get_neighbors(new_point)                self.rewire(new_point, neighbors)        return None    def reconstruct_path(self, end):        path = [end]        while self.tree[end]:            end = self.tree[end]            path.append(end)        path.reverse()        return path# Beispiel für die Anwendungstart = (0, 0)goal = (3, 3)grid = [    [0, 0, 1, 0],    [0, 0, 1, 0],    [0, 0, 0, 0],    [1, 1, 0, 0]]rrt_star = RRTStar(start, goal, grid)path = rrt_star.build_tree()print(path)

Erklärung des Beispiels:

  • Startpunkt: (0, 0)
  • Zielpunkt: (3, 3)
  • Karte: Ein 4x4 Raster mit einigen Hindernissen (1 bedeutet, dass auf dieser Kachel ein Hindernis ist)

Der Algorithmus wächst seinen Baum schrittweise von einem zufällig gewählten Punkt aus, überprüft und optimiert den Pfad, indem er Nachbarknoten berücksichtigt. Dies führt zu einem asymptotisch optimalen Pfad.

Warum RRT* als asymptotisch optimal gilt:

  • Minimierung der Kosten: RRT* minimiert die Gesamtkosten durch periodische Überprüfung und Umverteilung der Knoten.
  • Vergleich und Optimierung: Es überprüft nicht nur die neue Knotenverbindung, sondern auch die Verbindungen zu den Nachbarknoten und wählt denjenigen Weg, der die geringsten Kosten verursacht.
  • Asymptotische Optimalität: Durch ständige Berücksichtigung und Minimierung der Kosten, nähert sich der Pfad asymptotisch der optimalen Lösung an, wenn die Anzahl der Iterationen unendlich groß wird.

Aufgabe 3)

Sensorintegration in Robotiksystemen: In diesem Szenario arbeitest Du an der Integration und Kalibration von Sensoren in einem mobilen Roboter. Ziel ist es, die Genauigkeit und Zuverlässigkeit der Sensoren durch geschickte Datenverarbeitung und Kalibration zu verbessern. Du hast Zugang zu verschiedenen Sensoren, darunter Lidar, IMU (Inertial Measurement Unit) und GPS. Deine Aufgabe ist es, Sensoren so zu integrieren und zu kalibrieren, dass der Roboter präzise navigieren kann. Fokusbereiche: Zentrale und verteilte Integration, Datenfilterung und -fusion, initiale und rekursive Kalibration.

a)

Erkläre den Unterschied zwischen zentraler und verteilter Integration von Sensordaten. Überlege, welche Methode für ein selbstfahrendes Auto in einer städtischen Umgebung besser geeignet ist und begründe deine Antwort.

Lösung:

Unterschied zwischen zentraler und verteilter Integration von Sensordaten:

  • Zentrale Integration: Bei der zentralen Integration werden alle Sensordaten an eine zentrale Steuereinheit gesendet, die die Daten verarbeitet, filtert und die notwendigen Entscheidungen trifft. Diese Methode ermöglicht eine umfassende Übersicht und eine konsistente Entscheidungsfindung, da alle Daten an einem Ort zusammenkommen. Ein Nachteil könnte jedoch die Belastung der zentralen Einheit und mögliche Verzögerungen bei der Datenverarbeitung sein.
  • Verteilte Integration: Bei der verteilten Integration werden Sensordaten lokal an den jeweiligen Sensoreinheiten oder an mehreren dezentralen Knoten vorverarbeitet. Diese lokal vorverarbeiteten Daten werden dann entweder aggregiert oder separat weiterverwendet für spezifische Aufgaben. Dies führt zu einer schnelleren Datenverarbeitung und reduzierten Latenzzeiten, kann aber auch zu inkonsistenten oder widersprüchlichen Entscheidungen führen, wenn die Daten nicht ausreichend synchronisiert werden.
Welche Methode ist für ein selbstfahrendes Auto in einer städtischen Umgebung besser geeignet?Für ein selbstfahrendes Auto in einer städtischen Umgebung ist die verteilte Integration besser geeignet. Dies liegt an mehreren Faktoren:
  • Schnelligkeit und Reaktionszeit: Die städtische Umgebung erfordert schnelle Reaktionen auf dynamische Veränderungen wie plötzliche Hindernisse, Fußgänger oder andere Fahrzeuge. Verteilte Integration ermöglicht eine schnellere Datenverarbeitung, da die Daten lokal vorverarbeitet werden.
  • Skalierbarkeit: Die verteilte Integration erleichtert das Hinzufügen neuer Sensoren oder Einheiten ohne die Notwendigkeit, die zentrale Einheit maßgeblich zu überarbeiten oder zu belasten.
  • Reduzierte Latenz: Durch die lokale Datenverarbeitung werden Verzögerungen minimiert, was besonders im urbanen Verkehr von entscheidender Bedeutung ist.
  • Zuverlässigkeit: Durch die dezentrale Verarbeitung gibt es keinen einzigen Fehlerpunkt. Wenn ein Teil des Systems ausfällt, können andere Knoten weiterhin korrekt funktionieren.
Zusammenfassend lässt sich sagen, dass die verteilte Integration aufgrund der höheren Flexibilität, schnelleren Reaktionszeiten und besseren Skalierbarkeit für ein selbstfahrendes Auto in einer städtischen Umgebung besser geeignet ist.

b)

Du hast einen IMU-Sensor, der stark verrauschte Daten produziert. Beschreibe, wie Du den Kalman-Filter einsetzen würdest, um die Daten zu filtern. Formuliere die entsprechenden Zustands- und Messgleichungen und beschreibe den Ablauf des Filterungsprozesses.

Lösung:

IMU-Sensor und Kalman-Filter:Ein IMU-Sensor (Inertial Measurement Unit) liefert Informationen über die Beschleunigung und Drehrate eines mobilen Roboters, aber diese Daten sind oft verrauscht. Der Kalman-Filter kann helfen, diese Daten zu glätten und präzise Informationen zu extrahieren. Hier ist, wie Du den Kalman-Filter einsetzen könntest.Zustands- und Messgleichungen:1. **Zustandsgleichung**: Diese beschreibt die zeitliche Entwicklung des Zustandsvektors. Angenommen, der Zustand umfasst die Position und Geschwindigkeit des Roboters, kann die Zustandsgleichung wie folgt geschrieben werden:

\textbf{x}_{k} = \textbf{A} \textbf{x}_{k-1} + \textbf{B} \textbf{u}_{k-1} + \textbf{w}_{k-1}
Dabei ist:
  • \(\textbf{x}_{k}\) der Zustandsvektor zum Zeitpunkt \(k\), der die Position und Geschwindigkeit umfasst.
  • \(\textbf{A}\) die Zustandsübergangsmatrix. Diese beschreibt, wie sich der Zustand in Abhängigkeit vom vorherigen Zustand entwickelt.
  • \(\textbf{u}_{k}\) der Steuervektor (bspw. die gemessene Beschleunigung).
  • \(\textbf{B}\) die Steuerübergangsmatrix. Diese skaliert den Einfluss des Steuervektors \(\textbf{u}_{k}\) auf den Zustand.
  • \(\textbf{w}_{k}\) der Prozessrauschvektor. Dieser stellt Unsicherheiten im Modell dar.
2. **Messgleichung**: Diese beschreibt, wie die beobachteten Messungen \(\textbf{z}\) aus dem Zustand \(\textbf{x}\) resultieren:
\textbf{z}_{k} = \textbf{H} \textbf{x}_{k} + \textbf{v}_{k}
Dabei ist:
  • \(\textbf{z}_{k}\) der Messvektor zum Zeitpunkt \(k\) (bspw. die gemessenen Beschleunigungswerte).
  • \(\textbf{H}\) die Beobachtungsmatrix, die den Zusammenhang zwischen dem Zustand und der Messung beschreibt.
  • \(\textbf{v}_{k}\) der Messrauschvektor. Dieser stellt das Messrauschen dar.
Ablauf des Filterungsprozesses:Der Kalman-Filter-Prozess besteht aus zwei Hauptschritten, die wiederholt ausgeführt werden: Vorhersage und Aktualisierung.
  1. Vorhersageschritt: In diesem Schritt nimmt der Kalman-Filter basierend auf dem vorherigen Zustand eine Schätzung des neuen Zustands und der Unsicherheit vor.
    • Vorhersage des Zustands:
      \textbf{x}_{k|k-1} = \textbf{A} \textbf{x}_{k-1|k-1} + \textbf{B} \textbf{u}_{k-1}
    • Vorhersage der Kovarianzmatrix:
      \textbf{P}_{k|k-1} = \textbf{A} \textbf{P}_{k-1|k-1} \textbf{A}^T + \textbf{Q}
    Dabei ist:
    • \(\textbf{P}_{k|k-1}\) die vorhergesagte Kovarianzmatrix.
    • \(\textbf{Q}\) die Prozessrauschkovarianzmatrix.
  2. Aktualisierungsschritt: In diesem Schritt korrigiert der Kalman-Filter die Schätzung basierend auf den neuen Messdaten.
    • Kalman-Gewinn berechnen:
      \textbf{K}_{k} = \textbf{P}_{k|k-1} \textbf{H}^T (\textbf{H} \textbf{P}_{k|k-1} \textbf{H}^T + \textbf{R})^{-1}
    • Zustandsaktualisierung:
      \textbf{x}_{k|k} = \textbf{x}_{k|k-1} + \textbf{K}_{k} (\textbf{z}_{k} - \textbf{H} \textbf{x}_{k|k-1})
    • Kovarianzmatrixaktualisierung:
      \textbf{P}_{k|k} = (\textbf{I} - \textbf{K}_{k} \textbf{H}) \textbf{P}_{k|k-1}
    Dabei ist:
    • \(\textbf{K}_{k}\) die Kalman-Gewinnmatrix.
    • \(\textbf{R}\) die Messrauschkovarianzmatrix.
    • \(\textbf{I}\) die Identitätsmatrix.
Zusammenfassend:Durch die Anwendung des Kalman-Filters auf die verrauschten IMU-Daten können präzise Schätzungen für die Position, Geschwindigkeit und Beschleunigung des mobilen Roboters erreicht werden. Der Kalman-Filter minimiert Störungen und Rauschen in den Daten, indem er kontinuierlich die Zustände vorhersagt und anhand der Messungen aktualisiert.

c)

Angenommen, Dein Roboter verwendet eine Kombination aus Lidar, IMU und GPS-Daten zur Navigation. Erkläre, wie die erweiterten Kalman-Filter (EKF) zur Datenfusion in diesem Kontext verwendet werden können. Gehe dabei konkret auf die Vorhersage- und Update-Schritte ein.

Lösung:

Erweiterte Kalman-Filter (EKF) zur Datenfusion von Lidar, IMU und GPS:In der Robotik ist die präzise und zuverlässige Navigation entscheidend. Wenn der Roboter Lidar, IMU und GPS-Daten kombiniert, kann der erweiterte Kalman-Filter (EKF) verwendet werden, um die Daten dieser verschiedenen Sensoren zu fusionieren. Der EKF ist eine Erweiterung des klassischen Kalman-Filters und eignet sich besonders gut für nicht-lineare Systeme, die typisch für viele Robotikanwendungen sind.Grundprinzip des EKF:Der erweiterte Kalman-Filter erweitert das lineare Modell des Kalman-Filters, indem er nicht-lineare Zustands- und Messmodelle verwendet. Die nicht-linearen Modelle werden durch Taylor-Entwicklung erster Ordnung linearisiert. Dadurch kann der EKF auch Systeme mit nicht-linearen Dynamiken und Beobachtungen handhaben.Zustandsvector:Für unseren Roboter können wir den Zustandsvektor \( \textbf{x} \) wie folgt definieren:

\textbf{x} = \begin{pmatrix} x \ y \ \theta \ \text{v_x} \ \text{v_y} \ \text{v_\theta} \end{pmatrix}
Hierbei stehen die Variablen für:
  • \(x, y\): Position des Roboters.
  • \(\theta\): Orientierung des Roboters.
  • \(v_x, v_y\): Lineargeschwindigkeit des Roboters in x- und y-Richtung.
  • \(v_\theta\): Winkelgeschwindigkeit (Drehrate).
Vorhersageschritt (Prediction Step):Im Vorhersageschritt werden die Zustände und ihre Unsicherheiten basierend auf dem Modell und den Steuerbefehlen (z.B. vom IMU) vorhergesagt.
  • Zustandsvorhersage:
    \textbf{x}_{k|k-1} = f(\textbf{x}_{k-1}, \textbf{u}_{k-1})
    Hier ist \( f \) eine nicht-lineare Funktion, die die Bewegungsdynamik des Roboters darstellt.
  • Kovarianzvorhersage:
    \textbf{P}_{k|k-1} = \textbf{F}_{k-1} \textbf{P}_{k-1|k-1} \textbf{F}_{k-1}^T + \textbf{Q}
    \(\textbf{F}_{k-1}\) ist die Jacobimatrix von \( f \) bezüglich \( \textbf{x} \). \( \textbf{Q} \) ist die Prozessrauschkovarianzmatrix.
Update-Schritt (Update Step):Im Update-Schritt werden die vorhergesagten Zustände mit den neuen Sensordaten (z.B. von Lidar und GPS) aktualisiert.
  • Kalman-Gewinn berechnen:
    \textbf{K}_{k} = \textbf{P}_{k|k-1} \textbf{H}_{k}^T (\textbf{H}_{k} \textbf{P}_{k|k-1} \textbf{H}_{k}^T + \textbf{R})^{-1}
    \(\textbf{H}_{k}\) ist die Jacobimatrix der Beobachtungsfunktion \( h \) bezüglich \( \textbf{x} \). \( \textbf{R} \) ist die Messrauschkovarianzmatrix.
  • Zustandsaktualisierung:
    \textbf{x}_{k|k} = \textbf{x}_{k|k-1} + \textbf{K}_{k} (\textbf{z}_{k} - h(\textbf{x}_{k|k-1}))
    \( h(\textbf{x}) \) ist die nicht-lineare Funktion, die die Messungen beschreibt.
  • Kovarianzaktualisierung:
    \textbf{P}_{k|k} = (\textbf{I} - \textbf{K}_{k} \textbf{H}_{k}) \textbf{P}_{k|k-1}
Zusammenfassend:Mit dem EKF können die Daten von Lidar-, IMU- und GPS-Sensoren effizient fusioniert werden, um eine präzise und zuverlässige Schätzung der Position, Geschwindigkeit und Orientierung des Roboters zu erhalten. Der EKF führt dies durch kontinuierliche Vorhersage und Aktualisierung der Zustände, wobei er das Rauschen und Unsicherheiten in den Sensordaten berücksichtigt.

d)

Beschreibe den Prozess der initialen Kalibration eines Lidar-Sensors und den Bedarf einer Rekalibration während des Betriebs. Diskutiere mögliche Ursachen für die Drift des Sensors und wie die Rekalibration zur Driftkorrektur beiträgt. Welche mathematischen Modelle könnten hierbei angewendet werden?

Lösung:

Initiale Kalibration eines Lidar-Sensors:Die initiale Kalibration eines Lidar-Sensors ist entscheidend, um genaue und zuverlässige Messungen zu gewährleisten. Hier ist eine Schritt-für-Schritt-Beschreibung des Kalibrationsprozesses:

  • Positionierung und Justierung: Der Lidar-Sensor wird am Roboter befestigt und sorgfältig ausgerichtet, um sicherzustellen, dass er korrekt positioniert ist.
  • Referenzpunktmessung: Bekannte Referenzobjekte oder -punkte werden im Sichtfeld des Lidar-Sensors platziert, um die Genauigkeit der Messungen zu überprüfen.
  • Kalibrationsdatenaufnahme: Der Lidar-Sensor nimmt Messungen der Referenzpunkte auf. Diese Daten werden gesammelt, um ein Modell der Sensorcharakteristik zu erstellen.
  • Datenanalyse und Modellanpassung: Die aufgenommenen Kalibrationsdaten werden analysiert. Ein mathematisches Modell (z.B. lineare Regression) wird verwendet, um die Beziehung zwischen den gemessenen und den tatsächlichen Positionen der Referenzpunkte zu bestimmen.
  • Korrekturfaktoren bestimmen: Basierend auf der Analyse werden Korrekturfaktoren berechnet, um eventuelle systematische Fehler (z.B. Offsets oder Skalierungsfehler) zu korrigieren.
  • Kalibrationsparameter speichern: Die berechneten Kalibrationsparameter werden im System gespeichert und bei jeder Messung des Lidar-Sensors angewendet.
Bedarf einer Rekalibration während des Betriebs:Im Laufe der Zeit und bei Verwendung kann es notwendig sein, den Lidar-Sensor erneut zu kalibrieren. Hier sind einige Gründe, warum eine Rekalibration erforderlich sein könnte:
  • Temperaturänderungen: Veränderungen in der Umgebungstemperatur können die Messungen des Lidar-Sensors beeinflussen, da sich Materialien ausdehnen oder zusammenziehen.
  • Mechanische Erschütterungen: Stöße oder Vibrationen während des Betriebs können die Position und Ausrichtung des Lidar-Sensors verändern.
  • Alterung und Abnutzung: Mit der Zeit können Komponenten des Sensors altern oder verschleißen, was die Genauigkeit der Messungen beeinträchtigt.
  • Umgebungsfaktoren: Staub, Schmutz oder Feuchtigkeit können die Linsen oder Spiegel des Lidar-Sensors verschmutzen und zu Drift führen.
Driftkorrektur und Rekalibration:Rekalibration wird durchgeführt, um die Drift zu korrigieren und die Genauigkeit der Messungen wiederherzustellen. Während der Rekalibration werden die gleichen Schritte wie bei der initialen Kalibration durchgeführt, jedoch basiert die Analyse auf den Unterschieden zwischen den aktuellen Messungen und den erwarteten Werten.Mathematische Modelle zur Rekalibration:Um die Drift und die Notwendigkeit einer Rekalibration zu erkennen und zu korrigieren, können verschiedene mathematische Modelle verwendet werden:
  • Lineare Regression: Kann verwendet werden, um systematische Fehler zu erkennen und Korrekturfaktoren zu berechnen.
  • Least Squares (Kleinste Quadrate): Ein Optimierungsverfahren, das die Summe der Quadrate der Abweichungen minimiert, um die bestmögliche Anpassung zu finden.
  • Kalman-Filter: Ermöglicht es, kontinuierliche Schätzungen der Drift zu berechnen und zu korrigieren, während der Sensor in Betrieb ist.
  • Fourier-Analyse: Kann verwendet werden, um periodische Driftkomponenten zu identifizieren und zu entfernen.
Zusammenfassend:Die initiale Kalibration eines Lidar-Sensors stellt sicher, dass der Sensor präzise Daten liefert. Regelmäßige Rekalibration ist notwendig, um Drifts zu korrigieren, die durch verschiedene Faktoren während des Betriebs verursacht werden können. Mathematische Modelle wie lineare Regression, Least Squares, Kalman-Filter und Fourier-Analyse tragen zur Identifikation und Korrektur dieser Drifts bei.

Aufgabe 4)

Ein etabliertes Unternehmen möchte ein neues Robotersystem konzipieren, welches in der Lage ist, selbstständig Aufgaben in einer industriellen Umgebung auszuführen. Als Architekt des Systems steht es Dir frei, Dich für jeden Aspekt der Softwarearchitektur zu entscheiden. Jedoch soll sich das System an den gängigen Prinzipien der Softwarearchitektur in Robotik orientieren. Für die Konzeption stehen folgende Anforderungen im Fokus:

  • Das System muss modular sein, um zukünftige Erweiterungen und Wartungen zu erleichtern.
  • Eine MVC-Architektur soll angewandt werden, um die Trennung von Daten, Benutzeroberfläche und Steuerungslogik zu gewährleisten.
  • Die Middleware sollte ein robustes Kommunikationsprotokoll wie ROS verwenden.
  • Die Echtzeitfähigkeit des Systems ist entscheidend, da bestimmte Aufgaben innerhalb spezifischer Zeitrahmen ausgeführt werden müssen.
  • Fehlertoleranz ist erforderlich, um die Systemstabilität und Ausfallsicherheit zu garantieren.
  • Des Weiteren soll eine Dataflow-Architektur implementiert werden, um die Verarbeitung von Sensordaten effizient zu gestalten.
  • Das Design soll hierarchisch sein, somit Low-Level-Module und High-Level-Module klar voneinander getrennt werden.
Basierend auf diesen Informationen beantworte die folgenden Fragen:

a)

Beschreibe die Vorteile einer Modularitäten für das geplante Robotersystem. Wie unterstützt dies die zukünftige Weiterentwicklung und Wartung des Systems? Nenne zwei konkrete Beispiele.

Lösung:

Vorteile der Modularität für das geplante Robotersystem

Die Modularität eines Systems bezieht sich auf die Aufteilung des Systems in kleinere, überschaubare Einheiten oder Module, die unabhängig voneinander entwickelt, getestet und gewartet werden können. Für das geplante Robotersystem bietet die Modularität mehrere Vorteile:

  • Erleichterte Erweiterbarkeit und Wartung: Jeder Modul kann unabhängig von den anderen weiterentwickelt oder bearbeitet werden. Dies macht es einfacher, spezifische Komponenten zu aktualisieren oder zu ersetzen, ohne das gesamte System zu beeinflussen.
  • Fördert Wiederverwendbarkeit: Module, die einmal entwickelt und getestet wurden, können für andere Projekte oder zusätzliche Systeme wiederverwendet werden, was die Effizienz und Qualität in der Entwicklung steigert.
  • Verbesserte Fehlertoleranz: Fehler in einem spezifischen Modul beeinträchtigen nicht das gesamte System. Andere Module können weiterhin normal arbeiten, was die Stabilität und Zuverlässigkeit des Systems erhöht.

Unterstützung der zukünftigen Weiterentwicklung und Wartung

Modularität unterstützt die zukünftige Weiterentwicklung und Wartung des Systems auf verschiedene Weise:

  • Erweiterbarkeit: Wenn ein Unternehmen neue Funktionen oder Techniken einführen möchte, können diese als zusätzliche Module zum bestehenden System hinzugefügt werden, ohne dass das gesamte System umstrukturiert werden muss. Dies reduziert den Entwicklungsaufwand und die Risiken.
  • Einfachere Fehlerbehebung und Aktualisierungen: Bei auftretenden Fehlern können diese lokal in dem betroffenen Modul behoben werden, ohne andere Teile des Systems zu beeinträchtigen. Ebenso können Updates zielgerichtet durchgeführt werden, indem nur bestimmte Module aktualisiert werden, anstatt das gesamte System neu zu implementieren.

Konkrete Beispiele

  • Sensormodule: Stell Dir vor, ein neuer Sensortyp wird auf den Markt gebracht, der das bestehende System ergänzen soll. Dank der modularen Architektur kann ein neues Sensormodul entwickelt und in das bestehende System integriert werden, ohne dass andere Komponenten geändert werden müssen.
  • Steuermodule: Falls die Steuerungslogik geändert oder verbessert werden soll, z.B. um eine effizientere Pfadplanung zu ermöglichen, kann dies durch Verändern oder Ersetzen des Steuermoduls geschehen, ohne die Benutzeroberfläche oder die Datenverwaltung zu beeinflussen.

Zusammengefasst bietet die Modularität eine flexible und skalierbare Basis, die es ermöglicht, Technologien schnell und effizient an veränderte Anforderungen anzupassen, was für den langfristigen Erfolg des Robotersystems entscheidend ist.

b)

Erstelle ein UML-Diagramm, das die MVC-Architektur des Systems zeigt. Erläutere, wie die Trennung von Modell, Ansicht und Kontrolle (MVC) dazu beiträgt, die Wartbarkeit und Erweiterbarkeit des Systems zu verbessern.

Lösung:

UML-Diagramm der MVC-Architektur des Robotersystems

Im Folgenden findest Du ein UML-Diagramm, das die Struktur der MVC-Architektur für das geplante Robotersystem darstellt:

@startumlclass Model {  +SensorData  +RobotState  +updateState()  +getData()}class View {  -displayPanel  +updateView()  +showData()}class Controller {  -model: Model  -view: View  +controlRobot()  +updateView()  +processInput()}Model -- ControllerView -- Controller@enduml

Das UML-Diagramm zeigt die drei Hauptkomponenten:

  • Modell: Diese Komponente verwaltet die Daten und den Zustand des Systems. Sie führt alle Operationen aus, die zur Bearbeitung und Aktualisierung dieser Informationen erforderlich sind.
  • Ansicht: Die Ansicht ist für die Darstellung der Daten zuständig. Sie erhält die aktualisierten Daten vom Modell und zeigt sie dem Benutzer an.
  • Kontroller: Der Kontroller fungiert als Vermittler zwischen Modell und Ansicht. Er verarbeitet Benutzereingaben, aktualisiert das Modell und die Ansicht entsprechend.

Verbesserung der Wartbarkeit und Erweiterbarkeit dank MVC

Die Trennung von Modell, Ansicht und Kontrolle (MVC) bietet zahlreiche Vorteile für die Wartbarkeit und Erweiterbarkeit des Systems:

  • Klare Verantwortlichkeiten: Durch die Trennung der Verantwortlichkeiten in drei klar definierte Bereiche (Datenlogik, Präsentation und Eingangsverarbeitung) wird der Code übersichtlicher und leichter zu verstehen. Jeder Teil kann unabhängig voneinander entwickelt und getestet werden.
  • Förderung der Wiederverwendbarkeit: Komponenten des Modells, der Ansicht und des Kontrollers können unabhängig von einander wiederverwendet oder ausgetauscht werden. Beispielsweise kann die Benutzeroberfläche aktualisiert werden, ohne die Datenlogik zu beeinflussen.
  • Erleichterte Fehlersuche und Wartung: Da Änderungen in einem Bereich keine direkten Auswirkungen auf die anderen Bereiche haben, lässt sich die Fehlerbehebung und Wartung einfacher durchführen. Dies reduziert die Wahrscheinlichkeit, unbeabsichtigte Nebenwirkungen zu verursachen.
  • Skalierbarkeit: Neue Funktionen oder Erweiterungen können leicht hinzugefügt werden, indem neue Module oder Komponenten entwickeln, ohne das bestehende System zu destabilisieren. Zum Beispiel kann ein neues Anzeige- oder Steuerungsmodul hinzugefügt werden, während das Datenmodell unverändert bleibt.

Zusammengefasst erhöht die MVC-Architektur die Flexibilität und Nachhaltigkeit des Systems, indem sie klare Strukturen und Trennungen schafft, die es Entwicklern ermöglichen, schneller und effektiver auf Änderungen und Erweiterungen zu reagieren.

c)

Angenommen, das Robotersystem soll einen komplexen Greifvorgang in Echtzeit ausführen. Leite die erforderlichen mathematischen Anforderungen her und formuliere die Echtzeitbedingungen. Diskutiere, wie die Dataflow-Architektur im Zusammenspiel mit der hierarchischen Designstruktur dafür sorgt, dass diese Bedingungen eingehalten werden.

Lösung:

Echtzeitbedingungen und mathematische Anforderungen für den Greifvorgang

Um einen komplexen Greifvorgang in Echtzeit auszuführen, sind präzise mathematische Berechnungen und strikte Zeitrahmen erforderlich. Diese beinhalten die Berechnung der Bahnplanung, Steuerung und Verarbeitung von Sensordaten.

Mathematische Anforderungen

  • Bahnplanung: Die Trajektorie des Greifers muss unter Berücksichtigung der Kinematik und Dynamik des Roboters berechnet werden. Dies umfasst sowohl die direkte als auch die inverse Kinematik.Die Vorwärtstransformation zur Bestimmung der Position und Orientierung des Endeffektors kann durch die Multiplikation der Transformationsmatrixen für jedes Gelenk berechnet werden:\[ T = A_1 \times A_2 \times ... \times A_n \]Die inverse Kinematik zur Bestimmung der Gelenkwinkel für eine gegebene Endeffektorposition kann z.B. iterativ oder analytisch gelöst werden.
  • Geschwindigkeits- und Beschleunigungsberechnungen: Diese sind notwendig, um die Bewegung des Greifers zu kontrollieren. Die Berechnung basiert auf:\[ \tau = J^T F \]wobei \( \tau \) das Drehmoment, \( J \) der Jacobi und \( F \) die Endeffektorkraft ist.
  • Feedback-Kontrolle: Nutzung von PID-Reglern, um Positions- und Geschwindigkeitsfehler in Echtzeit auszugleichen:\[ u(t) = K_p e(t) + K_i \int_{0}^{t} e(\tau) d\tau + K_d \frac{de(t)}{dt} \]wobei \( u(t) \) das Stellglied, \( e(t) \) der Fehler, und \( K_p, K_i, K_d \) die PID-Gewichte sind.

Echtzeitbedingungen

  • Zeitrahmen: Der komplette Greifvorgang muss innerhalb eines vorgegebenen Zeitrahmens abgeschlossen werden, z. B. innerhalb von 500 ms.
  • Latenz: Die Latenz bei der Verarbeitung von Sensordaten und Steuerbefehlen muss minimal gehalten werden, z. B. weniger als 10 ms.
  • Zykluszeit: Der Regelzyklus muss kürzer sein als der systembedingte Zeitrahmen, z. B. ein Kontrollzyklus alle 1 ms.

Dataflow-Architektur und Hierarchisches Design

Das Zusammenspiel von Dataflow-Architektur und hierarchischem Design stellt sicher, dass die Echtzeitbedingungen eingehalten werden:

  • Dataflow-Architektur: Diese Architektur ermöglicht eine effiziente und parallele Verarbeitung der Sensordaten. Durch die Definition der Datenflüsse in Pipelines kann sichergestellt werden, dass Daten rechtzeitig verarbeitet und weitergeleitet werden:\[ \text{Pipeline} = \text{Process}_1 \rightarrow \text{Process}_2 \rightarrow ... \rightarrow \text{Process}_n \]
  • Hierarchisches Design: Die Trennung in Low-Level- und High-Level-Module ermöglicht eine klare Strukturierung und Vermeidung von Interferenzen zwischen verschiedenen Prozessen. Low-Level-Module konzentrieren sich auf direkte Steuerungsaufgaben wie Sensordatenverarbeitung und Motorsteuerung, während High-Level-Module die Bahnberechnung und strategische Entscheidungsfindung übernehmen.Ein Beispiel für die hierarchische Struktur könnte wie folgt aussehen:
    • High-Level-Module: Planung und Entscheidungsfindung
    • Low-Level-Module: Echtzeitsteuerung und Sensorverarbeitung

Zusammengefasst ermöglicht die Dataflow-Architektur die parallele und effiziente Verarbeitung von Sensordaten, während die hierarchische Struktur eine klare Trennung zwischen Steuerungs- und Verarbeitungsebenen bietet. Dadurch wird sichergestellt, dass Echtzeitbedingungen wie minimale Latenz und vorgegebene Zeitrahmen eingehalten werden.

Sign Up

Melde dich kostenlos an, um Zugriff auf das vollständige Dokument zu erhalten

Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.

Kostenloses Konto erstellen

Du hast bereits ein Konto? Anmelden