Maciej Adryan
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: | | ![](sources/pg-logo.jpg) | 'City Traffic Simulator' author: | | Maciej Adryan | Aleksander Błaszkiewicz | Michał Matejuk | Jakub Brzozowski header-includes: | \usepackage{fontspec} \usepackage{titletoc} \usepackage[english,polish]{babel} \selectlanguage{polish} \setmainfont{Arial} \raggedbottom \usepackage[fontsize=11pt]{scrextend} \usepackage{setspace} \linespread{1.20} \usepackage{xcolor} \usepackage{color} \renewcommand*{\figureautorefname}{rys.} \usepackage{float} \floatplacement{figure}{H} \lstset{ escapeinside={(*@}{@*)}, % if you want to add LaTeX within your code } \lstdefinelanguage{Dockerfile} { morekeywords={FROM, RUN, CMD, LABEL, MAINTAINER, EXPOSE, ENV, ADD, COPY, ENTRYPOINT, VOLUME, USER, WORKDIR, ARG, ONBUILD, STOPSIGNAL, HEALTHCHECK, SHELL}, morecomment=[l]{\#}, morestring=[b]", frame=single, aboveskip=1ex, belowskip=1ex, framexrightmargin=0em, framexleftmargin=0em, } \lstdefinestyle{yaml}{ basicstyle=\color{blue}\scriptsize, breaklines=true, rulecolor=\color{black}, string=[s]{'}{'}, stringstyle=\color{blue}, comment=[l]{:}, commentstyle=\color{black}, morecomment=[l]{-}, breaklines=true, columns=fullflexible, frame=single, aboveskip=1ex, belowskip=1ex, framexrightmargin=0em, framexleftmargin=0em, } \usepackage{titlesec} \titleformat{\chapter}[hang]{\normalfont\fontsize{13}{13}\bfseries}{\thechapter.}{1em}{\MakeUppercase} \titleformat{\section} {\normalfont\fontsize{11}{11}\bfseries\itshape}{\thesection}{1em}{} \titleformat{\subsection} {\normalfont\fontsize{11}{11}\itshape}{\thesubsection}{1em}{} \usepackage{indentfirst} \setlength{\parindent}{1.25cm} \setlength{\parskip}{1pt} \selectfont \definecolor{mygreen}{rgb}{0,0.6,0} \definecolor{mygray}{rgb}{0.5,0.5,0.5} \definecolor{mymauve}{rgb}{0.58,0,0.82} \selectlanguage{polish} \titlespacing*{\chapter}{0pt}{12pt}{6pt} \lstset{ % backgroundcolor=\color{white}, % choose the background color basicstyle=\scriptsize, % size of fonts used for the code breaklines=true, % automatic line breaking only at whitespace captionpos=b, % sets the caption-position to bottom commentstyle=\color{mygreen}, % comment style escapeinside={\%*}{*)}, % if you want to add LaTeX within your code keywordstyle=\color{blue}, % keyword style stringstyle=\color{mymauve}, % string literal style frame=single, aboveskip=1ex, belowskip=1ex, framexrightmargin=0em, framexleftmargin=0em, } \makeatletter \newcommand{\fakechapter}[1]{% % go to vertical mode and don't allow a page break here \par\nopagebreak % step up the counter \refstepcounter{chapter} % teach nameref the title \def\@currentlabelname{#1} % add to TOC \addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1} } \newcounter{oldtocdepth} \newcommand{\hidefromtoc}{% \setcounter{oldtocdepth}{\value{tocdepth}}% \addtocontents{toc}{\protect\setcounter{tocdepth}{-10}}% } \newcommand{\unhidefromtoc}{% \addtocontents{toc}{\protect\setcounter{tocdepth}{\value{oldtocdepth}}}% } \usepackage{hyperref} \usepackage{bookmark} classoption: twoside subparagraph: yes ... \titlespacing*{\chapter}{0pt}{12pt}{6pt} \selectlanguage{polish} \hidefromtoc \begin{otherlanguage}{polish} \pagebreak \chapter*{Streszczenie} \label{StreszczenieRozdzial} \addcontentsline{toc}{chapter}{\protect\numberline{}Streszczenie} Celem pracy był projekt i realizacja prototypowego symulatora ruchu miejskiego i mikroserwisów obejmujących aspekty, jak: \begin{itemize} \item wpływ zachowań kierowców na zakorkowanie miasta, \item optymalny dobór infrastruktury drogowej. \end{itemize} W ramach pracy stworzono symulator ruchu miejskiego, który składa się z 4 komponentów: \begin{itemize} \item symulator - aplikacja wykonana w technologii Unity odpowiedzialna za zbudowanie mapy i przeprowadzenie symulacji zgodnie z wprowadzonymi parametrami. Pozwala na pełną dowolność w budowaniu infrastruktury, zapewniając nie tylko różne gotowe elementy, ale także możliwość tworzenia własnych. Zawiera warstwę logiczną, która pozwala kierowcom respektować zasady ruchu drogowego jak dozwolona prędkość, światła drogowe, skrzyżowania ze światłami, skrzyżowania z pierwszeństwem, skrzyżowania z warunkowym skrętem oraz ronda, \item backend - aplikacja wykonana w technologii .NET z wykorzystaniem bazy danych NoSQL (MongoDB). Odpowiedzialna za komunikację z pozostałymi podsystemami, zapisuje dane dotyczące przeprowadzanych symulacji, uruchamia backend obliczeniowy, dostarcza gotowych danych statystycznych do aplikacji frontendowej, \item backend obliczeniowy - aplikacja wykonana w Pythonie z wykorzystaniem frameworka Flask. Służy do tworzenia statystyk dotyczących przebiegu symulacji oraz udostępniania API do inicjowania tych statystyk. Statystyki są tworzone dla każdego uruchomienia symulacji na podstawie danych otrzymanych z aplikacji backendowej, \item frontend - aplikacja wykonana za pomocą pythonowego frameworka Dash by Plotly. Służy do wizualizacji otrzymanych wyników oraz, w razie potrzeby, przetwarzania tych danych. Celem tego podsystemu jest uproszczenie analizy wyników oraz umożliwienie użytkownikowi odtworzenia przebiegu symulacji. \end{itemize} Wszystkie podsystemy są ze sobą ściśle powiązane i komunikują się ze sobą warstwą sieciową. Wysokopoziomowym zamysłem jest umożliwienie użytkownikowi frontendowego serwisu przeglądania wyników wygenerowanych przez symulator i obliczonych przez backend obliczeniowy, do których ma dostęp poprzez backend. W rezultacie powstał kompletny system, na który składają się mikroserwisy i sam symulator, które połączone potrafią tworzyć, analizować i zachowywać wyniki przeprowadzonych symulacji. System pozwala na badanie rozległych sieci dróg, ale także dzięki swojej dokładności jest w stanie zbadać przepustowości pojedynczych elementów drogowych w zależności od warunków zewnętrznych. Aleksander Błaszkiewicz był odpowiedzialny za wykonanie symulatora w technologii Unity, skonfigurowanie serwera VPS, zaimplementowanie ciągłego dostarczania oraz przeprowadzenie badań. Jest autorem rozdziałów i podrozdziałów \ref{definicjaSymulacjiPodrozdzial}, \ref{probkowaniePodrozdzial}, \ref{symRuchuWgrach}, \ref{InfrastrukturaPodrozdzial}, \ref{SymulatorPodrozdzial}, \ref{pieckimigowo}, \ref{autonomicznePojazdyPodrozdzial}, \ref{rondoVSskrzyzowanie}, \ref{zajmowaniePasowPodrozdzial} oraz współautorem rozdziałów i podrozdziałów \ref{ProjektSystemuRozdzial}, \ref{PhantomTrafficPodrozdzial} i \ref{PodsumowanieRozdzial}. Maciej Adryan był odpowiedzialny za utworzenie warstw frontendowych oraz backendowych aplikacji dashboardowej opisanej w podrozdziale \ref{AplikacjaDashboardowaPodrozdzial}, opracowanie systemu tworzenia niniejszego dokumentu tekstowego opisanego w podrozdziale \ref{ProcesTworzeniaDokumentuPodrozdzial} oraz sprawował pieczę nad poprawnością jego powstawania. Jest autorem rozdziałów i podrozdziałów: \ref{AplikacjaDashboardowaPodrozdzial} i \ref{ProcesTworzeniaDokumentuPodrozdzial} oraz współautorem rozdziałów i podrozdziałów: \ref{WstepICelRozdzial}, \ref{SymulacjaRuchuRozdzial}, \ref{ProjektSystemuRozdzial} i \ref{PodsumowanieRozdzial}. Jakub Brzozowski był odpowiedzialny za przetwarzanie danych o symulacjach w celu późniejszej analizy przebiegu symulacji. W tym celu stworzył aplikację w języku Python wykorzystującą framework Flask, która agreguje i dostosowuje dane do formatu wymaganego do wyświetlenia w aplikacji dashboardowej. Jest autorem rozdziałów i podrozdziałów: \ref{BackendObliczeniowyPodrodział} oraz współautorem rozdziałów i podrozdziałów: \ref{ProjektSystemuRozdzial} i \ref{PodsumowanieRozdzial}. Michał Matejuk był odpowiedzialny za połączenie wszystkich warstw systemu przez stworzenie aplikacji backendowej w technologii ASP .NET będącej pośrednikiem pomiędzy aplikacją symulatora a backendem obliczeniowym i aplikacją frontendową oraz stworzenie, utrzymywanie i operacje na danych w dokumentowej bazie danych MongoDB. Jest autorem rozdziałów i podrozdziałów: \ref{AplikacjaBackendowaPodrozdzial}, \ref{UruchomieniePodrozdzial}, \ref{PhantomTrafficPodrozdzial} oraz współautorem rozdziałów i podrozdziałów: \ref{WstepICelRozdzial}, \ref{AnalizaRozdzial} i \ref{PodsumowanieRozdzial}. \end{otherlanguage} \begin{otherlanguage}{english} \pagebreak \chapter*{Abstract} \label{StreszczenieAngRozdzial} \addcontentsline{toc}{chapter}{\protect\numberline{}Abstract} The goal of the work was to design and implement a prototype of a city traffic simulator that covers aspects such as: \begin{itemize} \item the impact of driver behavior on city congestion, \item optimal selection of road infrastructure \end{itemize} The project consists of four subsystems: \begin{itemize} \item simulator - a Unity-based application responsible for building the map and conducting the simulation according to the parameters introduced. It allows full flexibility in building infrastructure, providing not only various ready-made elements but also the ability to create custom ones. It contains a logical layer that allows drivers to respect traffic rules such as allowed speed, traffic lights, intersections with lights, intersections with priority, intersections with conditional turns, and roundabouts, \item backend - a .NET-based application using a NoSQL database (MongoDB). Responsible for communication with the other subsystems, it stores data on conducted simulations, starts the computational backend, and provides ready statistical data to the frontend application, \item computational backend - a Python-based application using the Flask framework. It is used to create statistics on the simulation and to provide an API for initiating these statistics. Statistics are created for each simulation run based on data obtained from the backend application, \item frontend - an application created using the Python Dash by Plotly framework. It is used to visualize the obtained results and, if necessary, to process these data. The goal of this subsystem is to simplify the analysis of results and enable the user to recreate the simulation. \end{itemize} All subsystems are tightly connected and communicate with each other through the network layer. The high-level idea is to enable the user of the frontend service to browse the results generated by the simulator and calculated by the computational backend, which they have access to through the backend. \end{otherlanguage} \selectlanguage{polish} \titlespacing*{\chapter}{0pt}{0pt}{6pt} \unhidefromtoc \titleformat{\chapter}[hang]{\normalfont\fontsize{13}{13}\bfseries}{\thechapter.}{1em}{\MakeUppercase} \titlespacing*{\chapter}{0pt}{-3em}{1.1\parskip} \titlelabel{\thetitle\quad} \linespread{1.0} \tableofcontents \linespread{1.20} \titlespacing*{\chapter}{0pt}{12pt}{6pt} \pagebreak \chapter*{Słownik skrótów i pojęć (Michał Matejuk)} \addcontentsline{toc}{chapter}{\protect\numberline{}Słownik skrótów pojęć (Michał Matejuk) } \fakechapter{A fake chapter}\label{SlownikPojecPodrozdzial} - *frontend* - część systemu informatycznego odpowiedzialna za wyświetlanie i interakcję z użytkownikiem (UI), zazwyczaj za pomocą przeglądarki internetowej, - *backend* - część systemu informatycznego odpowiedzialna za logikę biznesową i przetwarzanie danych, zazwyczaj uruchamiana na serwerze, - *CI/CD* (Continuous Integration/Continuous Deployment) - metodologia tworzenia oprogramowania, polegająca na ciągłym integrowaniu i wdrażaniu zmian do aplikacji, - *krzywa Beziera* - matematyczny sposób reprezentowania krzywych w grafice komputerowej, służący do tworzenia wielokątów i splajnów, - *Github* - platforma internetowa, na której użytkownicy mogą przechowywać i współdzielić swój kod. GitHub umożliwia współpracę nad projektami oraz ich wersjonowanie, dzięki czemu możliwe jest śledzenie zmian w kodzie oraz powrót do poprzednich wersji, - *Github Actions* - narzędzie dostępne na platformie GitHub, umożliwiające automatyzację procesów związanych z tworzeniem, testowaniem i wydawaniem oprogramowania. Dzięki niemu można zdefiniować sekwencję działań, które zostaną wykonane w odpowiedzi na określone zdarzenia (np. push do repozytorium) i uruchamiać je w chmurze. GitHub Actions pozwala na stworzenie spójnego i zautomatyzowanego procesu tworzenia i wydawania oprogramowania. Inne znane narzędzia tego typu to np. Azure DevOps lub Jenkins, - *metadane* - informacje o innych danych. Są one zazwyczaj dodawane do plików lub dokumentów, aby opisać ich zawartość lub ułatwić ich wyszukiwanie. Metadane w projekcie opisują między innymi tagi/typy pojazdów lub numer uruchomienia symulacji, - *caching* - proces przechowywania danych w celu ich szybszego dostępu w przyszłości. W przypadku stron internetowych caching polega na zapisywaniu kopii stron lub poszczególnych elementów na lokalnym komputerze użytkownika, dzięki czemu przy kolejnym odwiedzeniu tej samej strony część danych jest pobierana z lokalnej pamięci, co znacznie przyspiesza jej wczytanie, - *API* (ang. *Application Programming Interface*) - interfejs programistyczny, który umożliwia komunikację pomiędzy różnymi aplikacjami lub systemami. API definiuje sposób, w jaki aplikacje mogą wymieniać dane i wykonywać określone operacje. Dzięki API możliwe jest wykorzystywanie funkcjonalności innych aplikacji bez konieczności zgłębiania ich wewnętrznej struktury, - *REST* (ang. *REpresentational State Transfer*) - architektura sieci Web, która definiuje zasady tworzenia i wymiany danych w sieci. REST opiera się na wykorzystaniu protokołu HTTP do przekazywania danych w postaci dokumentów, takich jak XML lub JSON. REST jest często wykorzystywany do tworzenia API, ponieważ umożliwia proste i elastyczne wymianę danych pomiędzy różnymi aplikacjami. - *Docker* - narzędzie, które umożliwia tworzenie, uruchamianie i zarządzanie kontenerami w dowolnym systemie operacyjnym. Kontenery to lekkie i izolowane od siebie środowiska, które zawierają wszystko, co jest potrzebne do uruchomienia aplikacji, w tym kod, zależności i konfiguracje. Dzięki temu, że kontenery są lekkie i izolowane, można je łatwo przenosić i uruchamiać na różnych systemach operacyjnych bez konieczności modyfikowania kodu ani konfiguracji. Docker umożliwia też tworzenie i wdrażanie aplikacji w postaci kontenerów, co ułatwia zarządzanie nimi i wdrażanie na różnych środowiskach, - *YAML* - język opisowy, który służy do zapisywania danych w formie tekstowej. Jest on bardzo prosty, dzięki czemu łatwo można go wykorzystać do konfiguracji różnych aplikacji i systemów. YAML opiera się na indeksowaniu danych za pomocą kluczy i wartości, a także na strukturze danych za pomocą odpowiednich znaków takich jak dwukropek, kreska i wcięcia. Dzięki temu dane zapisane w YAML są bardzo czytelne nawet dla osób bez doświadczenia w programowaniu. - *spawner* - obiekt w unity odpowiedzialny za instancjonowanie innych obiektów typu **GameObject**, - *DTO* (ang. *Data transfer object*) - obiekt, który można zserializować i jest używany do przesyłania informacji pomiędzy komponentami, - *UX* (ang. *User experience*) - określa doświadczenie użytkownika, jakie towarzyszy mu podczas korzystania z produktu, - *Kubernetes* - przenośna, rozszerzalna platforma oprogramowania służąca do zarządzania zadaniami i serwisami uruchamianymi w kontenerach, - *SSR* (ang. Server Side Rendering) - technika renderowania stron internetowych, w której proces renderowania strony jest przeprowadzany na serwerze, a nie w przeglądarce użytkownika. # Wstęp i cel pracy \label{WstepICelRozdzial} Większość ludzi w miastach porusza się pojazdami, które korzystają z infrastruktury drogowej. Jednym z największych problemów użytkowników sieci drogowych są zatory. W ogólności, im więcej pojazdów pojawi się w miejskiej sieci, tym dłużej zajmie pokonanie danej trasy. Aby zminimalizować skutki rosnącej liczby pojazdów na drogach, ważne jest odpowiednie dobranie elementów infrastruktury, ale także poprawne zachowanie kierowców. Dużą rolę odgrywają miejskie zespoły planowania ruchu - specjalne jednostki odpowiedzialne za zarządzanie i optymalizację ruchu drogowego w miastach. Są one zaangażowane w monitorowanie ruchu drogowego, wprowadzanie rozwiązań mających na celu poprawę jego efektywności oraz współpracę z kierowcami i użytkownikami drogi w celu promowania poprawnych zachowań. Zespoły te korzystają z różnych metod optymalizacji ruchu drogowego, w tym analizy danych ruchu, modeli matematycznych i symulacji komputerowych. Dzięki temu mogą one przeprowadzać testy różnych scenariuszy i zmian w infrastrukturze drogowej, aby zidentyfikować te, które prowadzą do najlepszych wyników. Celem projektu jest zbadanie, w jakim stopniu wybrane czynniki wpływają na powstawanie zatorów reprezentując przepływ samochodów przez różne konfiguracje dróg w postaci wykresów i obliczając statystyki jak mapy cieplne pozycji oraz mapy cieplne prędkości Kluczowymi zagadnieniami, które należy przeanalizować w ramach tej pracy inżynierskiej są: - rodzaje symulacji, - zasady budowania symulatora ruchu miejskiego, - interakcje pomiędzy mikroserwisami, - utrzymywanie serwera VPS, - automatyczne wdrażanie i ciągłe dostarczanie, - rola autonomicznych pojazdów w usprawnieniu ruchu w sieci miejskiej (przypadek braku czasu reakcji), - zjawisko tworzenia się sztucznych zatorów na drogach z powodu wysokiego czasu reakcji kierowców, - czynniki determinujące wymianę tradycyjnego skrzyżowania na rondo, - wpływ harmonogramu świateł na przepustowość skrzyżowań, Kolejne rozdziały tej pracy przedstawią proces wytwarzania oprogramowania, użyte przez nas technologie, oraz podejście do problemu i zaproponowane rozwiązania: - Rozdział 2 - definicja symulatora, rodzaje symlacji, problem korków w miastach oraz istniejące rozwiązania, - Rozdział 3 - opisuje cel i przeznaczenie systemu oraz specyfikacje wymagań funkcjonalnych i użytkowych - Rozdział 4 - opis infrastruktury i środowiska, na którym udostępnione są poszczególne mikroserwisy, opis kluczowych elementów symulatora, frontendu, backend oraz backendu obliczeniowego, diagramy przepływu danych pomiędzy komponentami - Rozdział 5 - badania przeprowadzone w symulatorze oraz wnioski tych badań # Symulacja ruchu drogowego \label{SymulacjaRuchuRozdzial} ## Definicja symulacji (Aleksander Błaszkiewicz) \label{definicjaSymulacjiPodrozdzial} *Symulacja* to przybliżone odtwarzanie zjawisk czy zachowań pewnego obiektu i środowiska, w którym się ten obiekt znajduje za pomocą modelu matematycznego, który stanowi próbę przybliżenia rzeczywistych zachowań obiektu przy jednoczesnym ograniczeniu jego złożoności. Stopień dokładności modelu zależy od konkretnych potrzeb/zastosowania, ale przy nietrywialnych problemach będzie on zawsze, w mniejszym lub większym stopniu, zależny od modelowanego zjawiska. > ‘‘All models are wrong but some are useful’’ - George Box Symulatorem nazywamy program, który pozwala przeprowadzić symulację. Taki program charakteryzuje się tym, że jest przystosowany do danego typu symulacji. Przykładowo w przypadku symulatorów ruchu miejskiego będą to dedykowane widoki do edycji parametrów pojazdów, układu ulic czy harmonogramu świateł. Cele i zastosowania symulatorów są bardzo różnorodne. Przykładowymi celami mogą być m.in.: - zbadanie, czy dany system działa z optymalną wydajnością, - trenowanie pracowników, - rozrywka (gry wideo), - przeprowadzenie symulacji w warunkach niemożliwych do odwzorowania w aktualnych warunkach na Ziemii (np. lądowanie na innej planecie), - przeprowadzenie symulacji w warunkach na tyle niebezpiecznych, że stwarzałyby zagrożenie dla ludzi. Problemy tworzenia symulacji zależą od badanego zjawiska oraz środowiska, w którym pracują, ale do najczęstszych należą: - są one zwykle bardzo złożonymi systemami, które wymagają dużej ilości danych wejściowych i wyjściowych, a także precyzyjnego modelowania różnych procesów i zjawisk. Dlatego tworzenie symulatorów może być czasochłonne i kosztowne, - dokładność symulacji (niekiedy odwzorowywane modele muszą być bardzo dokładne, aby zapewnić wiarygodne wyniki), - trudność w doborze właściwych parametrów, - ograniczenia wydajnościowe programu symulującego, - ograniczenia czasowe (symulacja długotrwałych procesów może być czasochłonna), - ryzyko błędów w implementacji. ## Typy symulacji ### Próbkowanie (Aleksander Błaszkiewicz gówno) \label{probkowaniePodrozdzial} Symulacje można podzielić na dwa główne typy[^rodzaje_symulacji_twi]: - *dyskretne* - zdarzenia dzieją się w określonych punktach czasowych, zmieniając stan symulowanego obiektu. Liczba "punktów" czasu w trakcie symulacji jest skończona, - *ciągłe (dynamiczne)* - stan zmienia się w sposób ciągły - taka symulacja zamodelowana jest przy pomocy równań różniczkowych. Trzeba przy tym pamiętać, że komputery z powodu swojej cyfrowej natury nie mogą przeprowadzać rzeczywistych ciągłych symulacji. Bardzo często spotykanym zabiegiem jest ustalenie na tyle krótkiego odstępu pomiędzy kolejnymi punktami symulacji, aby symulacja dawała wrażenie ciągłej (ang. *near real-time*). Zabieg dzielenia czasu na coraz mniejsze odcinki ma na celu zwiększenie realizmu symulacji. [^rodzaje_symulacji_twi]: Internet [WHAT IS SIMULATION? WHAT DOES IT MEAN? (DEFINITION AND EXAMPLES https://www.twi-global.com/technical-knowledge/faqs/faq-what-is-simulation)](https://www.twi-global.com/technical-knowledge/faqs/faq-what-is-simulation) ### Rozmiar (Maciej Adryan) [^rodzaje_symulacji]: Internet [Burghout, W., Koutsopoulos, H. N., & Andréasson, I. (2005). Hybrid Mesoscopic–Microscopic Traffic Simulation. Transportation Research Record, 1934(1), 218–225.](https://doi.org/10.1177/0361198105193400123) Ze względu na wymagania oraz możliwości przeprowadzenia symulacji, naturalnie wyodrębniły się trzy najpopularniejsze rodzaje symulacji ruchu drogowego[^rodzaje_symulacji]: - mikrosymulacje (ang. *micro simulations*) - pozwalają uzyskać najwyższy poziom detali oraz szczegółowość, - symulują poszczególne agenty biorące udział w ruchu drogowym. - mesosymulacje (ang. *meso simulations*) - pozwalają uzyskać niższy poziom detali w porównaniu do mikrosymulacji, - są szybsze, mniej czasochłonne i zasobochłonne, - ze względu na mniejszą złożoność ułatwiają symulowanie większych i bardziej zaawansowanych scenariuszy w porównaniu do mikrosymulacji. - symulacje hybrydowa (ang. *hybrid simulations*) - pozwalają na dobór poziomu detali w taki sposób, aby wybrane komponenty były symulowane w sposób dokładny lub mniej dokładny w zależności od potrzeb użytkownika. ## Symulatory ruchu miejskiego w grach (Aleksander Błaszkiewicz) \label{symRuchuWgrach} Popularnym zastosowaniem symulatorów ruchu miejskiego są gry. Logika symulacji pozwala na zintegrowanie elementów infrastruktury drogowej z resztą gry. Dzięki symulacji budowanie dróg nie jest prostym zadaniem, a wymaga przemyślenia, w jaki sposób funkcjonuje miasto. Jednym z przykładów gier opierających się na symulowaniu ruchu drogowego jest gra **Cities: Skylines** (\autoref{CitiesSkylines}). W grze tej gracz wciela się w projektanta miasta. Jego zadaniem jest zaprojektowanie sieci drogowej w taki sposób, żeby sztuczni agenci mogli sprawnie pokonywać miasto i żeby nie powstawały korki. ![Gra "Cities: Skylines" \label{CitiesSkylines}](https://i.imgur.com/1MhjbpN.png) Kolejnym przykładem gry opierającej się na podobnej mechanice jest gra **Mini Metro**. W grze tej gracz staje się planistą sieci metra, którego zadaniem jest optymalizacja tej sieci (\autoref{MiniMetro}). ![Gra"MiniMetro" \label{MiniMetro}](https://i.imgur.com/FRFGTNX.png) Innym przykładem z rodziny gier **Mini** jest **Mini Motorways**. Zasada działania jest taka sama jak w przypadku poprzedniej gry, jednak tutaj zamiast sieci metra gracz planuje sieć drogową aut (\autoref{MiniMotorways}). ![Gra "Mini Motorways" \label{MiniMotorways}](https://i.imgur.com/R64s5zi.png) Ostatni przykład to gra **Freeways**, w której gracz ma się skupić bardziej na optymalizacji jednego skrzyżowania, niż na całej sieci drogowej (\autoref{Freeways}). ![Gra "Freeways" \label{Freeways}](https://cdn.cloudflare.steamstatic.com/steam/apps/780210/ss_6932c9f368ea31cf1d3baa33361b3266feb5f58a.1920x1080.jpg?t=1572994131) ## Zastosowanie symulatorów do planowania ruchu miejskiego (Maciej Adryan) Oprócz zastosowania w dziedzinie rozrywki, symulatory znajdują szerokie zastosowanie w praktyce. W zależności od stopnia zaawansowania pozwalają one na mniej lub bardziej skomplikowane i szczegółowe symulowanie, a co za tym, badanie oraz dalszą analizę ruchu drogowego której celem mogą być m.in: - planowanie sieci ulic, - planowanie rozmieszczenia skrzyżowań oraz dobrania prawidłowego typu spośród skrzyżowań: - o ruchu okrężnym (ronda), - o ruchu kierowanym, - bezkolizyjne, - równorzędne, - nierównorzędne, - w kształcie litery "Y" (rozwidlenia), - w kształcie litery "T", - w kształcie litery "X". - planowanie rozmieszczenia sygnalizacji świetlnej, - planowanie rozmieszczenia znaków drogowych, - planowanie odpowiednich dla infrastruktury oraz architektury ograniczeń prędkości, - unikanie zastojów (ang. *deadlocks*), - planowanie architektury mającej zapewnić pieszym bezpieczeńtwo - rozplanowanie przejść dla pieszych. - badanie wpływu tymczasowego bądź stałego zamknięcia danej ulicy (np. celem jej modernizacji) i wynikających z niej potencjalnych problemów w ruchu miejskim, - skutki zwiększenia/zmniejszenia ilości pasów ruchu, - integracja oraz planowanie ruchu komunikacji miejskiej, np. - autobusów, - tramwaji, - trolejbusów. - integracja oraz planowanie architektury komunikacji miejskiej, np. - przystanki autobusowe, - przystanki tramwajowe, - przystanki trolejbusowe, - przejazdy kolejowe. - optymalizacja i skoordynowanie czasu trwania sygnalizacji świetlnej na wybranym odcinku drogi lub w danym sąsiedztwie, - skutki integracji nowych punktów zainteresowania na mapie miejskiej, takich jak: - galerie handlowe, - centra rozrywki, - węzły komunikacyjne (dworce autobusowe, dworce kolejowe, lotniska), - parkingi, - osiedla mieszkaniowe. - badanie spodziewanej emisji spalin towarzyszącej wyżej wymienionym zdarzeniom. ## Wybrane rozwiązania dostępne na rynku * [Road Traffic Simulation Software](https://www.anylogic.com/road-traffic/) -- Zaawansowane narzędzie komercyjne widoczne na \autoref{RTSS}, skupiające się na: - planowaniu sieci dróg i autostrad, - ocenianiu przepływu oraz tendencji do korkowania się ulic, - planowaniu harmonogramów sygnalizacji świetlnej, - integrowaniu budynków użyteczności publicznej z siecią drogową. ![Symulator "Road Traffic Simulation Software" \label{RTSS}](https://i.imgur.com/u7KjrbR.png) * [SUMO (Simulation of Urban MObility)](https://www.eclipse.org/sumo/) -- Otwartoźródłowe narzędzie widoczne na \autoref{SUMO}, udostępnione na licencji Eclipse Public License Version 2. Służy do przeprowadzania mikrosymulacji na dużą skalę. ![Symulator "SUMO" \label{SUMO}](https://i.imgur.com/O7NwlRJ.png) * [OpenTrafficSim](https://opentrafficsim.org/manual/) -- Otwartoźródłowe narzędzie na licencjach BSD, Apache oraz MIT służące do przeprowadzania mikro- oraz makrosymulacji będące owocem pracy grupy pracowników Uniwersytetu Technicznego w Delft. * [SIMWALK Road Traffic Simulator](https://www.simwalk.com/modules/simwalk_roadtraffic.html) -- Narzędzie komercyjne widoczne na \autoref{SIMWALK}. Dedykowane jest do badania interakcji ruchu pieszego i samochodowego o małym i średnim natężeniu np. badanie poszczególnych skrzyżowań, małych obszarów miejskich okolic węzłów komunikacyjnych. ![Symulator "SIMWALK Road Traffic Simulator" \label{SIMWALK}](https://i.imgur.com/4hftBuS.jpg) * [SIMTERACT TRAFFIC AI](https://simteract.com/projects/traffic-ai/) -- Biblioteka, dostępna komercyjnie, którą można zintegrować z symulatorem bądź grą wideo. Została wykorzystana do szkolenia inteligentnych algorytmów służących do kierowania prawdziwymi, autonomicznymi pojazdami. Biblioteka pozwala na: - wprowadzenie dynamicznych zachowań kierowców: - zmienianie pasów, - wyprzedzanie, - omijanie kolizji. - konfigurowanie liczności pojazdów, - występowania specjalnych stref wpływających na zachowania kierowców, - konfigurowanie scenariuszy zachowań, - tworzenie opartego o siatkę systemu dróg. * [Traffic3D](https://traffic3d.org/) -- Otwartoźródłowe narzędzie widoczne na \autoref{Traffic3D}. Udostępnione na licencji Mozilla Public License Version 2.0. Narzędzie jest wyposażone w komponenty ułatwiające mimikowanie istniejących dróg za pomocą projektu OpenStreetMap. Narzędzie korzysta również z wyżej wspomnianego narzędzia [SUMO (Simulation of Urban MObility)](https://www.eclipse.org/sumo/). ![Symulator "Traffic3D" \label{Traffic3D}](https://i.imgur.com/dGtspoh.png) # Projekt systemu ## Cel i przeznaczenie systemu \label{ProjektSystemuRozdzial} ### Założenie i cel tworzenia aplikacji Aplikacja nastawiona jest na optymalizację infrastruktury drogowej i zachowania kierowców. Chcemy, aby jak najwięcej parametrów było modyfikowalnych, a dodatkowo, żeby odbywało się to w przemyślany sposób (na przykład nie podawanie średniego czasu reakcji kierowców, tylko funkcji rozkładu tych czasów). Zależy nam także na bardzo szczegółowym tworzeniu map (wielopasowe skrzyżowania z warunkowymi pasami skrętu). Ważne jest również, aby aplikacja umożliwiała tworzenie bardzo szczegółowych map skrzyżowań. Końcowy użytkownik aplikacji nie musi mieć wiedzy technicznej, aby skorzystać z jej funkcjonalności - wyniki będą przedstawione w prosty i czytelny sposób w aplikacji frontendowej. ### Ogólna charakterystyka systemu Aplikacja przeprowadza symulację ruchu na mapie przygotowanej przez użytkownika. Nieprzerwanie komunikuje się z mikroserwisem backendowym, wysyłając informacje o dobranych parametrach oraz samych samochodach (pozycje, odległość od najbliższego samochodu, odległość do najbliższego skrzyżowania itp.). Po zakończeniu symulacji mikroserwis backendu obliczeniowego odbiera te dane, przetwarza je (wylicza interesujące wskaźniki, generuje odpowiednie heatmapy) i oddaje je aplikacji backendowej, która umieszcza te wyniki w bazie danych. Finalnie użytkownik może przejrzeć podsumowanie symulacji w aplikacji frontendowej. ### Ograniczenia dotyczące projektu aplikacji W projektowanej aplikacji nie planujemy losowych zdarzeń jak: - wypadki, - kierowcy zajmujący złe pasy ruchu, - piesi, - pojazdy uprzywilejowane. ### Środowisko pracy systemu Backend oraz backend obliczeniowy to skonteneryzowane aplikacje działające na serwerze Linuksowym. Aplikacja symulatora stworzona w środowisku Unity zbudowana jest dla systemu operacyjnego Windows. Frontend natomiast może być włączony na każdym urządzeniu posiadającym nowoczesną przeglądarkę internetową. ## Specyfikacja wymagań funkcjonalnych Specyfikacja zawiera ogólne wymagania użytkowe dotyczące aplikacji symulatora ruchu drogowego, a także szczegółowe wymagania dotyczące symulatora, backendu, backendu obliczeniowego oraz frontendu. ### Ogólne wymagania użytkowe - użytkownik może przeprowadzić symulację ruchu drogowego na okrojonym fragmencie miasta, - dane dotyczące map i wyników symulacji zapisywane są w bazie danych, - użytkownik może zobaczyć wyniki symulacji. Może w szczególności przeglądać różne wyniki symulacji dla różnych zestawów parametrów dla tej samej mapy. ### Wymaganie względem symulatora - auta potrafią poruszać się po siatce ulic i skrzyżowań, - auta respektują zasady ruchu drogowego (światła, skrzyżowania z pierwszeństwem, ronda), - symulatorowi można podawać zestawy parametrów, które wpływają na wynik symulacji (zachowania kierowców, czasy świateł), - można tworzyć i zapisywać mapy. ### Wymagania względem backendu - serwis musi w czasie rzeczywistym zapisywać dane o przeprowadzanej przez aplikację Unity symulację, - serwis nie może pozwolić sobie na utratę informacji od aplikacji Unity, bo skutkować to będzie niedokładnymi późniejszymi wynikami aplikacji obliczeniowej, - serwis powinien pozwalać na jak najszybszy odczyt nowych danych z symulacji, w celu sprawnego przeprowadzania obliczeń, - serwis na żądanie aplikacji frontendowej powinien udostępniać dane o wybranej symulacji - statystyki, heatmapy, strukturę dróg. ### Wymagania względem backendu obliczeniowego - serwis musi udostępniać API dla backendu do inicjowania tworzenia statystyk, - serwis musi być odporny na złe dane, - serwis musi tworzyć statystyki i zapisywać je do bazy danych, - serwis musi tworzyć statystyki takie jak: - średnia prędkość wszystkich pojazdów, - mapa cieplna (ang. *heatmap*) pozycji, - mapa cieplna prędkości, - mapa cieplna przyśpieszeń. ### Wymagania względem frontendu - serwis ma prezentować przekazane mu przez backend wyniki dotyczące przeprowadzonej symulacji w schludny oraz czytelny sposób, - typ danych które będą w danym momencie wyświetlane będzie konfigurowalny przez użytkownika serwisu frontendowego, - interfejs aplikacji będzie responsywny i zmieniał wyświetlane dane w zależności od ww. konfiguracji, - serwis ma być odporny na złe dane, - nawigacja po serwisie odbywać się będzie w intuicyjny sposób. ## Specyfikacja wymagań użytkowych ### Wymagania jakościowe - interfejs serwisu frontendowego oraz symulacji jest schludny, przejrzysty i naturalny w obsłudze (nie wymaga szkolenia ani poradnika, żeby go obsłużyć), - użytkownik nieposiadający wiedzy z zakresu programowania jest w stanie stworzyć nowe mapy do symulacji. ### Wymagania wydajnościowe - serwis frontendowy działa poprawnie na każdym urządzeniu z nowoczesną przeglądarką, - symulacja działa w minimum 60 klatkach na sekundę w rozdzielczości 1080p na komputerze z systemem operacyjnym Windows z procesorem Ryzen 5 3600 lub lepszym i z 8Gb pamięci RAM lub więcej. # Charakterystyka i implementacja systemu ## Infrastruktura (Aleksander Błaszkiewicz) \label{InfrastrukturaPodrozdzial} ### Przepływ danych między podsystemami Diagram głównych komponentów systemu oraz przepływu danych pomiędzy nimi przedstawiono na \autoref{ModelPrzeplywu}. ![Model przepływu danych pomiędzy systemami \label{ModelPrzeplywu} ](https://i.imgur.com/bdUJIC6.png) **Użytkownik włącza symulację (prostokąt Symulator)** Niebieski - w trakcie trwania symulacji *Unity* wysyła do backendu zserializowane informacje o mapie, samochodach oraz ustawieniach co stały interwał. Zielony - po zakończeniu symulacji *Unity* wysyła informację do backendu obliczeniowego, że wszystkie dane symulacji zostały już załadowane i może zacząć obliczenia **Użytkownik włącza aplikację frontendową (prostokąt *Frontend*)** Czerwone - użytkownik pobiera od backendu obliczone dane symulacji ### Dedykowany serwer Jako platformę, na której uruchomione są wszystkie mikroserwisy wybraliśmy serwis z maszynami dedykowanymi mikr.us. Ograniczenia serwera to współdzielony procesor, 768Mb pamięci RAM oraz tylko jeden port ale to w zupełności wystarcza na potrzeby projektu. ### Domeny Mikr.us oferuje darmowe subdomeny z sufixem ".bieda.it" jak przedstawiono na \autoref{domeny}. Można je zdobyć z poziomu panela serwera. ![Pzykładowe darmowe domeny z sufixem ".bieda.it" \label{domeny}](https://i.imgur.com/TCCz2Wx.png) Wszystkie darmowe domeny domyślnie kierują na port wewnętrzny 80 serwera. ### Wystawienie usług na świat z jednym portem Nasza architektura zakłada istnienie 3 mikroserwisów. Chcieliśmy, aby do wszystkich z nich był dostęp z poziomu sieci internet, jednak ograniczeniem jest wcześniej wspomniany jeden port. Rozwiązaniem problemu jest usługa typu reverse-proxy zawierające mapę połączeń domena-port_wewnętrzny. Przy obsługiwaniu żądania przez serwer, reverse-proxy najpierw sprawdza oryginalną domenę i na podstawie tego parametru kieruje żądanie do odpowiedniego mikroserwisu jak przedstawiono na \autoref{ReverseProxy}. ![Diagram ilustrujący Reverse Proxy \label{ReverseProxy}](https://i.imgur.com/qL3IXTz.png) ### Konteneryzacja Wszystkie mikroserwisy oprócz symulatora są skonteneryzowane, a jako narzędzie konteneryzacji wybraliśmy Docker. Serwer mikr.us sam w sobie nie jest dedykowaną maszyną, tylko kontenerem Proxmox, dlatego wybór Dockera nie był przypadkowy - jest to jedyne narzędzie które działa na serwerze nie wymagając przy tym dodatkowej konfiguracji. ### Automatyczne wprowadzanie Aby nasz projekt był zgodny z kulturą DevOps, wszystkie mikroserwisy oprócz symulatora mają zaimplementowane akcje, które automatycznie konteneryzują aplikację, wysyłają ją do repozytorium Dockerhub, a następnie łączą się z naszym serwerem i włączają zaktualizowaną wersję jak przedstawiono na \autoref{GHActions}. Byliśmy to w stanie osiągnąć dzięki [Github Actions](https://github.com/features/actions). Dodatku do serwisu Github, który oferuje nieograniczony i darmowy dostęp do maszyn Ubuntu dla projektów open-source. ![Diagram potoku CD \label{GHActions}](https://i.imgur.com/0FOnFzs.png) ## Symulator (Aleksander Błaszkiewicz) \label{SymulatorPodrozdzial} ### Pojazd Silnik fizyki Unity jest znacznie bardziej zaawansowany niż to, czego wymagamy w tym symulatorze, a co za tym idzie, wymaga znacznie więcej zasobów. Po dokładnym przyjrzeniu się wymaganiom projektu, doszliśmy do wniosku, że silnik fizyki w ogóle nie jest potrzebny. Ruch pojazdów można zasymulować równaniami matematycznymi. Poza tym każdy pojazd przestrzega zasad ruchu drogowego, dlatego nie ma potrzeby sprawdzania kolizji pomiędzy pojazdami. Pojazd składa się z podstawowych brył. Zdecydowaliśmy się na uproszczony model aby nie rozpraszać się rzeczami, które nie mają wpływu na zachowanie w trakcie symulacji, ale także ze względu na wydajność - prosty model zajmuje o wiele mniej czasu, żeby go wyrenderować \autoref{Pojazd}. ![Model pojazdu wykorzystywany w symulatorze \label{Pojazd}](https://i.imgur.com/FVcHm5P.png) ### Czujnik odległości Pojazd musi być świadomy tego, czy może się poruszać. W tym celu posiada klasę **ProximitySensor**. Czujnik odległości odpowiada nie tylko za wykrywanie odległości do najbliższego obiektu, ale także za: - wykrywanie odległości do najbliższego obiektu oraz jego identyfikacja (czy to inny samochód, czy to detektor kolizji ograniczający poruszanie się na skrzyżowaniach -na przykład skręt kolizyjny, warunkowy lub światła drogowe), - przekazywanie maszynie stanów informacji na temat typu wykrytego obiektu. ### Silnik Jako że pojazd nie jest bryłą sztywną obsługiwną przez silnik fizyczny Unity (klasa **Rigidbody**), to musi posiadać jednostkę sterującą jego zachowaniem natury fizycznej. Posiada więc klasę **Engine**, która odpowiada za jego poruszanie się. Udostępnia ona m.in. metodę **SetCanMove()**, która informuje silnik, czy samochód ma przyspieszać czy zwalniać. Aby zachowanie samochodu jak najlepiej odwzorowywało rzeczywistość, jego siła hamowania wyrażona jest w funkcji dystansu do obiektu, przed którym ma zahamować. Pozwala na to zmienna Unity typu **Curve** którą przedstawiono na \autoref{krzywaHamowania}. ![Krzywa siły hamowania pojazdu w symulatorze reprezentująca siłę hamowania w stosunku do dystansu do kolejnego pojazdu\label{krzywaHamowania}](https://i.imgur.com/WZYeiXw.png) Na podstawie zmiennej **canMove** silnik przykłada do samochodu przyspieszenie oraz odpowiedzialny jest za nałożenie prędkości na trasę samochodu, którą pobiera z klasy **PathPlanner** ### Układanie trasy Aby pojazd wiedział, po jakich drogach ma się poruszać, posiada klasę **PathPlanner**. Odpowiedzialna jest ona za pobranie z danych elementów infrastruktury drogowej konkretnych dróg, którymi będzie poruszał się pojazd. Zasada działania dróg wytłumaczona jest w podrozdziale 4.2.9 ### Maszyna stanów Pojazd posiada maszynę stanów, dzięki czemu łatwiej zorganizować kod odpowiedzialny za poruszanie się pojazdu, a także łatwo go zserializować. Maszyna stanów wspiera także propagację stanów, czyli na przykład w sytuacji w której pierwszy pojazd zatrzymał się, ponieważ ma czerwone światło, drugi pojazd stojący za nim również ustawi taki stan (mimo że bezpośrednim powodem zatrzymania się, nie jest czerwone światło, tylko pojazd, który zatrzymał się przed nim) ### Tworzenie dróg Drogi reprezentowane są za pomocą krzywych Beziera. Każdy pas na jezdni to oddzielna krzywa B-sklejana. Skorzystaliśmy z gotowego narzędzia Sebastiana Lague. Narzędzie posiada funkcje:[^sebastian_krzywe] [^sebastian_krzywe]: Internet [Sebastian Lague - paczka do krzywych beziera https://assetstore.unity.com/packages/tools/utilities/b-zier-path-creator-136082](https://assetstore.unity.com/packages/tools/utilities/b-zier-path-creator-136082) - znalezienie najbliższego punktu na krzywej na podstawie podanej pozycji na scenie, - znalezienie punktu na krzywej na podstawie przebytego dystansu, - wyznaczenie wektora normalnego w dowolnym punkcie krzywej (żeby samochód był zwrócony wzdłuż krzywej), - stworzenie modelu drogi. ![Narzędzie do edytowania krzywych beziera](https://i.imgur.com/JJwuf4Z.png) Na potrzeby uproszczenia symulacji wszystkie drogi reprezentowane są na płaszczyźnie poziomej *XZ* (rzut z góry) ### Podstawowa droga Aby zapewnić generyczność systemu dróg, stworzyliśmy element, który nazwaliśmy **IRoadElement**. Instancją **IRoadElement** jest zbiór poddróg, które pozwalają przejechać przez dany element sieci dróg. Charakteryzuje się tym, że posiada: - wejścia, - wyjścia, - poddrogi łączące wejścia z wyjściami. Szczególnym przypadkiem **IRoadElement** jest zwykły odcinek drogi, która ma jedno wejście, jedno wyjście i nie zawiera poddróg jak na \autoref{prostyIRoadElement}. ![Najprostszy IRoadElement \label{prostyIRoadElement}](https://i.imgur.com/e8NL2Kd.png) **IRoadElement** jest świadomy tego, w jaki sposób należy po nim przejechać, znając wejście i wyjście. Ta teoria zostanie wytłumaczona lepiej w podrozdziale **Skrzyżowanie** ### Skrzyżowanie Skrzyżowanie to typ **IRoadElement**, który zawiera w sobie poddrogi służące do przejechania po danym elemencie jak na \autoref{przykladSkrzyzowania}. ![Przykład skrzyżowania w symulatorze\label{przykladSkrzyzowania}](https://i.imgur.com/uP79DqM.png) Zakładając, że trasa agenta składa się z drogi 1, skrzyżowania, a następnie drogi 2, to jeżeli taki agent poda skrzyżowaniu (**IRoadElement**) takie dane, to skrzyżowanie odpowie mu, że aby przez nie przejechać w takiej konfiguracji, należy to zrobić drogą numer 3 W ramach debugu i lepszego zrozumienia, w jaki sposób zachowuje się konkretne skrzyżowanie, inspektor Unity tej klasy udostępnia wygodne listy jak na \autoref{ispektorListySkrzyzowanie}. ![Listy udostępnione przez inspektor Unity dla klasy skrzyżowania \label{ispektorListySkrzyzowanie}](https://i.imgur.com/xHNaHzX.png) **PreviousRoadElements** to referencje na drogi, które dochodzą do skrzyżowania **NextRoadElements** to referencje na drogi, które wychodzą ze skrzyżowania **PathCreatorsMap** to obiekty, które dla danej pary dróg wchodzącej i wychodzącej zawiera referencję na poddrogę, która łączy te dwie drogi ### Budowanie zależności między drogami Opisany wyżej system pozwala samochodom pokonywać dowolnie skomplikowane skrzyżowania, ale najciekawsza część systemu to samo pokonywanie konkretnego skrzyżowania. Skrzyżowanie musi wiedzieć, w jaki sposób należy je pokonać. Jednym z potencjalnych rozwiązań jest udostępnienie listy ze środka klasy do edytora Unity i dać użytkownikowi wypełnić wszystkie referencje ręcznie. Jest to rozwiązanie poprawne, jednak bardzo czasochłonne. W naszym symulatorze chcieliśmy to zautomatyzować. W tym celu istnieje seria klas, które dla każdej drogi jaka jest w symulacji biorą jej koniec i początek, a następnie przeszukują wszystkie inne dostępne ścieżki, żeby znaleźć te, które się z nimi łączą. Taka kalkulacja wykonywana jest za każdym razem, gdy startuje symulacja. Ta sama metoda wyszukiwania dróg używana jest do łączenia potencjalnych dróg oraz do łączenia poddróg z potencjalnymi drogami ### Budowanie zależności między drogami - przykład 1 Najprostszy element drogowy to element zawierający w sobie jedną drogę jak na \autoref{elementDrogowyPrzyklad1}. ![Przykład elementu drogowego \label{elementDrogowyPrzyklad1}](https://i.imgur.com/cPggjtV.png) Na początku symulacji taki element najpierw sprawdza, jakie znajdują się w nim drogi. W tym wypadku to **Droga 1**. Następnie na podstawie punktów startowych i końcowych dróg w środku wyznacza swoje punkty startowe i końcowe jak na \autoref{elementDrogowyPrzyklad2}. ![Element drogowy z wejściami i wyjściami \label{elementDrogowyPrzyklad2}](https://i.imgur.com/2m6enpD.png) Taki element gotowy jest na to, aby połączyć się z innymi elementami w celu zbudowania sieci drogowej. Najprostszy przykład takiej sieci to 2 elementy drogowe jak na \autoref{elementDrogowyPrzyklad3}. ![Prosta sieć z elementami drogowymi \label{elementDrogowyPrzyklad3}](https://i.imgur.com/Yelxj8Z.png) Na początku symulacji już po wyznaczeniu punktów końcowych oraz startowych elementów drogowych następuje część kojarzenia między sobą poszczególnych elementów. Pierwszy krok to zebranie listy punktów startowych i końcowych. W tym wypadku będzie to lista punktów **[1,2,3,4]** jak na \autoref{elementDrogowyPrzyklad4}. Kolejny krok to znalezienie par punktów, między którymi odległość jest mniejsza niż ustalona. W tym wypadku taką parą są punkty **[2,3]**. Po znalezieniu takiej pary następuje kojarzenie jak na \autoref{elementDrogowyPrzyklad4}. ![Kojarzenie elementów \label{elementDrogowyPrzyklad4}](https://i.imgur.com/6I0BkPx.png) Element drogowy 1 zapisuje w sobie informację, że jednym z jego potencjalnych elementów wyjściowych jest Element drogowy 2. Tak zbudowana sieć pozwala teraz pojazdowi pokonać trasę składającą się z elementów drogowych 1 oraz 2. Pojazd, który chce przejechać taką trasę, "spyta się" elementu drogowego 1 - w jaki sposób mogę przez ciebie trafić do elementu drogowego 2? Element drogowy numer 1 odpowie wtedy, zwracając pojazdowi drogę znajdującą się w środku - **Droga 1**. ### Budowanie zależności między drogami - przykład 2 Najprostszym przykładem z elementem drogowym zawierającym więcej niż jedna droga jest takie rozwidlenie jak na \autoref{trudnaDrogaPrzyklad1}. ![Przykład sieci drogowej zawierającej więcej niż jedna droga \label{trudnaDrogaPrzyklad1}](https://i.imgur.com/vBfoKa8.png) Krok pierwszy - kojarzenie punktów startowych z końcowymi jak na \autoref{trudnaDrogaPrzyklad2}. ![Skojarzenie punktów na elementach drogowych \label{trudnaDrogaPrzyklad2}](https://i.imgur.com/jjVqUfy.png) Należy zwrócić uwagę na to, że punkt **2** został skojarzony z dwoma punktami - **3** oraz **4**. Jest to jak najbardziej możliwa sytuacja. Oznacza to, że z elementu drogowego 1 można dostać się do kilku innych dróg. Zakładając, że autko planuje pokonać trasę elementów drogowych 1, 2 oraz 4, jego seria zapytań będzie wyglądała następująco: - zapytanie elementu drogowego, w jaki sposób go pokonać, aby dojechać do elementu drogowego 2. Element odpowie referencją na obiekt **Droga 1**, - zapytanie elementu drogowego, w jaki sposób go pokonać, aby dojechać do elementu drogowego 4. Element odpowie referencją na obiekt **Droga 3**. Na tym przykładzie widać, dlaczego same drogi ukryte są w elementach drogowych. Pojazd nie musi wiedzieć, w jaki sposób działają skrzyżowania. Logika ta ukryta jest w samym skrzyżowaniu i to ono odpowiada na pytania, w jaki sposób je pokonać w zależności od poprzedniego oraz następnego elementu drogowego. Po "przepytaniu" elementów drogowych pojazd zostanie z listą dróg [**Droga 1**, **Droga 3**, **Droga 1**], a to jest już coś, co pojazd jak najbardziej rozumie. Wystarczy teraz zmieniać pokonany dystans na każdej drodze, a po pokonaniu całej drogi zacząć pokonywać następną ### Sygnalizacja świetlna Sygnalizacja świetlna zrealizowana jest jako prostopadłościany z komponentem **Collider**, które wykrywa **ProximitySensor** i przekazuje tę informację pojazdowi jak na \autoref{skrzyzowanieEdytorUnity}. ![Prezentacja wyglądu skrzyżowania w edytorze Unity \label{skrzyzowanieEdytorUnity}](https://i.imgur.com/nANEBQM.png){ height=250px } Skrzyżowanie ma na sobie klasę **LightsTimer**. Klasa ta odpowiedzialna jest za ustalenie harmonogramu świateł. Wygodny edytor dostępny jest w inspektorze Unity. Przedstawiono go na \autoref{klasaHarmonogramSwiatel} ![Wygląd wzbogaconego inspektora Unity dla klasy zajmującej się ustaleniem harmonogramu świateł \label{klasaHarmonogramSwiatel}](https://i.imgur.com/rlhukEk.png){ height=250px } Klasa iterując po elementach listy odpowiednio: - wyłącza wszystkie światła powiązane ze skrzyżowaniem, - włącza światła w danym elemencie listy, - czeka **Duration** sekund i powtarza całą sytuację. Coś, co trudno było z początku przewidzieć, to zachowanie pojazdów, w którym niekiedy są blokowane przez światła, które ich nie dotyczą. Rozwiązaniem tego problemu jest przypisanie świateł do drogi, której mają dotyczyć (\autoref{dzialanieSwiatel1}). ![Początkowa zasada działania wykrywania świateł \label{dzialanieSwiatel1}](https://i.imgur.com/lVOAAgq.png){ height=250px } Na takie jak na \autoref{dzialanieSwiatel2}. ![Ulepszona zasada działania wykrywania świateł \label{dzialanieSwiatel2}](https://i.imgur.com/xvYaOYl.png){ height=250px } Dzięki temu jeżeli pojazd przypadkiem trafi promieniem w czerwone światło, które go nie dotyczy, nie spowoduje to zatrzymania się pojazdu ### Skręty kolizyjne - definicja Przykładowe skrzyżowanie, w którym pojazdy znajdujące się na dole mogą jechać prosto lub skręcić w lewo, a pojazdy znajdujące się na górze mogą jechać tylko prosto jak na \autoref{skretyKolizyjne1}. ![Przykładowe skrzyżowanie ze skrętami kolizyjnymi \label{skretyKolizyjne1}](https://i.imgur.com/7FQdVKJ.png){ height=250px } W momencie gdy pojazdy z przeciwległych pasów mogą się poruszać, istnieje szansa, że zderzą się w tym punkcie jak na \autoref{skretyKolizyjne2}. ![Moment zderzenia \label{skretyKolizyjne2}](https://i.imgur.com/yp7D3e1.png){ height=250px } Aby temu zapobiec, należy w jakiś sposób kontrolować ruch pojazdów, które przecinają inny pas. W tym wypadku będą to pojazdy z dołu. Na takich pasach stoi zatem bariera (czerwony kolor) jak na \autoref{skretyKolizyjne3}. ![Definicja bariery \label{skretyKolizyjne3}](https://i.imgur.com/NvgTBzn.png){ height=250px } Taka bariera nie może jednak cały czas blokować pojazdów. Powinna to robić jedynie w momencie, w którym istnieje ryzyko zderzenia. Zatem bariera powiązana jest z czujnikiem pojazdów i jest aktywna tylko jeżeli czujnik pojazdów coś wykrywa, co przedstawiono na \autoref{skretyKolizyjne4}. ![Bariera razem z czujnikiem \label{skretyKolizyjne4}](https://i.imgur.com/t5DqmkS.png){ height=250px } Poniżej przykład działania całego mechanizmu. Pojazd 2 chce dokonać skrętu kolizyjnego. Bariera wykryła pojazd 1 jadący z naprzeciwka, dlatego się uaktywniła. Pojazd 2 wykrywa barierę i się przed nią zatrzymuje jak na \autoref{skretyKolizyjne5}. ![Cały mechanizm \label{skretyKolizyjne5}](https://i.imgur.com/ilKiCah.png){ height=250px } Następnie pojazd 1 wyjeżdża już z zakresu czujnika aut, co powoduje dezaktywację z bariery, co z kolei pozwala pojazdowi 2 pokonać skrzyżowanie jak na \autoref{skretyKolizyjne6}. ![Zwolnienie bariery po przejechaniu pojazdu \label{skretyKolizyjne6}](https://i.imgur.com/MHSGlll.png){ height=250px } Rozwiązanie wydaje się być gotowe, jednak istnieje kilka przypadków szczególnych, które zostaną omówione w następnych podrozdziałach. ### Skręty kolizyjne - problem przynależności bariery Przykład skrzyżowania omówiony przy definicji skrętów warunkowych jest trywialny - w rzeczywistości w symulatorze istnieją bardziej skomplikowane skrzyżowania. Minimalnym przykładem skrzyżowania uwidaczniającego problem przynależności bariery jest \autoref{problemBariery1}. ![Minimalny przykład skrzyżowania z problemem przynależności bariery \label{problemBariery1}](https://i.imgur.com/FD26j3B.png){ height=250px } Zakładając, że jest to skrzyżowanie ze światłami i według harmonogramu świateł pojazd 1 oraz 2 nigdy nie mogą się zderzyć, pojawia się problem. Czujnik zapobiegający zderzeniom na skręcie warunkowym blokuje również przejazd auta numer 2. Dzieje się tak całkowicie przypadkiem - bariera stoi po prostu na dwóch pasach jednocześnie. Aby to rozwiązać, wprowadziłem przynależność bariery do danego pasa ruchu. Bariera posiada w sobie referencję na obiekt drogi, którego ma dotyczyć. Jako że nie da się tego ustalić automatycznie, w inspektorze Unity skrypt bariery ma w sobie pole widoczne w inspektorze na \autoref{inspektorBariera}, do którego wygodnie można dodawać daną drogę ![Inspektor Unity klasy bariery \label{inspektorBariera}](https://i.imgur.com/OtQlyx5.png){ height=250px } Oprócz samego dowiązania drogi do bariery wymagało to również modyfikacji zasady działania wykrywania przeszkód przez czujnik odległości. Z początkowego wykrywania w sposób jak na \autoref{sposobWykrywania1}. ![Sposób wykrywania przeszkód przed wprowadzeniem przynależności bariery do pasa \label{sposobWykrywania1}](https://i.imgur.com/aqPSfCi.png){ height=250px } Na sposób jak na \autoref{sposobWykrywania2}. ![Sposób wykrywania przeszkód po wprowadzeniem przynależności bariery do pasa \label{sposobWykrywania2}](https://i.imgur.com/ivQnpn9.png){ height=250px } ### Skręty kolizyjne - problem przeciwległych pasów kolizyjnych Problem pojawia się w momencie, gdy obydwa pojazdy jadące z naprzeciwka dokonują jednocześnie skrętu kolizyjnego jak przedstawiono na \autoref{przeciwlegleKolizje1} ![Problem przeciwległych pasów kolizyjnych \label{przeciwlegleKolizje1}](https://i.imgur.com/1wed54E.png){ height=250px } Czujniki w obydwu pojazdach wykryją pojazd jadący z naprzeciwka, przez co obydwa się zatrzymają. Doprowadziło to do zmiany zasady działania czujnika odległości. Wcześniejsza zasada, która strzelała promieniem RayCast szukając kolizji z pojazdem została wzbogacona o dodatkowy warunek. Teraz czujnik odległości szuka kolizji ze specjalnym obiektem, który wyznacza tył auta jak na \autoref{nowaKolizja}. ![Nowa zasada wyznaczania przeszkód przez czujnik odległości \label{nowaKolizja}](https://i.imgur.com/uQzeXlE.png){ height=250px } W przypadku gdy dwa pojazdy jadą na siebie z naprzeciwka, czujnik odległości nie zwraca wykrycia przeszkody - taka sytuacja zawsze oznacza tylko chwilowe ustawienie pojazdów w ten sposób i nie powinna być traktowana jako przeszkoda ### Skręty kolizyjne - problem zakleszczenia W ruchu drogowym, który odbywa się według wszystkich poprzednich zasad może dojść do wyjątkowej sytuacji zakleszczenia na skrzyżowaniu ![Problem zakleszczenia skrzyżowania przez skręty kolizyjne](https://i.imgur.com/BjLtLqC.png){ height=250px } Pojazdy oznaczone jako "A" chcą przez skrzyżowanie przejechać prosto, a pojazdy oznaczone jako "B" chcą wykonać skręt kolizyjny. Zgodnie z zasadą działania czujników na skrzyżowaniu, w momencie, który jest przedstawiony na rysunku auta oznaczone jako "A" spowodują ustawienie barier dla obydwu samochodów oznaczonych jako "B". Samochody oznaczone jako "B" nigdy nie przejadą przez skrzyżowanie, co zablokuje również samochody "A". Aby temu zapobiec, czujniki na skrzyżowaniach sprawdzają dodatkowy warunek. Oprócz wcześniejszego sprawdzania, czy w ich środku znajduje się pojazd, teraz sprawdzają również, czy znajdujący się w środku pojazd się porusza. Jeżeli nie, to bariera się odblokowuje do kolejnej zmiany swojego stanu (wjechanie kolejnego pojazdu). W tym wypadku po puszczeniu pojazdów B problem zakleszczenia znika jak na \autoref{rozwiazanieZakleszczenia}. ![Rozwiązanie sytuacji zakleszczenia \label{rozwiazanieZakleszczenia}](https://i.imgur.com/y3jj4ie.png){ height=250px } Istnieje jednak szansa, że puszczenie jednej serii samochodów w przypadku zakleszczenia nie rozwiąże go. Logika czujnika jest napisana tak, żeby kontynuowała puszczanie po jednym samochodzie w przypadku powtórzenia sytuacji zakleszczenia. Dzięki temu wszystkie sytuacje zakleszczenia są poprawnie obsłużone ### Skrzyżowanie o ruchu okrężnym - struktura Problem przedstawienie ronda używając istniejącego rozwiązania do generowania dróg można rozwiązać po zauważeniu, że rondo tak naprawdę jest po prostu serią skrzyżowań. Rondo jak na \autoref{rondoPrzedPo} można przeciąć na okręgu, co po "rozłożeniu" układa się w serię skrzyżowań. ![Rondo przed i po rozłożeniu \label{rondoPrzedPo}](https://i.imgur.com/krde2ra.png){ height=250px } Pojedyncze skrzyżowanie może być przedstawione jako struktura zawierająca kawałek drogi należącej do ronda, wjazd oraz wyjazd jak na \autoref{rondo3}. ![Struktura przedstawiająca odcinek ronda \label{rondo3}](https://i.imgur.com/T9pcLee.png) Rondo zatem jest serią skrzyżowań złożonych ze struktur tego typu. ### Serializacja symulacji w czasie rzeczywistym Pojazdy implementują klasę **ISerializable**, co pozwala w łatwy sposób je serializować. Klasa **Car** posiada metodę **GenerateCarDTO**, która zwraca obiekt typu **CarDTO**. Obiekt typu **CarDTO** posiada pola: - id - losowo nadany identyfikator, - position - wektor 2D pozycji, - velocity - wektor 2D prędkości. Co każdą klatkę symulacji obiekt **JsonSerializer** serializuje wszystkie auta i zapisuje wynik w liście. Dodatkowo wyznacza też hash mapy oraz hash ustawień. Tak przygotowane parametry składają się na obiekt typu **SimulationChunkDTO**, który zawiera: - frameNumber - liczba reprezentująca aktualną klatkę, - cars - lista zserializowanych pojazdów, - mapHash - hash, który stworzony jest z tego, jak zbudowa jest mapa. Pozwala jednoznacznie określić, do jakiej mapy należy to *DTO*, - settingsHash - hash, który stworzony jest z ustawień kierowców wprowadzonych przez użytkownika. ### Serializacja mapy Wszystkie drogi zbudowane są z krzywych beziera, a te serializujemy według ogólnie przyjętej zasady podawania po kolei współczynników jako punkty *[X, Y]* w kolejności [Punkt A, Modyfikator punktu A, Modyfikator punktu B, Punkt B] jak na \autoref{krzyweBeziera} ![Zasada serializacji krzywych beziera \label{krzyweBeziera}](https://i.imgur.com/kGQHPP0.png) Klasa **JSONSerializer** na początku symulacji znajduje wszystkie obiekty typu **PathCreator**, a następnie wyciąga z nich referencje na obiekt typu **BezierCurve** i z każdego obiektu tego typu tworzy listę punktów według wcześniej ustalonej zasady. Końcowy obiekt ma strukturę jest obiektem typu **MapDTO**, który zawiera listę obiektów **RoadDTO**, które to zawierają listę obiektów **Vector2** ### Szukanie ścieżki Za szukanie najkrótszej ścieżki pomiędzy elementami odpowiedzialna jest klasa **PathFinder**. Posiada ona metodę **FindPath()**, która zwraca listę obiektów **IRoadElement**, które należy pokonać, aby dostać się z elementu **start** do elementu **end** najkrótszą trasą. Algorytm rekurencyjnie zaczynając od elementu start wykonuje swój krok, czyli: - wyznacza wszystkie możliwe wariacje tras, które mogą powstać. W każdym następnym, - wykonuje sam siebie podając aktualnie już sprawdzone elementy **IRoadElement** jako dodatkowy argument. Algorytm kończy się w momencie gdy dotrze do elementu **end**. Wtedy zwraca listę sprawdzonych elementów, które go do tego elementu doprowadziły. ### Spawnery pojazdów Klasa odpowiedzialna za tworzenie pojazdów to **CarSpawner**. Tworzy ona pojazdy co losowy czas, który ustala się za pomocą zmiennych udostępnionych do inspektora Unity: - minSpawnInterval, - maxSpawnInterval. Spawner w momencie tworzenia samochodu może mu nadać jedną z 3 właściwości: - podać samochodowi zadaną trasę jako listę obiektów **IRoadElement**, - stworzyć samochód podając mu tylko element początkowy **IRoadElement** i polecić mu, aby przejechał trasę losowo (przy każdym przejeździe na nowe skrzyżowanie pojazd wybierze losową opcję), - stworzyć samochód podając mu listę możliwych tras - pojazd wybierze wtedy jedną z możliwych tras i zadziała analogicznie do pierwszego podpunktu. ### Gizmo - Kierunki dróg Aby ułatwić tworzenie aplikacji, edytor Unity udostępnia programiście możliwość rysowania dodatkowych gizmo ponad już istniejącymi. W symulatorze znalazło się na to kilka zastosowań. Każda droga, która znajduje się na mapie jest jednokierunkowa. Aby ułatwić wizualizację, w którą stronę odbywa się ruch na danej drodze, istnieje pomocnicza metoda na klasie reprezentującej drogę, która rysuje niebieskie strzałki symbolizujące kierunek poruszania jak na \autoref{gizmoStrzalki}. ![Strzałki wskazujące kierunek ruchu \label{gizmoStrzalki}](https://i.imgur.com/hA1G7vA.png) ### Gizmo - Wykrywacze aut Aby lepiej zwizualizować zachowanie klasy **ProximitySensor**, oprócz wykonywania samego RayCast, rysuje też ona wizualizację tego, co się stało. Przedstawia to \autoref{gizmoCzujnik}: - niebieski promień - nic nie zostało wykryte w żadnej odległości, - czerwony promień - wykryto przeszkodę, która potencjalnie może zatrzymać pojazd. ![Linie symbolizujące stan czujnika odległości\label{gizmoCzujnik}](https://i.imgur.com/AlYGNgz.png) ### Wykrywacze aut ![Wykrywacz w stanie spoczynku \label{gizmoWykrywacz1}](https://i.imgur.com/yjjTJkH.png) ![Wykrywacz w stanie wykrycia \label{gizmoWykrywacz2}](https://i.imgur.com/PEp5Q7y.png) Wykrywacze, które używane są przy skrętach kolizyjnych oraz w przypadku ustępowania pierwszeństwa symbolizowane są jako zielone prostopadłościany na \autoref{gizmoWykrywacz1}, które stają się czerwone, gdy powinny zgłosić, że w ich obrębie jest jakiś pojazd jak na \autoref{gizmoWykrywacz2}

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully