In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Seminararbeit - autonome Verkehrsleitsysteme

von Kay Kleinvogel und Lisa-Marie Nehring

Übersicht:

Die Hauptaufgabe dieser Arbeit ist das Forschen an effizienteren Ampelsystemen. Dies ist von Nöten, da jedes Jahr immer mehr Fahrzeuge auf den Straßen unterwegs sind. Um die Kapazitäten zu schaffen, muss entweder die Fläche der Infrastruktur erhöht werden, welches durch die bereits dichte Bebauung in den Städten nur begrenzt möglich ist, oder die vorhandenen Flächen müssen effizienter genutzt werden. Ampelkreuzungen bieten dabei das höchste Optimierungspotential, da hier oft Wartezeiten zustande kommen, in welchen sich kein Verkehrsteilnehmer bewegt. Diese Wartezeiten können durch Optimierung der Schaltphasen minimiert werden.

Ampelschaltung

Für die Optimierung der Ampel müssen wir uns erst einmal bewusst machen, wie diese funktioniert. Einfach ausgedrückt schaltet diese nur verschiedene LEDs entweder aus oder an. Dies tut sie Abhängig von der genauen Art der Schaltung. Für eine einfache Schaltung wird zum Beispiel nur darauf geachtet welche Zeit die verschiedenen Spuren haben. So hat zum Beispiel jede Spur ein Zeitintervall von 20 Sekunden unabhängig von der Anzahl der Fahrzeugen auf der Spur. Die Anzahl an Fahrzeugen die diese Ampel in einen bestimmten Zeitintervall verwalten kann, ist fest und kann nicht verändert werden. Bei doppelter Anzahl der Fahrzeuge verdoppelt sich auch die Wartezeit. Die Wartezeit ist dadurch also direkt proportional zu der Anzahl der Verkehrsteilnehmer an dieser Ampel.

// Code zur Anschauung der Wartezeit in Verhältnis zu Autos/Stunde

Ampelalgorithmus

Ein wichtiger Teil des Algorithmuses ist die konstante Abfrage nach der Anzahl der Fahrzeuge auf dein einzelnen Spuren. Nehmen wir dafür ein einfaches Beispiel. Wir haben eine Kreuzung von Zwei gleichstark befahrenen Straßen. Beide Straßen können nur geradeaus überquert werden. Um diese beiden Interferierenden Verkehrsströme nun zu sammeln benötigen wir eine Ampelschaltung. Um nun zu entscheiden, welche Spur fahren darf, müssen wir überprüfen wie viele Fahrzeuge auf der jeweiligen Spur sind. Dafür nehmen wir die Summe der beiden einzelnen Spuren jeder Orientierung (v1,v2 = vertikale Spuren ; h1,h2 = horizontale Spuren). Die Summe, also die Gesamtzahl der Fahrzeuge wird dann mit v respektive h dargestellt. Dies kann durch den Folgenden Code wiedergegeben werden.


In [2]:
v1 = int(input('v1: '))
v2 = int(input('v2: '))
h1 = int(input('h1: '))
h2 = int(input('h2: '))
v = v1+v2
h = h1+h2

print ('V', v)
print ('H', h)


v1: 5
v2: 5
h1: 5
h2: 5
V 10
H 10

Nun müssen wir diese Werte miteinander vergleichen. Dafür bietet es sich an die totale Anzahl an Fahrzeugen zu vergleichen. Dazu nutzen wir eine einfache Abfrage welche nach jeden Fahrzeug überprüft welche Spur mehr Fahrzeuge beeinhaltet. In diesen Beispiel haben wir zwei Möglichkeiten. Es können entweder die horizontalen oder vertikalen Fahrzeuge fahren. Hier schauen wir uns ein Beispiel an, was passieren würde, falls eine Ampelphase jeweils ein Fahrzeug lang wäre. Die Problematik dabei ist, dass die einzelnen Fahrzeuge eine jeweilige Reaktionszeit haben und daher eine lange Verzögerung entsteht, welche sich negativ auf die Kapazität der Kreuzung auswirkt.


In [3]:
print("h v")
while(v > 0 or h > 0):
    if(h>v):
        h = h-1
    else :
        v = v-1
    print(h,v)


h v
10 9
9 9
9 8
8 8
8 7
7 7
7 6
6 6
6 5
5 5
5 4
4 4
4 3
3 3
3 2
2 2
2 1
1 1
1 0
0 0

Der Aufbau des Modells

Für den entsprechenden Aufbau des Modells nutzen wir eine Kreuzung von 2 vierspurigen Straßen, welche sich schneiden.(siehe Abbildung 1). Die rechte Spur ist jeweils für die Rechtsabbieger vorgesehen, und die linke für die Linksabbieger beziehungsweise die Fahrer, welche geradeaus fahren möchten. In jeder einzelnen Spur sind dabei Sensoren eingebaut, welche Die Anzahl der Fahrzeuge auf der jeweiligen Spur anzeigen. Diese Zahl kann als binärer Wert ausgegebn werden. Gehen wir zum Beispiel von der Situation in der Abbildung 2 aus, und legen für ein Fahrzeug auf dem Sensor den Wert 1 und für kein Fahrzeug den Wert 0 fest, so erhalten wir für die Spur in diesen Moment einen Wert von 0011. Diese Ampel kann nun mit einen anderen Wert von der schneidenen Spur verglichen werden. Hat diese zum Beispiel den Wert 1001 so ist im Moment ein Fahrzeug vorne an der Kreuzung und ein anderes auf dem Weg. Hierbei würde der Algorythmus nun 0011 und 1001 vergleichen. Es würde sich anbieten den kleineren Wert zu wählen, aber hierbei würde dann eine leere Spur immer die Priorität erhalten. Um dieses Problem zu umgehen, können die Werte umgedreht werden (abcd --> dcba) und der höhere Wert erhält die Priorität, dies löst unser Problem, da eine leere Spur nun den Wert 0000 hat und daher jede Spur mit einen Fahrzeug an beliebiger Stelle einen höheren Wert erreicht. Nehmen wir dafür unser vorheriges Beispiel so vergleichen wir die beiden Werte 1100 und 1001. Da 1100 nun einen höheren numerischen Wert hat bekommt diese Spur die Grünphase zugesprochen. Betrachten wir dies nun mit Fahrzeugen erscheint dies auch logisch, da dort bereits 2 Fahrzeuge warten, während auf der anderen Spur nur 1 Fahrzeug wartet und das zweite erst dabei ist den Ampelbereich zu betreten.

Straßenkreuzung 2.0

In dieser Version der Kreuzung nutzen wir eine vierspurige Straße mit 2 Spuren in jede Richtung. Diese besteht aus der Linksabbiegerspur und der Spur für die Geradeausfahrer. Die Rechtsabbierger werden durch eine vorherige seperate Spur von der Kreuzung entfernt, da dies den Verkehrfluss erheblich verbessert. Betrachten wir nun die Spuren so erkenenn wir Zwei weitere Markierungen. Dies sind Sensoren, welche die Anzahl der Fahrzeuge auf der jeweiligen Spur erkennen. Fährt ein Fahrzeug über den gelben Sensor so wird der Wert der Spur um 1 erhöht, und sobald ein Fahrzeug die Spur verlässt, also den roten Sensor überquert wird dieser Wert um 1 verringert. Daher haben wir stets den Momentanwert der Anzahl aller Fahrzeuge an der Kreuzung, und das Wissen an welcher Stelle sich diese befinden.

Vergleich der Spuren

Um diese Spuren nun zu vergleichen, können wir betrachten, welche Kombination an sich nicht kollidiernden Spuren die höchste Anzahl an Fahrzeugen besitzt. Diese können dann Grün erhalten.

Am Anfang suchen wir uns Werte für die jeweiligen Spuren, dazu wird im eigentlichen Versuch der Momentanwert einer jeden Spur genutzt. In dieser Simulation verwenden wir dazu User Input.


In [4]:
# Beispiel für "Vergleich der Spuren"
#Kompatible Kombinationen: A(G1,G3) B(G1,L1) C(L1,L3) D(G3,L3) E(L2,L4) F(G2,G4) G(G2,L2) H(G4,L3)
#<X>#S = Status der Spur (0 = rot ; 1 = grün)

G1 = int(input('G1: '))
G2 = int(input('G2: '))
G3 = int(input('G3: '))
G4 = int(input('G4: '))
L1 = int(input('L1: '))
L2 = int(input('L2: '))
L3 = int(input('L3: '))
L4 = int(input('L4: '))


G1: 5
G2: 5
G3: 5
G4: 5
L1: 5
L2: 55
L3: 5
L4: 5

Nun definieren wir unsere Reset Funktion. Diese wird benötigt, da die Ampeln vor jeden Umschalten auf rot gesetzt werden.


In [5]:
def reset():
    G1S = 0
    G2S = 0
    G3S = 0
    G4S = 0
    L1S = 0
    L2S = 0
    L3S = 0
    L4S = 0

Nun addieren wir die jeweiligen numerischen Werte der Spuren, in den Kombinationen welche sich nicht beeinträchtigen. Daher finden wir heraus, welche beiden Spuren zeitgleich grün erhalten müssen, wenn wir die Anzahl an Fahrzeugen maximieren möchten. Danach erstellen wir eine Liste mit den einzelnen Werten, aus welcher wir nun den höchsten Wert herausnehmen.


In [6]:
A = G1+G2
B = G1+L1
C = L1+L3
D = G3+L3
E = L2+L4
F = G2+G4
G = G2+L2
H = G4+L3

l = [A,B,C,D,E,F,G]
m = max(l)

Nun definieren wir unsere check Funktion, welche uns erlaubt entsprechend der Werte die Ampeln umzuschalten. Dafür nutzen wir l.index(m) um herauszufinden an welcher Stelle der Liste der Maximalwert steht. Diese Position gleichen wir nun mit der Kombination an Spuren ab, und setzen den Status der entsprechenden Spuren auf 1 (grün). Des weiteren nutzen wir print() um eine Ausgabe mit der jeweiligen Kombination zu erhalten.


In [7]:
# A(G1,G3) B(G1,L1) C(L1,L3) D(G3,L3) E(L2,L4) F(G2,G4) G(G2,L2) H(G4,L3)

def check():
    reset()

    if(l.index(m)==1):
        print ('G1+G3')
        G1S = 1
        G3S = 1
    elif(l.index(m)==2):
        print ('G1+L1')
        G1S = 1
        G3S = 1
    elif(l.index(m)==3):
        print ('L1+L2')
        L1S = 1
        L2S = 1
    elif(l.index(m)==4):
        print('G3+L3')
        G3S = 1
        L3S = 1
    elif(l.index(m)==5):
        print('L2+L4')
        L2S = 1
        L4S = 1
    elif(l.index(m)==6):
        print('G2+G4')
        G2S = 1
        G4S = 1
    elif(l.index(m)==7):
        print('G2+L2')
        G2S = 1
        L2S = 1
    elif(l.index(m)==8):
        print('G4+L3')
        G4S = 1
        L3S = 1

Hier nutzen wir jetzt die oben beschriebene Funktion check() und geben die Menge an Fahrzeugen aus, welche mit dieser Möglichkeit der Schaltung bedient werden.


In [8]:
check()
print (m)


G3+L3
60

TO-DO

Abbruchbedingung

Für die Abbruchbedingung muss geschaut werden, ob die Summe der aktiven Spuren = 0 ist bevor die Grünphase vorbei ist, da sobald sich keine Fahrzeuge mehr auf der Spur befinden, auch kein Bedarf mehr für eine grüne Ampel besteht.

Grünphasentimer

Bei dem Timer für die Grünphase sollte variables geschtaltet werden. Hierfür sollte die Ampel schauen, wie viele Fahrzeuge in einer gegeben Phasenzeit es geschafft haben die Ampel zu überqueren. Sollte sich herausstellen, dass die Länge der Grünphase unzureichend war, sollte die Software eine entsprechende Verlängerung der Phase vornehmen. Des weiteren sollte die Länge der Phase abhängig von der Anzahl an Fahrzeugen sein. Dies sollte über das Verhältniss der Menge beider Spuren geregelt sein. Ein Beispiel hierfür wäre, dass eine Spur mit 30% mehr Fahrzeugen als die andere Spur auch eine ca. 30% längere Grünphase erhält. Dieses Verhältniss hat ein Maximum, welches durch den Abstand von Ampel und Sensor gegeben ist.

Fahrzeug Simulation Unity 3d

Datengewinnung

Um die optimalen Attribute des Fahrzeuges für einen optimalen Verkehrsfluss in der 3D Simulation zu garantieren, benötigen wir Daten. Da zu dieser Kombination aus Fahrzeug und Strecke keine vorgefassten Datenbanken gibt, müssen wir unsere eigenen Daten generieren. Hierfür wurde ein Pfad erstellt, welcher alle Möglichkeiten ausnutzte, und eine gewisse Strecke beträgt, um einigermaßen genaue Werte zu liefern. Die Werte, welche verändert werden ist auf der einen Seite die Maximalgeschwindigkeit des Fahrzeuges, sowie die Stärke des Abtriebes. Hierfür wurde eine Unendlichschleife implementiert, und bei jeder absolvierten Runde auf der Strecke wurden die beiden Werte um 10% erhöht. Paralel dazu läuft ein Datenlogger, welche jede Sekunde den aktuellen Wegpunkt, die Zeit in dieser Runde, die momentane Geschwindigkeit sowie die Position in der X und Z Achse. Diese Daten sind benötigt, um zu allererst die schnellste Rundenzeit zu ermitteln, aber auch um eine Überwachung der Kurvenlage und Geschwindigkeit an bestimmten Stellen im Parkour zu beobachten. Die aktuelle Maximalgeschwindigkeit und Motorstärke werden am Anfang jeder Runde angegeben. Eine Idee wäre es, sobald eine Verschlechterung der Zeit ermittelt wird, welche bei erhöhter Geschwindigkeit durch zum Beispiel starkes schleudern in der Kurve entstehen können, sollte die Geschwindigkeit auf den vorherigen Wert zurückgesetzt werden. Danach sollte der Wert zur Erhöhung der Geschwindigkeit halbiert werden (5% ; 2.5% ; 1.75% ; ...). Dies stellt sicher, dass ein präziser Wert für die beste Runde gesichert werden kann. Diese Daten wurden in der Datei data1.txt gespeichert. Die Auswertung dieser Daten befindet sich im gleichnamigen Journal.

Anfahrt an Kreuzung

Um eine geeignete Kontrolle der Ampel zu erreichen, müssen wir sicher stellen, dass die Fahrzeuge mit konstanter Geschwindigkeit anfahren. Hierzu versuchten wir zuerst die Position des Fahrzeuges beim erreichen des letzten Wegpunktes (Ende der Strecke) einfach auf den Startwert zu setzen. Das Problem bei dieser Technik ist allerdings, dass die Geschwindigkeit der vorherigen Runde beibehalten wird, und das Auto daher aus der Fahrt anfährt, und nicht aus dem Stand. Daher nutze ich den Unity Befehl rigidbody.velocity = vector3.null um alle Geschwindigkeiten auf 0 zu setzen, und dadurch das Auto zu zwingen aus dem Stand zu starten. Dazu führte ich eine Simulation durch, in welcher ich analog zum obrigen Abschnitt ('Datengewinnung'), einen Datenlogger nutze, welche hier nur die Anzahl der Runden, sowie die benötigte Zeit in eine Textdatei schrieb. Die Entsprechende Datei trägt den Namen lap1.txt. Der Nutzen dieser Datei ist, dass wir in der Lage sind zu überprüfen, ob das Fahrzeug sich mit einer konstanten Geschwindigkeit der Ampel nähert, da alle anderen Umstände gleich sind, müsste die Zeit, welche das Fahrzeug für die verschiedenen Runden benötigte konstant sein, und der Graph müsste einer Konstanten gleichen.


In [9]:
data = pd.read_csv('/Users/kay/SmartIntersection-Ger/Data/DrivingStraight/lap.txt',sep=' ')
data.columns=['lap','time']
data.set_index('lap')


Out[9]:
time
lap
2 17.32000
3 17.38000
4 17.34000
5 17.34000
6 17.34000
7 17.30000
8 17.30000
9 17.44000
10 17.34000
11 17.18001
12 17.36000
13 17.12000
14 17.30000
15 17.13998
16 17.34003
17 17.29999
18 17.44000
19 17.38000
20 17.34000
21 17.31998
22 17.32001
23 17.14001
24 17.31998
25 17.30002
26 17.45999
27 17.32001
28 17.07999
29 17.32001
30 17.24002
31 17.44000
32 17.33997
33 17.35999
34 17.40002
35 17.14001

In [18]:
np_data= data.values
print(np_data)


[[  2.       17.32   ]
 [  3.       17.38   ]
 [  4.       17.34   ]
 [  5.       17.34   ]
 [  6.       17.34   ]
 [  7.       17.3    ]
 [  8.       17.3    ]
 [  9.       17.44   ]
 [ 10.       17.34   ]
 [ 11.       17.18001]
 [ 12.       17.36   ]
 [ 13.       17.12   ]
 [ 14.       17.3    ]
 [ 15.       17.13998]
 [ 16.       17.34003]
 [ 17.       17.29999]
 [ 18.       17.44   ]
 [ 19.       17.38   ]
 [ 20.       17.34   ]
 [ 21.       17.31998]
 [ 22.       17.32001]
 [ 23.       17.14001]
 [ 24.       17.31998]
 [ 25.       17.30002]
 [ 26.       17.45999]
 [ 27.       17.32001]
 [ 28.       17.07999]
 [ 29.       17.32001]
 [ 30.       17.24002]
 [ 31.       17.44   ]
 [ 32.       17.33997]
 [ 33.       17.35999]
 [ 34.       17.40002]
 [ 35.       17.14001]]

In [42]:
plot = plt.plot(np_data[:,0],np_data[:,1])
plt.xlabel('Anzahl an Durchgängen')
plt.ylabel('s / runde')
plt.title('Geschwindigkeit des Fahrzeuges')
plt.yticks([16.5,17,17.5,18])
plt.show()


Hier ist zu betrachten, dass der Unterschied zwischen den Geschwindigkeiten recht gering ist, und daher können wir davon ausgehen, dass die Fahrzeuge mit einer konstanten Geschwindigkeit die Kreuzung erreichen. Dies bedeutet, dass unser Script zum abbremsen des Fahrzeuges erfolgreich war.


In [ ]: