H2 und H-infinity control

Bei Robotics.Stackexchange gibt es mal wieder Hausaufgaben für andere Leute zu lösen. Diesmal möchte jemand den Unterschied wissen zwischen H square control und H-infinity control und stellt dazu noch sehr detailierte Fragen: https://robotics.stackexchange.com/questions/12803/whats-the-diffrence-between-h-2-and-h-infty-controll Schon allein an der Art der Fragestellung kann man ablesen, dass der Fragende die Antwort eigentlich selber weiß. Weil das Fragen sind wie aus einer Matheklausur. Die Antwort ist bekannt, es ist nur noch die Frage ob auch die Stackoverflow Community sie schon weiß. Das schöne daran ist, dass die richtige Antwort schon längst jemand gegeben hat, dafür auch gebührend hochgevoted wurde und sogar das begehrte Accept Häckchen erhalten hat. Problem gelöst, was gibt es schöneres? Nun mal nicht so schnell, meine Antwort steht da noch nicht drunter. Ich möchte sie hier im Blog geben. Schließlich ist das hier das Trollheaven-Blog, das weltbeste Blog für Fragen der Robotik und künstlicher Intelligenz. Um es sehr kurz zu machen: H2 und H-infinity control sind nicht wirklich Algorithmen um damit Roboter zu steuern, sondern genau betrachtet sind es mathematisch ausformulierte Probleme, ähnlich wie das Minimax, das Knapsack oder das Busybeaver Problem. Das heißt, man kann H-Infinity nicht wirklich verstehen, sondern was man verstehen kann ist nur, was gesucht wird. Gesucht wird der Algorithmus um ein komplexes System zu steuern. Sei es die Hand eines Roboters, die Fußbewegungen eines Roboters oder gerne auch die Parameter für das Stabbalance Problem. All das kann man in der H-Infinity Syntax ausdrücken. Auch der sogenannte LQG-controller ist eigentlich kein richtiger Controller, sondern wiederum handelt es sich um die Formulierung eines Problem. Genauer gesagt ist es das LQG problem, also die Frage wie der Controller aussieht.

Komischerweise sieht das Schulmathematik, oder was immer bei Stackoverflow diskutiert wird, ein wenig anders. Dort denken die Leute doch tatsächlich, dass sie bereits die Lösung hätten, wenn sie definieren können was H-square bedeutet. Das können sie nicht, sondern sie wissen nur, dass sie keine Ahnung haben. Um die angesprochenen Robotik- und Controller Aufgaben zu lösen, also eine Gleichung bzw. einen Algorithmus aufzustellen benötigt man etwas gänzlich anderes als Mathematik. Genauer gesagt benötigt man eine Heuristik, welche von der Domäne abhängig ist und die man entweder in Papern nachlesen kann, oder durch manuelle Steuerung selber herausfinden kann. Wie diese Heuristik konkret aussieht lässt sich nicht mathematisch definieren sondern nur natürlichsprachlich. Eventuell kann man auch Pseudocode angeben. Vielleicht mal ein Beispiel: angenommen man möchte einen Controller entwickeln um das Cartpole Beispiel zu lösen. Dann könnte die Lösung so aussehen, dass man ein wenig mit dem Cart herumprobiert und irgendwann auf die Idee kommt, dass wenn das Pendel einmal einen Schwellwert unterschritten hat, man es am besten fallen lässt und dann lieber nochmal neu aufschwingt. Solange es hingegen in dem Schwellwertbereich befindet, kann man durch rechtzeitiges Gegenlenken ein Fallen verhindern. Das ist — grob gesagt — eine Heuristik. Um sie formal zu formulieren, reicht Mathematik allein nicht aus, sondern man muss schon die Sprache Python bemühen, zur Not tut es auch Lisp oder Forth. Aber was in keinem Fall ausreicht ist, wenn man sich klarmacht, dass sich das Pendel in einem zustandsraum bewegt, der prinzipiell unendlich groß ist. Das ist zwar nicht verkehrt, aber das ist nur die Beschreibung des Problems, nicht jedoch die eigentliche Lösung.

Ich vermute mal, dass die Stackoverflow meine Antwort nicht so gut findet, weil sie erstens auf Deutsch geschrieben ist und zweitens erheblich von den anderen Antworten abweicht. Deshalb poste ich sie auch nur hier in mein eigenes Blog. Vielleicht hilft es ja trotzdem dem OP …

Instabile Quadcopter PID Steuerung

https://robotics.stackexchange.com/questions/14022/unstable-quadcopter-pid
Und mal wieder die tägliche Dosis an Robotics.Stackexchange Hausaufgabenkost. Diesmal geht es um die PID Steuerung für einen Quadcopter. Ein wie ich finde — sehr schönes Spielzeug. Der OP schildert dass sein Fluggerät nach dem Start zu einer Seite wegdriftet und hat dankenswerterweise auch noch den Sourcecode seines PID Controllers angefügt. Gut gemacht, die Frage wurde korrekt gestellt, betrifft ein interessantes Thema und demzufolge gibt es auch eine First-Class Antwort darauf. Also, …, das Problem liegt im User Interface, genauer gesagt in der Mensch Maschine Schnittstelle. Allein einen PID Controller zu programmieren reicht nicht, weil Driftings und ähnliche Probleme bei instabilen Systemen zum Alltag gehören. Was hingegen wirklich weiterhilft ist, wenn man die Sache grafisch veranschaulicht, am besten indem man die Flugsteuerung als overlay einblendet und dann manuell mittels Joystick nachregelt. So wie das im folgenden Paper erläutert wurde:

Tim Moritz Julius Fricke: Flight Control with Large Time Delays and Reduced Sensory Feedback, 2017, https://www.researchgate.net/profile/Tim_Fricke/publication/316884189_Flight_Control_with_Large_Time_Delays_and_Reduced_Sensory_Feedback/links/5919d1dd0f7e9b1db65283db/Flight-Control-with-Large-Time-Delays-and-Reduced-Sensory-Feedback.pdf

Das ist zugegebenermaßen etwas härtere Kost (300 Seiten DIN A4 wollen erstmal verdaut werden), aber bevor man einen PID Controller programmieren kann ist es wichtig zunächst einmal manuell via Joystick zu ermitteln wohin man das Fluggerät eigentlich stabilisieren möchte. Mein Tipp lautet: soviele Informationen wie nur möglich aus dem Quadcopter auslesen und in einem übersichtlichen grafischen Modell darstellen, danach das Driften mittels Joysticksteuerung ausgleichen und die gewonnene Heuristik in den Sourcecode übernehmen. Viel Glück.

Neuer Boston Dynamics Roboter ist wertlos

Wiedereinmal hat es Boston Dynamics der ganzen Welt gezeigt dass sie humanoide Roboter bauen können. Auf der Bühne war ein ferngesteuerter Atlas Roboter zu sehen der als Höhepunkt sogar das Gleichgewicht verloren hat und ähnlich wie Asimo vor vielen Jahren auch von der Bühne gestürzt ist. Aber war das ganze eine einmalige Demonstration von Inginieurkunst? Leider nein, wie die meisten Roboter auch ist die Software nicht als opensource verfügbar. Es war also die erste öffentliche Vorführung eines humanoiden Roboters, aber nicht die erste Vorführung wo man im Anschluss das System nachbauen kann. Aber genau darum geht es, Wissen zu vermitteln, OpenSource zu programmieren und die Robotik-Entwicklung zu beschleunigen. Leider ist BostonDynamics kein Freund von OpenSource, sie haben zwar technisch anspruchsvolle Systeme entwickelt, aber keine Systeme von denen man etwas lernen kann.

Was nützt aber ein Roboter der auf einer bühne agiert, den man aber nicht anfassen darf? Richtig, er wird weder kommerziell vermarktet werden und er wird nicht im Informatik-Unterricht zum Einsatz kommen. Böse formuliert ist es ein Zaubertrick, also kein echter Roboter sondern nur eine Show. Die Leute nehmen es zur Kenntnis, klatschen artig und vergessen es dann wieder. Die Aufgabe von Wissenschaft ist jedoch etwas umfangreicher als nur Beifall zu erheischen. Bei Wissenschaft geht es darum, Technik weiterzuentwickeln, die Welt zu verbessern und Menschen auf neue Ideen zu bringen. Nichts davon erfüllt der Boston Dynamics Roboter. Er ist so irreal wie Data aus Startrek: wir wissen dass er existiert, aber wie wissen auch dass er nicht echt ist.

Das Problem mit diesen Darpa Geheimprojekten ist, dass wie der Name schon suggeriert sie geheim sind. Damit ist gemeint, dass die Bauanleitung nicht im Internet zu finden ist, dass es keine Aufzeichnungen dazu gibt und auch auf Stackoverflow niemand sagen können wie das Teil programmiert wurde. Wenn aber Boston Dynamics keinen Roboter geliefert hat, was haben sie gestern abend dann auf der Bühne gezeigt? Das ist die große Frage. Vermutlich war es eine Mischung aus Comedy und Selbstüberschätzung die zu sehen war. Sigfried und Roy haben Löwen verschwinden lasen und Marc Raibert hat eine Bauchrednerpuppe — so ungefähr die Idee dahinter. Natürlich kann man diese Details ausblenden und nur die Technik als solche feiern, also wie toll der Roboter doch die Balance berechnet hat und wie sicher er wiedereinmal die Kiste gegriffen hat. Doch was nützt die beste Technologie wenn es weltweit nur 100 Leute wissen wie sie funktioniert? Richtig, sie ist genauso rückwärtsgewand als wenn Microsoft ein supertolles Windows 10 Betriebssystem herausbringt aber nicht den Sourcecode veröffentlicht. Es ist technologisch nutzlos, es bringt uns nicht weiter.

Zur Ehrenrettung von Marc Raibert muss man anfügen, dass zumindest seine älteren Paper aus den 1980’er als OpenAccess im Internet stehen. Das war die Zeit als er mit hopping robots und Finite State Machines experimentiert hat. Ganz so geheimnisvoll geht es also doch nicht zu. Aber in jüngerer Zeit gibt es bessere Autoren, die einerseits High-End Robotik beschreiben, aber gleichzeitig auch noch als OpenSource. Zu nennen sind hier Michiel van de Panne (Simbicon), Craig Reynolds (Steering) und Linus Torvalds (Erfinder von git). Anders formuliert, die heute gezeigte Darbietung mit dem Boston Dynamics Roboter ist wesentlich schlechter als der Hype vermuten lässt.

Robot-Learning funktioniert nicht

Die ETH Zürich hat vor einiger Zeit ein Video veröffentlicht was Machinelles Lernen auf Robotik anwendet.
http://www.video.ethz.ch/conferences/2014/rnls/03_tuesday/f24478df-1ae2-4a73-9553-03613e0b5ae5.html Dort werden Techniken vorgestellt wie Optimal Control, Reinforcement Learning und Stochastic Optimization. Obwohl die ETH zu den angesehensten Universitäten der Welt gehört taugt das obige Video leider gar nichts. Die darin verwendete Mathematik stammt aus dem 19. Jahrhundert, die Prinzipien (insbesondere die Kernel-basierenden Suchverfahren) sind nicht im Stande auch nur Spielzeugroboter zu steuern. Warum also wird dem Thema so ausufernd viel Aufmerksamkeit geschenkt, so dass es gleich noch 2 weitere Videos mit ähnlichem Inhalt gibt? Ich glaube das hat weniger damit zu tun, dass man hier eine Technologie entdeckt hat, um das alte Problem der Künstliche Intelligenz anzugehen, sondern es hat etwas mit denjenigen Personen zu tun, die die Vorträge ausgearbeitet haben. Es sind Leute, mit einem Background in Elektrotechnik. Sie denken in Analog-Schaltungen bei dem Oszilatoren und Widerstände zusammenschaltet werden und sich daraus dann ein Signal ergibt. Leider lassen sich damit keine intelligente Systeme konstruieren, sondern lediglich sehr simple PID Controller. Also Regler, die Eingangsgrößen in Ausgangsgrößen transformieren. Die Beziehung wird durch eine mathematische Kurve dargestellt und die Idee ist es, diese lineare Kurve mathematisch zu beschreiben.

Die Schwierigkeit ist, dass in der Robotik solche Probleme nicht auftreten. Wäre es so simpel, dass man einen Robotergreifer über eine y=mx+n Funktion beschreiben könnte, dann würde an der Thematik nicht seit 100 Jahren erfolglos geforscht werden. Sondern Roboter werden grundsätzlich über Turing-mächtige Computer gesteuert und die werden nicht gelernt, sondern sie werden programmiert. Ich will damit sagen, dass der obige Vortrag nicht nur im Detail Fehlerhaft ist, sondern dass der Professor nochnichtmal das Problem als solches verstanden hat. Wäre das hier ein Bewertungsportal wie http://www.ratemyprofessors.com/ lautet die Note: sechs, durchgefallen.

Robot-Control-System geht gut voran

Das Programmieren mit git funktionert besser als gedacht. Die Software wurde ausgebaut und neue Features integriert. Der Roboter hat jetzt Beine erhalten, die animiert sind und kann auf der linken und rechten Straßenseite fahren. Geplant ist als nächtes, dass die Wegewahl automatisch funktioniert, dass man Objekte aufnehmen kann und natürlich ein zweites Auto. Letzteres wird aber nach hinten geschoben weil sowas sehr komplex ist und bereits in Richtung „Traffic Simulation“ geht.

Neue Version des Robot-Control-Systems

Wieder einmal ist es Zeit für eine Programmversion. Gegenüber dem letzten Mal hat sich viel verändert. Zum einen wird jetzt git als Version-Control-System eingesetzt, daher auch der obige Screenshot. Der Grund ist, dass sich damit der Sourcecode leichter memorieren lässt. Zum ersten Mal ist es möglich, den kompletten Sourcecode auswendig zu lernen. Git ist vergleichbar mit einem Vokabeltrainer.

Inhaltlich wurde auf die Box2D Physik-Engine verzichtet. Stattdessen wird eine Makrosimulation genutzt, wo also nur angedeutet wird, wie sich das Auto auf der Straße bewegt. Dadurch braucht man weniger Lines of Code und das Programm ist leichter für außenstehende verständlich. Trotzdem bleibt der Aufwand um die Behavior Trees zu erstellen unverändert hoch. Und hier ist nach wie vor der Flaschenhals. Aktuell hat das Programm nur 3-4 Tasks die implementiert wurden (followpoint, followwaygraph usw.). Die Leistungsfähigkeit der Künstlichen Intelligenz hängt davon ab, wieviele Actions / Behavior man implementiert. Leider ist jedoch das manuelle Coden dieser Behavior sehr zeitaufwendig.

# coding: utf8
'''
to do: rechteckiges auto
titel: selfdriving car
date: June 26, 2017

Anleitung: https://wiki.ubuntuusers.de/Git/
neues Projekt erzeugen: git init
Datei hinzufügen: git add 1.py
Änderung hinzufügen: git commit -am "AENDERUNGSBESCHREIBUNG"
Show: 
  git log
  git status
GUI: gitk --all

Branches:
  neuen Branch: git branch issue12
  Branch wechseln: git checkout issue12
  merge: git checkout master
         git merge --no-ff ssue12
  Branch löschen: git branch -d issue12

'''
import pygame, random, math, threading, sys

class Settings(object):
  def __init__(self):
    self.screen = (600, 338)
    self.fps = 20  # 30 fps
    self.white = (220, 220, 220)
    self.black = (0, 0, 0)
    self.grey = (150, 150, 150)
    self.red = (230, 0, 0)
    self.blue = (0, 0, 230)
  def calcdistance(self, p1, p2):
    """ Euclidean ordinary distance """
    return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
  def angle_between_two_points(self, p1, p2):
    angle = math.degrees(math.atan2(p2[1] - p1[1], p2[0] - p1[0]))
    angle += 90
    if angle < 0: angle += 360
    return angle
  def floattoint(self, p):
    return (int(p[0]), int(p[1]))
  def polarpoint(self, p1, angle, radius):
    """ polar coordinates for a point on circle """
    angle = (angle - 90) * math.pi / 180
    x = p1[0] + radius * math.cos(angle)
    y = p1[1] + radius * math.sin(angle) 
    x,y=self.floattoint((x,y))
    return (x, y)
  def incircle(self,p1,radius,p2):
    """ checks if p2 is in circle """
    square_dist = (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2
    return square_dist <= radius ** 2    

class Physics(Settings):
  def __init__(self):
    super(Physics, self).__init__()
    self.pos = (40,150)
    self.radius=20

class Dictionary(Physics):
  def __init__(self):
    super(Dictionary, self).__init__()
    self.primitive =[] # group, name, taskname 
    self.primitive.append(("move","path","taskfollowpath()")) 
    self.primitive.append(("move","waypoint","taskfollowwaypoints()")) 
    self.primitive.append(("move","mouse","taskmove()")) 
    self.primitive.append(("radius","smaller","task2()")) 
    self.primitive.append(("pos","right","taskright()")) 
    self.primitive.append(("pos","left","taskleft()")) 
    self.primitive.append(("-","-","misc()")) 
    self.primitive.append(("-","-","misc()")) 
    self.linestart=0
    self.linemax=5
  def taskmenu(self):
    self.status="Running"
    temp = self.menuselect1
    taskname="self."+ self.primitive[temp][2]
    eval(taskname)
    self.status="Ready"
  def pause(self,frames):
    for i in range(frames):
      self.updateGUI()
  def movetoangle(self,angle):
    radius=3
    self.pos=self.polarpoint(self.pos, angle, radius)
  def movetopos(self,goal):
    for i in range(1000):
      dist=self.calcdistance(goal,self.pos)
      angle=self.angle_between_two_points(self.pos,goal)
      print angle
      self.movetoangle(angle)
      self.pause(1)
      if dist<10: break
  def taskmove(self):
    self.movetopos(self.mouse)
  def task2(self):
    for i in range(10):
      self.radius-=1
      self.pause(1)
  def taskright(self):
    p=(self.pos[0]+50,self.pos[1])
    self.movetopos(p)
  def taskleft(self):
    p=(self.pos[0]-50,self.pos[1])
    self.movetopos(p)

class Waypoints(Dictionary):
  def __init__(self):
    super(Waypoints, self).__init__()
    # graph
    self.node=[] # x,y
    self.node.append((54,290))
    self.node.append((300,290))
    self.node.append((530,180))
    self.node.append((150,150))
    self.path=[] # waypoint
    self.path.append(0)
    self.path.append(2)
    self.path.append(0)
    self.path.append(1)
  def taskfollowwaypoints(self):
    for i in range(len(self.node)):
      self.movetopos(self.node[i])
  def taskfollowpath(self):
    for i in range(len(self.path)):
      self.movetopos(self.node[self.path[i]])


class GUI(Waypoints):
  def __init__(self):
    super(GUI, self).__init__()
    random.seed()
    pygame.init()
    self.window = pygame.display.set_mode(self.screen)
    self.mouse = (0, 0)
    self.menuselect1 = 0   
    self.control = 1 # mode 
    self.status="Ready"    
  def updateGUI(self):
    pygame.time.wait(1000/self.fps)
    self.window.fill(self.white)
    self.painttext()
    self.paintmenu()
    self.paintobjects()
    pygame.display.update()   
    self.inputhandling()
  def painttext(self):
    # mouse
    myfont = pygame.font.SysFont("freesans", 16)
    text = str(self.control)+" " + str(self.mouse)+" "+str(self.step)
    label = myfont.render(text, True, self.black)
    self.window.blit(label, (220,30))
  def paintmenu(self):
    myfont = pygame.font.SysFont("freesans", 16)
    count=0
    xstart, ystart=400,15
    height=18
    for i in range(self.linestart,self.linestart+self.linemax):
      # select
      color=self.black
      if self.menuselect1==i:
        color=self.blue
        if self.status=="Running": color=self.red # running color
        label = myfont.render(">", True, color)
        self.window.blit(label, (xstart-10,ystart+count*height)) 
      # scrolling
      self.linestart=self.menuselect1-(self.linemax/2)
      if self.linestart<0: self.linestart=0
      elif self.linestart>len(self.primitive)-self.linemax:
        self.linestart=len(self.primitive)-self.linemax
      # textout
      if self.primitive[i][0]==self.primitive[i-1][0]: text = str(i)
      else: text = str(i)+" "+self.primitive[i][0]
      label = myfont.render(text, True, color)
      self.window.blit(label, (xstart,ystart+count*height))
      label = myfont.render(self.primitive[i][1], True, color)
      self.window.blit(label, (xstart+90,ystart+count*height))
      count+=1      
  def paintobjects(self):
    # robot
    pygame.draw.circle(self.window, self.blue, self.pos, self.radius, 0)    
    # waypoint
    myfont = pygame.font.SysFont("freesans", 14)
    for i in range(len(self.node)):
      pygame.draw.circle(self.window, self.black, self.node[i], 3, 0)           
      label = myfont.render(str(i), True, self.black)
      self.window.blit(label, self.node[i])
    # path
    for i in range(len(self.path)-1):
      p1 = self.node[self.path[i]]
      p2 = self.node[self.path[i+1]]
      pygame.draw.line(self.window, self.blue, p1, p2, 1)
  def inputhandling(self):
    for event in pygame.event.get(): 
      if event.type == pygame.QUIT: sys.exit(0) 
      if event.type == pygame.MOUSEMOTION: 
        self.mouse = event.pos
      if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_1: pass
        if event.key == pygame.K_2: pass
        if event.key == pygame.K_UP: 
          temp=self.menuselect1-1
          if temp==-1: temp=len(self.primitive)-1
          self.menuselect1=temp
        if event.key == pygame.K_DOWN:
          temp=self.menuselect1+1
          if temp==len(self.primitive): temp=0
          self.menuselect1=temp
        if event.key == pygame.K_RIGHT: self.taskmenu()       

class Game: 
  def __init__(self):
    self.myGUI = GUI()    
    for self.myGUI.step in range(10000000):
      self.myGUI.updateGUI()
 
if __name__ == "__main__":
  myGame = Game()

Missionsplanung für Multi-Robot-System

[1] Zugegeben, die Aufgabe ist nicht gerade alltäglich. Es kommt nicht oft vor, dass bei Robotics Stackexchange jemand mit einem Quadcopter Waypoints abfliegt, laut dem verlinkten H.264 Video offenbar schon eine fertige Software im Einsatz hat und jetzt die Crowd um Hilfe bittet, um das System ein wenig aufzumotzen und noch spritziger zu gestalten. Zu meinem Bedauern muss ich leider zugeben, mich mit Künstlicher Intelligenz im Allgemeinen und Robotik im Speziellen nicht so gut auszukennen, als dass ich wirklich einen Tipp geben könnte. Nur soviel vielleicht: Waypoints sind Knoten in einem größeren Graphen. Wenn der Graph aus 20 Nodes besteht kann man davon 3 Nodes auswählen und die von dem UAV abfliegen lassen. Solche Graphen, besser bekannt als Navmesh, können entweder manuell in die Map eingezeichnet werden, oder aber automatisiert über Routenverlegungs-Algorithmen berechnet werden.

Der Zweite Teil der Frage mit dem Multi-Robot-System ist auch für mich relevant. Mein Fachwissen reicht leider nicht aus, um das umfassend zu beantworten, aber Multithreading dürfte hier das Zauberwort sein. Die Idee ist es, jeden Quadcopter mit einem eigenen Prozess zu starten und dann über die Main-Loop welche den Framecounter hochzählt zu synchronisieren. Eine Multi-Threading Anwendung für einen Roboter habe ich schonmal in Python programmiert, der Sourcecode müsste noch irgendwo im Archiv schlummern, für ein Multi-Agenten-System müsste man das Konzept hochskalieren, was relativ kompliziert sein dürfte. Das Hauptproblem besteht im Timing:

Angenommen, der Roboter1 führt laut dem High-Level-Planner die Tasks a und b aus. a dauert 100 Frames, b dauert 50 Frames. Gleichzeitig führt Roboter2 den Task c aus, welcher 50 Frames dauert. Jetzt startet man das System und es passiert folgendes:

          0   25   50    75   100   125   150
Roboter 1 |-----------a--------||-----b----|
Roboter 2 |----c----|

Das Problem ist, dass sich jetzt Roboter2 schon bewegt, und Roboter1 gleichzeitig dazu eine Ausweichroute berechnen muss. Noch komplizierter wird es, wenn während sich die Umgebung verändert, klar wird, dass die Tasks auf dem Roboter abgeändert werden müssen. Weil Roboter2 jetzt an der falschen Position ist, muss Roboter1 von „a-b“ auf „a-d“ als neuen Taskplan wechseln. Aber das größte Hinderniss kommt noch. Wenn man in den Threads absolute Timinigangaben in Millisekunden definiert, kann man in der Simulation das ganze nicht einfach schneller laufen lassen. Das jedoch braucht man um verschiedene Task-pläne durchzutesten, weil ein bestimmtes Ziel erreicht werden soll. Kurz gesagt, mehrere Roboter, die noch unterschiedliche Tasks ausführen dürfen kann ziemlich Messy werden und sollte nur von erfahrenen Programmiererinnen angegangen werden.