Observera att detta är en arbetsversion av sidan!
Dölj style3dev.css för att bli av med annoteringarna!

Lektion 10: Trafiksimulering
Denna lektion innehåller Obligatorisk uppgift 5 (OU5) som består av fyra deluppgifter

Moment: Objektorienterad design och implementation. Simulering. Flera samverkande klasser.
Uppskattad arbetstid: Schemalagd handledningstid inkl redovisning: 12 timmar. Utöver det räkna med eget arbete med lika många timmar.
Arbetssätt: Arbeta gärna tillsammans med någon, men skriv egen kod. Diskutera med varandra

Tips: Använd debuggern

Redovisning: Obligatorisk redovisning enligt anvisningar på kurssidan.
Notera att den pythonkod du redovisar skall följa kodningsreglerna
Speciellt:
  • Strukturen på en python-fil: import-satser överst, sedan funktions-definitioner, sist funktionsanrop.
  • Ingen "lös" kod mellan funktions-definitionerna, all lös kod samlas till efter funktions-definitionerna.
  • Rimliga variabelnamn.
  • Inga globala variabler.
  • Utnyttja redan givna funktioner eller funktioner du redan har skrivit
Backup: När du arbetar på egen dator måste du se till att det löpande görs backup på de filer (py-filer) som du skapar under kursens gång. Om du har filerna enbart lagrade på din dator finns risk att allt försvinner vid ett datorhaveri eller vid stöld.
Samarbete, fusk och plagiat:
  • Muntligt samarbete är tillåtet och rekommenderas.
  • Men den kod du redovisar skall du skriva helt själv, förutom given kod som finns på kursens hemsida.
  • Du får INTE på något sätt dela med mig av kod/svar till någon annan, förutom det muntliga samarbetet.
  • Du får INTE ta del av kod/svar någon annan på något sätt delar med sig av, förutom det muntliga samarbetet.
  • Vid misstanke om fusk eller plagiat, precis som vid annan examination, kan detta anmälas till universitetets disciplinnämnd.

Simuleringar

Ett stort användningsområde för datorer är simuleringar d.v.s. att man skapar en datormodell av någon process (i vid bemärkelse) som man vill studera. Det kan röra sig om en modell för vädret eller klimatet, för hur snödrivor bildas kring olika byggnader, för trafikflödet i ett gatunät eller för en värld med sköldpaddor som föds, äter, lever och dör.

Meningen med modellen är att man skall kunna göra förutsägelser om vad som händer om man ändrar olika parametrar (släpper ut mer koldioxid, ändrar byggnadens form, gör en rondell eller ger sköldpaddorna mer mat ...).

Det är i regel väsentligt billigare att experimentera med en datormodell än att göra det i verkligheten (bygga en eller flera olika rondeller) eller att göra det med fysiska modeller (bygga modellhus och blåsa potatismjöl på dem i en vindtunnel).

När man bygger modeller så måste man i regel förenkla och bortse från många detaljer som man inte tror har någon eller åtminstone bara liten effekt.

Ett sätt att göra simuleringar är att dela upp tiden i ett antal diskreta tidssteg. Man utgår från ett starttillstånd och stegar sig sedan fram i tiden och noterar vad som händer i varje tidssteg:

skapa komponenterna (delarna) time = 0 while True: time = time + 1 uppdatera alla komponenter presentera tillståndet (eventuellt)

Ibland kan man veta exakt vad som sker i varje steg men oftast har man bara approximationer. Dessutom kan det finnas ett visst mått av slump som man får approximera med någon sannolikhet.

Problembeskrivning

Antag att vi skall simulera ett trafikflöde genom en korsning. Exempelvis följande:
Korsningen Kungsängsleden/Valsätravägen och Dag Hammarskölds väg
Trafik kommer från öster (in). Skall vidare västerut (W) eller söderut (S).
Man vet till sällan exakt vid vilka tidpunkter ett fordon anländer till en korsning men man kan ha mätt upp en intensitet så att man har en sannolikhet för att ett fordon skall anlända ett visst tidssteg. Flödet av fordon västerut respektive söderut regleras av trafiksignaler.

Program skall skrivas som ska simulera trafikflödet i olika typer av trafiksystem. Ett trafiksystem består, i vårt fall, av körfält (oftast kallat filer), trafiksignaler och köer. Vägar byggs upp av filer men vi kommer inte ha några vägobjekt.

Exempel 1

En väg med en trafiksignal:
system1

Exempel 2

En väg med en svängfil och två ljussignaler:
system1

Exempel 3

En korsning:
korsning0

Korsningen kontrolleras alltså av de fyra ljussignaler s1, s2, s3 och s4.

Ett problem i denna typ av korsningar är att fordon som kommer från E och vill svänga vänster mot S i korsningen blir blockerade av fordon som kommer från W vilket i sin tur blockerar fordon som vill köra rakt fram.

Exempel 4

För att lösa problemet kan man bygga svängfiler för fordon som kommer från E respektive W och som skall vänster i korsningen d.v.s. man vill ha en korsning av detta utseende:

korsning

Denna korsning har alltså svängfiler kontrollerade av signalerna vid s2 och s5.

En typisk fråga är då hur långa svängfilerna ska göras för att undvika att köerna på dessa växer in i de andra filerna.

Datormodell

Ett trafiksystem ska byggas upp av två generella komponenter: trafikljus (klassen Light) och körfält (klassen Lane). Det innehåller också ett varierande antal fordonsobjekt (klassen Vehicle). Vi kommer också behöva köer men sådana representeras enkelt med listor. Vidare används klassen Destinations för att simulera ankomster av fordon till systemet.

Klassen Destinations

Denna klass används för att simulera ankomster av nya fordon till trafiksystemet. Varje anrop till dess step-metod returnerar antingen None, som anger att inget fordon kom i detta steg, eller en destination ('W' eller 'S').

Klassen skulle kunna baseras på uppmätta eller uppskattade intensiteter vid olika tidpunkter på dygnet men i vårt fall returnerar den en förutbestämd sekvens. Detta gör det enklare att förutsäga resultatet och därmed också kontrollera att programmet är korrekt.

Klassen är helt given och behöver inte ändras. Den kan hämtas här: fDestinations.py. I denna fil finns även kod för att testa klassen..

Övn 1: Testkör denna fil, för att se hur klassen Destinations fungerar.

Klassen Vehicle

Klassen Vehicle är mycket enkel. Dess uppgift är bara att hålla reda på när fordonet skapades och vart det ska.

Ett fordon behöver alltså inte veta var det är, inte titta på signaler och inte kunna se andra fordon. Fordonen kan betraktas som pjäser som flyttas av "någon annan". Klassen är helt given och skall ej ändras. Koden (definitionen) finns i samma fil som klassen Lane. Se längre ned.

Klassen Lane

En fil är en struktur som innehåller ett antal fordon och ett antal tomrum. Fordonen kan bara komma in i början och lämna i slutet av filen d.v.s. filen är som ett "rör":
lane0
Se diskussion och specifikation av klassen.

Anmärkning: I fortsättningen använder vi ordet fil eller fil-objekt för att beteckna ett körfält och datafil eller Python-fil för att beteckna filer i datorns filsystem.

Klassen Lane är given, bortsett från en metod som skall skrivas klart. I övrigt skall den inte ändras. Klassen kan hämtas här: fVehicleAndLane_Stud.py. I denna fil finns även klassen Vehicle samt kod för att testa dessa båda klasser. (Kommentarerna i filen ser kanske lite konstiga ut för tecknen å, ä och ö, men när du laddar ned filen och öppnar den i exvis Thonny visas dessa tecken som normalt.)

Övn 2: Testkör denna fil, för att se hur klasserna Vehicle och Lane fungerar.

Uppgift A: Modifiera metoden number_in_lane i klassen Lane

Du skall modifiera metoden så att den beräknar och returnerar korrekt värde, se beskrivning ovan.

Uppgift B: Skriva klassen Light

Klassen används för att representera trafiksignaler. Se diskussion och specifikation av klassen.

Filen fLight_Stud.py finns att hämta som innehåller ett minimalt skal till klassen Light och kod som testar klassen om/när den är färdig. Klassen skall du skriva färdigt så att den passar specifikationen och den kod som testar den. (Kommentarerna i filen ser kanske lite konstiga ut för tecknen å, ä och ö, men när du laddar ned filen och öppnar den i exvis Thonny visas dessa tecken som normalt.)


Klassen TrafficSystem

Denna klass definierar ett specifikt trafiksystem. Den håller reda på vilka filer och vilka signaler som ingår och hur fordonen ska flyttas genom systemet.

För att t.ex. representera systemet i exempel 1 behövs två filer och en signal:

system1Impl
medan exempel 2 kräver tre filer och två signaler:
system2Impl

Se beskrivningen av klassen !

Exempel 1

Det som skall representeras är följande:
system1

d.v.s. ett system med två filer och en trafiksignal. Till systemet hör också en kö av inkommande fordon, till höger.

Detta görs av klassen TrafficSystem1 som finns färdig att hämta från: fTrafficSystem1_Stud.py, förutom en metod som skall modifieras, se uppgift C. Filen innehåller också kod som testar klassen TrafficSystem1. (Kommentarerna i filen ser kanske lite konstiga ut för tecknen å, ä och ö, men när du laddar ned filen och öppnar den i exvis Thonny visas dessa tecken som normalt.)

Övn 3: Testkör denna fil, för att se hur trafiksystemet fungerar.

Uppgift C: Modifiera metoden number_in_system i klassen TrafficSystem1

Se beskrivning i ovan text. Notera att metoden skall utnyttja andra lämpliga metoder för att lösa dess uppgift, om sådana finns. Bekräfta att det fungerar genom att köra testkoden i fTrafficSystem1_Stud.py. När metoden fungerar, gå vidare med övning 4.

Övn 4: Testa trafiksystemet genom att ändra trafikljusets rytm.


Uppgift D: Det andra trafiksystemet

Det som skall representeras är följande:
korsning
Det behövs tre filer och två signaler samt en kö med inkommande fordon till höger vid E.

Börja med göra en kopia av din python-fil fTrafficSystem1_stud.py (det fungerande första systemet). Döp kopian till fTrafficSystem2_Stud.py eftersom detta program skall simulera det andra systemet. I filen, byt klassens namn TrafficSystem1 till TrafficSystem2.

Trafiksystemet ska således bestå av tre filer (lane, lane_west och lane_south) och ljussignalerna light_west och light_south (tidigare kallade s1 och s2) samt en inkommande kö av fordon vid E:

korsningsimplementation

Det kan vara snyggt att ha korta körfiler efter (till vänster om) signalerna så att man ser hur fordonen passerar.

Vid punkten E finns det en (ej utritad).

Vid ett tidssteg kan en eller flera av följande saker hända i denna ordning:

En simulering består alltså av en tidsstegning där ovanstående saker händer. Det är lämpligt att momenten, precis som i verkligheten, utförs i den ordning de står ovan. Det kan vara praktiskt att låta din step-metod i sin tur anropa olika metoder du själv introducerar för att ta hand om olika delar. Det kan minska mängden upprepad kod i step och göra den mer överskådlig.

Observera att klasserna Vehicle, Lane och Light inte ska behöva ändras - endast TrafficSystem behöver göras om.

Indata till en simulering

Programmet styrs av följande indata:

De två första värdena varierar vanligen med tiden. Detta hanteras av klassen Destination. I verkliga simuleringar skulle klassen producera fordon enligt uppmätta intensiteter men, för att ni ska få kontrollerbara resultat, ger denna version samma sekvens varje gång.

Värdena i de två sista punkterna, d.v.s. längderna på filerna och ljussignalernas karakteristik, får du bestämma själv. Prova dock nedanstående och jämför med den statistik som visas längre ner.

lane_length = 11 # Length first (rightmost) lane lanews_length = 8 # Length lanes in front of signals light_period = 14 # Period for the lights west_green = 6 # Green period westbound light south_green = 4 # Green period southbound light

Resultat av en körning

  1. När programmet körs skall det löpande göras utskrifter enligt följande så att man ser vad som händer: Ögonblicksbilder av systemet fås med hjälp av snapshot.

    Exempel på sekvenser av ögonblicksbilder med ovan givna indata:

    Tidssteg 0-5:
    0: ( 0)(G) [........] [...........] [] (G) [........] 1: ( 1)(G) [........] [..........S] [] (G) [........] 2: ( 2)(G) [........] [.........SS] [] (G) [........] 3: ( 3)(G) [........] [........SSS] [] (G) [........] 4: ( 4)(G) [........] [.......SSSS] [] (R) [........] 5: ( 5)(G) [........] [......SSSSS] [] (R) [........]
    Förklaring: I tidssteg 5 (ovan), är det sammanlagt 5 st fordon i systemet, trafikljuset för W-filen visar grönt, trafikljuset för S-filen visar rött. Alla fem fordonen har destinationen S.



    Tidsteg 19-25:

    19: (19)(G) [.....W..] [SWWSWSSSWSW] [] (R) [SSSSS.SS] 20: (20)(R) [....W...] [WWSWSSSWSWW] [] (R) [SSSSSSSS] 21: (21)(R) [...W...W] [WSWSSSWSWWS] [] (R) [SSSSSSSS] 22: (22)(R) [..W...WW] [SWSSSWSWWSW] [] (R) [SSSSSSSS] 23: (23)(R) [.W...WW.]*[SWSSSWSWWSW] ['W'] (R) [SSSSSSSS] 24: (23)(R) [W...WW..]*[SWSSSWSWWSW] ['W'] (R) [SSSSSSSS] 25: (24)(R) [W..WW...]*[SWSSSWSWWSW] ['W', 'S'] (R) [SSSSSSSS]
    Om det i ett tidssteg råder "blockering", dvs att ett fordon ej kan röra sig från lane till en av de andra filerna, så skall detta synas vid utskriften. Låt detta indikeras av *-tecknet vid delningspunkten mellan filerna. I de tre sista stegen (23-25) är delningspunkten blockerad (indikeras av *-tecknet) och dessutom finns det först ett och sedan två fordon i kön.
  2. Statistik innehållande:
    • Genomsnittliga, minimala och maximala tider (antal tidssteg) för fordon att passera respektive ljussignal (väst/syd).
    • Andel tidssteg som delningspunkten X varit blockerad, se figuren ovan. Med blockerad menas att det inte går flytta ett fordon från filen lane på grund av att sista platsen i dess destinationsfil är upptagen.
    • Andel tidssteg som det funnits fordon i kön vid startpunkten (E).

    Samla tiderna för fordon som lämnar systemet i två list-objekt, ett för vardera utgång, och använd dessa för att ta fram statistiken. Använd antingen skriv en egen kod med statistik-metoder (och lägg i en fil) eller använd pythons standard-modul statistics där finns bl.a. funktionerna min, max, mean och median

    Statistiken skall skrivas ut av metoden print_statistics i TrafficSystem2. Metoden skall kunna anropas när som helst under simuleringen. Exempel på utskrift med ovanstående indata.

    Statistics after 100 timesteps: Created vehicles: 48 In system : 8 At exit West South Vehicles out: 21 19 Minimal time: 20 21 Maximal time: 38 53 Mean time : 27.6 39.3 Median time : 27.0 43.0 Blocked : 18.0% Queue : 14.0%
Här finns en programkörning med samtliga utskrifter

Innan du redovisar

  1. Se till att din pythonkod du redovisar skall följa kodningsreglerna
  2. Du skall förstå och kunna förklara även den givna koden, inte bara den du skrivit själv.
  3. Kontrollera att all efterfrågad statistik finns med och att resultaten är korrekta!
  4. Se till att programmet producerar utskrifter enligt ovanstående exempel.

Här följer några helt frivilliga övningar för den som har tid och tycker det är kul

Dessa görs lämpligen efter att du fått OU5 godkänd. De frivilliga övningarna skall ej redovisas.
  1. Om kön kontinuerligt växer inträffar något vi kanske kan kalla trafikinfarkt. Ett sätt att åstadkomma detta är att ändra trafikljusens periodicitet. Lägg till någon typ av felhantering på lämplig plats i koden som "larmar" när kön håller på att bli för lång. Bestäm själv om trafiksystemet skall åtgärda det på något sätt, annat än att bara "larma".
  2. Med konstruktorn i klassen TrafficSystem2 skapas alltid samma trafiksystem. Vi gör det med satsen:
    ts = TrafficSystem2()
    Det har fixa värden för filernas längder och trafikljusens periodicitet. Vi kan säga att det med konstruktorn skapas ett standardmässigt trafiksystem. Konstruktorn har inga parametrar med vilka man kan påverka det hela. Det skapas alltid är samma objekt. Men om man istället definierar konstruktorn med parametrar som har defaultvärden ges möjlighet till att skapa olika objekt. Exempel:
    light_period = 14  # periodlängden för båda trafikljusen
    west_green = 6     # gröntiden för trafikljuset som styr west-filen
    south_green = 4    # gröntiden för trafikljuset som styr south-filen
    # Skapa ett trafiksystem med följande egenskaper för de två trafikljusen.
    ts = TrafficSystem3(light_period, west_green, south_green)
    
    Angående parametrar med defaultvärden, se föreläsningen om funktioner (Fö3).

    Skapa en ny klass TrafficSystem3 som är en kopia av TrafficSystem2, men där den nya klassen har två konstruktorer. Den ena skall vara standardkonstruktorn (från TrafficSystem2). Den andra konstruktorn skall vara en där man ger parametrar som styr filernas längder och trafikljusens periodicitet. Skapa ett trafiksystem med satsen:
    ts = TrafficSystem3(...).
    Testkör och se att det fungerar.

  3. Klassen Destinations skapar alltid samma flöde av fordon. Ändra konstruktorn i klassen Destinations så att man kan skapa ett annorlunda flöde. Det kan ex.vis göras beroende av slumpen.
  4. Det vore väl elegant om trafiksystemet visualiserades med grafik istället. Fundera på om och hur metoden snapshot även kunde rita en bild över trafiksystemet. Bara fundera..., ty det är ett mycket stort jobb.

Tillbaka