\documentclass[%
	corpo=11pt,
    twoside,
    stile=classica,
    oldstyle,
    tipotesi=custom,
    greek,
    evenboxes,
]{toptesi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}

\usepackage{hyperref}
\usepackage{setspace}
\hypersetup{%
    pdfpagemode={UseOutlines},
    bookmarksopen,
    pdfstartview={FitH},
    colorlinks,
    linkcolor={black},
    citecolor={black},
    urlcolor={black}
  }

%%%%%%% use PDFLATEX 

\usepackage{lipsum} %to insert random text

\usepackage{geometry} %for the margins
\newcommand\fillin[1][4cm]{\makebox[#1]{\dotfill}} %for the dotted line in the frontispiace

\usepackage{dcolumn}
\newcolumntype{d}{D{.}{.}{-1} } %to vetical align numbers in tables, along the decimal dot

\usepackage{amsmath}

% \usepackage{natbib} % for the bibliography
% % \usepackage[]{natbib}
% \bibliographystyle{plainnat}

\usepackage{csquotes}
\usepackage[sorting=none]{biblatex}
\addbibresource{references.bib}

%%%%%%% Local definitions
\newtheorem{osservazione}{Osservazione}% Standard LaTeX
\newtheorem{observation}{Observation}% Standard LaTeX

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% \usepackage{minted}
% \usemintedstyle{borland}
% \usemintedstyle{tango}
\usepackage{listings}
\usepackage{xcolor}

\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\definecolor{javapurple}{rgb}{0.5,0,0.35}

\lstset{
  language=Java,
  aboveskip=3mm,
  belowskip=3mm,
  showstringspaces=false,
  columns=flexible,
  basicstyle={\footnotesize},
  numbers=none,
  numberstyle=\tiny\color{gray},
  keywordstyle=\color{javapurple},
  commentstyle=\color{dkgreen},
  stringstyle=\color{mauve},
  showspaces=false, 
  breaklines=false,
  breakatwhitespace=true,
  tabsize=2
}
\usepackage{float}
\usepackage{subcaption}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



\begin{document}\errorcontextlines=9
%\english

\input{./title.tex} %the frontispiece

%%%%%%% Dedication
\ifclassica%
\begin{dedica}
    Alla mia famiglia e ai miei amici più cari.
\end{dedica}
%%%%%%% 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\sommario%summary
Lo studio della dinamica del veicolo si basa sull’analisi di modelli matematici che descrivono il comportamento dinamico di un autoveicolo in termini prestazionali, di stabilità e di sicurezza. Nei sistemi di controllo di stabilità del veicolo, una grandezza che entra in gioco è  l’angolo di assetto beta: un parametro che descrive la stabilità del veicolo durante una curva o in situazioni di emergenza. Per ottenere un calcolo diretto del beta, solitamente, è necessario utilizzare strumenti esterni, anche molto costosi, come la reta CAN o la piattaforma inerziale IMU. L’utilizzo di uno smartphone e dei suoi sensori, permette di ridurre i costi a discapito della precisione nel calcolo dei parametri. Per ottenere una stima dell’angolo beta è necessario calcolare alcuni parametri di dinamica del veicolo ovvero l’angolo di sterzo, velocità e accelerazione longitudinali e accelerazione laterale. Lo scopo del lavoro svolto è quello di migliorare un’applicazione smartphone, già esistente, per la stima di tali parametri con particolare attenzione all’angolo volante. A tal scopo, sono stati effettuati dei miglioramenti dell’app, modificando e aggiornando l’intera struttura e i moduli che la compongono. E' stata effettuata un'analisi del flusso e dell'elaborazione dei dati adattandone la struttura per la memorizzazione interna. Si è prestata particolare attenzione al miglioramento della rilevazione e accuratezza dell'angolo volante. E' stato quindi possibile eliminare alcune delle problematiche note, nella versione precedente dell'app, come la copertura accidentale dei markers, il basso frame-rate di acquisizione dell'angolo volante e soprattutto il de-allineamento dei dati acquisiti.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% \ringraziamenti%acknowledgements
% %Acknowledge the people you love and/or work with



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%\tablespagetrue
\figurespagetrue%to include the list of tables
%and the list of figures - yuo can comment these commands

\indici%table of content
%It automatically generated

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%Citation


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\mainmatter
\part{Prima Parte}

%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Introduzione}
    %%%%%%%%%%%
    \section{Principi generali}
    L'obiettivo del lavoro svolto è quello di studiare e migliorare la stima dell'angolo volante $\theta$ e la sua sincronizzazione con i dati acquisiti dagli altri sensori istante per istante. La correttezza dell'angolo $\theta$, infatti, condiziona la precisione dell'angolo di assetto $\beta$, il quale rappresenta l'angolo fra direzione longitudinale del moto e la direzione della velocità V del veicolo. L'angolo di assetto $\beta$ è importante in quanto fornisce informazioni sulla stabilità del veicolo nel momento in cui un veicolo sterza, o in situazioni di emergenza, deve essere corretta la traiettoria dello stesso. La misura diretta di queste grandezze richiede l'uso di dispositivi costosi e solitamente non disponibili nelle vetture di utilizzo standard. Per questo motivo si vuole ottenere una stima tramite l'utilizzo dei sensori presenti in ogni smartphone:
    
    \begin{itemize}
        \item Fotocamera
        \item Accelerometro
        \item Giroscopio
        \item GPS
    \end{itemize}
    
    L'accelerometro ed il giroscopio permettono di calcolare le componenti di accelerazione e velocità angolare mentre il gps determina una stima della traiettoria del veicolo e la sua velocità istantanea.\\ La fotocamera permette di ottenere una stima dell'angolo di sterzo, puntando dei markers applicati allo sterzo ed elaborandone l'immagine affinché vengano riconosciuti. Per migliorare quest'ultimo aspetto, il modulo fotocamera e gli algoritmi utilizzati saranno saranno ricostruiti e migliorati.

%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Problema dell'angolo volante}
    %%%%%%%%%%%
    \section{Descrizione del problema}
    L'acquisizione dell'angolo volante $\theta$ avviene a runtime tramite l'utilizzo di OpenCv, una libreria grafica di cui si parlerà successivamente. L'algoritmo precedentemente sviluppato ed i moduli che lo sfruttano sono stati rivisti e migliorati poiché, durante le fasi di test, si è notato uno sfasamento variabile (\ref{fig:SfasamentoFis}) dovuto alla mancanza di un meccanismo che garantisca un clock di sistema condiviso per tutti i sensori dello smartphone. Questa mancanza viene accentuata da tre problemi principali:
    
    \begin{itemize}
        \item Copertura accidentale dei Markers durante i test
        \item Framerate in un range non costante e troppo basso % cambiamenti improvvisi di rotazione del volante
        \item De-allineamento dei dati durante la cattura
    \end{itemize}
    
    Queste problematiche portano ad uno sfasamento che si accentua nel tempo determinando un errore più o meno variabile.

    \begin{figure}[H]\centering
    \includegraphics[scale=0.5]{Pictures/Cap_2/steering_wheel_Angle.jpg}
    \caption{Sfasamento variabile individuato durante un test track.}\label{fig:SfasamentoFis}
    \end{figure}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\part{Seconda Parte}
%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Applicazione Android: Vehicle-App}
    % In questo capitolo descriveremo nel dettaglio la struttura dell'applicazione, il suo funzionamento e tutti i moduli che la compongono con qualche accenno al loro upgrade ed alla libreria per il calcolo dell'angolo volante trattati rispettivamente dei capitoli successivi.
    %%%%%%%%%%%
    \section{Struttura dell'applicazione}

%   In questo capitolo descriveremo nel dettaglio la struttura dell'applicazione, il suo funzionamento e tutti i moduli che la compongono con qualche accenno al loro upgrade ed alla libreria per il calcolo dell'angolo volante trattati rispettivamente dei capitoli successivi.
        \subsection{Android studio}
        Di seguito è mostrata la struttura interna dell’applicazione gestita da Android Studio.
        Come si può facilmente intuire la struttura rappresenta un vero e proprio albero, in cui vi è una root da cui tutto parte e da lì si diramano le varie componenti.
    
        \begin{itemize}
%
            \item   \textbf{app Folder:} rappresenta la cartella root del progetto: sono esclusi solo i file relativi a librerie esterne e moduli aggiuntivi.
            \item   \textbf{manifests Folder:} contiene il file manifest che descrive permessi e proprietà dell'app.
            \item   \textbf{java Folder:} al suo interno troviamo tutte le classi java del progetto, inclusi i test da effettuare sulle classi. E’ possibile creare delle sottocartelle per comodità, ad esempio per raggruppare classi dello stesso tipo.
            \item   \textbf{cpp Folder:} questa è una cartella che viene creata dall’utente (nel nostro caso) per poter inserire classi in linguaggio nativo. Nel nostro caso contiene solo i file relativi ad OpenCv.
            \item   \textbf{jniLibs Folder:} anche questa è appositamente creata dall’utente per poter incorporare librerie in linguaggio nativo esterne(nel nostro caso Open-Cv). All’interno sono presenti i file binari .a per ogni architettura supportata.
            \item   \textbf{res Folder:} questa è la libreria delle resources, al suo interno contiene tutte le risorse che andremo ad utilizzare nel codice: animazioni, i file di layout in .xml, i file di menu in .xml, i valori delle stringhe definite e tutte le immagini (drawable) utilizzate, incluse le icone.
            \item   \textbf{openCv Library:} rappresenta il modulo esterno importato nel progetto Android direttamente dall’IDE, si viene a creare una cartella a parte contenente pressoché la stessa struttura di un’applicazione vera e propria, contiene infatti un file manifest e una cartella java, contenente le chiamate alle classi native della libreria come spiegato nel cap. \ref{libreriaOpenCv}.
        \end{itemize}
        
    %%%%%%%%%%%
    \section{Analisi delle classi}
        In questa sezione si andranno ad analizzare le classi di supporto, i servizi e le strutture dati che compongono l'applicazione. In particolare troveremo 4 tipi di classi.
        
        \begin{itemize}
            % \itemsep-1em 
            \item \textbf{Base Class:} raggruppa tutte le classi che definiscono le strutture dati utilizzate per memorizzare tutte le informazioni ottenute dai sensori. Ogni classe farà da contenitore dei dati          estrapolati da ogni specifico sensore.
            
            \item \textbf{Helper Class:} rappresenta le classi utili allo svolgimento di routine secondarie e predefinite come l'utilizzo di animazioni. Saranno presenti anche delle classi usate nella versione precedente per l'accesso ad un database che permette la gestione di file, di testo e CSV, utili per un'analisi esterna dei dati: le seguenti classi sono state aggiornate in \textit{Utility Class}.
            
            \item \textbf{Utility Class:} contiene le classi per l'interfacciamento con un database interno che sfrutta \textbf{SQLlite}, una libreria che ha il compito di strutturare il database e garantirne la scrittura o la lettura. Fornisce inoltre la classe \textit{FitHelper} per la memorizzazione dei dati di configurazione della prova ed il salvataggio in file txt. 
            
            \item \textbf{Service Class:} contiene il servizio per poter utilizzare la camera in background e tutte le sue versioni precedenti.
        
        \end{itemize}
         Queste classi verranno utilizzate dalle Activity e Fragment Class che rappresentano la vera interfaccia con l'utente.
        
        %%%%%%%%
        \subsection{Classi Base}\label{ClassiBase}
            Come detto precedentemente, le classi base vengono utilizzate per contenere i dati ottenuti o per definire 
%Ref            
            strutture dati statiche. %RIFERIMENTO a car paremeter
            Si descriveranno i tipi di classi accennando le modifiche, trattate al cap. \ref{ModificheDellaStruttura}%inserire riferimento
            , che sono state introdotte nella nuova versione nell'app.\\\\
            
           % \begin{interlinea}{1}
            \textbf{\noindent Entries Classes:\\}
            %\end{interlinea}
                Rappresentano le strutture dati per la memorizzazione dei valori acquisiti dai sensori dello smartphone e per la successiva estrapolazione. In tal modo si potranno elaborare tutte le informazioni necessarie all'algoritmo di stima per il calcolo del $\beta$. Come vedremo, rispetto alla versione precedente, tutte
%Ref                
                le entries classes sono state modificate con aggiunta di un Timestamp. L'inserimento è stato necessario per contribuire alla soluzione del problema di de-allineamento dei dati (\ref{DeAllineamento}).\\
                Vediamo la descrizione delle classi:
                \\
                \begin{itemize}
                    \item \textbf{FrameEntry Class:}
                    \begin{lstlisting}
public class FrameEntry {
    private Mat mat;
    private Long tmsp;

    public FrameEntry(Mat mat, Long tmsp) {
        this.mat = mat;
        this.tmsp = tmsp;
    }

    ...
    
}
                    \end{lstlisting}
 
                    Questa classe viene utilizzata per contenere il Frame e il timestamp, associato alla cattura, durante l'utilizzo della fotocamera.
                    Durante lo svolgimento di una prova viene utilizzata una lista, o meglio una \textbf{blockingQueue}, di questi oggetti che vengono quindi immagazzinati da un produttore. Un consumatore, contemporaneamente, ha il compito di estrarre un elemento alla volta ed eseguire tutti i calcoli e le manipolazioni necessarie per ottenere l'angolo volante \textbf{$\theta$} associato al timestamp. Il meccanismo produttore-consumatore, utilizzato per l'acquisizione dei frame in real-time, verrà spiegato meglio nel cap. \textbf{\ref{ClasseService}}.
                            
                    \item \textbf{StEntry Class:}
                    \begin{lstlisting}
public class StEntry implements Parcelable {

    private double theta;
    private Long tsmp;

    public StEntry(double theta, Long tsmp) {
    this.theta = theta;
    this.tsmp = tsmp;
    }
    
    ...
    
}
                    \end{lstlisting}
                    
                    Permette di memorizzare il valore $\theta$ dell'angolo volante e il suo timestamp di acquisizione. La classe implementa metodi dell'interfaccia \textit{Parcelable}\footnote{Questo tipo di interfaccia è utile nel caso in cui è necessario scambiare dei dati non primitivi tra Activity o Service. La classe quindi rappresenta una struttura dati creata ad-hoc che subisce una serializzazione quando deve essere passata tramite Intent.} di Android. 
                                       
                    \item \textbf{AccelEntry Class:} 
                    \begin{lstlisting}
public class AccelEntry implements Parcelable {

    private double x;
    private double y;
    private double z;
    private Long tsmp;

    public AccelEntry(double x, double y, double z, Long tsmp) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.tsmp = tsmp;
    }
    
    ...
    
}
                    \end{lstlisting}
                    
                    Con questa classe è possibile memorizzare i valori delle componenti x, y, z delle accelerazioni provenienti dall'accelerometro. 
                    
                    \item \textbf{GpsEntry Class:}
                    \begin{lstlisting}
public class GpsEntry implements Parcelable {

    private double speed;
    private double lat;
    private double lon;
    private Long tsmp;

    public GpsEntry(double speed, double lat, double lon, Long tsmp) {
        this.speed = speed;
        this.lat = lat;
        this.lon = lon;
        this.tsmp = tsmp;
    }
    
    ...
    
}
                    \end{lstlisting}
                    
                    Questa classe presenta le stesse funzioni di AccelEntry ma permette la memorizzazione dei valori restituiti dal sensore GPS ovvero valocità, latitudine, longitudine e timestamp di acquisizione.
                    
                    \item \textbf{GyroEntry Class:}
                    \begin{lstlisting}
public class GyroEntry implements Parcelable {

    private double x;
    private double y;
    private double z;
    private Long tsmp;

    public GyroEntry(double x, double y, double z, Long tsmp) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.tsmp = tsmp;
    }
    
    ...
    
}
                    \end{lstlisting}
                    
                    Anche questa classe permette di memorizzare le componenti x, y, z e timestamp di acquisizione del sensore giroscopio.
                    
                \item \textbf{CarParameters Class:}
                \begin{lstlisting}
public class CarParameters {

    double  C1 , //[N/rad] rigidezza deriva pneumatici anteriori
            C2,     //[N/rad] rigidezza deriva pneumatici posteriori
            a1,      //[m] semipasso ant
            a2,      //[m] semipasso post
            tau,    //rapporto sterzo anteriore
            J,   //[kg*m^2] mom di inerzia cassa
            m;      //[kG] massa veicolo
    String param;   //univoco per ogni segmento di veicolo
    int img;        //Drawable associato al segmento

    ...
    
}
                \end{lstlisting}
                
                \end{itemize}
        %%%%%%%%        
        \subsection{Classi Helper}
        
        \textbf{AnimationHelper Class:}
        \noindent
        Questa classe contiene tutti i metodi per gestire le animazioni all'interno dell'applicazione. Questi metodi si concentrano in particolare su animazioni che riguardano i \textit{Drawable}\footnote{Un Drawable è un'astrazione generale di "Qualcosa che può essere disegnata". Spesso un oggetto Drawable è trattato come una risorsa utilizzata per disegnare qualcosa sullo schermo. Sono fornite quindi delle API utilizzate per interagire con le risorse grafiche sottostanti. A differenza di una View, un Drawable non può reagire ad eventi e quindi interagire con l'utente.} della \textit{View}.

        \noindent
        Una parte dei metodi della libreria non è stata più utilizzata per motivi di implementazione e di inserimento di nuovi moduli nell'app.
        %%%%%%%%
        \subsection{Classi Service}\label{ClasseService}
        
        Questa sezione contiene le classi \textit{Services} ed in particolare sia quelle utilizzate nell'app che quelle sostituite, durante lo svolgimento della tesi, al quale sono state applicate delle migliorie. I \textit{Services} vengono utilizzati in background per svolgere operazioni durature nel tempo e che non hanno bisogno di interazioni con l'utente. Nell'applicazione è stato sviluppato un service che sfrutta le API camera2 per l'acquisizione dei frame catturati dalla fotocamera. Parleremo quindi della sua implementazione.
        \\
        \\
        \\
        \textbf{Camera2Service Class:}
        
        \begin{lstlisting}
public class Camera2Service extends Service {

//  private HardwareCamera mCamera;
    private LinkedBlockingQueue<FrameEntry> mFrameEntries = new LinkedBlockingQueue<>();
    private FrameEntry tmpFrameEntry;
    
    ...
    
        \end{lstlisting}
        
        Il service \textit{Camera2Service} utilizza una \textbf{LinkedBlockingQueue} \cite{blockingQueue}, ovvero una coda bloccante utilizzata per immagazzinare ogni frame ed il suo rispettivo timestamp in un FrameEntry (\ref{ClassiBase}). Questo per poter attuare il meccanismo produttore-consumatore del quale parleremo in seguito. E' possibile notare l'eliminazione dell'attributo \textbf{mCamera} utilizzato dalla prima versione delle fotocamera API per collegare il service al layer hardware sottostante e eseguire la cattura di un frame; questo perché, utilizzando camera2, il layer sottostante viene controllato automaticamente esonerando il programmatore da un controllo e link manuali.
        \\
        \\
        Vediamo, adesso, come è stato effettuato l'Override dei metodi del service modificati per implementare le nuove migliorie:
        
        \begin{lstlisting}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i(TAG, "onStartCommand flags " + flags + " startId " + startId);

    if (COUNTER) {
        frameCounter = 0;
        lastNanoTime = System.nanoTime();
    }

    markers = (HashMap<String, Double>) intent.getSerializableExtra("markers");
//  zoomLevel = intent.getIntExtra("zoom", 0);
    initDelta = intent.getDoubleExtra("currentValue", 0);
    values = new DescriptiveStatistics(5);
//    Handler h = new Handler();
//    h.postDelayed(new Runnable() {
//        @Override
//        public void run() {
//            int camera = Camera.CameraInfo.CAMERA_FACING_BACK;
//            //int camera =
//            if (PreferenceManager
//                  .getDefaultSharedPreferences(getApplicationContext())
//                  .getBoolean(SettingsFragment.KEY_PREF_CAMERA, false))
//                camera = Camera.CameraInfo.CAMERA_FACING_FRONT;
//            mCameraId = camera;
//            mCamera = new HardwareCamera(CameraService.this, CameraService.this, mCameraId);
//            mCamera.setZoomLevel(zoomLevel);
//            mCamera.connect();
//            (TAG, "Service started!");
//            mThread = new Thread(new BackgroundCamera());
//        }
//    }, 500);
    mThread = new Thread(new BackgroundCamera());
    mRgb = new Mat();
    cameraMatrix = new Mat(3, 3, CvType.CV_32F);
    coeffs = new Mat(5, 1, CvType.CV_32F);
    
    /**Add init for ChArUco board**/
        
    dictionary = Aruco.getPredefinedDictionary(Aruco.DICT_6X6_250);
    board = CharucoBoard.create(5, 7, (float) 0.04, (float) 0.02, dictionary);
    params = DetectorParameters.create();
    
    /******************************/

    cameraMatrix.put(0, 0, 1024.3446);
    cameraMatrix.put(0, 1, 0);
    cameraMatrix.put(0, 2, 640.0);
    cameraMatrix.put(1, 0, 0);
    cameraMatrix.put(1, 1, 1024.3446);
    cameraMatrix.put(1, 2, 360);
    cameraMatrix.put(2, 0, 0);
    cameraMatrix.put(2, 1, 0);
    cameraMatrix.put(2, 2, 1);

    coeffs.put(0, 0, 0.08816299);
    coeffs.put(1, 0, -0.21675685);
    coeffs.put(2, 0, 0);
    coeffs.put(3, 0, 0);
    coeffs.put(4, 0, 0);

    readyCamera();

    return super.onStartCommand(intent, flags, startId);
}
        \end{lstlisting}
        \noindent
        Nella prima parte di codice: vengono inizializzati i parametri per ottenere il conteggio degli fps e si ottiene il valore iniziale dei markers ovvero l'ultima posizione utile dallo start della prova.
        E' possibile notare (nella parte commentata) come non sia più necessario il collegamento manuale al layer sottostante per il controllo dell'hardware della fotocamera. Inoltre, adesso non è più necessario creare un thread secondario con ritardo di 500ms per evitare sovrapposizioni.
        \
        Una volta inizializzati tutti i parametri per la detection della \textit{CharucoBoard} (\ref{Charuco}) ed il calcolo dell'angolo, viene lanciato il metodo \textit{readyCamera()}:
        \\
        \begin{lstlisting}
        
public void readyCamera() {
    CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
    try {
        String pickedCamera = getCamera(manager);
    
        manager.openCamera(pickedCamera, cameraStateCallback, null);
        
        imageReader = ImageReader.newInstance(320, 240, ImageFormat.YUV_420_888, 2);
        imageReader.setOnImageAvailableListener(onImageAvailableListener, null);
        
        Log.i(TAG, "imageReader created");
    } catch (CameraAccessException e){
        Log.e(TAG, e.getMessage());
    }
}

        \end{lstlisting}
        \noindent
        In questo metodo viene resa operativa la fotocamera e viene stanziato un \textit{Imagereader}\footnote{Permette l'accesso diretto a un'immagine ottenuta dalla Surface del display} nel quale viene passato il listener \textbf{onImageAvailableListener}:
        \\
        \begin{lstlisting}
protected ImageReader.OnImageAvailableListener onImageAvailableListener =
                                = new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Log.i(TAG, "onImageAvailable");
        Image image = reader.acquireLatestImage();
        if (image != null) {

            if (COUNTER) {
                frameCounter++;
                if (frameCounter >= 30) {
                    final int fps = (int) (frameCounter * 1e9 / 
                                                    (System.nanoTime() - lastNanoTime));
                    Log.i(TAG, "drawFrame() FPS: "+fps);
                    frameCounter = 0;
                    lastNanoTime = System.nanoTime();
                }
            }

            // sanity checks - 3 planes
            Image.Plane[] planes = image.getPlanes();
            assert (planes.length == 3);
            assert (image.getFormat() == mPreviewFormat);

            processImage(image);
            image.close();
        }
    }
};
        \end{lstlisting}
        \noindent
        Il listener in questione è molto importante poiché, dopo aver effettuato un conteggio degli fps, fornisce un frame, ovvero un'immagine che viene acquisita e che, dopo un sanity check, viene passata al metodo \textbf{processImage}:
        \\
        \begin{lstlisting}
private void processImage(Image image){
        //Process image data
        Long timestamp = System.currentTimeMillis();
        try {
            Mat m = new Mat();
            JavaCamera2ToFrame tempFrame = new JavaCamera2ToFrame(image);
            Imgproc.cvtColor(tempFrame.rgba(), m, Imgproc.COLOR_BGRA2BGR);
            mFrameEntries.put(new FrameEntry(m, timestamp));
            tempFrame.release();


        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!isRunning) {
            mThread.start();
        }
}
        \end{lstlisting}
        \noindent
        In questo metodo l'immagine viene trasformata in un formato comprensibile alle ChArUco API ed inserita, insieme al timestamp corrispondete, all'interno del costrutto \textit{mFrameEntries}:
        
        \begin{lstlisting}
        
        mFrameEntries.put(new FrameEntry(m, timestamp));
        
        \end{lstlisting}
        
        \noindent
        Il costrutto viene stanziato come \textbf{LinkedBlockingQueue<FrameEntry>} ovvero un costrutto FIFO usato nel caso di condivisione dati tra 2 thread diversi, identificati come:
        
        
        \begin{figure}[H]\centering
        \includegraphics[scale=0.5]{Pictures/Cap_3/blocking-queue.png}
        \caption{Costrutto BlockingQueue.}\label{fig:figuraBlockingQueue}
        \end{figure}
        
        
        \begin{itemize}
            \item \textbf{Producer Thread}: nel nostro caso rappresentato dal listener \textit{onImageAvailableListener} che raccoglie il frame, lo elabora e lo inserisce nella coda.
            \item \textbf{Consumer Thread}: invocato tramite il metodo \textit{mThread.start()} preleva il dato dalla coda e, come vedremo successivamente, lo elabora. Il thread in questione è rappresentato dalla classe BackgroudCamera che estende vari metodi \textit{Runnable}\footnote{E' un'interfaccia che viene implementata in quelle classi le quali istanze vogliono essere eseguite da un thread.}.
        \end{itemize}
        
        \begin{lstlisting}
        
private class BackgroundCamera implements Runnable {

    @Override
    public void run() {
        isRunning = true;
        Mat mFrame;
        double prevTheta = Double.MAX_VALUE;
        double theta = 0;
        SynchronizedDescriptiveStatistics mean = new SynchronizedDescriptiveStatistics(WINDOW_SIZE);
        while (isRunning) {
            try {
                if (mFrameEntries.isEmpty()) continue;
                tmpFrameEntry = mFrameEntries.take();
                mRgb = tmpFrameEntry.getMat();
                if (mRgb == null) continue;
                if (resetState) {
                    resetState = false;
                    continue;
                }
                
            ...
            
        final Intent i = new Intent("angle_update");
        i.putExtra("theta", theta);
        i.putExtra("thetaTime", tmpFrameEntry.getTmsp());
        sendBroadcast(i);
}

        \end{lstlisting}
        
        \noindent
        Il seguente consumer thread effettua un check all'interno della coda condivisa tramite il metodo  \textit{mFrameEntries.take();} e se è presente un oggetto \textit{FrameEntry} lo estrae eliminandolo dalla coda. Il procedimento, naturamente, viene eseguito in modalità \textit{Thread-Safe}\footnote{Meccanismo utilizzato per sincronizzare gli accessi di uno o più thread ad una sezione di codice. Avremo quindi un accesso in mutua esclusione.}. Dal \textit{FrameEntry} viene estratto il frame e salvato nella variabile \textit{mRgb} per poi essere elaborato dai metodi della libreria \textit{OpenCv} (\ref{libreriaOpenCv}). Se invece, in coda non c'è nessun elemento, il thread rimmarà in attesa evitando il \textit{polling}\footnote{Quando viene effettuato un continuo controllo ad ogni ciclo. Questo meccanismo è sconsigliato quando si vogliono ottenere alte prestazioni poiché vengono consumati cicli inutili di CPU dal processore.}. Il thread quindi va in \textit{sleep} e viene risvegliato solo quando è disponibile un nuovo dato nella coda.\\
        Una volta identificato e calcolato l'angolo $\theta$ rispettivo al frame, il valore viene inserito in un \textit{Intent}\footnote{Struttura dati che contiene una descrizione astratta di un'operazione che deve essere eseguita. Ad esempio può essere usato per lanciare una nuova \textit{Activity} oppure può essere inviato in broadcast come BroadcastIntent per passare delle strutture dati tra componenti di un'applicazione. Il componente ricevente i dati, utilizza un \textit{Broadcast Receiver} } ed inviato in \textit{broadcast} per poi essere catturato da un \textit{Broadcast Receiver} (\ref{parte5}).
        
        
        %%%%%%%%
        \subsection{Classi Utilities} 
            In questa sezione troviamo varie tipologie di classi utilizzate per la gestione del database, il salvataggio di alcuni parametri necessari per la fase di inizializzazione di una prova, la gestione dei file Raw e il calcolo dell'angolo di assetto $\beta$.\\
            
            \noindent
            \textbf{DatabaseUtilities Class:}\\
            \noindent
            La seguente classe permette la gestione di tutte le operazioni in lettura e scrittura sul database interno. Stanziando l'oggetto \textbf{DatabaseUtilities} viene creata la struttura del database con le \textit{tables} contenenti i dati per ogni sensore o parametro elaborato. Per garantire il corretto salvataggio dei dati, la classe è stata modificata sostituendo le funzioni deprecate con quelle attualmente utilizzate con \textit{SQLlite}. Anche la struttura della \textit{table} viene modificata: vengono aggiunti nuovi attributi per l'inserimento di nuovi dati provvisti di \textit{timestamp} associato.\\

            \noindent
            \textbf{StorageUtility Class:}\\
            \noindent
            Questa classe viene utilizzata per la lettura e la scrittura dei valori di configurazione iniziale della prova. Le operazioni avvengono tramite 2 funzioni \textit{readFileAccs()} e \textit{writeFileAccs()} utilizzando il costrutto \textit{InputStream} che, come descritto dal nome, permette la lettura di uno stream di dati. La configurazione iniziale, eseguita dopo la Camera2Tracker Activity (\ref{Camera2TrackerActivity}), è caratterizzata da una prima fase statica e da una seconda di accelerazione gestite tramite la classe \textbf{fitHelper}.\\
            Mentre nella precedente versione dell'app il salvataggio su file era stato disabilitato, adesso, alla termine di ogni prova, vengono creati una serie di file testuali sia per i dati raw di ogni sensore che per quelli elaborati.
            
        %%%%%%%%    
        \subsection{Classi Room}
            La libreria Room \cite{roomApi} fornisce un layer d'astrazione di SQLite semplificando l'accesso e la gestione del database. In particolare, si ottengono i seguenti benefici:
            
            \begin{itemize}
                \item Verifica delle queries SQL nel tempo di compilazione.
                \item Utilizzo di annotazioni java per minimizzare la scrittura di codice ripetitivo (boilerplate).
                \item Gestione semplificata dei path del database. 
            \end{itemize}
            
            \noindent
            All'interno del progetto, nei nuovi folder \textbf{repository} e \textbf{data}, vengono implementati i 3 principali componenti Room. Come descritto dal nome, il folder \textit{repository} contiene una classe repository che fa da wrapper al primo componente Room ovvero \textbf{Database class}: contiene il database e ne gestisce l'accesso principale.
            Il folder \textit{data} contiene le altre 2 componenti Room:
            
            \begin{itemize}
                \item \textbf{Data entities:} rappresentano le tabelle del database creato.
                \item \textbf{Data access object (Dao):} fornisce i metodi per eseguire un insert, un update, un delete o una query al database.
            \end{itemize}
            
            \noindent
            La classe database fornisce all'applicazione delle istanze dei \textbf{DAO} associati al database. Successivamente, l'applicazione può usare le istanze per eseguire delle operazioni sul database recuperando i dati come oggetti \textbf{Data entities} associati.\\
            L'utilizzo delle Room API è stato parziale così da non stravolgere totalmente la struttura dell'app. In futuro, come detto nel capitolo finale, si potrebbe effettuare un porting totale adeguato a tutti i moduli dell'applicazione.
    % \section{Panoramica su Upgrade dei moduli dell'app}


    %% parlare dei moduli e del cambiamento dell'app.
    \section{Modifiche della struttura principale}\label{ModificheDellaStruttura}
        In questa sezione e nelle successive sezioni viene descritto il ripristino dell'applicazione ormai datata aggiornando o sostituendo librerie di codice e parti dell'applicazione così da renderla più performante ed adeguarla ai pattern odierni di programmazione. 
        \begin{figure}[H]\centering
            \includegraphics[scale=0.3]{Pictures/Cap_3/GenericUpload.jpg}
            \caption{Vista generale dell'upgrade UI.}\label{fig:GenericUpload}
        \end{figure}
    
        %%%%%%%%
        \subsection{Utilizzo dei fragment}
        
        I Fragment \cite{corsoMobileApp} permettono una gestione migliore della \textit{user interface} introducendo modularità e riusabilità degli elementi grafici di un'\textit{Activity} dividendo così la UI in blocchi più piccoli. L'aumento di modularità è dovuto al fatto che ogni activity complessa, può essere divisa in più fragment ottenendo una migliore organizzazione e mantenimento. Inoltre, lo stesso fragment, può essere riutilizzato da più activity o apparire più volte nella stessa.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.38]{Pictures/Cap_3/twoVersionSameScreen.jpg}
            \caption{Due versioni dello stesso schermo con grandezze differenti.}\label{fig:twoVersionSameScreen}
        \end{figure}
        
        \noindent
        Come vediamo nella figura 3.2, a sinistra viene visualizzato uno schermo più grande contenente un \textit{Navigation Drawer}\footnote{Un pannello UI laterale che mostra un menù per la navigazione principale dell'appicazione.} controllato dall'activity ed una \textit{Grid List}\footnote{Mostra gli elementi in una griglia bidimensionale di righe e colonne.} controllata dal fragment. A destra invece abbiamo una \textit{Bottom Navigation Bar} controllata dall'activity ed una \textit{Linear List}\footnote{Mostra gli elementi disponendoli in una lista.} controllata dal fragment. I fragment, quindi, possono incapsulare un determinato numero di componenti in modo riutilizzabile.
        Un fragment è un oggetto che, concettualmente, può essere considerato una via di mezzo tra un'Activity ed una \textit{View}\footnote{Classe alla base della creazione dei componenti per la user interface. Una View occupa un'area rettangolare dello schermo ed è responsabile della creazione degli elementi grafici e della gestione di eventi.}. Da un lato, come un'Activity, possiede un complesso life cycle gestito da numerose callback e permette l'interazione da l'utente e l'app. Dall'altro, possede una gerarchia di views che possono entrare a far parte dell'\textit{Activity Visual Tree\footnote{Insieme di view dell'activity disposte secondo un ordine gerarchico.}}.\\
        La gestione dei fragments, ovvero la loro sostituzione, aggiunta o rimozione dal \textit{Back Stack}\footnote{Struttura dati usata per diversi contesti, le cui modalita d'accesso ai dati seguono la madalità LIFO(Last In Fist Out).}, è garantita dal FragmentMenager.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.4]{Pictures/Cap_3/twoUILayout.jpg}
            \caption{Due esempi di UI layout con le rispettive relazioni tra i framents e le loro host activities.}\label{fig:twoUILayout}
        \end{figure}
        
        \noindent
        Considerando l'esempio della \autoref{fig:twoUILayout} in \textit{Example 1} abbiamo un Host Fragment che fa da host a 2 Child Fragment creando uno split-view screen; nell'\textit{Example 2} invece fa da host ad un solo child fragment costituendo una \textit{swipe view}\footnote{Classe che permette di navigare attraverso schermate adiacenti come le \textit{tabs} con una gesture.}.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.4]{Pictures/Cap_3/fragmentManagerAssociation.jpg}
            \caption{Ogni host ha il suo FragmentManager.}\label{fig:fragmentManagerAssociation}
        \end{figure}
        
        \noindent
        Dato questo setup possiamo a un \textit{FragmentManager} come un oggetto associato ad ogni host di un determinato livello che gestisce i suoi fragment figli. Le funzioni utilizzate per il \textit{FragmentManager} associato al livello sono \textit{supportFragmentManager}, \textit{parentFragmentManager} e \textit{childFragmentManager}.\\
        Per effettuare una \textit{FragmentTransaction} transizione da un fragment all'altro, vengono utilizzate le funzioni add() o replace().
        
        \begin{lstlisting}
        
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, TestFragment.class, null)
    .setReorderingAllowed(true)
    .addToBackStack("name") // name can be null
    .commit();
    
        \end{lstlisting}
        
        \noindent
        In questo esempio, il FragmentManager sostituisce qualsiasi fragment sia presente nel \textit{fragment\_container} con il \textit{TestFragment} e lo aggiunge quindi al Back Stack.\\
        \noindent
        Questi elementi appena descritti sono molto importanti poiché sono sfruttati internamente e quindi alla base delle API della \textit{BottomNavigationView} che descriveremo del sotto-paragrafo seguente.
        
        %%%%%%%%
        \subsection{Navigation component}\label{BottomNavigationView}
            
            Il termine \textit{Navigation} si riferisce all'interazione che permette all'utente di navigare attraverso, all'interno o tornare indietro, tra le varie parti del un contenuto di un'applicazione. Il \textbf{Navigation component} \cite{navigationComponent} di \textit{Android Jetpack}\footnote{Insieme di librerie create per aiutare lo sviluppatore nel seguire le best practise, ridurre il boilerplate code e scrivere codice stabile tra le versioni Android.}, permette di implementare varie tipologie di navigazione, che vanno dal click di un semplice bottone a pattern più complessi come la \textbf{Bottom app bar} o il \textbf{Navigation drawer}\footnote{Un pannello grafico che mostra lateralmente il menù per la navigazione principale.}. L'obiettivo è quello di assicurare una \textbf{user experience} intuitiva, consistente e prevedibile.
            
            \begin{figure}[H]\centering
                \includegraphics[width=\textwidth]{Pictures/Cap_3/navigatorScreen.jpg}
                \caption{Screenshot del Navigator Graph.}\label{fig:navigatorGraph}
            \end{figure}
            % \vspace{1ex}%
            \begin{itemize}
                \item \textbf{Bottom Navigation View} 
                \\\\
                All'interno dell'applicazione, la navigazione tra i contenuti è gestita da un barra di selezione posta nella parte inferiore dello schermo. La sua visualizzazione e la disposizione degli elementi della barra è coordinata dalla \textit{Bottom Navigation View} \cite{bottomNavigationView}. Quest'ultima ha anche il compito di definire la navigazione tra i contenuti sfruttando un \textbf{Navigator Graph} rappresentato in \autoref{fig:navigatorGraph}: la disposizione dei contenuti e la struttura della navigazione viene fatta con un editor grafico di \textit{Android Studio}. Un volta implementati tutti i componenti per la navigazione, cliccando su un elemento della bottom bar viene visualizzato il contenuto corrispondente descritto dal Navigator Graph.     
                    
            \end{itemize}
    %%%%%%%%%%%
    \section{Le classi Activity}\label{LeClassiActivity}
    In questa sezione vedremo le activity che costituiscono l'applicazione e attenzioneremo le loro caratteristiche principali.
        %%%%%%%%
        \subsection{Main Activity}\label{MainActivity}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.17]{Pictures/Cap_3/DashboardFragment_focus.png}
                \caption{Screenshot della Main activity con Dashboard Fragment.}\label{fig:DashboardFragment_focus}
            \end{figure}
            
            La \textit{Main Activity} rappresenta la parte principale dell'applicazione poiché ha il compito di creare e gestire 80\% delle parti che la costituiscono. Si occupa infatti della creazione e della gestione di tutti i \textbf{Fragments} (\ref{Fragments}) garantendone la coordinazione tramite la \textit{Bottom Navigation} (\ref{BottomNavigationView}) e di tutti i processi che questi le delegano, ad esempio, l'attivazione e la gestione dei sensori dello smartphone, l'invio e la ricezione dei dati con altre activity o fragment, l'accesso al database o la memorizzazione delle impostazioni. Permette la realizzazione di un nuovo pattern scalare e stabile dal punto di vista del mantenimento del codice.   
        
        %%%%%%%%
        \subsection{Camera2Tracker Activity}\label{Camera2TrackerActivity}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.14]{Pictures/Cap_3/CameraTrackerActivity.png}
                \caption{Screenshot della Camera Tracker Activity.}\label{fig:CameraTrackerActivity}
            \end{figure}
            
            La classe presentata costituisce il punto di partenza per lo start di una prova. L'interfaccia semplificata permette di inquadrare i markers e, una volta rilevati, acquisire l'angolo volante di partenza. Come supporto all'utente viene mostrato il framerate, tramite un contatore di fps, per dare un feedback sulla qualità della scena. Poiché le condizioni di luce influiscono molto sulla velocità di cattura dei fotogrammi (\ref{BassoFramerate}) è stato inserito un pulsante per abilitare il flash della fotocamera durante la prova.  
            %%Differenza con CameraTrackerActvity precedente
            %%Retrocamera eliminato
    %%%%%%%%%%%
    \section{I fragment}\label{Fragments}
        In questa sezione verranno descritti i fragment che costituiscono l'applicazione e permettono l'interazione dell'utente con tutte le funzionalità.
        %%%%%%%%
        \subsection{Dashboard Fragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/DashboardFragment.png}
                \caption{Screenshot del Dashboard Fragment.}\label{fig:DashboardFragment}
            \end{figure}
            
            Rappresenta la schermata principale dell'applicazione: all'interno di questo fragment è contenuta la mappa con la posizione corrente dell'utente \cite{smartLocationLibrary} e il suo percorso sulla mappa nel caso questo di muova \cite{geoRegression}. Tramite il pulsante in basso a destra è possibile accedere alla CameraTrackerActivity che abilita la fotocamera e nel caso di rilevamento della \textit{charuco board} permette l'inizio della prova.
            In questa sezione è possibile accedere ai settings per regolare le impostazioni dell'applicazione.
        
        %%%%%%%%
        \subsection{SavedTests Fragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/SavedTestFragment.png}
                \caption{Screenshot del Saved Test Fragment.}\label{fig:SavedTestFragment}
            \end{figure}
            
            Vengono raggruppati in una lista tutte le prove ordinate cronologicamente. Permette il passaggio al \textit{Result Fragment} (\ref{ResultFragment}) con i dati della prova selezionata.
        
        %%%%%%%%
        \subsection{Result Fragment}\label{ResultFragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/ResultTestFragment.png}
                \caption{Screenshot del Result Test Fragment.}\label{fig:ResultTestFragment}
            \end{figure}
            
            In questa sezione vengono mostrati i dati acquisiti. In particolare, in alto è possibile selezionare i segmenti di prova con una mappa che visualizza e traccia il percorso effettuato durante la prova. In basso vengono plottati i dati del segmento di prova selezionato, permettendo un'analisi dei dati più dettagliata.
        
        %%%%%%%%
        \subsection{Car Selection Fragment}\label{CarSelectionFragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/CarSelectionFragment.png}
                \caption{Screenshot del Car Selection Fragment.}\label{fig:CarSelectionFragment}
            \end{figure}
            
            Fornisce un apposito menù dal quale è possibile selezionare il \textit{segmento} di veicolo utilizzato durante la prova.
        
        %%%%%%%%
        \subsection{Board Generator Fragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/BoardGenerator.png}
                \caption{Screenshot del Board Generator Fragment.}\label{fig:BoardGenerator}
            \end{figure}
            
            Permette la creazione ed il salvataggio della \textbf{board} (\ref{Charuco}) all'interno del dispositivo. In questo modo è possibile recuperare la board e stamparla per poter eseguire le prove.
        
        %%%%%%%%
        \subsection{Settings Fragment}
            \begin{figure}[H]\centering
                \includegraphics[scale=0.19]{Pictures/Cap_3/SettingsFragment.png}
                \caption{Screenshot del Settings Fragment.}\label{fig:SettingsFragment}
            \end{figure}
            
            In questa sezione sono contenute le impostazioni dell'applicazione: Si può abilitare il \textbf{salvataggio} dei dati in formato CSV e testo(.txt). Un campo apposito permette l'inserimento della mail utilizzata per inviare i dati raccolti dalle prove così da poterli manipolare tramite pc. In questo modo si ottiene un trasferimento veloce dei dati, da un dispositivo all'altro, senza dover utilizzare un dispositivo di memoria esterno. Nel caso l'impostazione sia disabilitata, i dati vendono comunque salvati nella memoria interna dello smartphone. Inoltre un'impostazione secondaria permette di abilitare un tutorial per spiegare le funzionalità della schermata principale (\textit{Dashboard Fragment} \ref{fig:DashboardFragment}).
            
    %%%%%%%%%%%
    \section{Elementi deprecati}\label{ElementiDeprecati}
        \subsection{HardwareCamera Class}\label{HardwareCameraClass}
        
            Questa classe permetteva di gestire il layer hardware della fotocamera e poter sfruttare le funzioni principale. E' stata sostituita in quanto la gestione del codice ed il collegamento ai layer superiori era onerosa per il programmatore e non molto stabile. Portava anche delle limitazioni: mancanza di supporto di molte funzioni come regolazione del framerate, pre-frame control e stabile integrazione e collegamento con la libreria OpenCv. Per questi motivi è stata sostituita con le nuove \textbf{Camera2 API} (\ref{Camera2Api}).
    %%%%%%%%%%%    
    \section{Analisi dell'esecuzione e del flusso dati} 
    
        Uno degli obiettivi è stato quello di analizzare tutto il \textbf{flusso} di scambio e acquisizione dati per trovare alcune criticità e risolverle. Quest'analisi ha permesso di ottenere una vista d'insieme delle informazioni scambiate e di come i vari moduli interagiscano tra loro. Questo punto è stato essenziale per capire come approcciarsi ad ogni singolo modulo e cosa modificare per garantire performance più alte e integrità delle informazioni durante l'invio o la ricezione dei dati.
        Prima di focalizzarsi sulle varie interazioni, che vedremo nelle successive sottosezioni, si riporta lo \textbf{schema complessivo dell'analisi} e la sua descrizione:
        
        \begin{figure}[H]\centering
            \includegraphics[scale=0.27, angle = 90]{Pictures/Cap_3/Vehicle-app.drawio.pdf}
            \caption{Vehicle-App: struttura e moduli.}\label{fig:figura2}
        \end{figure}
        
        %%%%%%%%
        \subsection{Parte 1}\label{parte1}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso1.jpg}
                \caption{Prima parte dell'analisi del flusso dati.}\label{fig:imgFlusso1}
            \end{figure}
            
            All'avvio dell'applicazione, vengono inizializzati tutti i fragments gli elementi grafici, ad esempio la mappa, e di background come i sensori e le variabili per l'invio e la recezione dei dati. Il \textit{Button} della vista principale (\ref{fig:DashboardFragment}) permette di accedere alla fotocamera (\ref{fig:CameraTrackerActivity}).
            
        %%%%%%%%
        \subsection{Parte 2}\label{parte2}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso2.jpg}
                \caption{Seconda parte dell'analisi del flusso dati.}\label{fig:imgFlusso2}
            \end{figure}
            
            Una volta avviata la fotocamera vengono avviate le componenti \textbf{OpenCv} per l'identificazione dei \textbf{markers}. Se una \textit{board} (\ref{Charuco}) viene identificata, viene reso visibile un \textit{button} che avvia 2 processi: inizia l'esecuzione del \textbf{Service}, che vedremo nella sezione successiva, e invia il valore iniziale e timestamp dell'angolo $\theta$ all'activity principale. 
            Eseguiti i processi, la fotocamera viene chiusa tornando alla sezione principale.  
            
        %%%%%%%%
        \subsection{Parte 3}\label{parte3}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso3.jpg}
                \caption{Terza parte dell'analisi del flusso dati.}\label{fig:imgFlusso3}
            \end{figure}
        
            Il Service acquisisce in background i frame uno alla volta tramite un loop e li inserisce in una lista. 
            Successivamente crea un \textit{thread} che, sfruttando il pattern produttore-consumatore (\ref{fig:figuraBlockingQueue}), preleva continuamente i frame dalla lista e ne calcola l'angolo $\theta$ inviandolo in \textbf{broadcast}.
            
        %%%%%%%%
        \subsection{Parte 4}\label{parte4}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso4.jpg}
                \caption{Quarta parte dell'analisi del flusso dati.}\label{fig:imgFlusso4}
            \end{figure}
        
            A questo punto viene avviata una fase pre-test per acquisire i sistemi di riferimento iniziali e salvarli per essere usati durante l'elaborazione dati della parte 5. 
            I listener dei sensori (accelerazione, giroscopio, gps) vengono registrati e parte l'acquisizione dati trasmessi in \textbf{broadcast}.
            Infine si effettua lo start del thread per il calcolo del $\beta$.
            
        %%%%%%%%
        \subsection{Parte 5}\label{parte5}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso5.1.jpg}
                \caption{Quinta parte dell'analisi del flusso dati: Acquisizione.}\label{fig:imgFlusso5.1}
            \end{figure}
            
            Il thread generato è fondamentale per l'elaborazione finale dei dati. Tramite un Broadcast Receiver\footnote{Classe base che riceve e gestisce strutture dati inviate in broadcast} raccoglie, per tutta la durata del suo processo, i dati provenienti da tutti i sensori e li inserisce nelle rispettive liste.
            
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso5.2.jpg}
                \caption{Quinta parte dell'analisi del flusso dati: Elaborazione.}\label{fig:imgFlusso5.2}
            \end{figure}
            
            Successivamente, quattro thread annidati prelevano in loop dalle rispettive liste.
            Se uno dei dati non è presente si aspetta; nel caso contrario si procede con il calcolo del $\beta$.
            Terminata l'elaborazione, i dati in input e output risultate vengono salvati in una lista.
            
        %%%%%%%%
        \subsection{Parte 6}\label{parte6}
            \begin{figure}[H]\centering
                \includegraphics[width=0.75\textwidth]{Pictures/Cap_3/flusso6.jpg}
                \caption{Sesta parte dell'analisi del flusso dati.}\label{fig:imgFlusso6}
            \end{figure}
            
            Non appena l'utente decide di terminare la prova il thread per il calcolo del $\beta$ ed il Camera Service vengono terminati e tutti i sensori disattivati.
            Vengono recuperati tutti i dati e salvati in un database.
            Su richiesta dell'utente potranno essere inviati tramite mail, per poterli elaborare ulteriormente su altri dispositivi.
        
        
%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Libreria OpevCV}\label{libreriaOpenCv}

    \begin{figure}[H]\centering
        \includegraphics[scale=0.6]{Pictures/Cap_4/opencv.png}
        \caption{Logo OpenCv}\label{fig:LogoOpenCv}
    \end{figure}
    
    Per l'acquisizione dell'angolo volante, l'utilizzo della libreria OpenCv \cite{openCv} si è rivelato fondamentale. In questa sezione vedremo quali sono stati gli upgrade per semplificare e migliorare l'acquisizione dei markers e quindi il passaggio dalla libreria \textbf{ArUco} \cite{openCvAruco} a \textbf{ChArUco} \cite{openCvChAruco}.
    %%%%%%%%%%%
    \section{Compilazione}
        Le nuove versioni di OpenCV non contengono le librerie per il riconoscimento dei markers, pertanto, è stata necessaria una compilazione ad-hoc. Per raggiungere questo obiettivo è stato utilizzato CMake. Una volta scaricati tutti i file sorgente della libreria, in particolare \textbf{OpevCV-4.5.1}, è stato possibile eseguire la compilazione \cite{openCvWhitContribModules} integrando \textbf{ArUco} e \textbf{ChArUco}.

    \begin{figure}[H]
        \centering
        \includegraphics[width=\textwidth]{Pictures/Cap_4/compilazioneLibreria.jpg}
        \caption{Utilizzo di Cmake per compilare file OpenCv}\label{fig:compilazioneLibreria}
    \end{figure}

    %%%%%%%%%%%
    \section{ArUco}\label{Aruco}
        
        La stima della posizione dei markers è stata possibile grazie ad ArUco, una libreria, che permette di trovare una corrispondenza tra i punti in un ambiente reale e la loro proiezione in un immagine 2d. Nelle seguenti sottosezioni parleremo delle funzioni e del codice utilizzato nel progetto.
    
        %%%%%%%%
        \subsection{Creazione ed utilizzo dei markers}
        
         \begin{figure}[H]
            \centering
            \includegraphics[scale=0.3]{Pictures/Cap_4/markersN.jpg}
            \caption{Insieme di markers.}\label{fig:markersN}
        \end{figure}
        
        Un marker ArUco è composto da un bordo nero, che ne definisce il perimetro e facilita la detection, e da una matrice binaria interna che costituisce l'id del marker. L'id è un numero ottenuto convertendo la codifica binaria in un numero su base decimale. Un marker presenta ogni angolo univoco poiché dev'essere distinguibile da altri marker che possono essere disposti diversamente nell'ambiente circostante creando così errori di detection. I markers vengono raggruppati in set, chiamati \textbf{dizionari}, in funzione della dimensione che determina anche la grandezza della matrice interna. Per il progetto, ad esempio, è stato utilizzato un dizionario rappresentante l'insieme dei markers 5x5.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.5]{Pictures/Cap_4/markerCells.jpg}
            \caption{Marker diviso in celle.}\label{fig:markerCells}
        \end{figure}
    
        Per poter effettuare una detection il marker dev'essere prima stampato. La stampa può essere effettuata da codice, tramite una funzione specifica, o da un sito web \hyperlink{https://chev.me/arucogen/}{https://chev.me/arucogen/} che ne fornisce l'immagine jpeg.    
        
        \noindent
        Una volta acquisita l'immagine contenente uno o più markers, il processo di detection viene effettuato tramite 2 step principali.\\ 
        Nel \textbf{primo step} si effettua una detection cercando di trovare, all'interno dell'immagine, delle forme quadrate candidate ad essere possibili markers. All'interno dell'immagine, tutto il resto viene scartato utilizzando dei filtri che permettono il thresholding dell'immagine.\\
        Nel \textbf{secondo step} si effettua la detection dei possibili candidati applicando delle trasformazioni ed ottenendo cosi un'immagine del markers divisa in celle secondo la grandezza del marker e dei suoi bordi.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.5]{Pictures/Cap_4/imageDetectedmarkers.jpg}
            \caption{Detection dei markers.}\label{fig:imageDetectedmarkers}
        \end{figure}
        \noindent
        
        Questo processo viene eseguito tramite la funzione \textbf{detectMarkers()}:
        
        \begin{lstlisting}
private java.util.List<org.opencv.core.Mat> corners = new ArrayList<>();
private MatOfInt ids = new MatOfInt();

    ...
    
Aruco.detectMarkers(mRgb, Aruco.getPredefinedDictionary(Aruco.DICT_ARUCO_ORIGINAL)
                                                                                , corners, ids);
    ...
        \end{lstlisting}
        
        \begin{itemize}
            \item Il primo parametro \textbf{mRgb} è l'immagine che contiene i marker da rilevare.
            \item Il secondo rappresenta l'oggetto dizionario: in questo caso il dizionario scelto è il DICT\_ARUCO\_ORIGINAL.
            \item Il terzo parametro \textbf{corners} è la lista degli angoli dei markers rilevati. Gli angoli di ogni marker vengono rilevati in senso orario partendo dall'angolo in alto a sinistra.
            \item Il quarto parametro \textbf{ids} è la lista degli Id di ogni marker rilevato in \textit{corners}. Da notare che il vettore ritornato in questo quarto parametro ha le stesse dimensioni del terzo.
        \end{itemize}
        
         \begin{figure}[H]
            \centering
            \includegraphics[scale=0.5]{Pictures/Cap_4/imageEsimatePose.jpg}
            \caption{Stima della posizione dei markers.}\label{fig:imageEsimatePose}
        \end{figure}
        
        Ottenuti i markers è stata ottenuta la loro posizione e rotazione rispetto alla fotocamera, utilizzando la funzione \textbf{estimatePoseSingleMarkers()}:
        
        \begin{lstlisting}
private Mat rvecs = new Mat();
private Mat tvecs = new Mat();

    ...
    
for (int i = 0; i < corners.size(); i++) {
        Aruco.estimatePoseSingleMarkers(corners.subList(i, i + 1), (float) 0.05, cameraMatrix
                                                                                , coeffs
                                                                                , rvecs, tvecs);
    
    ...
}

        \end{lstlisting}
        
        \begin{itemize}
            \item Il primo parametro rappresenta il vettore di angoli dei markers ritornati dalla funzione \textit{detectMarkers()}. Viene considerata una subList in un ciclo \textbf{for} poiché nel nostro caso è necessario effettuare la dection di più markers.
            \item Il secondo parametro è la lunghezza di un lato del marker in metri.
            \item Il quinto parametro (di output) \textbf{rvecs} rappresenta il vettore di rotazione per ogni marker rilevato in \textbf{corners}.
            \item Il sesto parametro (di output) \textbf{tvecs} rappresenta il vettore di traslazione per ogni marker rilevato in \textbf{corners}.
        \end{itemize}
        
        Per ottenere una stima della posizione è necessario calibrare i parametri della fotocamera utilizzata. Tali parametri sono \textbf{cameraMatrix} e \textbf{distCoeffs} (Coefficienti di distorsione).
        per la calibrazione viene utilizzata la funzione \textbf{loadCalib()}: 
        
        \begin{lstlisting}
        
loadCalib(){
    cameraMatrix = new Mat(3, 3, CvType.CV_32F);
    coeffs = new Mat(5, 1, CvType.CV_32F);

    cameraMatrix.put(0, 0, 1024.3446);
    cameraMatrix.put(0, 1, 0);
    cameraMatrix.put(0, 2, 640.0);
    cameraMatrix.put(1, 0, 0);
    cameraMatrix.put(1, 1, 1024.3446);
    cameraMatrix.put(1, 2, 360);
    cameraMatrix.put(2, 0, 0);
    cameraMatrix.put(2, 1, 0);
    cameraMatrix.put(2, 2, 1);

    coeffs.put(0, 0, 0.08816299);
    coeffs.put(1, 0, -0.21675685);
    coeffs.put(2, 0, 0);
    coeffs.put(3, 0, 0);
    coeffs.put(4, 0, 0);
}    

        \end{lstlisting}    

        \noindent        
        Nel progetto in questione è stata effettuata una calibrazione manuale. Tramite il metodo \textit{put} viene inserito ogni valore della matrice rispettivamente, nella riga (primo parametro) e colonna (secondo parametro) designata, il valore per la calibrazione passato nel terzo parametro.


    %%%%%%%%%%%
    \section{ChArUco}\label{Charuco}
    
        Come vedremo nel capitolo successivo, la sostituzione di ArUco con la libreria ChArUco porta a dei miglioramenti per quanto riguarda l'accuratezza del calcolo della posizione di un marker. Questo è dovuto al fatto che una board di markers è molto efficiente nella detection ma produce risultati con un'accuratezza elevata. Con una scacchiera, invece, è possibile avere un'accuratezza migliore ma una scarsa versatilità in termini di visibilità poiché la parziale occlusione non è permessa. Per questo motivo è stato definito un terzo approccio che risulta essere la combinazione dei due descritti precedentemente e consiste nell'utilizzo della \textbf{ChArUco board}.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.47]{Pictures/Cap_4/charucoDefinition.jpg}
            \caption{Definizione della ChArUco board.}\label{fig:charucoDefinition}
        \end{figure}

        %%%%%%%%
        \subsection{Creazione ed utilizzo della ChArUco Board}

        La creazione di una ChArUco board è simile a quella, descritta precedentemente, %% mettere riferimento
        per la creazione di un Marker se non per il concetto di univocità, che ora è esteso a tutta la board.
        
        \begin{lstlisting}
        
try {

    Imgproc.cvtColor(inputFrame, mRet, Imgproc.COLOR_BGRA2BGR);
    Aruco.detectMarkers(mRet, Aruco.getPredefinedDictionary(Aruco.DICT_6X6_250), corners
                                                                                        , ids
                                                                                        , params);
    
    // if at least one marker detected
    if (ids.total() > 0) {
        //Aruco.drawDetectedMarkers(mRet, corners, ids);
        charucoCorners = new Mat();
        charucoIds = new MatOfInt();
        Aruco.interpolateCornersCharuco(corners, ids, mRet, board, charucoCorners, charucoIds
                                                                                , cameraMatrix
                                                                                , coeffs);
        
    ...

}

        \end{lstlisting}
        
        
        

        \begin{lstlisting}
        
if (charucoCorners.total() > 0) {
    Aruco.drawDetectedCornersCharuco(mRet, charucoCorners, charucoIds, new Scalar(255, 0, 0));
        
    ...
        \end{lstlisting}


        \begin{lstlisting}

    boolean valid = Aruco.estimatePoseCharucoBoard(charucoCorners, charucoIds, board
                                                                        , cameraMatrix
                                                                        , coeffs
                                                                        , rvecs
                                                                        , tvecs);

    ...

        \end{lstlisting}
        
        
        \begin{lstlisting}
        
    if (valid)
        Aruco.drawAxis(mRet, cameraMatrix, coeffs, rvecs, tvecs, (float) 0.1);
    
    ...
    
}
        \end{lstlisting}
        
        \noindent        
        Infatti, come per Aruco (\ref{Aruco}), si effettua la detection dei markers tramite \textbf{detectMarkers()} e successivamente si identifica la board tramite \textbf{interpolateCornersCharuco()}. Se la board è stata rilevata, si calcola la posizione e la rotazione tramite \textbf{estimatePoseCharucoBoard()}. I risulati vengono inseriti nei vettori di rotazione e traslazione \textbf{rvecs} e \textbf{tvecs}. Successivamente vengono disegnati sul frame elaborato tramite la funzione \textbf{drawAxis()}.
        

        %%%%%%%%
        \subsection{Utilizzo della board su supporto volante}

        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.48]{Pictures/Cap_4/misureSupportoVolante.png}
            \caption{Misure del supporto per la board da applicare al volante.}\label{fig:misureSupportoVolante}
        \end{figure}
                
        Il calcolo dell'angolo volante $\theta$ prevede l'applicazione dei markers sul volante per poterne rilavare la posizione. Per facilitare l'applicazione è stato costruito un supporto circolare: la stima delle grandezze ideali è stato ottenuta costruendo un supporto in cartone da applicare al volante. Il supporto, dopo una serie di prove, è stato modellato ottenendo delle grandezze che garantiscano una guida priva di interferenze: è stato scelto un diametro di 27 cm con l'aggiunta di piccole fenditure lunghe 2.5 cm e distanti 7/8 cm dal centro. Attraverso le fenditure vengono inserite delle fascette in velcro per poter attaccare il supporto alle razze del volante.\\
        In futuro, il supporto potrebbe essere stampato tramite stampante 3d con dei ganci mobili per adattarsi a qualsiasi tipo di volante.
    
    %%%%%%%%%%%
    \section{Camera2 API}\label{Camera2Api}
    % parlare dell'integrazione tra opencv e camera2 mettere immagini
        Durante il lavoro svolto è stata utilizzata la nuova classe di API disponibile in Android. Nella versione precedente dell'app era stata utilizzata, anche se deprecata, la prima versione (\ref{HardwareCameraClass}) di API per necessità legate all'implementazione di OpenCv. E' stato possibile integrare le \textbf{Camera2 API} \cite{camera2} con le nuove versioni di OpenCv.
        La nuova classe di API porta un aumento sostanziale delle capacità delle applicazioni di controllare il sottosistema della fotocamera garantendo maggiore affidabilità e massimizzando la qualità e le prestazioni.
        L'API modella il sottosistema della camera \cite{cameraHal3} come una pipeline ricevendo in input delle richieste per ogni singolo frame ed effettuandone la cattura restituendone in output i metadata del frame ed un set di immagini bufferizzate.
    
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.5]{Pictures/Cap_4/camera2_API.jpg}
            \caption{Modello sottosistema Camera2 API.}\label{fig:modelloFotocameraHal3}
        \end{figure}
        
        Vengono così eliminati dei limiti su alcuni aspetti legati allo sviluppo dell'algoritmo per il calcolo dell'angolo volante. Ad esempio è stato possibile modificare il valore del framerate della registrazione video permettendo di fruttare a pieno l'hardware del dispositivo.
        
        %%%%%%%%
        % \subsection{Funzionalità e classi di controllo}
        %%(Inserire codice pronto per camera2)
        %%codice modifica framerate



    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\part{Terza Parte}

%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Soluzioni al problema dell'angolo volante}
    


    %%%%%%%%%%%
    \section{Algoritmo di Tracking e stima dell'angolo volante}
    
        L'algoritmo di Tracking utilizzato è composto da una fase in \textbf{foreground}, realizzata nella Camera2Tracker Activity (\ref{Camera2TrackerActivity}), che ha il compito di dare un feedback iniziale all'utilizzatore e costituisce il punto di partenza della prova.\\Finita la prima fase, vengono abilitate la seconda e la terza fase che agiscono in \textbf{background} effettuando il calcolo dell'angolo volante senza essere percepite dall'utente. Come descritto nella sezione \ref{parte3}, le due fasi lavorano in maniera asincrona sfruttando il pattern \textit{Produttore-Consumatore} contenuto realizzato nel \textit{Service} (\ref{ClasseService}). 
    
  
        %%%%%%%%
        \subsection{Descrizione delle fasi}
            Vedremo in dettaglio come operano tutte le fasi per ottenere il risultato finale:
            
            \begin{itemize}
                \item \textbf{Prima fase in Foreground} 
                \\\\
                    Come vediamo dalla figura (\ref{fig:CoperturaDeimarkers}), in questa fase viene acquisito il valore di partenza dell'angolo volante. Infatti, non appena l'utente inquadra i markers, si attiva un pulsante che da un feedback sulla corretta detection dei markers ed, una volta premuto, determina il salvataggio dell'angolo di partenza e l'inizio della prova. Nei primi 30 secondi, l'utente viene guidato con un'interfaccia così da ottenere i dati necessari per l'assetto e la correzione del sistema di riferimento.
                \item \textbf{Seconda fase in Background} 
                \\\\
                    In questa fase viene avviato il \textit{Camera2Service} che si occupa di acquisire un frame dopo l'altro ed inserirlo in una \textit{LinkedBlockingQueue} (\ref{fig:figuraBlockingQueue}). Il service svolge quindi il ruolo di produttore del dato in input. Contemporaneamente crea ed esegue un \textit{Thread} che svolge tutti i processi della fase successiva.
                \item \textbf{Terza fase in Background} 
                \\\\
                    Nell'ultima fase, il thread in questione acquisisce il ruolo di consumatore prelevando il un frame alla volta dalla \textit{LinkedBlockingQueue}. Successivamente trasforma il frame in un oggetto \textbf{matrice} per poter essere elaborato dalla \textbf{libreria OpenCv}: Si sfruttano le librerie \textbf{Aruco}/\textbf{Charuco} per stimare il valore dell'\textbf{angolo volante}. 
            \end{itemize}
        
        
        %%%%%%%%
%%        \subsection{Differenza nell'elaborazione tra Aruco e Charuco}

    %%%%%%%%%%%
    \section{Copertura accidentale dei Markers} 
        
        L'implementazione della Charuco board, come visto nella sezione \ref{Charuco}, ha contribuito ad attenuare la problematica della detection in caso di occlusione parziale dei markers.
    
        \begin{figure}[H]
            \begin{subfigure}{0.5\textwidth}
                \includegraphics[width=0.6\linewidth]{Pictures/Cap_5/BoardNonOccultata.jpeg} 
                \caption{Board non occultata.}\label{fig:BoardNonOccultata}
            \end{subfigure}
            \begin{subfigure}{0.5\textwidth}
                \includegraphics[width=0.6\linewidth]{Pictures/Cap_5/BoardparzialmenteOccultata.jpeg}
                \caption{Board parzialmente occultata.}\label{fig:BoardparzialmenteOccultata}
            \end{subfigure}
        \caption{Detection dei markers e calcolo dell'angolo con occlusione parziale.}\label{fig:CoperturaDeimarkers}
        \end{figure}
        
        \noindent
        Con questo approccio è possibile aumentare le probabilità di riuscita del calcolo dell'angolo volante durante le manovre del conducente. Per garantirne il calcolo è necessario che almeno 5 markers della board siano visibili. Maggiore è il numero di markers rilevati, migliore e preciso sarà l'angolo $\theta$ calcolato. La fase dei test (\ref{ProveSimulate}) è stata importante per capire fino a che punto è possibile spingersi per ottenere un compromesso tra un buon risultato ed una detection durante le manovre rapide. 
        
    
    %%%%%%%%%%%
    \section{Basso framerate}\label{BassoFramerate}
    
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.4]{Pictures/Cap_5/MediaFPS.jpeg}
            \caption{Media della velocità di cattura dei frame.}\label{fig:MediaFPS}
        \end{figure}
    
        Utilizzando un Samsung Galaxy S20 che implementa una fotocamera capace di registrare a 60 fps, è stato possibile superare le limitazioni della vecchia API passando da una registrazione che si aggirava intorno ai 20 fps ad una media di 32/34 fps con picchi di 45 fps. Naturalmente queste medie sono state ottenute tenendo conto del peso computazionale dovuto all'elaborazione real-time dei singoli frame per la detection dei markers. La velocità, oltre ad essere influenzata dall'hardware, dipende dal variare delle condizioni di luce e da eccessive vibrazioni. Il problema della variazione delle condizioni di luce e la possibile presenza di riflessi è stato attenuato permettendo l'attivazione del flash durante l'esecuzione della prova: la luce diretta sulla board migliora la detection dei markers aumentando il framerate medio di circa 5 fps. \\
        Tutte le seguenti migliorie hanno indirettamente contribuito al problema del de-allineamento dei dati riducendo possibili ritardi dovuti alla cattura.
        
    %%%%%%%%%%%
    \section{De-allineamento dei dati}\label{DeAllineamento}
        %% Immagini sul flusso
        %%%%%%%%
        \subsection{Inserimento dei Timestamp}
            Trovare una soluzione al problema del de-allineamento dei dati è stato fondamentale, poiché, nella versione precedente i dati venivano acquisiti senza un riferimento all'istante di cattura. Questo portava uno sfasamento tra i valori acquisiti dall'applicazione e quelli ideali ottenuti tramite un simulatore o una IMU\footnote{Inertial Measurement Unit: dispositivo elettronico utilizzato per misurare accelerazione, orientazione, velocità angolari ed altre forze gravitazionali. E' costituito da 3 accelerometri, 3 giroscopi e, a seconda delle esigenze di acquisizione, da 3 magnetometri. Possiede un componente per ognuno dei 3 assi del veicolo.}. L'errore andava quindi a ripercuotersi sul calcolo dei valori finali e soprattutto sull'angolo di assetto $\beta$.\\
            Il problema è stato risolto inserendo in fase di acquisizione un \textbf{timestamp} associato ad ogni dato catturato da un sensore: In questo modo interpolando i dati, acquisiti dai sensori con frequenze diverse, è possibile ottenere una frequenza comune costante.
        %%%%%%%%
        \subsection{Duplicazione dei dati}
            L'inserimento dei timestamp ha portato alla scoperta di un'altra problematica, rappresentata in \autoref{fig:st_angle_prova_con_duplicati}, che era passata del tutto inosservata: Lo stesso dato rilevato, veniva acquisito e salvato più volte a causa della struttura di acquisizione implementata. Il valore di un dato catturato in un determinato istante di tempo, veniva elaborato e salvato un numero di volte del tutto casuale. Infatti, senza timestamp, si aveva la sensazione che il valore si mantenesse semplicemente costante nel tempo. Questo fenomeno contribuiva al de-allineamento dei dati e determinava l'utilizzo di una quantità di memoria necessaria maggiore per il salvataggio della prova effettuata. 
            
            \begin{figure}[H]
                \centering
                \includegraphics[scale=0.9]{Pictures/Cap_5/st_angle_prova_con_duplicati.jpg}
                \caption{Duplicati dell'angolo volante: a sinistra i \textit{valori}, a destra i rispettivi \textit{timestamp}.}\label{fig:st_angle_prova_con_duplicati}
            \end{figure}
        
            \noindent
            Il problema è stato risolto modificando la struttura di acquisizione implementata e soprattutto l'algoritmo per il salvataggio dei dati su file di testo. Questa soluzione è importante per una corretta interpolazione dei dati in \textit{RAW}\footnote{Si intende un dato che contiene informazioni non ancora elaborate.}, effettuata in post produzione. Vediamo nella seguente immagine un file con i valori dell'angolo volante $\theta$ senza la presenza di duplicati.
            
            \begin{figure}[H]
                \centering
                \includegraphics[scale=0.9]{Pictures/Cap_5/st_angle_prova_senza_duplicati.jpg}
                \caption{Assenza di duplicati dell'angolo volante: a sinistra i \textit{valori}, a destra i rispettivi \textit{timestamp}.}\label{fig:st_angle_prova_senza_duplicati}
            \end{figure}            

%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Risultati}
    
    In quest'ultima parte verranno inizialmente analizzati i risultati ottenuti dalle prove simulate e, nella seconda parte, le prove reali svolte su strada. 


    %%%%%%%%%%%
    \section{Prove Simulate}\label{ProveSimulate}
    
        La prima sezione del capitolo descrive le prove svolte tramite simulatore composto dal software \textit{CarMaker} e da una postazione di guida. Successivamente una board con i marker e stata applicata sul volante. Lo smartphone, invece, è stato montato su un supporto laterale in direzione del volante.\\
        Lo scopo principale di queste simulazioni è quello di confrontare il valore dell'angolo volante restituito dal simulatore, con quello calcolato dall'applicazione.\\
        Per le prime 2 prove è stato utilizzato come modello un \textit{BMW Serie 5}.
        
        \begin{figure}[H]
            \centering
            \includegraphics[scale=0.7]{Pictures/Cap_6/Bmw_serie_5.JPG}
            \caption{Primo veicolo utilizzato: \textit{BMW Serie 5}.}\label{fig:Bmw_serie_5}
        \end{figure}  
        
        
        %% solo per angolo volante
        %% prima e seconda prova per misurare affidabilità. 
        % %%%%%%%%
        % \subsection{Prova 1: Rettilineo e curva semplice}
        %%%%%%%%
        \subsection{Prova 1: Rotatoria}
        
        La prima simulazione è stata effettuata su un segmento circolare di diametro 100 m per riprodurre l'andamento del veicolo su una rotatoria. Dopo una fase di accelerazione, il veicolo mantiene una velocità costante per tutta la prova.
        
        \begin{figure}[H]
            \begin{subfigure}{0.5\textwidth}
                \includegraphics[width=1.3\linewidth]{Pictures/Cap_6/Rotatoria100/rotatoria.JPG} 
                \caption{Simulazione con visuale conducente.}\label{fig:rotatoria}
            \end{subfigure}
            \begin{subfigure}{0.5\textwidth}
                \hfill
                \includegraphics[width=0.7\linewidth]{Pictures/Cap_6/Rotatoria100/rotatoria_percorso.JPG}
                \caption{Mappatura del percorso: Rotatoria.}\label{fig:rotatoria_percorso}
            \end{subfigure}
        \caption{Raffigurazioni del percorso della prima prova simulata.}\label{fig:RaffigurazionePrimaProva}
        \end{figure}
        
        \noindent
        L'obiettivo principale è di ottenere un primo riscontro sull'affidabilità delle misure percorrendo un tragitto uniforme.
        
        \begin{figure}[H]
            \centering
            \includegraphics[width=0.8\linewidth]{Pictures/Cap_6/Rotatoria100/rotatoria100_171804.jpg}
            \caption{Grafico della prima prova che mostra il confronto tra l'angolo volante del simulatore e dell'app su un segmento circolare con diametro 42m.}\label{fig:rotatoria100_171804}
        \end{figure}

        \noindent        
        Confrontando i risultati ottenuti dall'applicazione con quelli del simulatore vediamo come l'allineamento sull'asse dei tempi sia praticamente identico. Anche il valore dell'angolo volante, in un percorso privo di cambiamenti di direzione, risulta fedele. Solo in piccoli istanti il valore risulta diverso ma tale differenza è dovuta a piccole interferenze date dalla luce e dalla calibrazione della camera. La prima prova ha dato comunque dei risultati soddisfacenti.\\
        Da notare come in tutte le prove, nella parte iniziale dei grafici, vedremo un picco generato volontariamente: Poiché non è possibile generare un input che determini lo start della prova in contemporanea, inizialmente, viene ruotato il volante di 180 gradi generando una \textbf{singolarità} per permettere l'allineamento dei grafici sull'asse del tempo. 
        \\\\
        Successivamente la prova è stata replicata su un segmento circolare con un diametro di 42 metri per generare valori di $\theta$ più elevati.
        
        \begin{figure}[H]
            \centering
            \includegraphics[width=0.8\linewidth]{Pictures/Cap_6/Rotatoria42/rotatoria42_180916.jpg}
            \caption{Grafico della prima prova che mostra il confronto tra l'angolo volante del simulatore e dell'app su un segmento circolare con diametro 42m.}\label{fig:rotatoria42_180916}
        \end{figure}
        
        Anche in questo caso, nonostante le maggiori oscillazioni, i due andamenti risultano ben allineati e i valori coincidono a parte una singolarità presente a 140°. L'angolo misurato ha subito un incremento di 30° avendo percorso una curva più stretta.
        
        %%%%%%%%
        \subsection{Prova 2: Percorso semplice}
        
        La secondo prova simulata risulta molto simile alla prima ma è resa più complicata con un percorso e un andamento meno lineari.  
        
        \begin{figure}[H]
            \begin{subfigure}{0.5\textwidth}
                \includegraphics[width=1.3\linewidth]{Pictures/Cap_6/PercorsoSemplice/Catture/Examples_IPGRoad5_Motorway_LeavingMotorway.JPG} 
                \caption{Simulazione con visuale conducente.}\label{fig:PersorsoSemplice}
            \end{subfigure}
            \begin{subfigure}{0.5\textwidth}
                \hfill
                \includegraphics[width=0.7\linewidth]{Pictures/Cap_6/PercorsoSemplice/Catture/percorsoSemplice.JPG}
                \caption{Mappatura del percorso semplice.}\label{fig:PersorsoSemplice_percorso}
            \end{subfigure}
        \caption{Raffigurazioni del percorso della seconda prova simulata.}\label{fig:RaffigurazioneSecondaProva}
        \end{figure}
        
        \noindent
        Durante questa prova sono stati percorsi 2 tragitti diversi che chiameremo tragitto \textbf{A} e \textbf{B}.
        
        \begin{itemize}
            \item \textbf{Tragitto A:}
            
                \begin{figure}[H]
                    \centering
                    \includegraphics[width=0.4\linewidth]{Pictures/Cap_6/PercorsoSemplice/Catture/percorsoSemplice_soloCurva.png}
                    \caption{Percorso semplice: Tragitto A.}\label{fig:percorsoSemplice_soloCurva}
                \end{figure}
            
                In questo primo tragitto è stato percorso il primo tratto ad una velocità crescente per poi affrontare una curva a velocità costante.
            
                \begin{figure}[H]
                    \centering
                    \includegraphics[width=0.8\linewidth]{Pictures/Cap_6/PercorsoSemplice/Grafici/142321.jpg}
                    \caption{Grafico della seconda prova che mostra i valori rilevati\\ per il tragitto A.}\label{fig:PercorsoSempliceTragitoA_142321}
                \end{figure}
            
                Nonostante la velocità elevata, la tipologia della curva ha portato ad un valore massimo $\theta$ di circa 25 gradi ma con oscillazioni più fitte: nonostante siano più difficili da seguire, anche qui i due andamenti sono risultati per allineati.
            
            \item \textbf{Tragitto B:}
            
                \begin{figure}[H]
                    \centering
                    \includegraphics[width=0.4\linewidth]{Pictures/Cap_6/PercorsoSemplice/Catture/percorsoSemplice _semiRot.png}
                    \caption{Percorso semplice: Tragitto B.}\label{fig:percorsoSemplice _semiRot}
                \end{figure}                
                
                Nel secondo tragitto, dopo una fase di accelerazione viene compiuta una curva a sinistra seguita da metà rotatoria e una leggera curva.
                
                \begin{figure}[H]
                    \centering
                    \includegraphics[width=0.65\linewidth]{Pictures/Cap_6/PercorsoSemplice/Grafici/134846.jpg}
                    \caption{Grafico della seconda prova che mostra i valori rilevati\\ per il tragitto B.}\label{fig:PercorsoSempliceTragitoB_134846}
                \end{figure}
            
                In questo caso le oscillazioni del valore $\theta$ sono nettamente maggiori e, nella parte finale, l'applicazione ha generato valori con un leggero ma costante delay: Questo fattore è molto importante poiché la versione precedente dell'app generava un delay variabile generando notevoli difficoltà nell'allineamento e nell'interpolazione dei dati. 
                
                \begin{figure}[H]
                    \centering
                    \includegraphics[width=0.42\linewidth]{Pictures/Cap_6/PercorsoSemplice/Grafici/134846_zoom.JPG}
                    \caption{Grafico della seconda prova che mostra la differenza massima di $\theta$\\ rilevati per il tragitto B sui due dispositivi.}\label{fig:PercorsoSempliceTragitoB_134846_zoom}
                \end{figure}
                
                Il grafico in \autoref{fig:PercorsoSempliceTragitoB_134846_zoom} è importante poiché rileva uno scostamento medio di circa 0.5 gradi dal valore ideale IPG. Il massimo scostamento rilevato in pochissimi istanti è di circa 1 grado.
        \end{itemize}
        %%%%%%%%
        \subsection{Prova 3: Percorso su pista}
    
        \begin{figure}[H]
            \begin{subfigure}{0.5\textwidth}
                \includegraphics[width=1.16\linewidth]{Pictures/Cap_6/Pista/r8front.JPG} 
                \caption{Simulazione su pista con visuale autovettura.}\label{fig:r8front}
            \end{subfigure}
            \begin{subfigure}{0.5\textwidth}
                \hfill
                \includegraphics[width=0.82\linewidth]{Pictures/Cap_6/Pista/Circuito_pista.JPG}
                \caption{Mappatura del percorso: Circuito pista.}\label{fig:Circuito_pista}
            \end{subfigure}
        \caption{Raffigurazioni del percorso della prova simulata su pista.}\label{fig:RaffigurazioneTerzaProva}
        \end{figure}
        
        In questa prova su pista è stata utilizzata una supercar (Audi r8) per testare la capacità dell'applicazione nel rilevare l'angolo $\theta$ a velocità elevate. Le curve sono state percorse seguendo una traiettoria ideale con uno stile di guida aggressivo. E' interessante analizzare il confronto tra i valori calcolati dai due sistemi.  
        
        \begin{figure}[H]
            \centering
            \includegraphics[width=0.65\linewidth]{Pictures/Cap_6/Pista/percorso_pista_184006.jpg}
            \caption{Grafico della terza prova simulata su pista.}\label{fig:percorso_pista_184006}
        \end{figure}
        
        Dal grafico si notano picchi non uniformi dovuti a piccoli cambi di direzioni o, in alcuni punti, fasi di sottosterzo per riportare il veicolo in traiettoria. Nonostante la guida sportiva i valori di $\theta$ rimangono sempre molto fedeli soprattutto lungo l'asse tempi. Nella fase finale si può notare una piccola perdita dell'angolo volante da parte dell'applicazione dovuta ad una brusca sterzata seguita da una copertura della board con entrambe le braccia. Nonostante ciò, il valore è stato subito recuperato restando fedele nel tempo.
        
        % \subsection{Conclusioni sulle prove simulate}\label{ConclusioniProveSimulate}
        
        
    %%%%%%%%%%%
    \section{Prove su strada}

        Un volta verificato, nel capitolo precedente, la correttezza dell'angolo $\theta$ e del suo valore nel tempo, si è deciso di fare lo stesso per le altre componenti di dinamica del veicolo.
        Infatti, lo scopo di questa sezione è quello di verificare la correttezza delle altre componenti, confrontando, i valori calcolati dai sensori dello smartphone con quelli calcolati da una \textbf{IMU}\footnote{Inertial Measurement Unit: dispositivo elettronico utilizzato per misurare accelerazione, orientazione, velocità angolari ed altre forze gravitazionali. E' costituito da 3 accelerometri, 3 giroscopi e, a seconda delle esigenze di acquisizione, da 3 magnetometri. Possiede un componente per ognuno dei 3 assi del veicolo.}. Quest'ultima è sta fissata specularmente allo smartphone sul finestrino del passeggero. Al termine di ogni prova i dati sono stati esportati, elaborati e confrontati su \textit{MatLab}. Per ogni test effettuato sarà mostrato il percorso visto da Google Maps e il valore delle accelerazioni.
        %%%%%%%%
        \subsection{Prova 4: Percorso Urbano}
        
            La quarta prova è stata effettuata su un percorso che circumnaviga il Politecnico di Torino a bordo di una Hyundai Veloster. La prova, nonostante il percorso non sia molto lungo, dura circa quattro minuti per via dei semafori. 
            
            \begin{figure}[H]
                \centering
                \includegraphics[width=0.65\linewidth]{Pictures/Cap_6/PercorsoUrbano/30_09_21_1306_percorso.jpeg}
                \caption{Tragitto della quarta prova fatta in città.}\label{fig:30_09_21_1306_percorso}
            \end{figure}
            
            Tuttavia, questa prima prova su strada ha permesso di testare il funzionamento e l'affidabilità dei dispositivi e di ottenere i primi confronti tra i dati.
            
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoUrbano/30_09_21_1306_v3.png}
                \caption{Confronto delle accelerazioni tra IMU e Applicazione.}\label{fig:30_09_21_1306_v3}
            \end{figure}
            
            \noindent
            Un primo confronto, tra le accelerazioni dei 2 sistemi, è stato necessario per allineare i rispettivi assi. In particolare l'\textbf{IMU} presenta gli assi x ed y invertiti e l'asse y negativo rispetto a quello dell'applicazione.
            
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoUrbano/30_09_21_1306_confAccX.png}
                \caption{Confronto con sovrapposizione dell'accelerazione X tra IMU e Applicazione.}\label{fig:30_09_21_1306_confAccX}
            \end{figure}
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoUrbano/30_09_21_1306_confAccY.png}
                \caption{Confronto con sovrapposizione dell'accelerazione Y tra IMU e Applicazione.}\label{fig:30_09_21_1306_confAccY}
            \end{figure}
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoUrbano/30_09_21_1306_confAccZ_corretto.png}
                \caption{Confronto con sovrapposizione dell'accelerazione Z tra IMU e Applicazione.}\label{fig:30_09_21_1306_confAccZ}
            \end{figure}
            
            \noindent
            Successivamente, ogni componente dell'accelerazione dei due sistemi è stata sovrapposta. I risultati ottenuti sono abbastanza fedeli soprattutto per quanto riguarda la componente longitudinale x dell'accelerazioni. Anche le altre risultano allineate ma con valori leggermente diversi a causa del rumore creato dalle oscillazioni dello smartphone attaccato al supporto.
            
            \newpage
            
        %%%%%%%%
        \subsection{Prova 5: Percorso su sentiero collinare}
            
            La quinta prova è stata fatta nella zona collinare di Torino su un percorso ricco di tornanti che costeggia il Parco di San Vito. 
            
            \begin{figure}[H]
                \begin{subfigure}{0.5\textwidth}
                    \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/percorsoCollinare.jpeg} \caption{Percorso totale.}\label{fig:percorsoCollinare}
                \end{subfigure}
                \begin{subfigure}{0.5\textwidth}
                    \hfill
                    \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/percorsoCollinareZoom.jpeg}
                    \caption{Zoom sulla prima parte del percorso collinare}\label{fig:percorsoCollinareZoom}
                \end{subfigure}
            \caption{Raffigurazioni del percorso della prova su sentiero collinare.}\label{fig:RaffigurazioneSentieroCollinare}
            \end{figure}
            
            \noindent
            Grazie a questo percorso è stato possibile ottenere dei dati durante una guida più sportiva con una velocità sostenuta e priva dei rallentamenti tipici del percorso urbano.
            
            % \begin{figure}[H]
            %     \centering
            %     \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/30_09_21_1622_v3.png}
            %     \caption{Confronto delle accelerazioni tra IMU e Applicazione su sentiero collinare.}\label{fig:30_09_21_1622_v3}
            % \end{figure}
            
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/30_09_21_1622_accX.png}
                \caption{Confronto con sovrapposizione dell'accelerazione X tra IMU e Applicazione su sentiero collinare.}\label{fig:30_09_21_1622_accX}
            \end{figure}
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/30_09_21_1622_accY.png}
                \caption{Confronto con sovrapposizione dell'accelerazione Y tra IMU e Applicazione su sentiero collinare.}\label{fig:30_09_21_1622_accY}
            \end{figure}
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/30_09_21_1622_accZ.png}
                \caption{Confronto con sovrapposizione dell'accelerazione Z tra IMU e Applicazione su sentiero collinare.}\label{fig:30_09_21_1622_accZ}
            \end{figure}
            %perdita segnale gps per batteria scarica
            %piccolo rumore angolo vol. corretto con calib.
            
            Anche in questa prova, sovrapponendo tutte le componenti dell'accelerazione dei due sistemi, si ottengono ottimi risultati sia per i valori che per l'allineamento sull'asse dei tempi. Nonostante le componenti Z risultino diverse in alcuni istanti, i calcoli complessivi risultano essere molto promettenti se si pensa all'aver utilizzato uno stile di guida sportivo in un percorso caratterizzato da stretti tornanti con pendenza variabile.
            
            
            \begin{figure}[H]
                \centering
                \includegraphics[width=1\linewidth]{Pictures/Cap_6/PercorsoSentieroCollinare/30_09_21_1622_Steering.png}
                \caption{Andamento dell'Angolo volante $\theta$ durante la prova su percorso collinare.}\label{fig:30_09_21_1622_Steering}
            \end{figure}
            
            Avendo verificato l'affidabilità del calcolo dell'angolo volante $\theta$ tramite le prove simulate (\ref{ProveSimulate}), viene infine mostrato l'andamento dell'angolo volante durante la prova su percorso collinare. Dal grafico non si notano irregolarità se non una perdita dell'angolo, in pochissimi istanti, che viene recuperata senza ritardi: queste singolarità sono state registrate solo durante alcune sterzate brusche e sono comunque state corrette in post filtrando i dati con \textit{MatLab}.
            
%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Conclusioni}\label{Conclusioni}   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    Il lavoro di tesi svolto mi ha permesso di attenzionare gli aspetti principali relativi alla dinamica del veicolo per capire come raggiungere gli obiettivi prefissati relativi all'upgrade dell'applicazione. Le implementazioni apportate mi hanno permesso di studiare e comprendere bene sia concetti legati all'interazione con la sensoristica dello smartphone, sia argomenti riguardanti la computer vision ed in particolare la realtà aumentata nel mondo mobile.\\
    Le prove simulate tramite CarMaker e su strada hanno dato risultati soddisfacenti: confrontando i risultati prodotti dall'applicazione con CarMaker e la piattaforma inerziale (IMU), si è potuto constatare come tutte le modifiche implementate abbiano portato alla soluzione del problema di sincronizzazione dell'angolo volante $\theta$ garantendo dei valori affidabili.
    
    \section{Miglioramenti Futuri}
    
        Attualmente i sensori dello smartphone acquisiscono dati con frequenze variabili a causa della mancanza di un clock unico. Per risolvere questo problema ed elaborare tutti i dati avendo un unico asse dei tempi, si potrebbe implementare un algoritmo che vada, in post, ad interpolare tutti i dati prodotti dallo smartphone. Per quanto riguarda la gestione dei database si potrebbe implementare la totale gestione del database tramite \textit{ROOM} ovvero una libreria che conferisce un design pattern modulare e meno \textit{verbose}. Si potrebbe anche sperimentare una sostituzione dei Markers con \textit{Tiny YOLO} ovvero una \textit{Convolutional Neural Network} utilizzata per il riconoscimento di oggetti e rotazione istante per istante. Per ottenere un ulteriore incremento della precisione di calcolo dell'angolo volante, potrebbe essere implementata una calibrazione automatica della board con i Markers. Tramite una stampante 3D si potrebbe progettare e costruire un supporto ad-hoc per garantire una veloce e semplice installazione dei markers sul volante.
        

\printbibliography

\end{document}

