app

SLAMCam: Tracciamento della posizione tramite immagini

Laboratorio didattico di Ingegneria dell'Informazione

Studenti: Eros Reato, Leonardo Vianello, Massimiliano Biason





Protocolli di trasporto

TCP

Il protocollo TCP (Transmission Control Protocol) fa parte della "Internet Protocolo Suite" (anche definita TCP/IP suite) ed è uno dei più usati nel mondo delle comunicazioni digitali. Tramite il meccanismo degli acknowledgements, consente una trasmissione affidabile e ordinata dei dati. Altra caratteristica importante da sottolineare è il fatto che il TCP è un protocollo orientato alla connessione: prima di trasmettere effettivamente i dati è necessario stabilire una connessione tra client e server e mantenerla attiva finché la comunicazione non finisce. Mediante il protocollo TCP è possibile garantire inoltre una comunicazione full duplex e con un flusso di byte di lunghezza variabile.

UDP

Il protocollo UDP (User Datagram Protocol) è di tipo connectionless (non richiede che si stabilisca una connessione prima dell’invio dei dati) e non affidabile (non garantisce la ricezione corretta e completa dei dati e non viene eseguito nessun controllo su una possibile perdita di datagram). I pacchetti vengono quindi inviati al destinatario senza assicurarsi che esso li abbia ricevuti. Per questi motivi questo protocollo garantisce comunicazioni a bassa latenza (oltre a quanto già detto, i messaggi sono anche più corti perché non si utilizzano byte per le informazioni di controllo) e non esiste controllo di flusso (ordinamento dei datagrammi e controllo degli errori).


TCPvsUDP


Socket

I socket sono un’astrazione software per rappresentare un’interfaccia di comunicazione e si possono vedere come degli intermediari tra il livello di trasposto e il livello applicazione dello stack TCP/IP. Socket locali e remoti in comunicazione formano una coppia composta da indirizzo e porta di client e server, si instaura quindi una connessione logica tra loro.

Parte Server

Nel progetto sviluppato si è scelto quindi di utilizzare una comunicazione basata su socket UDP per il trasferimento dei frames dal client al server (nello specifico da uno smartphone Android ad un computer) perché era necessaria una comunicazione veloce, che non si interrompesse nel caso in cui venissero persi pacchetti o se si perdesse la connessione. Per il trasferimento delle stringhe contenenti le posizioni delle camere invece si è preferito utilizzare socket TCP, principalmente per essere sicuri che i dati venissero ricevuti corretti ed in ordine. Per fare ciò è stata utilizzata la libreria PraticalSocket.
Si espone ora brevemente lo schema logico del programma:

  1. Inizialmente il server rimane in attesa (fase di handshake) finchè non viene instaurata una connessione TCP con il client (di default sulla porta 9999).

       void HandleTCPClient(TCPSocket *sock) {
    	 
          cout << "Handling client ";
          try {
             cout << sock->getForeignAddress() << ":";
          } catch (SocketException e) {
             cerr << "Unable to get foreign address" << endl;
          }
          try {
             cout << sock->getForeignPort();
          } catch (SocketException e) {
             cerr << "Unable to get foreign port" << endl;
          }
          cout << endl;
       }
    
    


  2. Con la connessione del client inizia il trasferimento dei frame tramite socket UDP (di default sulla porta 8888), i quali vengono decodificati utilizzando alcune funzioni appartenenti alla libreria OpenCV.

       bool ReadFrames(cv::Mat &frame) {
    	 
          unsigned short servPort = LOCAL_PORT;
          UDPSocket sock(servPort);
    
          char buffer[BUF_LEN];                     // Buffer of received frame
          int recvMsgSize;                          // Size of received message
          string sourceAddress;                     // Address of datagram source
          unsigned short sourcePort;             	// Port of datagram source
          
          try{
             do {
                recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort);
             } while ((unsigned)recvMsgSize > sizeof(int));
          
             int total_pack = ((int* ) buffer)[0];
             char* longbuf = new char[PACK_SIZE * total_pack];
          
             for (int i = 0; i < total_pack; i++) {
                recvMsgSize = sock.recvFrom(buffer, BUF_LEN, sourceAddress, sourcePort);
                if (recvMsgSize != PACK_SIZE) {
                   continue;
                }
                memcpy( & longbuf[i * PACK_SIZE], buffer, PACK_SIZE);
             }
     
             cv::Mat rawData = cv::Mat(1, PACK_SIZE * total_pack, CV_8UC1, longbuf);
             frame = cv::imdecode(rawData, CV_LOAD_IMAGE_COLOR);
          
             if(frame.empty()){
                cerr << endl << "Failed to load image" << endl;
             }
          
             if (frame.size().width == 0) {
                cerr << "decode failure!" << endl;
                return false;
             }
          
             free(longbuf);
                 
             return true;
          }
          catch (SocketException & e) {
             cerr << e.what() << endl;
             return false;
          }   
    	
       }
    
    


  3. Dopo aver ricevuto i dati, l'immagine viene elaborata tramite l'algoritmo ORBSLAM spiegato in precedenza e viene restituito un file contentente le posizioni delle camere nello spazio.

  4. A questo punto, con l'utilizzo dei socket TCP, è quindi possibile l'invio delle stringhe al dispositivo Android, il quale utilizzerà tali dati per la ricostruzione del percorso anche nel display del terminale. Per l'invio di queste stringhe si è scelto di utilizzare un thread apposito (con l'utilizzo della libreria pthread, la libreria standard UNIX per la gestione di thread) in maniera tale che non diventasse bloccante per la ricezione dei frame.

       void *SendFile(void* arg) {
    	 
          struct thread_args *my_arg = (struct thread_args*) arg;
          char BufferString[BUF_STRING_LEN];  
          char *res;
    
          while(1) {
             res=fgets(BufferString, BUF_STRING_LEN, (*my_arg).fin);
          
             if( res==NULL)
                break;
    
             (*my_arg).TCPServer->send(BufferString, strlen(BufferString));
          }
       
          fclose((*my_arg).fin);
          wait_TCP = 0;
          pthread_exit(0);
       }