marci-dipterv-latex/src/content/ursim_impl.tex

389 lines
38 KiB
TeX

% !TeX root = ../thesis.tex
%----------------------------------------------------------------------------
\chapter{Felhő alapú robot vezérlés}
\label{chapter:ursim}
%----------------------------------------------------------------------------
A negyedik ipari forradalom az \enquote{okos} gyártásról szól. A korábban helyileg telepített számítási erőforrásokat felhasználó, előre meghatározott, kötött gyártás irányítást felváltja a felhő alapú folyamat irányítás és optimalizálás. Itt már nem csak a gyártás a lényeg, a folyamatok elemzésével és mély megértésével a hangsúly a precíz optimalizációra és a produktivitás növelésre hegyeződik ki. Ehhez felhasználják az ipar legújabb vívmányait: Egyre nagyobb teret hódít a gyártástechnikában az Ipari \acrshort{iot} (\acrshort{iiot}), a mesterséges intelligencia és gépi tanulás, a gép-gép kommunikáció, valósidejű adatgyűjtés, \textit{Big Data} és még sok minden más \cite{whatisindustry4}.
A robotvezérlés precíz időzítést igényel, hiszen itt pár-tíz milliszekundumos eltérések akár katasztrofális következményekkel járhatnak. Éppen ezért sokáig a robotok közvetlen vezérlésének kiszervezése a felhőbe nem volt megoldható. A peremhálózati rendszerekkel viszont ez megváltozhat.
Ez az alkalmazás a peremhálózati rendszerek alacsony késleltetését aknázza ki, hiszen a robotika vezérlésnél fontos a precíz irányítás, úgy vélem itt különösen jól alkalmazható ez a technológia. A peremhálózati rendszereknek hála tovább csökkenthetővé válik a gyárakban helyben telepített informatikai infrastruktúra mértéke, hiszen több alkalmazás válik felhőbe \enquote{költöztethetővé} és ezáltal annak előnyeit is nagyobb mértékben lesznek képesek kihasználni a gyártósorok.
Ebben a feladatban egy már rendelkezésre álló demonstrációt alakítottam át úgy, hogy az kihasználja a peremhálózati rendszerek adta lehetőségeket.
\section{Környezet ismertetése} % Általánosan a robotkar cumók ismertetése
A feladat megkezdésekor rendelkezésemre állt egy gondosan megtervezett demó szcenárió, amely kifejezetten a hálózati késleltetés jelentőségének bemutatására lett tervezve. Ez a szcenárió magába foglalja mind a szoftveres és a hardveres megoldást, a kettőt összekötő hálózatot és még egyedileg 3D nyomtatott munkadarabokat is.
\subsection{\textit{Universal Robots}}
A demonstráció során használt robotkarokat a \textit{Universal Robots} nevű dán vállalat tervezi és gyártja. A gyártó palettáján több robotkar is megtalálható. Ez a dolgozatírás idejében kétszer három plusz egy modellt jelent, az \textit{UR3} és \textit{UR3e}, az \textit{UR5} és \textit{UR5e}, az \textit{UR10} és \textit{UR10e} illetve az \textit{UR16e}. Mindegyik robotkar egyre növekvő módon egyre növekvő kihívásoknak képes eleget tenni. % TODO: Cite
A \textit{Universal Robots} által gyártott robotkarok vezérlését a hozzákapcsolt \textit{Control Box} végzi. Ez lényegében egy Mini-ITX számítógép, amely egyéni \Gls{linux} alapú operációs rendszert futtat. Ezen a számítógépen fut a robotkarok alacsony szintű vezérlője, amelyet különböző hálózati protokollok segítéségével kezelhetünk, illetve a grafikus felület, amely lokális kapcsolaton csatlakozik ezekhez az interfészekhez \cite{urscriptreference}.
A demonstrációhoz használt két robotkar típusa \textit{UR3} és \textit{UR3e}.
\subsubsection{Fizikai jellemzők}
Jellemzően többfajta felépítésű robotkar létezik. A demonstráció során használt mindkettő robotkar csuklós felépítésű, azaz az emberi karhoz hasonlóan hajlatokkal (joint) rendelkezik a merev tagok között.
A használt robotkarok hat darab ilyen hajlattal rendelkeznek, amely kellő szabadságot nyújt nekik a mozgás terén. A hat jointból ötöt +/- 360$^\circ$-ban lehet elforgatni, az utolsót, amely a robotkar \enquote{csuklóját} adja, pedig végtelenszer lehet körbeforgatni. Egy ilyen robotkar képes fél méteres (500mm) körön belül mozgásokat végezni, és három kilogramm hasznos terhet mozgatni. Maga a robotkar saját súlya 11 kilogramm \cite{ur3_specs}.
A két robotkar végére két különböző gripper van telepítve, mindkettő \textit{OnRobot} gyártmány, viszont az egyik egy \textit{rg2} a másik pedig \textit{rg2-ft}. Fizikai jellemzőit tekintve a két gripper hasonló: mindkettő képes legalább 100mm-re kinyílni és 2 kilogramm erővel vízszintesen tömeget megemelni. A szorításra maximum 40 Newton erőt képesek kifejteni \cite{rg2datasheet,rg2tftdatasheet}. A fő különbséget a gripperek kommunikációs interfésze nyújtja.
\subsubsection{Kommunikációs interfész}
Az demonstráción használt \textit{Universal Robots} robotkarok többféle kommunikációs interfésszel rendelkeznek, amelyeket a \textit{Control Box} valósít meg \cite{robot_client_interfaces}:
\begin{itemize}
\item \textbf{Primary/Secondary Interfaces}: A robotok vezérlő szoftvere két interfészt tart fenn a robotkar státuszának módosítására és megfigyelésére. Az elsődleges (primary) interfészen olvasni és írni is lehet a robot állapotát, a másodikon csak olvasni. Ez az interfész elsősorban a grafikus kezelőfelülettel történő kommunikációra lett kifejlesztve. URScript parancsokat képes küldeni és fogadni 10Hz-es rátával (azaz 100ms gyakorisággal küldhetünk vagy fogadhatunk valamilyen információt).
\item \textbf{Real-time Interfaces}: A valós idejű interfész működése hasonló az előbb említett elsődleges és másodlagos intefész működéséhez. Úgyanúgy státuszt tudunk vele megkapni és URScript parancsokat fogad. A fő különbség a működési ráta. Itt akár 500Hz-es rátát is elérhetünk (azaz 2ms gyakorisággal) \cite{robot_controll_tcpip}.
\item \textbf{Dashboard Server}: A robot grafikus kezelőfelületét is képesek vagyunk közvetlenül, programozottan vezérelni \acrshort{tcp} kapcsolaton keresztül. Ez olyan alapvető funkciókat enged végrehajtani, mint az eltárolt programok betöltése, futtatása, felhasználók hozzáférési szintjének szabályozása, és alapvető robot státusz információk lekérdezése.
\item \textbf{Socket Communication}: Ez egy alacsony szintű \acrshort{tcp} alapú kommunikáció, amelyben a robot kliensként viselkedik és a szerver, amihez kapcsolódik, látja el utasításokkal. Természetesen ezen a protokollon is \textit{URScript} parancsokat adhatunk ki, ezekben segítő utasítások vannak a kapcsolat életciklusának kezelésére.
\item \textbf{\acrshort{xml}-\acrshort{rpc}}: Ez a protokol lehetőséget ad sokkal széles körűbb alkalmazásokra, mint az előbbiekben említett \textit{URScriptet} használó protokollok. Hiszen a robotok egyes funkcióit a paraméterekkel együtt függvényhívás szerűen végezhetjük. Ezzel sokkal komplexebb programok készíthetőek.
\item \textbf{\acrfull{rtde}}: Az \acrshort{rtde}-t egy robusztus megoldásnak tervezték, amely kiválthatja a \textit{Real-time} interfészt. Lehetővé teszi az \textit{UR} kontroller számára az egyéni státusz-adat és regiszter állapotok átvitelét. Ez az interfész valósidejű szinkronizációt tesz lehetővé akár 125Hz-es rátával (8ms/üzenet).
\end{itemize}
Emellett a hozzákapcsolt grippereknek is lehet önálló kommunikációs interfésze. Míg az \textit{OnRobot RG2} csak a robotkarra kapcsolható közvetlen, így azt a fent említett kommunikációs protokollok segítségél, a robotkaron keresztül lehet vezérelni, az \textit{RG2-FT} lehetővé teszi, hogy közvetlenül a hálózatra csatlakoztassuk a grippert, ezzel önálló módon kezeljük azt.
\subsubsection{\textit{URScript}}
A \textit{URScript} imperatív, interpretált szkriptnyelv, amelyet elsősorban robotikai alkalmazásokra fejlesztettek ki \cite{urscriptreference}. A nyelv szintaktikája sokban hasonlít a \gls{python} programozási nyelvre.
A fő eltérés a \gls{python} programozási nyelvhez képest, hogy explicit jelezni kell, ha egy blokk véget ér (\texttt{end} kulcsszó). URScript snippeteket gyakran \textit{on-the-fly} tudunk küldeni \acrshort{tcp} kapcsolaton keresztül. Feltételezhetően ez miatt kell jelölni a blokkok végét, hiszen másként nem lenne lehetősége a vezérlő szoftvernek eldönteni, hogy egy blokk véget ért-e.
Az \textit{URScript} programokban lehetőség van többszálúsítást alkalmazni. Az egyes szálakat hagyományos módon függvény szerűen tudjuk definiálni. Mivel a \textit{URScript} elsődleges felhasználása, hogy robotokat vezéreljen valós időben, ezért a szálak ütemezése is erre alapozott.
A robotokat 125Hz-es rátával kell vezérelni (azaz 8ms-ként kell utasítást adni nekik). Hogy ezt megvalósíthassa, a végrehajtó környezet minden szálnak pontosan 8ms időt allokál, hogy fusson. A futásra készen álló szálakból \textit{round-robin} ütemezés szerint választ az ütemező. % TODO itt kibogarászni hogy a physical idő meg a timeframe-ek hogy vannak
\subsubsection{Szimulátor}
\label{sec:ursim_simulator}
A tanszéken található kettő robotkar természetesen nem áll mindig rendelkezésre. Más csapatok más-más projektekben is használják. A fejlesztés és a tesztelés megkönnyítésére több szimulátor is megvizsgálásra került a projekten korábban dolgozó csapat által. Végül a leghatékonyabban használhatónak a \textit{DockURSim}\footnote{\url{https://github.com/ahobsonsayers/DockURSim}} bizonyult.
Lényegében a \textit{DockURSim} a robotkarokat közvetlenül irányító, \textit{Control Boxon} futó szoftver becsomagolva egy \textit{Docker} konténerbe. Konkrét robotkart nem irányít, de a kommunikációs interfészeket és azok funkcionalitását teljes mértékben implementálja. Lehetőségünk van a beépített \acrfull{rdp} szerveren keresztül grafikus felületet\footnote{Ez a grafikus felület a \textit{Teach Pendanton} grafikusan kezelhető felület.} kezelni és ott meg tudjuk figyelni a robotkar mozgását.
Egyetlen hátránya ennek a megoldásnak, hogy a robotkarokat önállóan egy izolált környezetben tudja csak szimulálni, így a környezet fizikai hatásai, a gripperek működése és a robotkarok egymással való találkozását nem képes modellezni.
\subsection{Demonstráció}
A megvalósított demonstráció egy általános összeszerelési szcenáriót mintáz. Ebben a szcenárióban a két robotkar egyenként felemel egy-egy munkadarabot. Ezeket a levegőben összeillesztik, majd ezután az egyik robotkar elengedi, a másik pedig leteszi.
Bár egyszerűnek tűnik, ebben a szcenárióban nagy hangsúly helyeződik a hálózat pontos időzítésérére, hiszen a kritikus ponton, amikor a két robotkar összeilleszti a munkadarabot, kiemelten fontos, hogy egyszerre mozogjanak, hiszen ha ez nem sikerül, akkor összeillesztés helyett eltörik a munkadarabot.
A következőkben részletesen bemutatom ennek a szoftvernek a felépítését és működését.
\subsubsection{Környezet}
A demonstrációs szcenáriót lejátszó robotkarok egy asztalon kerültek telepítésre. Két oldalt a két robotkar helyezkedik el. A robotkaroknak a szcenáriót tervező csapat az Erik és a Fred neveket adták. Az Erik nevezetű robotkaron található az \textit{rg2-ft} típusú gripper, amely önállóan csatlakozik a hálózatra, ezzel szemben a Fred nevezetű robotkaron lévő gripper a robotkar kommunikációs interfészén keresztül kapja az információt.
Az asztalon a két munkadarabnak kijelölt helye van, ahonnan a robotkarok felemelik, az összeillesztés az asztal közepénél történik, majd valahol itt is helyezi el a kész darabot. A környezet vizuális bemutatásául \aref{fig:work_table}.\ ábra szolgál.
\begin{figure}[h!]
\centering
\includegraphics[width=0.6\textwidth]{figures/work_table}
\caption{A demonstrációs környezet elemeit tartalmazó asztal vázlatos alaprajza}
\label{fig:work_table}
\end{figure}
\subsubsection{Vezérlés}
\label{sec:ursim_demo_control}
A demo vezérlésére a munkám elején egy \gls{python} nyelven megírt monolit program állt rendelkezésemre. A program úgy lett tervezve, hogy elindításával csatlakozik mindkettő robotkar \acrshort{rtde} interfészére, majd sorban elküldi a robotkaroknak a demo végrehajtásához szükséges utasításokat. A % TODO
gripper vezérlését \acrshort{rest} \acrshort{api}-n keresztül végzi.
A két robotkar vezérlése két külön szálon van megvalósítva. Mindkettő szálon egymás után következnek az utasítások, a kritikus szinkronizációs részek egyszerű szál-szinkronizációs primitívekkel vannak megoldva.
A programban több futtatási mód is implementálva van. Egyrészt gyors és lassú végrehajtási sebesség között lehet választani. Ez a robotkarok mozgási sebességét befolyásolja. Emellett úgynevezett \enquote{Jogging} módban is lehet az alkalmazást futtatni. Ebben a módban a program minden mozdulat végrehajtása előtt várakozik egy gomb lenyomására a további parancsok kiadása előtt. Így lehetőség adódik az esetleges hibás mozgások felderítésére, illetve a program teljesen automatikus lefuttatása előtt manuális tesztelésre.
A program a konfigurációs beállításait egy \gls{ini} fájlban tárolja. Ezek a konfigurációs beállítások elsősorban a robotkarok és a hozzá tartozó gripperek hálózati címeit adja meg, illetve a konkrét sebesség módokhoz tartozó konfigurációs értékeket.
Az \gls{ini} fájl mellett a program a konkrét lépésekhez tartozó koordinátákat egy \textit{Microsoft Excel} \gls{xlsx} fájlból olvassa ki. Ebben a fájlban mindkét robotkarhoz tartozóan fel vannak sorolva az egyes pózok leírásai sorrendben. % koordináták mind tool- és joint térben. %TODO verify naming
\section{Felkészítés a peremhálózati alkalmazásra} % Itt foglalom össze, hogy hogy terveztem meg
A fenti monolit demó átültetése alapos tervezést igényelt egy felhő és perem számítástechnikai rendszerbe oly módon, hogy annak előnyeit megfelelően ki tudja használni.
\subsection{Architektúra}
Mivel a \textit{KubeFed} keretrendszert választottam az alkalmazásom futtatására, így ennek megfelelően kellett megválasztanom annak felépítését. Az alkalmazást mikroszolgáltatás architektúrára terveztem. \textit{KubeFed} használatával lényegében az alkalmazásunk (egy vagy több) \textit{Kubernetes} klaszterben fut. A mikroszolgáltatás architektúra az ilyen környezetben azért jó, mert az alkalmazás komponensek egymás belső működésétől függetlenek. Így amíg az egyes komponensek egymás \acrshort{api} felületét elérik, nem számít, hogy hol és milyen környezetben futnak. Természetesen a felületek elérése is egy összetett probléma önmagában, ennek megoldásáról \aref{chapter:dynamic_scheduling}.\ fejezet értekezik.
Ennek érdekében a tervezés első lépéseként felbontottam funkcionális egységekre a jelenlegi demonstráció vezérlését. A felbontásnál viszont itt nem csak a funkcionális egységek elkülönítését tartottam szem előtt. Mivel egy három rétegű architektúrára tervezek, ezért figyelembe kellett vennem annak elvárásait és adottságait.
A végleges funkcionálisan felbontott részegységekről \aref{fig:usrsim_services_plan-blocks}.\ ábra ad áttekintést. Érdemes megfigyelni, hogy végeredményében a három rétegű architektúra egy három szintű robot vezérlést implementál, amely a vezérlési feladatokat egyre nagyobb absztrakcióval kezeli. Az egyes rétegeken belüli rendszerek tervezését a következő szekciókban ismertetem.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\textwidth]{figures/ursim_services_plan-blocks}
\caption{Vezérlés felbontása funkcionális egységekre a futtatási környezet és a késleltetés igények szerint}
\label{fig:usrsim_services_plan-blocks}
\end{figure}
\subsubsection{\textit{On-site} réteg}
Mivel azt szeretnénk, hogy a telephelyen belüli (On-site) rendszer a lehető legminimálisabb legyen, így oda csak maga a robotkar -- és a hozzá tartozó \textit{Control Box}, amely az alacsony szintű vezérlését megvalósítja -- kerül. Természetesen ebből több is kerülhet egy telephelyre, vagy akár több telephelyre kihelyezett rendszert is irányíthatunk egyszerre.
\subsubsection{Peremhálózati réteg}
\label{sec:edge_layer_plans}
A középső rétegben alacsony késleltetésünk van a robotkar felé, viszont csak limitált erőforrásaink vannak. Ennek érdekében csak azokat a komponenseket lenne érdemes ide tenni, amelyeknél ezek a szempontok fontosak és azt is minimálisan tartani. Ezen a szinten ezért egy \enquote{középszintű} vezérlő komponenst terveztem. Ez már az általam írt kód szerint dolgozik, így tetszőleges logikát tudok bele implementálni. Az alacsony késleltetés miatt innen megbízhatóan és közvetlenül lehet a robotkarnak mozgató utasításokat kiadni illetve az állapotát beolvasni.
A \enquote{középszintű} vezérlés feladata egy olyan absztrakciót adni az egyel magasabb réteg felé, hogy ott már olyan módon lehessen irányítani a robotok működését, hogy azt ne zavarja a késleltetés. Ezt az absztrakciót úgy terveztem meg, hogy nagyobb, összefüggő lépés sorozatok végrehajtását tegye lehetővé a magasabb réteg felé.
A lépés sorozatoknál elvárás, hogy biztonságos állapotból indulnak és biztonságos állapotba érkeznek. Így a kettő lépés sorozat végrehajtása között eltelt idő nem befolyásolja károsan a rendszert. A lépés sorozatok engednek bizonyos fokú külső befolyást is, viszont ezeket szigorúan úgy kell implementálni, hogy itt se számítson a késleltetése a beavatkozásoknak.
A lépés sorozatokat \textit{Programnak} neveztem el. Egy ilyen \textit{program} lépéseinek végrehajtása a futtatás. A magasabb rétegek ilyen előre összeállított programokat képesek a peremhálózati rendszer felé küldeni, illetve annak futási állapotáról információkat kapni.
\subsubsection{Felhő réteg}
Mivel a legmagasabb réteg -- a felhő -- és a középső réteg között a kapcsolat nem megbízható, előfordulhatnak magas, akár ingadozó késleltetések is, szélsőséges esetben a kapcsolat megszakadása, ezért ezt figyelembe véve ide olyan komponenseket lehet csak helyezni, amelyek erre nem érzékenyek.
Mivel a peremhálózati rétegben már kialakítottunk egy absztrakciót, amely elfedi a késleltetési problémákat a felhő réteg elől, így a felhő rétegben futó szoftver ezt biztonságosan tudja használni a robotkar mozgatására.
Az Ipar 4.0 megközelítésben különös szerepet játszik a \textit{Big Data}, azaz a nagy mennyiségű adat gyűjtése és feldolgozása és ennek felhasználása.
Az adatok gyűjtése nem késleltetés érzékeny, ezért nem okoz problémát, ha a peremhálózatról egyenesen a felhőbe küldjük. Mivel a dolgozatom a \textit{Big Data} konkrét elemzésére nem tér ki, ezért az elkészített rendszerben a mérési adatok gyűjtése és feldolgozása kimerül annak magas szintű tervezésében.
\subsection{Peremhálózati szolgáltatások} % Peremhálózati rendszer basically
\label{sec:single_robot_contoller_plans}
\Aref{sec:edge_layer_plans}.\ alfejezetben ismertetettek alapján terveztem meg a peremhálózati rendszerben futó mikroszolgáltatás halmazt. Jelenleg ez egyetlen egy mikroszolgáltatásból áll, ez a szolgáltatás a \textit{programok} futtatását valósítja meg.
Egy program egyszerre csak egy robotkar működését írja le, így egy ilyen komponens egyszerre csak egy robotkarhoz kapcsolódik. A demonstráció végrehajtásához két komponensnek kell kapcsolódnia.
Emellett meg kell oldania olyan problémákat is, amelyek \aref{sec:ursim_demo_control}.\ alfejezetben ismertetett programnál adottak -- vagy legalábbis közel-triviálisan megoldhatóak -- voltak.
\subsubsection{Végrehajtási keretrendszer}
\label{sec:ursim_execution_framework}
Annak érdekében, hogy a komponens absztrakt módon megoldást adjon a késleltetések áthidalására a felhő és a peremhálózati rendszerek között a robot mozgatásához szükséges lépéseket egy \textit{programba} szedve kapja meg. Egy ilyen \textit{program} utasításokból épül fel, amelyet interpretáltan, imperatív módon hajt végre a komponens.
Szerettem volna egy olyan végrehajtási környezetet megalkotni, ahol könnyen lehet az egyes utasítások mögött cserélni a funkcionalitást. Ezzel lehetőség nyílik arra, hogy a programokat szinte módosítás nélkül újrahasznosítsuk.
Munkám során mások is dolgoztak a robotkarok alacsony szintű vezérlésén. Terveink szerint ezeket a törekvéseket egy projektben fogjuk majd egyesíteni. Ez is alátámasztotta a dinamikusan cserélhető funkcionalitás szükségességét.
Ezek motiválták, hogy az egyes utasításokat önálló komponensbe szervezzem. Ezeket a komponenseket \textit{plugin}-nak neveztem el. A \textit{program} által végrehajtható utasításokat az egyes \textit{plugin}-ok implementálják. Egy \textit{plugin} többet is akár, illetve egy utasítást több \textit{plugin} is implementálhat\footnote{Ilyen esetben csak egy \textit{plugin}-t használhatunk egyszerre}. Ennek köszönhetően egy utasítás mögött egy másik \textit{plugin} használatával könnyen lecserélhetjük a funkcionalitást. A program futtatása előtt definiálni kell, hogy konkrétan melyik \textit{plugin}-okat szeretnénk használni a futtatás során.
Egy \textit{plugin} által definiált utasításnak, a funkcionalitás mellett, végrehajtásának vége előtti megszakítását is implementálnia kell, hiszen bármikor előfordulhat olyan helyzet, hogy le kell állítani a futtatást. Emellett a végrehajtás állapotának megfigyelhetőségéhez szükséges funkciókat is meg kell valósítania.
\subsubsection{Szinkronizáció}
\label{sec:ursim_snyc_proto}
A demonstráció során kiemelt szerepe van a két robotkar lépéseinek összehangolására kritikus pontokban.
Mivel itt már nem két szál összehangolásáról van szó, hanem két egymástól független alkalmazásról, amelyek az architektúra, a peremhálózati keretrendszer és a futtatási környezetből adódóan jóformán csak hálózaton tudnak kommunikálni, ezért ez teljesen más megoldást kívánt.
Alapos kutatás után sajnos nem találtam erre a problémára teljes, kész megoldást, ezért kidolgoztam egy saját megoldást.
Mivel fontos, hogy a szinkronizáció pontos legyen minden komponensnél, ezért annak az ötletét, hogy hálózaton küldött üzenetek szolgáljanak a szinkronizáció alapjául, teljesen elvetettem.
Ezzel szemben lehetőség van a futtató környezetet adó fizikai számítógépek óráinak meglepően precíz szinkronizációjára Hálózati idő protokoll (\acrlong{ntp}; \acrshort{ntp}) vagy Precíz idő protokoll (\acrlong{ptp}; \acrshort{ptp}) használatával. Ezt alapul véve kidolgoztam egy megfelelően precíz szinkronizációs protokollt.
Szinkronizáció gyanánt a résztvevők (Az egyes \textit{Single Robot Controller} példányok) \enquote{megegyeznek} abban, hogy mikor lesz az az időpont, amikor tovább haladnak a végrehajtással. Ehhez szükség van arra, hogy megbizonyosodjanak róla, hogy minden szolgáltatás elért a végrehajtással arra a pontra, ahonnan csak együtt léphetnek tovább.
A \enquote{megegyezéshez} egy osztott in-memory adatbázist használtam, a \textit{Redis}\footnote{\url{https://redis.io/}}-t, amely nem csak hálózaton elérhető és atomicitást garantáló műveleteket biztosít, de beépítetten tartalmaz egy üzenetsort, amely alkalmas a folytatási idő kihirdetésére.
% Itt ezt jó lenne még taglalni
A szinkronizációs folyamat időbeni lefutása \aref{fig:pyprocsync_flow} ábrán látható (konkrét \textit{Redis} utasításokkal bemutatva).
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{figures/pyprocsync_flow}
\caption{Az általam tervezett szinkronizációs protokoll működése szekvencia diagramon ábrázolva}
\label{fig:pyprocsync_flow}
\end{figure}
A protokoll arra hagyatkozik, hogy ismerjük, hogy hány komponenst kell szinkronizálni. Mindig, amikor egy program a szinkronizációs ponthoz érkezik, akkor atomikus módon megnövel egy számlálót, ha a számláló értéke egyezik, a várakozó komponensek számával, akkor az utolsó komponens a pillanatnyi rendszeridőhöz hozzáad egy fix értéket, és ezt kihirdeti a többi komponens felé\footnote{Ha feltételezzük, hogy a rendszeridők szinkronban vannak, akkor lényegében mindegy, hogy melyik komponens hirdeti ki.}. Erre a hozzáadásra azért van szükség, hogy ellensúlyozza azt az időt (ami a hálózati késleltetésből adódóan változó lehet), amíg az üzenet eljut minden komponenshez. Ezek után a komponensek a kihirdetett időben folytatják a végrehajtást.
A szinkronizációs megoldást önállóan elérhető \gls{python} csomagként is publikáltam, így más projektekben is felhasználható\footnote{\url{https://github.com/marcsello/pyprocsync}}. Sajnos az implementáció csak \textit{Redis}-szel működik.
\subsubsection{Megfigyelés és vezérlés}
A szolgáltatásnak lehetőséget kell adni arra, hogy működése megfigyelhető legyen és be lehessen abba avatkozni. Erre a célra egy \acrshort{http} \acrshort{api}-t terveztem.
Azért ezt a megoldást választottam, mert a \acrshort{http} protokoll kellően elterjedt, ezért széleskörűen támogatott. Mivel a futtatókörnyezet \textit{Kubernetes} alapú, ezért ez a legkönnyebben kezelhető hálózati erőforrás. %TODO: alátámasztani/megindokolni
Az \acrshort{api} két fontos és fix funkciót kell kiszolgáljon. Szükség esetén a program futását meg kell tudni szakítani (akár egy lépés végrehajtása közben is), emellett a program futásának állapotát is lekérhetővé teszi.
A \textit{pluginek} további funkcionalitást regisztrálhatnak, amiket az \acrshort{api} használatával lehet elérni. Ennek segítségével meg lehet valósítani a \enquote{jogging} futtatást egy olyan \textit{pluginnel}, ami minden fő lépés után meghívásra kerül és várakozik az endpoint meghívására.
\subsection{Felhő szolgáltatások}
\label{sec:cloud_layer_plans}
% itt írok általánosságban azokról a cucmókról, ami a felhőbe kerül
\subsubsection{\textit{Program Service}}
Az architektúra, melyet \aref{sec:edge_layer_plans}.\ alfejezetben bemutattam lehetővé teszi, hogy a felhőben futó komponensek bármilyen programot adjanak a peremhálózati réteg számára, legyen az automatikusan vagy részben automatikusan generált.
Mivel sem a demonstráció, sem a dolgozatom szempontjából nem lényeges, hogy milyen módon keletkeznek a \textit{programok} magas szinten, ezért itt csak egy egyszerű, előre elkészített programokat tároló szolgáltatást terveztem.
A szolgáltatásba fel lehet tölteni a felhasználó által megírt programot, illetve le lehet tölteni onnan egy egyszerű \acrshort{rest} \acrshort{api} használatával.
\subsubsection{\textit{Job Orchestrator Service}}
Erre a komponensre is szintén azért van szükség, mert a felhőbe telepített komponenseket nem szerettem volna túlzottan bonyolulttá tenni.
Mivel a programok előre definiált formában léteznek, ezért kell egy komponens, ami felel azért, hogy azoknak betöltését és végrehajtását elindítsa a peremhálózaton.
Ennek a komponensnek a működése nagyban függ a konkrét keretrendszertől és futtató környezettől. Az alapvető feladata az, hogy szükség esetén egy absztrakcióval szolgáljon a konkrét programfuttatási eljárás fölé egyszerű \acrshort{rest} \acrshort{api} kiszolgálásával.
%\subsubsection{\textit{Metrics Service}}
% TODO: Ez mekkora kamu
% A metrika gyűjtés tervezése és implementációja még hátra van.
%A metrika gyűjtésének folyamatát a diplomatervezés második felében fogom megtervezni és megvalósítani.
\section{Implementáció}
Az összes általam választott szolgáltatást \gls{python} nyelven implementáltam le. Mivel népszerű programnyelv ezért mind a robotkarok vezérlésére, mind pedig a többi felhasználásra kész programozói könyvtárakkal rendelkezik, így ez kézenfekvő választás volt.
% ha nagyon kevés oldal van, akkor itt tudok írni a fejlesztői környezetről is illetve a tesztelésről
\subsection{Mikroszolgáltatások}
Mint ahogy \aref{fig:usrsim_services_plan-clean}.\ ábra is mutatja, \aref{sec:single_robot_contoller_plans}.\ és \aref{sec:cloud_layer_plans}.\ alfejezetben ismertetett mikroszolgáltatásokat implementáltam.
\begin{figure}[h!]
\centering
\includegraphics[width=1\textwidth]{figures/ursim_services_plan-clean}
\caption{A tervezett mikroszolgáltatások a futtatási környezetük szerint csoportosítva}
\label{fig:usrsim_services_plan-clean}
\end{figure}
A \textit{Program Service} és a \textit{Job Orchestrator Service} esetén a \acrshort{http} interfészt a \textit{Flask} mikrokeretrendszer segítségével implementáltam. A \textit{Single Robot Controller} esetében a program felépítése miatt a \gls{python} beépített \acrshort{http} szerverét egészítettem ki, hogy megvalósítsa az interfészt.
A \textit{Single Robot Controller} szolgáltatás esetén a komponens életciklusa eltér a hagyományos mikroszolgáltatásokétól. Ebben az esetben a szolgáltatás csak addig él, amíg futtatja a programot, ha már nincs rá szükség, akkor kilép. Elindításáért a \textit{Job Orchestrator Service} felel.
Egy szcenárió lejátszásához több \textit{Single Robot Contoller} komponens futtatása is szükséges lehet. Hogy az egy csoportba tartozó komponensek azonosíthatóak legyenek, a \textit{Job Orchestrator Service} egy futtatási azonosítót rendel a szcenárióhoz, az egy szcenárióban indított összes \textit{Single Robot Controller} osztozik a futtatási azonosítón.
A \textit{Program Service} esetén az eltárolt programokat egy dokumentum tárba rendeztem. Az általam válaszott dokumentum tár a \textit{MongoDB}\footnote{\url{https://www.mongodb.com/}} volt.
% Hát itt kénytelen leszek kódot magyarázni szerintem
% Redis
% Technológia stack
% az egyes mikroszolgáltatások különszedve
% Tiny HTTP Szerver, endpointok felsorolása
% Pluginok compilerekkel
%
% Esetleg pluginok felsorolással
\subsection{\textit{Single Robot Control} \textit{pluginek}}
A \textit{Single Robot Control} komponens által, a \textit{programban} definiált utasítások implementációjára \textit{pluginek} a programban osztályok csoportjaként kerültek megvalósításra.
Minden \textit{plugin} egy különálló osztályban van megvalósítva. Ennek példányosításával lefut az adott \textit{plugin} inicializációja (például: csatlakozás a robotkarokhoz), majd regisztrálja a rendelkezésre álló utasítás osztályokat. A \textit{plugin} osztályok implementálnak egy \texttt{close} függvényt is, amely a foglalt erőforrások felszabadításáért felel.
A beregisztrált utasítás osztályok lényegében egy \textit{factory} mintát valósítanak meg. Amikor a szolgáltatás betölt egy \textit{programot} akkor először minden lépését értelmezi azzal, hogy ezekkel a \textit{factory} osztályokkal példányosítja a konkrét utasítás objektumot. Ezen a ponton lehetőség van a bevitt adatok helyességének ellenőrzésére, hogy ez ne a futtatáskor történjen meg, ezzel futtatás közben spórolva a \acrshort{cpu} ciklusokkal és \textit{fail-early} megközelítés szerint még az előtt kiderülnek a problémák, hogy a program futtatásra kerülne.
Az értelmezett utasításokat listába szervezve készíti elő a futtatható \textit{programot} a szolgáltatás. Ezek a példányosított utasítások implementálják \aref{sec:ursim_execution_framework}.\ alfejezetben említett három fő funkciót: \texttt{execute}, \texttt{abort} és \texttt{describe}. Az egyes paraméterek összerendelése ebben a lépésben történik meg az utasításokhoz.
Amikor egy program futtatásra kerül, akkor a komponens egyesével lépkedve a listán minden elemnek meghívja az \texttt{execute} függvényét. Ha meg kell szakítani a futást, akkor az éppen futó lépes \texttt{abort} függvényét meghívva, leállítja azt, és a következő parancsot már nem hajtja végre.
Ha egy utasítás futtatása közben hiba lép fel, akkor a \textit{program} végrehajtása véget ér.
\subsubsection{\textit{Sync Plugin}}
Ez a \textit{plugin} valósítja meg kettő vagy több komponens által futtatott programban a szinkronizációs pontokat.
A \textit{plugin} inicializációjakor kapcsolódik a \textit{Redis} adatbázishoz, ehhez ekkor rendelkezésre kell állnia a kapcsolódási adatoknak, így ezt a program konfigurációjával együtt kell megadnunk.
Az általa szolgáltatott utasításnak két paramétere van, az egyik a szinkronizációs pont neve, a második pedig a komponensek száma, amelyek ugyanezen a ponton várakoznak. Annak érdekében, hogy ez egyedi legyen, és két különálló lefutása a programnak ne befolyásolja az adatbázis tartalmát, ezáltal ne legyen hatással a program lefutására, így az adatbázisban tárolt kulcsok nevéhez a futtatási azonosítót is hozzáfűzi.
Amikor a program futása erre a pontra érkezik, akkor az várakozni fog egészen addig, amíg az összes többi komponens által futtatott program egy ugyan ilyen nevű szinkronizációs pontra nem ér. Ezután \aref{sec:ursim_snyc_proto}.\ alfejezetben ismertetettek szerint \enquote{egyeztetett} időpontban halad tovább.
\subsubsection{\textit{UR-RTDE Plugin}}
Ez a \textit{plugin} jelenleg az egyetlen, amely a robotkar irányítását megvalósítja.
Inicializációkor csatlakozik a robotkar \acrshort{rtde} interfészére, amelyre blokkoló módon küldi a mozgási utasításokat. Ennek értelmében a cím, amelyre csatlakoznia kell, már itt rendelkezésre kell állnia, így azt a program konfigurációjaként adhatjuk meg.
Minden egyes utasítás akkor ér véget, ha a mozgás befejeződött, így tud a következő utasításra lépni. Hiba esetén az \acrshort{rtde} interfész lehetőséget ad arra, hogy ezt közölje azzal, aki az utasítást adta. Így egy utasítás hibájával (például érvénytelen mozgás, váratlan akadály vagy kézi megszakítás) a teljes \textit{program} végrehajtása megáll.
\subsubsection{\textit{Wait Plugin}}
A \textit{plugin} által megvalósított egyetlen utasítás addig várakoztatja a program futását, amíg annak folytatására külső befolyással nem kap utasítást.
Ez a \textit{plugin} regisztrál egy endpointot a \acrshort{http} \acrshort{api}-ban, amellyel a program végrehajtása folytatható.
Ennek a pluginnek a használatával megvalósítható, a demonstráció során is alkalmazott \textit{jogging} módú futtatás. A léptetés itt nem billentyűlenyomással, hanem az \acrshort{api} meghívásával történik.
\subsubsection{Egyebek}
A fent vázolt \textit{pluginek} mellett készítettem néhány egyszerűbbet is, amelyek egyszerűbb vagy csak hibakeresésnél használatos funkciókat implementálnak:
\begin{itemize}
\item \textbf{Log Plugin} Ezzel a \textit{pluginnel} a program futása közben előre definiált üzeneteket jeleníthetünk meg az alkalmazás naplójában.
\item \textbf{Sleep} Ez a \textit{plugin} lehetővé teszi hogy meghatározott ideig szüneteltessük a végrehajtást.
\end{itemize}
\subsection{\textit{Program} formátum}
A robotkarok vezérlésének lépései az általam tervezett formátumú \textit{programokban} vannak összegyűjtve. A \textit{Programok} felépítése és struktúrája egyszerű. Tervezés során figyelembe vettem, hogy mind ember által könnyen értelmezhető és szerkeszthető legyen, de könnyen feldolgozható is legyen szoftver által is.
A \textit{program} leírása tartalmazza első sorban annak verzióját. Ez a későbbi kompatibilitási problémák megoldásának egyszerűsítésére lett bevezetve, jelenleg csak az \texttt{1}-es verzió létezik. Az a program, ahol ennek a mezőnek az értéke nem \texttt{1}, az érvénytelen.
A verzió mellett a leírás tartalmazza a program nevét, ennek nem kell egyedinek lennie, csak diagnosztikai céllal létezik, emellett a program létrehozásának időpontját és a betöltendő \textit{pluginek} listáját is.
A \textit{program} leírása opcionálisan tartalmazhat egy \texttt{id} vagy \texttt{\_id} mezőt, ezzel megkönnyítve a program kezelését. A beolvasásnál ennek a mezőnek a jelenléte nem okoz hibát.
A program több módon is szerializálható. Ezekből csak a \acrshort{json} és \acrshort{yaml} formátumokat használtam, előbbit a komponensek egymás közti kommunikációjában a másodikat a programok ember által történő szerkesztésére. A struktúrára egy példa \acrshort{yaml} szerializációban \aref{ex:program}.\ példán látható.
\begin{lstlisting}[float=!ht,caption={Példa felépítse egy \textit{programak} YAML formátumban},label=ex:program]
---
version: 1
name: thesis_example
created_at: '2021-04-14T20:54:47.557000'
load_plugins: ["ur_rtde", "log", "wait"]
program:
- command: log
args:
level: INFO
message: This is a test program. Starting now...
- command: moveJ
args:
q: [2.625, -0.548, 1.614, 3.645, -1.567, 4.192]
speed: 1.0
acceleration: 4.0
- command: wait
args: {}
- command: moveL
args:
pose: [0.399, -0.1, -0.26798, 0, 3.14, 0]
speed: 0.05
acceleration: 0.75
\end{lstlisting}
A program sémájának validálására a \textit{Marshmallow}\footnote{\url{https://github.com/marshmallow-code/marshmallow}} könyvtárat használtam. Ezzel deklaratív módon tudtam validációs sémákat írni, amelyet minden komponensnél használtam, amely érintkezik valamilyen szinten \textit{programokkal}. Így a lehető leghamarabb vissza tudja utasítani a rendszer a hibás programokat.
Az eredeti szoftver által vezérelt mozgást futás időben fordítottam le az általam definiált \textit{program} struktúrára azáltal, hogy a program kódjában a \gls{python} által biztosított reflexiókat használva, az egyes lépéseket lecseréltem fájlba mentő funkciókra amely rögzíti az utasításokat és a paramétereiket.
%\section{Tesztelés}