Capstone Project, Final Sprint - MatteoMurcia/ARProject GitHub Wiki

Capstone Project, Final Sprint

Carol Moreno , Laura Parada, Laura Garcia, Matteo Murcia, Felipe Gutierrez

Introducción

El siguiente documento contiene la toda información y los pasos realizados durante el desarrollo del capstone project, este consiste en elaborar un sistema de priorización de pacientes en pisos de hospitalización de cardiología basado en el paradigma de Computación en el borde. Este proyecto tiene 3 pilares, estos son EoT (Edge of Things), AI (Inteligencia artificial) y AR (Realidad aumentada), estos fueron desarrollados de manera paralela, para luego realizar la unificación de todos los módulos del proyecto.

Para eso el módulo de EoT leyó un SG y datos de pulsación, estos datos fueron enviados a el módulo de AI el cual los procesa y devuelve estos datos procesados a EoT con una enfermedad y una probabilidad, después de esto EoT envía al módulo de AR los datos de probabilidad, enfermedad y pulsos por minuto, el módulo de AR recibe un json con la información, la procesa y la muestra en el diseño 3D el cual es desplegado sobre un target específico según el paciente.

Diagrama de Bloques General

Diagrama de bloques general

Este diagrama describe el proceso de funcionamiento del proyecto. Inicialmente el módulo ECG AD8232 (sensor) toma la información del ESG la cual, se procesa en el arduino y se envía a la raspberry, la raspberry le envía al bloque de Inteligencia Artificial para que puedan predecir la posibles enfermedades, el bloque de inteligencia artificial devuelve un JSON con la enfermedad y la probabilidad de que el paciente la sufra, en la raspberry se complementa la información con los pulsos por minuto de la persona y se envía un Json con los 3 datos a el bloque de Realidad Aumentada el cual al recibirlos los muestra en un modelo 3D, todos los Json se envían a través de un broker MQTT en la nube.

Diagrama de Bloques

Este diagrama se realizó para el módulo de AR , para esto se describió el proceso de funcionamiento del módulo desde el momento en el que se recibe el mensaje a través de un MQTT, como se maneja el MQTT a través de un client suscription el cual se programó utilizando el lenguaje C#, después de esto pasa a un script, en este script se toman los datos recibidos y se ubican en un texto 3D, después, el texto 3D es reflejado en el modelo 3D el cual se despliega sobre un target, después de relacionado lo anterior, se descarga vuforia y se corre el APK sobre el celular el cual utiliza su cámara y su pantalla para mostrar el modelo 3D sobre el target físico. Se puede observar ya en el celular 5 datos que cambian según el estado del paciente y según cada paciente, en este caso , estos son: Enfermedad, Probabilidad, pulso y dos datos históricos del paciente.

Criterios de diseño

Para el desarrollo de la solución particular se definieron los siguientes criterios de diseño:

  • Para la elaboración del modelo 3D y su implementación en realidad aumentada se utilizó Unity 3D ya que este facilita la elaboración de estos y cuenta con las librerías y dependencias necesarias para crear una aplicación de realidad aumentada, estas son Vuforia.
  • Para la elaboración de los target se implementó Photoshop ya que permite realizar de forma fácil una imagen adecuada para la proyección del modelo 3D.
  • Para la recepción de datos se elaboró un script para suscribir y publicar los datos enviados mediante MQTT.
  • Para mostrar los datos en forma de texto se implementó un script que recibe que los valores y los muestra en un GameObject text en Unity 3D, se implementan topics diferentes para cada paciente.

Diagrama de Flujo

Se dividió el diagrama de flujo en tres partes: Raspberry, broker y unity3D. Cómo primera instancia, se tuvo en cuenta los datos que la raspberry recibe de un pre-procesamiento con inteligencia artificial en el control de la enfermedad, es decir, probabilidad, pulsos y enfermedad relacionada. Dichos datos deben ser procesados en la misma y posteriormente enviados al broker HiveMQ. Para establecer la conexión entre el sensor y el broker, la raspberry debe enviar una publicación con los datos (en formato json) obtenidos por medio del protocolo TCP/IP por el puerto 1883. Si la conexión es aceptada, se procede a guardar cada dato en tópicos, sino, debe nuevamente enviar la solicitud de conexión.

Cuando la conexión a sido establecida con el broker, es necesario vincular o suscribir el dispositivo que se va a implementar para la visualización de los datos, en esta ocasión se vincula Unity 3D. En esta herramienta, se ejecutan las funciones establecidas en el script: suscripción, publicación y un método para enviar y actualizar los datos a la vista de unity3D.

Documentación de código generado

Paciente #1

 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using uPLibrary.Networking.M2Mqtt.Utility;
using uPLibrary.Networking.M2Mqtt.Exceptions;
using uPLibrary.Networking.M2Mqtt.Internal;
using Newtonsoft.Json;


public class NewBehaviourScript : MonoBehaviour
{
    private MqttClient client;
    public string brokerHostname = "broker.mqttdashboard.com";
    public int brokerPort = 1883;
    static string subTopic = "testtopic/AR/1";
    private string msg = "Pulso: 75";
    private string enferm = "Enfermedad: sano";
    private string proba = "Probabilidad: 50";
    private string anterior1 = "NA";
    private string anterior2 = "NA";
    TextMesh enf;
    TextMesh prob;
    TextMesh text;
    TextMesh Ant1;
    TextMesh Ant2;
    // Start is called before the first frame update
    void Start()
    {
        if (brokerHostname != null )
        {
            Debug.Log("connecting to " + brokerHostname + ":" + brokerPort);
            Connect();
            client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
            byte[] qosLevels = { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE };
            client.Subscribe(new string[] {subTopic}, qosLevels);
        }
        enf = GameObject.Find("Enf")
                         .GetComponent("TextMesh") as TextMesh;
        prob = GameObject.Find("Prob")
                         .GetComponent("TextMesh") as TextMesh;
        text = GameObject.Find("Text")
                         .GetComponent("TextMesh") as TextMesh;
        Ant1 = GameObject.Find("Ant1")
                         .GetComponent("TextMesh") as TextMesh;
        Ant2 = GameObject.Find("Ant2")
                         .GetComponent("TextMesh") as TextMesh;
    }
    
private void Connect()
    {
        Debug.Log("about to connect on '" + brokerHostname + "'");
        client = new MqttClient(brokerHostname);
        string clientId = System.Guid.NewGuid().ToString();
        try
        {
            client.Connect(clientId);
        }
        catch (Exception e)
        {
            Debug.LogError("Connection error: " + e);
        }
    }
    
void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
    {
        string json = System.Text.Encoding.UTF8.GetString(e.Message);
        Debug.Log("Received message from " + e.Topic + " : " + json);

        List info = JsonConvert.DeserializeObject>(json);


        msg = "Pulso: "+info[0].mensage;
        enferm = "Enfermedad: "+info[0].enfermedad;
        proba = "Probabilidad: "+info[0].probabilidad;
        anterior1 = "P:"+info[1].mensage+" E: "+info[1].enfermedad+" Pr: "+info[1].probabilidad;
        anterior2 = "P:"+info[2].mensage+" E: "+info[2].enfermedad + " Pr: "+info[2].probabilidad;
    }



    // Update is called once per frame
    void Update()
    {
        Debug.Log("update");

        text.text = msg;
        enf.text = enferm;
        prob.text = proba;
        Ant1.text = anterior1;
        Ant2.text = anterior2;
    }
}

public class Info
{
    public string enfermedad;
    public string probabilidad;
    public string mensage;
}

public static class JsonHelper
{
    public static T[] FromJson(string json)
    {
        Wrapper wrapper = JsonUtility.FromJson>(json);
        return wrapper.Items;
    }

    public static string ToJson(T[] array)
    {
        Wrapper wrapper = new Wrapper();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper);
    }

    public static string ToJson(T[] array, bool prettyPrint)
    {
        Wrapper wrapper = new Wrapper();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper, prettyPrint);
    }

    [Serializable]
    private class Wrapper
    {
        public T[] Items;
    }
}

Paciente #2

 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using uPLibrary.Networking.M2Mqtt.Utility;
using uPLibrary.Networking.M2Mqtt.Exceptions;
using uPLibrary.Networking.M2Mqtt.Internal;
using Newtonsoft.Json;


public class NewBehaviourScript : MonoBehaviour
{
    private MqttClient client;
    public string brokerHostname = "broker.mqttdashboard.com";
    public int brokerPort = 1883;
    static string subTopic = "testtopic/AR/1";
    private string msg = "Pulso: 75";
    private string enferm = "Enfermedad: sano";
    private string proba = "Probabilidad: 50";
    private string anterior1 = "NA";
    private string anterior2 = "NA";
    TextMesh enf;
    TextMesh prob;
    TextMesh text;
    TextMesh Ant1;
    TextMesh Ant2;
    // Start is called before the first frame update
    void Start()
    {
        if (brokerHostname != null )
        {
            Debug.Log("connecting to " + brokerHostname + ":" + brokerPort);
            Connect();
            client.MqttMsgPublishReceived += client_MqttMsgPublishReceived;
            byte[] qosLevels = { MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE };
            client.Subscribe(new string[] {subTopic}, qosLevels);
        }
        enf = GameObject.Find("Enf")
                         .GetComponent("TextMesh") as TextMesh;
        prob = GameObject.Find("Prob")
                         .GetComponent("TextMesh") as TextMesh;
        text = GameObject.Find("Text")
                         .GetComponent("TextMesh") as TextMesh;
        Ant1 = GameObject.Find("Ant1")
                         .GetComponent("TextMesh") as TextMesh;
        Ant2 = GameObject.Find("Ant2")
                         .GetComponent("TextMesh") as TextMesh;
    }
    
private void Connect()
    {
        Debug.Log("about to connect on '" + brokerHostname + "'");
        client = new MqttClient(brokerHostname);
        string clientId = System.Guid.NewGuid().ToString();
        try
        {
            client.Connect(clientId);
        }
        catch (Exception e)
        {
            Debug.LogError("Connection error: " + e);
        }
    }
    
void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
    {
        string json = System.Text.Encoding.UTF8.GetString(e.Message);
        Debug.Log("Received message from " + e.Topic + " : " + json);

        List info = JsonConvert.DeserializeObject>(json);


        msg = "Pulso: "+info[0].mensage;
        enferm = "Enfermedad: "+info[0].enfermedad;
        proba = "Probabilidad: "+info[0].probabilidad;
        anterior1 = "P:"+info[1].mensage+" E: "+info[1].enfermedad+" Pr: "+info[1].probabilidad;
        anterior2 = "P:"+info[2].mensage+" E: "+info[2].enfermedad + " Pr: "+info[2].probabilidad;
    }



    // Update is called once per frame
    void Update()
    {
        Debug.Log("update");

        text.text = msg;
        enf.text = enferm;
        prob.text = proba;
        Ant1.text = anterior1;
        Ant2.text = anterior2;
    }
}

public class Info
{
    public string enfermedad;
    public string probabilidad;
    public string mensage;
}

public static class JsonHelper
{
    public static T[] FromJson(string json)
    {
        Wrapper wrapper = JsonUtility.FromJson>(json);
        return wrapper.Items;
    }

    public static string ToJson(T[] array)
    {
        Wrapper wrapper = new Wrapper();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper);
    }

    public static string ToJson(T[] array, bool prettyPrint)
    {
        Wrapper wrapper = new Wrapper();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper, prettyPrint);
    }

    [Serializable]
    private class Wrapper
    {
        public T[] Items;
    }
}

Cuando se ejecuta la aplicación, éste se conecta al broker del MQTT utilizando la función connect(), luego se subscribe al tópico y se buscan los 5 TextMesh donde se mostrará la información entrante, en seguida, utilizando la función client_MqttMsgPublishReceived leemos el mensaje recibido en la suscripción del MQTT, el cual está en formato json, se lee el json utilizando JsonUtility y la clase Info, despues los 5 campos se guardan en 5 variables (mensaje, enfermedad, probabilidad, valor histórico 1, valor histórico 2) las cuales son utilizadas en la función update para cambiar la información en los 5 TextMesh los cuales se despliegan en el modelo 3D. Para la lectura de los datos que vienen en un arreglo con formato .json se implementa la libreria llamada json.net en Unity3D y con ello se logra mostrar el dicho modelo 3D los datos de probabilidad, la enfermedad asociada a la probabilidad y dos valores históricos.

Configuración experimental y resultados de validación

Se realizaron varias pruebas, iniciando con el diseño y la implementación del target montando uno aleatorio y subiéndose a una base de datos en vuforia para realizar la prueba en Unity y montar un APK de prueba que mostraba una imagen en 3D (Ilustración 1). Así se verificó el funcionamiento de un aplicación el realidad aumentada.

Ilustracion 1. Representación del modelo 3D en Unity

Posteriormente se hicieron pruebas en HiveMQ (Ilustración 2)para recibir un json y mandar la información por MQTT que posteriormente se muestra en la aplicación de realidad aumentada para ser reemplazado en el modelo 3D y mostrado en la aplicación.

Ilustracion 2. Representación de los mensajes publicados desde el Broker de MQTT

Finalmente con un teléfono móvil demostramos su funcionalidad y la manera en cómo se debe visualizar el modelo (Ilustración 3). exposición de los datos que se quieren mostrar y la posibilidad de actualizarse(Ilustración 4) y finalmente sus dimensiones (Ilustración 5).

Ilustracion 3. Representación de proyección de datos iniciales en el modelo AR para el paciente número 1

Ilustracion 4. Representación de datos actualizados en el modelo AR

Ilustracion 5. Demostración de dimensiones 3D

Se realiza el mismo procedimiento seguido para el target del paciente número 1. Se requiere de un fondo color negro (Ilustración 6) y se actualizan los datos del paciente número 2.

Ilustracion 6. Representación de proyección de datos iniciales en el modelo AR para el paciente número 2

⚠️ **GitHub.com Fallback** ⚠️