\chapter{Algoritmus řízení} Úkolem této práce bylo navrhnout algoritmus vyjednávání mezi jednotlivými křižovatkami tak, aby koordinovanou změnou svých offsetů vytvořili zelenou vlnu a~tedy aby křižovatkou projelo co nejvíce vozidel bez zbytečného zastavování. V~této kapitole bude takovýto algoritmus představen a~následně bude popsána jeho implementace do již fungujícího simulačního prostředí. Zelená vlna spočívá s~synchronizovaném nastavení signálních plánů tak, aby vozidlo jedoucí běžnou cestovní rychlostí při průjezdu křižovatkami řízenými světelnými signalizačními zařízení zastihlo na všech semaforech signál volno. V~případě pevných signálních plánů by tedy mohlo jen stačit vypočítat hodnoty offsetů zaručujících tento stav pro vybraný směr nebo směry jízdy. Pokud však se však v~oblasti k~hlavnímu proudu připojuje velké množství vozidel z~vedlejších ulic, začnou tyto automobily tvořit na optimalizovaném tahu fronty. Je tedy nutné přizpůsobit signální plány tak, aby tato fronta opustila křižovatku ještě před příjezdem vozidel využívajících zelenou vlnu. \section{Použité značení} Pro popis algoritmu, který se o~toto přizpůsobení snaží, nejprve zavedeme označení některých veličin. Jak již bylo zmíněno, délka cyklu se značí $T_\mathrm{c}$. Průměrnou rychlost vozidel (na volné silnici) budeme značit $v_\mathrm{P}$. Začátek zelené v~$i$-té signální skupině je $t_\mathrm{zz}^{(i)}$, délka této zelené $t_\mathrm{dz}^{(i)}$. Index $i$ se bude vynechávat, je-li signální skupina jednoznačně určená z~kontextu. Pomocí $d_{i,j}$ označíme vzdálenost mezi křižovatkami $i$ a~$j$ v~metrech, opět s~možností indexy vynechat. Konečně symbolem $\rho_i$ zastupuje hustotu provozu (počet vozidel za sekundu) během $i$-tého intervalu. Dále zavadíme pojem \emph{délka virtuální fronty}, ozn. $Q_\mathrm{V}^{(i)}$. Ta může nabývat kladných i~záporných hodnot. Pokud je její velikost kladná, značí (předpokládanou) délku fronty v~časovém okamžiku určeném indexem $i$. V~případě, že je záporná, představuje, opět předpokládaný, počet vozidel, která v~místě tvoření fronty projedou mezi časy $(i-1)$ a~$i$ bez zastavení. \section{Návrh algoritmu} \label{sec:navrh} Navržená strategie řízení se skládá z~agentů, kteří jsou přiřazeni k~jednotlivým křižovatkám. Každý agent může ovládat offset signálního plánu u~svého řadiče křižovatky, od něhož naopak získává údaje z~detektorů popisující aktuální stav provozu. Sousedící agenti navíc mezi sebou mohou komunikovat. Toho využívají především k~tomu, aby zjistili nastavení offsetu na vedlejších křižovatkách, respektive aby věděli kdy mohou od sousedů očekávat příjezd vozidel. Z~obdržených informací pak agent usoudí, jestli by změna sousedova offsetu nemohla přinést zlepšení situace a~pokud ano, pokusí se tuto změnu vyjednat. Jedním z~nosných prvků návrhu je funkce, která ohodnotí konkrétní nastavení offsetu a~umožní tak porovnat různé jeho hodnoty a~vybrat tu možná nejlepší. Za tuto míru kvality byl zvolen odhad počtu automobilů, která projedou křižovatkou bez zastavení. Do výpočtu jsou zahrnuta jen vozidla ze směrů od jiných řízených křižovatek. Pro výpočet hodnocení (v~programu nazývaném \texttt{rating}) je třeba znát pro každý jízdní pruh na vjezdech do křižovatky délku fronty a~očekávané časy příjezdů vozidel od sousední křižovatky. Délka fronty se řeší později, příjezdy pak agent získává přímo od souseda. Ten je počítá podle vztahu \begin{equation} t_\mathrm{z} = t_{\mathrm{zz}} + \frac{d}{v_\mathrm{P}} + \mathit{offset} \label{eq:tz} \end{equation} a~\begin{equation} t_\mathrm{k} = t_\mathrm{z} + t_{\mathrm{dz}} \;, \label{eq:tk} \end{equation} kde $t_\mathrm{z}$ je čas začátku příjezdu aut a~$t_\mathrm{k}$ čas konce. Dále pak $t_{\mathrm{zz}}$ je čas začátku svícení zelené, $d$ vzdálenost ke křižovatce, která žádá o~časy příjezdů, $v_P$ průměrná rychlost vozidel, $\mathit{offset}$ nastavený offset a~$t_{\mathrm{dz}}$ délka svícení zelené. K~těmto dvěma vypočítaným hodnotám se ještě přidává předpokládaný počet aut. Jeho odhad je získán z~hustoty provozu v~minulém cyklu. Křižovatka zasílající odhady takovýchto trojic údajů předává několik -- podle toho, kolik fází rozsvěcuje zelenou ve směru připouštějícím jízdu k~sousedovi žádajícímu o~odhady. \begin{figure}% \centering \includegraphics[width=\columnwidth]{expand}% \caption{Periodické prodloužení intervalu svícení zelené: Modře jsou označeny intervaly, kdy přijíždějí vozidla, zeleně interval, kdy dle signálního plánu svítí zelená (se započítáním offsetu). Horní obrázek představuje stav před prodloužením, dolní po něm.}% \label{fig:expand}% \end{figure} Hodnocení je pak počítáno po jednotlivých jízdních pruzích. Pro každý je nutné provést periodické prodloužení času svícení zelené odpovídající signální skupiny tak, aby v~každém okamžiku intervalu od času $0$ po čas posledního předpovězeného příjezdu vozidel bylo jasné, jaký znak bude v~tomto jízdním pruhu signalizován. Tento interval je dále značen $T$. Princip periodického prodloužení je představen na obrázku \ref{fig:expand}. Interval $T$ poté rozdělíme na posloupnost intervalů $\left(T_i\right)^{k}_{i=1}$. $T_i$ jsou intervaly typu $\left$, kde $b_i=a_{i+1}$ $\forall i \in \left\{ 1, \ldots, k-1\right\}$, $a_1$ a~$b_k$ jsou krajní body intervalu $T$ a~$a_i$ jsou chronologicky řazené všechny \emph{významné body}, Těmi se rozumí body, ve kterých se mění signalizovaný znak nebo ve kterých začíná či končí příjezd vozidel od souseda. Každý interval $T_i$ lze tím pádem rozdělit na čtyři druhy, podle toho jestli v~něm přijíždějí nebo nepřijíždějí vozidla a~jestli svítí nebo nesvítí zelená. Řekneme, že $T_i$ je \begin{itemize}%[(i)] \item typu 1 $\iff$ v~jeho průběhu přijíždějí auta do fronty a~svítí zelená \item typu 2 $\iff$ v~jeho průběhu přijíždějí auta do fronty a~nesvítí zelená \item typu 3 $\iff$ v~jeho průběhu nepřijíždějí auta do fronty a~svítí zelená \item typu 4 $\iff$ v~jeho průběhu nepřijíždějí auta do fronty a~nesvítí zelená \end{itemize} Pomocí tohoto dělení pak počítáme délku virtuální fronty na konci intervalů $T_i$ následujícím způsobem: \begin{equation} Q_\mathrm{V}^{(i)} = Q_\mathrm{V}^{(i-1)} + u(T_i)\,, \label{eq:qk} \end{equation} kde $Q_\mathrm{V}^{(i)}$ je délka virtuální fronty na konci $i$-tého intervalu. Funkce $\mathrm{u}(T_i)$ je definována takto: \mbox{$\mathrm{u}:\left \rightarrow \mathbb{R}$} \begin{equation} \mathrm{u}(T_i)= \begin{cases} |T_i|\cdot (c_o-\rho_i) &\text{pokud } T_i \text{ je typu 1} \\ |T_i|\cdot \rho_i &\text{pokud } T_i \text{ je typu 2} \\ -|T_i|\cdot c_o &\text{pokud } T_i \text{ je typu 3} \\ 0 &\text{pokud } T_i \text{ je typu 4} \end{cases} \;. \label{eq:uti} \end{equation} kde $|T_i|$ je délka intervalu $T_i$, $\rho_i$ hustota přijíždějících vozidel během intervalu $T_i$ a~$c_o$ je empiricky zjištěná konstanta -- počet vozidel, která za sekundu opustí křižovatku (tedy vlastně hustota odjíždějících vozidel). Definice funkce $\mathrm{u}(T_i)$, zapsaná vztahem (\ref{eq:uti}), není úplná. Ještě je nutné doplnit omezení \begin{equation} \left( \forall i~\in \left\{ 1, \dotsc , k~\right\} \right) \left( T_i \text{ je interval typu 3} \right) : \quad -\mathrm{u}(T_i) < Q_{\mathrm{V}}^{(i-1)} \label{eq:uti-omez} \end{equation} Tato podmínka říká, že pokud z~fronty pouze odjíždějí vozidla, nesmí se stát, aby volná kapacita po vyprázdnění fronty byla započítána do hodnocení. V~tuto chvíli totiž nepřijíždějí žádná vozidla, která by křižovatkou projela bez zastavování. Pokud vyjde v~nějakém okamžiku $Q_\mathrm{V}^{(i)}$ záporné, představuje (odhadovaný) počet aut, která mohou v~tomto intervalu křižovatkou projet bez zastavení a~tato hodnota se tedy odečte od hodnocení dané křižovatky (a~tím se hodnocení zvýší). Vyvstává otázka, jak zjistit aktuální délky front na jednotlivých jízdních pruzích jen pomocí údajů z~detektorů, které jsou k~dispozici. Ukazuje se, že toto není triviální problém a~jeho řešení přesahuje rámec této práce. Z~toho důvodu nejsou v~programu délky front odhadovány tak, jak by to bylo nutné v~reálné situaci, ale používají se hodnoty, které lze získat ze simulátoru Aimsun pomocí VGS API. Podrobnější informace o~modelování délky fronty představuje například \cite{pecherkova}. VGS API fronty počítá tím způsobem, že projde celý příslušný jízdní pruh a~v~něm do fronty započítá ta vozidla, která jedou nižší než stanovenou hraniční rychlostí. Při samotném vyjednávání pak mají agenti dvě role: jedna z~nich je zvaná \emph{pasivní}, druhá je \emph{aktivní}. Pasivní agent má pevně nastavený offset a~pouze reaguje na pokyny aktivního. Pokyny tvoří buď žádost o~očekávané časy příjezdů nebo návrh na změnu offsetu. Na první z~nich agent vždy odpovídá zasláním požadovaných údajů, u~druhého pak zvažuje, jestli by změna v~celkovém součtu přinesla zlepšení \texttt{ratingu}. Z~tohoto popisu pak vyplývá role aktivního agenta. Ten stejně jako pasivní agent pracuje se žádostmi o~časy příjezdů, navíc však aktivně mění svůj offset a~pokouší se vyjednat změnu u~sousedů. Vyjednávací cyklus pak probíhá v~několika krocích. Nejprve si všichni agenti vyžádají očekávané příjezdy vozidel na základě offsetů nastavených v~minulém cyklu. S~přihlédnutím k~těmto očekáváním pak aktivní agenti spočítají \texttt{rating} svého nastavení offsetu a~pokusí se zjistit, jestli by nějaká změna vlastního offsetu nevedla ke zlepšení hodnocení. Hledání nejlepšího vlastního offsetu probíhá ve třech krocích. Nejprve se porovná aktuální offset, offset zvýšený o~8 sekund a~offset snížený o~8 sekund. Vybere se nejlépe hodnocený z~těchto možností a~pokračuje se s~ním stejným způsobem, jen další uvažovaná změna je $\pm4$ sekundy. V~posledním kroku je pak otestován posun o~$\pm2$ sekundy. Když aktivní agenti naleznou své nejlepší offsety, rozešle se všem účastníkům zpráva o~nalezení stabilního stavu, která obsahuje nové hodnoty očekávaných příjezdů vozidel. Nyní aktivní agenti vyzkouší, jestli by k~dalšímu zlepšení nevedla změna offsetu u~některého z~jejich sousedů. Podobným způsobem jako při hledání vlastního nejlepšího offsetu zkusí odhadnout změnu \texttt{ratingu} při posunu sousedova offsetu o~$\pm4$ a~nejlepší ze tří možností se zašle jako žádost o~posun offsetu sousedovi spolu se změnou \texttt{ratingu}, kterou by přinesla. Každý pasivní agent poté sesbírá všechny návrhy a~otestuje, který z~nich má největší součet změny ratingu u~něj samotného a~změny u~navrhovatele a~ten potom přijme za vlastní. Pokud by všechny návrhy znamenaly záporný součet, jsou zamítnuty a~offset zůstává na původní hodnotě. V~každém případě jsou pak rozeslány informace o~novém stabilním stavu a~s~nimi opět očekávané příjezdy. Poté se ještě zasílání žádostí opakuje, jen s~posunem o~$\pm2$ a~$\pm1$ sekundu. Tímto každá křižovatka nalezne svůj konečný offset. Problém nastává při zaslání tohoto offsetu do řadiče křižovatky. Od toho není garantována okamžitá akce, způsob jakým žádaného offsetu dosáhne je jen v~jeho režii a~než se tak stane, může uběhnout i~několik cyklů. Z~tohoto důvodu se vypočítaný offset neposílá ihned po nalezení, ale agent vždy v~pěti cyklech napočítá optimální offset, z~těchto pěti hodnot spočítá průměr a~teprve ten se následně předá řadiči ke zpracování. Tím je také snížena reaktivnost agenta a~zamezí se tak případným přehnaným reakcím na chvilkové změny poptávky, kterým stejně není možné vyhovět. \section{Použité knihovny} Pro usnadnění práce a~také z~důvodu lepšího začlenění do současného řešení se v~programu používají tři volně dostupné knihovny: \texttt{IT++}, zjednodušující práci s~vektory, maticemi a~poli, \texttt{BDM} (Bayesian Decision Making), která obsahuje užitečné nástroje pro práci s~popisy k~vektorům a~obsahuje také nástroj pro ukládání průběžných hodnot z~experimentu a~\texttt{libconfig}, sloužící především pro práci s~konfiguračními soubory. První dvě knihovny jsou distribuovány pod GPL licencí, třetí pak pod licencí LGPL. \subsection{IT++} \texttt{IT++} je knihovna pro C++, která obsahuje třídy a~funkce pro provádění některých matematických operací, zpracování signálů a~další. Pro účely této práce jsou zajímavé právě funkce matematické. Pro zacházení s~vektory jsou v~knihovně mimo jiné zavedeny typy \texttt{vec} a~\texttt{ivec}. První jmenovaný je vektor obsahující prvky typu \texttt{double}, tedy čísla s~desetinou čárkou, druhý je pak složen z~prvků \texttt{int}, čili čísel celých. Práce s~vektory je velmi intuitivní, navíc lze často používat syntaxi podobnou jako v~programu MATLAB. Vektor můžeme nadefinovat jedním z~těchto způsobů \lstset{language=C++, showstringspaces=false} \begin{lstlisting} vec my_vector; vec my_vector(10); \end{lstlisting} přičemž první z~nich pro vektor nealokuje paměť. To je pak nutné udělat funkcí \texttt{setsize()}. Následně je možné uložit do vektoru jednotlivé prvky a~to například jedním z~následujících příkazů. \begin{lstlisting} vec a = "0 0.7 5 9.3"; // tedy a = [0 0.7 5 9.3] ivec b = "0:5"; // tedy b = [0 1 2 3 4 5] vec c = "3:2.5:13"; // tedy c = [3 5.5 8 10.5 13] ivec d = "1:3:5,0:2:4"; // tedy d = [1 3 5 0 2 4] vec e("1.2,3.4,5.6"); // tedy e = [1.2 3.4 5.6] \end{lstlisting} Navíc lze i-tému prvku vektoru \texttt{a} přistupovat pomocí \lstinline!a(i)! nebo \lstinline[]!a[i]!. Díky přetížení operátorů lze s~vektory přímo provádět běžné matematické operace, jako jsou: \begin{lstlisting} a+b // součet vektorů a+5 // přičtení čísla 5 ke všem prvkům vektoru a*b // skalární součin vektorů a*9 // vynásobení všech prvků vektoru číslem 9 \end{lstlisting} a~tak podobně. Práce s~maticemi funguje podle obdobných pravidel. Dále se v~knihovně nachází třída \texttt{Array}. Díky ní je možné vytvářet pole prvků libovolného typu (včetně výše popsaných vektorů) a~provádět s~nimi mnohé operace. Příkladem užití právě pro pole vektorů je například následující kód: \begin{lstlisting} Array pole_vektoru(2); my_vec_array(0) = "2 4 5 0 3";; my_vec_array(1) = "0.1 0.2 0.3 0.4 0.3 0.2 0.1"; \end{lstlisting} Pomocí \lstinline!pole_vektoru(i)! lze u~takto definovaného pole intuitivně přistupovat k~jeho prvkům. Navíc je možné provádět operace jako jsou posuny prvků, výběr podmnožiny prvků nebo třeba prostředního prvku a~mnohé další. \subsection{BDM} Knihovna \texttt{BDM} (Bayesian Decision Making) se zabývá, jak název napovídá, bayesovským rozhodováním. Ovšem v~této práci jsou z~ní přímo použity jen některé třídy, jmenovitě \texttt{UI}, \texttt{RV}, \texttt{datalink} a~\texttt{logger}. BDM navíc definujeme třídu \texttt{Participant}, která je základem rozhodovacích procesů a~od které se pak odvozuje agent pro řízení dopravy. Třída \texttt{UI} (User Info) slouží pro ukládání a~čtení libovolných uživatelských dat. Zde se používá pro načtení konfigurace pro simulátor a~pro jednotlivé agenty a~dále jako formát pro ukládání a~posílání zpráv mezi agenty. Účelem třídy \texttt{RV} je poskytovat popisné informace k~datovému vektoru. Proměnná typu \texttt{RV} se skládá z~jedné nebo několika položek. Každá taková položka má svoje jméno (\texttt{name}) a~délku -- počet prvků, které tomuto jménu odpovídají. Součet délek všech takových položek se potom musí rovnat délce vektoru, který je danou proměnnou typu \texttt{RV} popisován. Fungování je ilustrováno na obrázku \ref{fig:rv}. \begin{figure}% \centering \includegraphics[width=7cm]{rv}% \caption{Vektor typu \texttt{RV} (vlevo) fungující jako popis k~vektoru typu \texttt{vec}~(vpravo). Každá položka z~\texttt{rv\_vector} má v~levém sloupci své jméno (\texttt{name}) a~v~pravém velikost (\texttt{size}). V~tomto případě se tedy podvektor \texttt{c} rovná vektoru [5~4]}% \label{fig:rv}% \end{figure} Pro kopírování dat mezi vektory existuje třída \texttt{datalink}. Vytváří spojení mezi dvěma datovými vektory na základně shodně pojmenovaných prvků v~k~nim příslušných popisných vektorech. Po propojení vektoru s~podvektorem pak funkce data\-linku umožňují snadné kopírování dat oběma směry. Konečně \texttt{Logger} je třída určená pro ukládání dat z~programu. Tvoří abstraktní vrstvu mezi programem a~samotným zápisem dat. Při použití se jen na začátku nastaví, v~jaké formě chceme získat výsledné údaje (např. uložit do paměti, do souboru, do databáze atd.) a~dále třídu používáme bez ohledu na tuto volbu. Je tím zajištěna flexibilita pro případ, že se změní požadavky na výstupy z~programu. \subsection{Libconfig} Konfigurační parametry pro simulaci se ukládají do souboru ve formátu, který zavádí knihovna \texttt{libconfig}. Jde o~textový formát, který je stručnější a~pro člověka lépe čitelný než XML. Podporovaný formát souborů představuje hierarchickou strukturu. \emph{Konfigurace} se skládá ze skupiny \emph{nastavení}, která přiřazuje \emph{jménům} \emph{hodnoty}. Hodnotou může být \emph{skalár}, \emph{pole}, \emph{skupina} nebo \emph{seznam}. Skupiny nastavení lze do sebe dále vnořovat, čímž knihovna nabízí velmi flexibilní a~přitom stále přehledný způsob pro ukládání konfiguračních souborů. \section{Struktura programu} Fungování simulačního prostředí tak, jak bylo navrženo v~ÚTIA, je znázorněno na obrázku (\ref{fig:struktura}). Skládá se ze tří hlavních bloků: mikrosimulátoru Aimsun, emulátorů řadičů ELS3 a~z~tzv. Aimsun agenta a ze tří API zprostředkovávajícími komunikaci mezi bloky. Aimsun agent představuje řídící prvek celého systému. Přes \texttt{vgs\_api} spouští Aimsun a~stejným způsobem od Aimsunu průběžně přijímá hodnoty sledovaných dopravních veličin. Dále prostřednictvím \texttt{eh\_api} získává údaje z~řadičů křižovatek. Na základě všech shromážděných údajů pak sestavuje pokyny pro změny v~signálních plánech a~opět pomocí \texttt{ea\_api} tyto pokyny zasílá řadičům. Na závěr komunikace mezi mikrosimulátorem a~emulátory řadičů, která obsahuje údaje z~detektorů v~jednom směru a~ze stavů signálních skupin ve druhém, probíhá přes \texttt{ea_api}. \begin{figure}% \centering \includegraphics[width=\columnwidth]{aimsun-agents}% \caption{Komunikace jednotlivých komponent simulačního prostředí}% \label{fig:struktura}% \end{figure} \section{VGS API} Velmi důležitým úkolem při simulaci reálné oblasti v~mikrosimulátoru Aimsun je vložení reálných vstupních intenzit dopravy do zkoumaného modelu. Běžným postupem je ruční sčítání vozidel v~dané oblasti, zpravidla v~hodinovém rastru. Takto získaná data je možné vložit do simulátoru Aimsun jako hodinové zátěže, ovšem toto je třeba provést také ručně. %Jedním z nejdůležitějších úkolů při simulaci reálného zatížení dopravní sítě v mikrosimulátoru Aimsun je zatížení celé simulované dopravní sítě reálnými vstupními intenzitami dopravy, odpovídajícími měřením v simulované síti. Při běžných postupech se intenzity dopravy určují pomocí ručního sčítání dopravy, zpravidla v hodinovém rastru. Data, získaná tímto postupem, je možno potom přímo vložit opět ručně do Aimsunu jako hodinové dopravní zátěže. Přesnější metodou je výpočet intenzity provozu z~dat získaných přímo z~dopravních detektorů instalovaných v~předmětné oblasti. To však znamená ruční zadání stovek údajů do simulátoru. Právě z~tohoto důvodu vzniklo VGS API. To se stará o~nastartování Aimsunu a~vpouštění vozidel do oblasti. Vjezdy vozidel se při tom řídí údaji v~externím souboru, obsahujícím intenzity na jednotlivých ramenech. Uživatel pak jen zadá jméno a~umístění tohoto souboru a~vše ostatní se děje automaticky. Navíc lze volit mezi různými rozděleními pravděpodobnosti vjezdů, přičemž nejčastěji používané je rovnoměrné či Poissonovo rozdělení. %Pokud se snažíme o přesnější simulaci, můžeme pro zatižení dopravní sítě použít data získaná přímo z dopravních detektorů instalovaných v simulované oblasti. V takovém případě by bylo potřeba ručně vkládat několik set hodnot vstupních intenzit pro každý simulovaný den. VGS API vzniklo ve snaze tento handicap odstranit -- na začátku simulace rozhraní pouze uživatewl předá informace o tom, kde je umístěn soubor s intenzitami vjezdů do jednotlivých ramen a o jaká vjezdová ramena jde, VGS API se automaticky postará o nastartování Aimsunu a automaticky vpouští vozidla do systému podle údajů, obsažených v souboru s intenzitami vjezdů. Uživatel může volit různé druhy rozdělení pravděpodobnosti vjezdů, nejčastěji se používá rovnoměrné nebo Poissonovo rozdělení. Vedle práce se vstupní daty pro simulaci se VGS API stará také o~zpracování dat výstupních. Aimsun sice zvládá export výsledků simulací do textových souborů a~obsahuje i~nástroje pro jejich vizualizaci, neumožňuje však přímo některé důležité procesy jako je například porovnání výsledků ze dvou různých scénářů. VGS proto v~průběhu celého experimentu ukládá údaje o~sledované oblasti, a~to jak pro jednotlivé sekce, tak pro celý systém. Ve výsledku uživatel získává informace o~počtu zastavení vozidel, o~jeho zpoždění, průměrné rychlosti, době jízdy, stání, o~dopravím toku a~hustotě dopravy na jednotlivých segmentech či o~délkách front vozidel. Údaje o~délkách front jsou specialitou VGS, Aimsun tento údaj pro jednotlivé jízdní pruhy přímo neposkytuje a~VGS má proto implementován vlastní algoritmus pro jejich sčítání. %Druhým důležitám úkolem VGS API je shromažďovat data potřebná pro vyhodnocení experimentu. Aimsun sice disponuje jednoduchým rozhraním pro vizualizaci dat a jejich export do textových souborů, není ale možné například porovnávat jednotlivé scénáře simulací. VGS API proto periodicky ukládá všechny klíčové ukazatele jak pro jednotlivé segmenty dopravní sítě, tak i pro celý simulovaný systém. Uživatel má po ukončení simulace k dispozici údaje o počtu zastavení vozidla, o jeho zpoždění, průměrné rychlosti, době jízdy a době stání, o dopravím toku a hustotě dopravy na jednotlivých segmentech či o délkách front vozidel. Vzhledem k tomu, že Aimsun neposkytuje informace o délce fronty vozidel pro každý na jízdní pruh zvlášť (k dispozici jsou pouze údaje o akumulované délce fronty vyjádřené početm vozuidel, která na segmentu dopravní sítě v daný okamžik stojí), má VGS API implementovaný vlastní algoritmus výpočtu délky fronty vozidel na každém jízdním pruhu a poskytuje uživateli i takto získané informace o délce fronty. Implementací VGS API je DLL knihovna napsaná v~jazyce C pro 32 a~64 bitové systémy Windows XP a~výše. Navíc je součástí VGStoolbox, sada skriptů pro zpracování výstupních dat v~programu Matlab. %Vlastní VGS API je implementováno v jazyce C jako DLL pro 32- a 64-bitové systémy počínaje Windows XP, rozhraní je doplněno funkcemi pro obsluhu API a vyhodnocování statistických informací v prostředí nástroje Matlab \section{Program main_loop.exe} \label{sec:main_loop} Simulace je obsluhována z~programu \emph{main\_loop.exe}. Ten se stará o~načtení konfigurace, spuštění simulátoru Aimsun a~představuje komunikačního prostředníka mezi jednotlivými agenty. Konfigurační soubor obsahuje několik prvků. Seznam (\texttt{list}) \texttt{agents} se skládá z~popisu jednotlivých agentů. Především je definována třída, jejíž instance agenta představuje, a~unikátní jméno agenta (\texttt{name}). Další proměnné v~konfiguračním souboru záleží na použité třídě pro konstrukci agenta, každá třída může mít své specifické požadavky. Skupina (\texttt{group}) \texttt{logger} určuje, jak název napovídá, třídu použitou pro logování údajů během simulace. Skupina \texttt{system} obsahuje parametry pro simulátor a~konfigurační soubor uzavírají ostatní užitečné proměnné nespadající do výše uvedených kategorií. Komunikaci mezi agenty program zajišťuje obsluhou fronty zpráv. Zprávy jsou ve formátu knihovny \texttt{libconfig}, obsahují dvě složky povinné a~libovolný počet složek volitelných. Nezbytnými údaji jsou řetězec \texttt{to}, obsahující příjemce zprávy, a~řetězec \texttt{what}, který značí předmět zprávy. Další rozšíření formátu zpráv je pak na potřebách použitých agentů. Program se dá rozdělit na samostatné bloky tak, jak je zobrazeno na obrázku \ref{fig:mainloop}. Z~tohoto nákresu bude vycházet následující popis. \begin{figure}[t]% \centering \includegraphics[width=8.5cm]{mainloop3}% \caption{Strukuta fungování programu \texttt{main_loop.exe}, podrobněji popsaná v~podkapitole \ref{sec:main_loop}}% \label{fig:mainloop}% \end{figure} Během \emph{inicializace} je načten konfigurační soubor, jehož jméno se předává jako jediný parametr při spouštění \texttt{main_loop.exe}. Po načtení jsou parametry ze skupiny \texttt{system} předány VGS API, které se postará o~spuštění Aimsunu s~těmito parametry. Jde především o~intenzitu provozu na vstupech do dopravní sítě a~délku simulace. Následně je dle konfigurace vytvořeno pole ukazatelů na agenty \texttt{Ags}. Při konstrukci jednotlivých agentů je volána jejich funkce \texttt{from_setting()}, načítající konkrétní parametry každého agenta. Poté se vytváří instance třídy \texttt{logger} a~její provázání s~agenty. V~tu chvíli se pouští i~funkce \texttt{ds_register()} umožňující aktualizaci datalinků pro spojení mezi agenty a~simulátorem. Na závěr tohoto bloku programu je provedena inicializace fronty zpráv a~zaregistrování vektorů, které se budou v~průběhu simulace logovat. Pokračuje se ústředním for cyklem. Část \texttt{získání dat} v~sobě zahrnuje uložení logovaných hodnot ze simulátoru a~pak především uložení vektoru $d_t$ do proměnné \texttt{glob_dt} a~jeho odeslání agentům ke zpracování funkcí \texttt{adapt()}. V~následující fázi přichází na řadu vyjednávací cyklus, který se zabývá obsluhou fronty zpráv. Nejprve proběhne \emph{doručení zpráv}, které jsou aktuálně ve frontě. V~prvním kroku vyjednávacího cyklu je fronta zpráv samozřejmě prázdná, v~následujícím se prochází od konce a~adresovaným agentům jsou předávány příslušné zprávy. To se děje zavoláním funkce \texttt{recieve()} se zprávou jako parametrem. V~případě, že adresát není nalezen a~zprávu nelze doručit, je oznámeno varování, ale pokračuje se v~činnosti. Po vyprázdnění fronty nastává čas pro \emph{vysílání zpráv}. To spočívá ve spuštění funkce \texttt{broadcast()} u~všech agentů, kteří mají nyní příležitost vložit do fronty zpráv libovolný počet sdělení pro ostatní agenty; nemusí ovšem vysílat žádné. Další postup závisí na stavu fronty zpráv. Pokud je prázdná (tedy žádný agent nevyslal žádnou zprávu), končí vyjednávací cyklus a~pokračuje se v~běhu programu. V~případě, že fronta prázdná není, cyklus se opakuje a~proběhne další doručování zpráv z~fronty. Vyjednávání může být ukončeno také v~případě, kdy proběhne stanovený maximální počet vyjednávacích cyklů, nastavený v~proměnné \texttt{max_cycles}. Počet cyklů by však měl být nastaven tak, aby k~jeho dosažení nedocházelo; pokud se tak stane, může to být signálem problému v~programu. Poslední část hlavního cyklu je nazvaná \emph{krok simulace}. Spočívá nejprve v~zavolání funkce \texttt{act()} u~každého agenta. To je okamžik, kdy mohou agenti ovlivnit řízení signalizace. Právě zde probíhá zkopírování vypočítaných hodnot do vektoru \mbox{\texttt{glob_ut}}, tedy do proměnné představující vektor vstupních hodnot $u_t$. Na závěr hlavního cyklu je celá simulace posunuta o~jeden krok dále voláním funkcí \texttt{step()} u~loggeru \texttt{L}, \texttt{Ds} a~u~všech agentů. Délka kroku je pevně nastavena na 90~s~-- to z~toho důvodu, že v~Praze jsou údaje z~detektorů sbírány právě v~tomto intervalu. Tato smyčka se opakuje dokud není dosažen v~konfiguračním souboru nastavený čas simulace. \emph{Zakončení} spočívá již jen v~zapsání log souboru. \section{Implementace navrženého algoritmu} Algoritmus popsaný v~kapitole \ref{sec:navrh} je implementován pomocí agentů, kteří jsou instancemi třídy \texttt{GreenWaveTrafficAgent}, která je definována v~souborech \texttt{traffic_agent_offset.cpp} a~\texttt{traffic_agent_offset.h}. O~možnost získání zdrojových souborů informuje příloha \ref{app:src}. Třída je odvozena od předka jménem \texttt{BaseTrafficAgent}. Ten slouží prakticky jen jako prázdný agent, po přiřazení k~nějaké křižovatce nekoná žádnou akci a~umožňuje tak simulovat systém bez decentralizovaného řízení. Předkem této třídy je třída \texttt{Participant} z~knihovny BDM. \texttt{Participant}, tedy účastník, který představuje základní jednotku v~rozhodovacím procesu. Každý účastník má své jméno (\texttt{name}) a~je schopen ukládat výsledky. Už právě třída \texttt{Participant} deklaruje existenci funkcí \texttt{adapt()}, \texttt{broadcast()}, \texttt{recieve()}, \texttt{act()} a~\texttt{step()}. Tyto jsou v~něm definovány jako virtuální (\texttt{virtual}), tzn. pokud je v~potomkovi redefinujeme, je zaručeno volání těchto nových funkcí. \subsection{Veřejné funkce} Veřejné (\texttt{public}) funkce ve třídě \texttt{GreenWaveTrafficAgent} přepisují funkce definované v~předcích a~představují rozhraní pro komunikaci s~hlavním programem a~tím i~ostatními agenty. První po vytvoření instance agenta proběhne zavolání funkce \texttt{from_setting()}. V~té se nejprve volá stejnojmenná funkce předka, která načítá z~konfigurace základní parametry, jmenovitě: \begin{description} \item[lanes] Informace o~jízdních pruzích. \item[neighbours] Jména agentů u~sousedních křižovatek. \item[green_names] Jména jednotlivých signálních skupin. \item[green_starts] Čas, ve který příslušné signální skupiny rozsvěcují zelenou. \item[green_times] Délka trvání svícení zelené dané signální skupiny, uváděna jako poměr $\text{doba svícení}/\text{délka cyklu}$. \end{description} a~některé další, které pro tuto práci nejsou důležité. Popis jednotlivých jízdních pruhů si zaslouží podrobnější rozebrání. Ten totiž pro agenta představuje stěžejní část informací a~topologii jím řízené křižovatky. Do konfiguračního souboru se uvádějí všechny takové jízdní pruhy, které končí nějakou stop čárou dané křižovatky. Každý jízdní pruh pak má následující parametry: \texttt{sg}, což je signální skupina do které patří, \texttt{inputs} je seznam detektorů na vjezdu do tohoto pruhu, \texttt{outputs} je seznam detektorů na sousedních křižovatkách, ke kterým může vozidlo z~tohoto pruhu dojet. Pokud se vozidla mohou dostat do míst, kde žádný detektor není, je zde za každý takovýto směr uveden „falešný“ detektor \texttt{DUMMY_DET}. Dále konfigurace obsahuje \texttt{input_distances}, pole vzdáleností vstupních detektorů od stop čáry, podobně \texttt{output_distances} je pole vzdáleností výstupních detektorů (u~„falešných“ nehraje roli), \texttt{alpha} je pole poměrů odbočení k~jednotlivým výstupním detektorům, \texttt{queue} jméno fronty, která se na daném pruhu tvoří a~konečně \texttt{beta} je koeficient, kterým se násobí délka fronty během výpočtů, které agent provádí. Agent po získání informací o~jízdních pruzích vytvoří pro každý z~nich instance tříd \texttt{Lane} a~\texttt{LaneHandler}, které představují mezivrstvu mezi agentem a~fyzickým jízdním pruhem se skutečnými detektory. Dále se ve funkci \texttt{from_setting()} nastavují hodnoty konstant a~výchozí hodnoty některých proměnných. Konkrétně jde o~\begin{description} \item[car_leaving_time] Čas v~sekundách, ze který jedno auto opustí frontu ($c_0$). \item[VP] Průmerná rychlost jízdy automobilu mezi křižovatkami na volné silnici ($v_\mathrm{P}$). \item[actual_time] Uchovává aktuální čas simulace (v~sekundách). \item[negot_cycle] Počet již proběhlých vyjednávacích cyklů od posledního průměrování. \item[cycle_count] Hranice počtu cyklů, po jejímž dosažení nastává průměrování získaných offsetů. \item[total_offset] Hodnota součtu dosud vyjednaných offsetů od posledního průměrování. \item[negot_start] Krok, kterým se začíná při hledání ideálního offsetu u~souseda. \item[negot_limit] Krok, kterým se končí při hledání ideálního offsetu u~souseda. \item[find_best_start] Krok, kterým začíná hledání nejlepšího vlastního offsetu. \item[find_best_limit] Krok, kterým končí hledání nejlepšího vlastního offsetu. \item[passive] Indikuje zda agent zastává pasivní nebo aktivní roli. \end{description} Hodnoty konstant lze z~výchozích hodnot přepsat uvedením proměnné v~konfiguračním souboru. V~závěru je pak z~konfigurace načten výchozí offset, role agenta a~připraven vektor výstupních hodnot \texttt{outputs} spolu s~jeho popisem \texttt{rv_outputs}. Na úplném konci je ještě zapnuto logování hodnot offsetů pro pozdější vyhodnocení. \lstset{language=[Visual]C++,showstringspaces=false,numbers=left, numberstyle=\tiny, numbersep=0pt, tabsize=2, breaklines, breakautoindent=false, captionpos=b} %\begin{lstlisting}[caption=Funkce from_setting(), label=lst:from, firstnumber=587] % void from_setting(const Setting &set) { % BaseTrafficAgent::from_setting(set); % % RV rv_exp; % % car_leaving=2; % VP=45; % actual_time=0; % % negoT_{\mathrm{c}}ycle=1; % cycle_count=5; % total_offset=0; % % negot_offset=4; % negot_limit=1; % % passive=0; % % UI::get(last_offset, set, "offset", UI::compulsory); % UI::get(passive, set, "passive", UI::optional); % % for (int i=0;iqueue=queues(lanehs(i)->queue_index); % } % % planned_offset=last_offset; % % //set state variables to default values % final_state=false; % new_stable_state=false; % send_requests=false; % need_exps=true; % negot_offset=4; % } %\end{lstlisting} Funkce \texttt{recieve()} zpracovává přijaté zprávy od ostatních agentů. Na začátku je přijatá zpráva rozdělena do jednotlivých proměnných. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}, inicializace}, label=lst:recieveInit, firstnumber=491] % void receive(const Setting &msg){ % string what; % string to; % string from; % vec value; % RV *rv; % % UI::get(what, msg, "what", UI::compulsory); % UI::get(to, msg, "to", UI::compulsory); % UI::get(from, msg, "from"); % UI::get(rv, msg, "rv"); % UI::get(value, msg, "value"); %\end{lstlisting} Dále se činnost liší podle „předmětu“ zprávy, tedy podle řetězce uloženého v~části \texttt{what}. V~případě přijetí žádosti o~očekávané časy příjezdů je jen uloženo jméno odesílatele do seznamu žadatelů \texttt{requesters}. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}}, label=lst:recieveExpReq, firstnumber=last] % if (what=="expected_times_request"){ % requesters.push_back(from); % } % %\end{lstlisting} Pokud jsou naopak obsahem přijaté zprávy očekávané časy příjezdů automobilů od souseda, aktivní agent najde nový optimální offset pro svůj signální plán a~spočítá \texttt{rating}, pasivní provede jen tento výpočet. Na konci je stavová proměnná \texttt{new_stable_state} nastavena na \texttt{true}, což určuje další činnost agent ve fázi vysílání zpráv. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}}, label=lst:recieveNewExp, firstnumber=last] % else if (what=="new_expected_cars") { % rv_recieved_exps=*rv; % recieved_exps=value; % % if (!passive) { % planned_offset=find_best_offset(planned_offset,8); % planned_offset=normalize_offset(planned_offset); % } % % planned_rating=count_rating(planned_offset, recieved_exps, rv_recieved_exps); % % // we have new stable state to broadcast % new_stable_state=true; % } %\end{lstlisting} Obdržení zprávy s~hlavičkou „\texttt{stable_state}“ znamená, že některý ze sousedů změnil své nastavení offsetu a~zasílá tedy nové hodnoty očekávaných příjezdů. Pokud je aktuální hodnota \texttt{negot_offset} větší nebo rovna \texttt{negot_limit}, zkouší pomocí funkce \texttt{find_best_exps()}, která z~hodnot $0$, $+\mathtt{negot\_offset}$ nebo $-\mathtt{negot\_offset}$ by po přičtení k~sousedově offsetu znamenala nejlepší rating. Hodnota \texttt{negot_offset} je snížena na polovinu a~je připraveno odeslání žádosti pro souseda. Pokud byla proměnná \texttt{negot_offset} již nižší než než \texttt{negot_limit}, přepíná se agent do konečného stavu, neboť již nemá další prostor k~vyjednávání se sousedy. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}}, label=lst:recieveStable, firstnumber=last] % else if (what=="stable_state") { % rv_recieved_exps=*rv; % recieved_exps=value; % % planned_rating=count_rating(planned_offset, recieved_exps, rv_recieved_exps); % % if (!passive) { % for (int i=0;i=negot_limit) { % negot_offset/=2; % send_requests=true; % } % else { % final_state=true; % } % } % else { % final_state=true; % } % } %\end{lstlisting} Při přijetí zprávy se žádostí o~změnu offsetu agent zkoumá, zda je součet změny hodnocení po aplikaci navrženého offsetu a~zlepšení hodnocení u~souseda, které od změny offsetu očekává, větší než nula. Taková změna se přijímá a~podle ní se upravuje \texttt{planned_offset}. V~každém případě se agent připraví na zaslání aktuálních hodnot očekávaných příjezdů. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}}, label=lst:recieveOffChange, firstnumber=last] % else if (what=="offseT_{\mathrm{c}}hange_request") { % double final_rating_diff; % % rv_recieved_changes=*rv; % recieved_changes=value; % % for (int i=0;i=0) { % planned_offset+=(int)recieved_changes(ind(0)); % planned_offset=normalize_offset(planned_offset); % planned_rating+=final_rating_diff; % } % } % % new_stable_state=true; % } %\end{lstlisting} Když přijde zpráva s~předmětem, který \texttt{GreenWaveTrafficAgent} neumí zpracovat, předává ji předkovi. Pokud by se stalo, že ani ten si se zprávou neporadí, pošle zprávu svému předkovi (tedy třídě \texttt{bdm::Participant}), který v~takovém případě vyvolá varování o~nezpracovatelné zprávě. %\begin{lstlisting}[caption={Fragment funkce \texttt{recieve()}}, label=lst:recieveBase, firstnumber=last] % else { % BaseTrafficAgent::receive(msg); % } % } %\end{lstlisting} Funkce \texttt{broadcast()} vysílá zprávy do fronty zpráv a~to v~závislosti na hodnotách agentových stavových proměnných. Pokud není prázdný seznam \texttt{requests}, sestaví odpověď pro každého z~agentů uvedeného v~seznamu a~tento seznam následně vyprázdní. Dále pak za každou ze stavových proměnných, která se rovnají \texttt{true} sestaví příslušnou zprávu a~hodnotu změní na \texttt{false}. %\begin{lstlisting}[caption=Funkce broadcast(), label=lst:broadcast, firstnumber=429] % void broadcast(Setting& set){ % % //ask neighbours for exptected arrive times % if (need_exps) { % for (int i=0; i$ a~uloží se do vektoru $u_t$, představovaným proměnou \texttt{global_ut}. Tím se tato hodnota dostane k~řadiči křižovatky. %\begin{lstlisting}[caption=Funkce act(), label=lst:act, firstnumber=615] % void act(vec &glob_ut){ % if (negoT_{\mathrm{c}}ycle==cycle_count) { % vec action; % action.set_size(rv_action._dsize()); % % ivec index = RV(name+"_offset",1).dataind(rv_action); % % action(index(0))=normalize_offset(total_offset/cycle_count, false); % action2ds.filldown(action,glob_ut); % % total_offset=0; % negoT_{\mathrm{c}}ycle=1; % } % else { % total_offset+=planned_offset; % negoT_{\mathrm{c}}ycle++; % } % last_offset=planned_offset; % } %\end{lstlisting} O~zápis aktuálních hodnot do logovacího souboru se stará \texttt{log_write()}. Ukládá se dvousložkový vektor. První prvek obsahuje vypočítaný offset \texttt{planned_offset}, druhý jeho hodnocení \texttt{planned_rating}. %\begin{lstlisting}[caption=Funkce log_write(), label=lst:logwrite, firstnumber=644] % void log_write() const { % if (log_level[logoffset]){ % vec offset_vec(2); % offset_vec(0)=planned_offset; % offset_vec(1)=planned_rating; % log_level.store(logoffset, offset_vec); % } % } %\end{lstlisting} Poslední volaná veřejná funkce agenta je \texttt{step()}, která jen posouvá interní ukazatel času v~agentovi o~délku kroku simulace. %\begin{lstlisting}[caption=Funkce step(), label=lst:step, firstnumber=635] % void step() { % actual_time+=step_length; % } %\end{lstlisting} \subsection{Chráněné funkce} Chráněné (\texttt{protected}) funkce zajišťují většinu výpočtů v~agentovi, starají se o~sestavení očekávaných časů příjezdů vozidel k~sousedům, hledání optimálního offsetu, počítání hodnocení a~tak podobně. Funkce \texttt{expected_cars()} sestavuje údaje o~příjezdech vozidel pro všechny sousedy. Odhady jsou založeny na aktuální hodnotě offsetu uložené v~proměnné \texttt{planned_offset} a~jsou na závěr uloženy do vektoru \texttt{outputs}, určenému k~odeslání. Výpočet probíhá podle rovnic (\ref{eq:tz}) a~(\ref{eq:tk}). Počet vozidel je získán jako součin odhadu počtu vozidel, která projedou jízdním pruhem za dobu svícení zelené a~příslušného poměru odbočení $\alpha_i$, kde $i$ je číslo výstupního směru. Výpočet počtu vozidel, která jízdním pruhem projedou je záležitostí funkce \texttt{expected_output()} ze třídy \texttt{LaneHandler}. V~současnosti tato funkce vrací počet vozidel, která projela v~minulém cyklu. Pro nalezení nejlepšího nastavení vlastního offsetu slouží rekurentní funkce \texttt{int find_best_offset(cont int center, int interval)}. Ta hledá ze tří hodnot offsetů: $\mathtt{center}+\mathtt{interval}$, $\mathtt{center}$ a~$\mathtt{center}-\mathtt{interval}$ tu s~nejlepším hodnocením. Nalezená hodnota se pak použije jako první parametr pro další volání sebe sama; jako nový interval se vezme polovina předchozího. Toto volání probíhá, dokud \texttt{interval} nedosáhne hranice \texttt{find_best_limit}. Pak je funkce ukončena a~je vrácen nalezený offset. Funkce zkoumající efekty změn offsetu u~souseda má prototyp \texttt{int find_best_exps (int offset_change, string neighbour, double \&rating_change)}. Výstupem je nalezená změna offsetu. Parametr \texttt{offset_change} značí, o~kolik sekund bude agent zkoušet posunout sousedův offset, \texttt{neighbour} je jméno zkoumaného souseda a~do proměnné \texttt{rating_change} bude uložena předpokládaná změna hodnocení, kterou nalezený nový offset přinese oproti současnému stavu. Funkce si nejprve připraví hodnoty očekávaných příjezdů tak, jak by pravděpodobně vypadaly při změně sousedova offsetu v~kladném a~v~záporném smyslu o~hodnotu \texttt{offset_change}. Následně pak vypočítá hodnocení s~těmito a~bez těchto změn. Změna s~nejlepším hodnocením je návratovou hodnotou funkce. Počítání hodnocení probíhá ve funkci \texttt{double count_rating(const int offset, const vec recieved_exps, const RV rv_recieved_exps)} v~několika vnořených cyklech. Vnější probíhá přes všechny (vjezdové) jízdní pruhy křižovatky. Pro každý pruh následuje ve vnořené smyčce hledání očekávaných příjezdů ve vektoru přijatých dat \texttt{recieved_exps}. Pokud o~nich nejsou pro zkoumaný pruh informace, nebude se započítávat do hodnocení. V~případě nalezení nějakých předpokladů jsou tyto uloženy do vektorů \texttt{t_cars_begin} (začátky příjezdů), \texttt{t_cars_end} (konce příjezdů) a~\texttt{cars_density} (hustoty vozidel) pro další výpočet. Ten začíná zjištěním času rozsvícení a zhasnutí zelené v~signální skupině příslušné danému jízdnímu pruhu, se započítáním offsetu předanému funkci v~parametru \texttt{offset}. Interval svícení zelené je pak funkcí \texttt{expand_greens()} s~periodou $T_{\mathrm{c}}$ zopakován tak, aby pokryl celý časový úsek, pro který jsou k~dispozici informace o~příjezdech vozidel. Ilustraci k této funkci představuje obrázek \ref{fig:expand}. Ze znalosti očekávaných příjezdů a~stavu semaforů během nich pak vychází počítání příspěvku k~hodnocení. Ten probíhá v~\texttt{do} -- \texttt{while} cyklu. Pro jeho řízení je definována proměnná \texttt{t_act}, představující ukazatel na právě zpracovávaný okamžik ve zkoumaném intervalu. Její hodnota začíná na 0 a~pokud dosáhne hodnoty proměnné \texttt{t_limit}, způsobí ukončení smyčky. Do proměnné \texttt{t_limit} je uložen čas posledního zhasnutí zelené, získaný z~výše zmíněné funkce \texttt{expand_greens()}. \texttt{t_act} se tedy během výpočtu posouvá po významných bodech intervalu a~v~každém takovém bodě se ověřuje, jakého typu (dle definice z~kapitoly \ref{sec:navrh}) je následující interval. Podle toho se zjistí příslušná délka virtuální fronty $Q_\mathrm{V}^{(i)}$ a~pokud je tato záporná, odečte se od hodnocení \texttt{rating}. Po té, co funkce projde všechny jízdní pruhy, vrátí výslednou hodnotu uloženou v~proměnné \texttt{rating} jakožto hodnocení nastavení s~daným offsetem a~danými předpoklady o~příjezdech vozidel. \texttt{GreenWaveTrafficAgent} pak ještě obsahuje několik pomocných funkcí usnadňujících a~zpřehledňujících výpočty. Jde například o~převod libovolné proměnné na typ \texttt{string}, nalezení indexu minimálního prvku ve vektoru či hledání indexu (pořadí v~konfiguračním souboru) zadané signální skupiny. Za zmínku stojí funkce \texttt{int normalize_offset(int offset, bool zero=true)}. Ta provádí posun zadaného \texttt{offsetu} tak, aby zapadl do žádaného intervalu. Při vynechání druhého parametru jde o~interval $\left<-\frac{T_{\mathrm{c}}}{2};\frac{T_{\mathrm{c}}}{2}\right>$, pokud má druhý parametr hodnotu \texttt{false}, pak $\left<0;T_{\mathrm{c}}\right>$. Druhá forma normalizace se provádí před zaslání offsetu řadiči křižovatky, ten totiž hodnotu v~daném intervalu očekává. První forma je důležitá pro průměrování offsetů. Pokud by nebyla použita (nebo pokud by se používala druhá), nebyl by ve výsledném průměru zahrnut fakt, že hodnota $x$ a~$x+T_{\mathrm{c}}$ je v~případě nastavení offsetu totožná.