Showing posts with label #UnrealEngine. Show all posts
Showing posts with label #UnrealEngine. Show all posts

Saturday, January 26, 2019

Un Vector Interior

Cuando terminé mi blog llamado “Hey Vector, a quien me parezco?” utilizando el Vector de Anki y los API's de Machine Learning de SAP Leonardo…Comencé a pensar en que debería empezar a trabajar…primero…obviamente me estaba quedándo sin ideas…pero después…de pronto…se ocurrió una buena idea…que pasaría si pudieramos controlar a Vector…desde adentro? Es decir…que pasaría si pudieramos simular que estamos adentro de Vector y que pudieramos ver a través de sus ojos y pudieramos controlarlo…

Así como el proyecto se inició 😉


La Idea


Así que, sabía que quería poder controlar a Vector…podía utilizar Amazon Alexa, pero por supuesto…eso dejaría fuera la parte “interior”…así que no era una opción…luego pensé en utilizar Unreal Engine…puesto que ya lo había utilizado para mi blog “API's de SAP Leonardo Machine Learning en el Go” donde utilicé las APIs de Machine Learning de SAP Machine, Unreal Engine y Oculus Go para poder demostrar las APIs de SAP Leonardo. Utilizar Unreal Engine y Oculus Go sonaba como la combinación perfecta, así que comencé a trabajar en eso.


Que vamos a utilizar?


Yo se que he mencionado muchas cosas…así que vamos a obtener un poco más de información 😉

Vector

La evolución de Cozmo por parte de Anki. Vector no solo trae más potencia y más independencia pero también un micrófono, así que finalmente puedes hablarle 😉 y además viene con Amazon Alexa…así que es un impresionante robotito…

Unreal Engine

Unreal Engine es sin lugar a dudas “El motor de creación más potente”. Puede ser programado utilizándo C++ o Blueprints (Programación Visual) y lo mejor de todo…es totalmente gratuito! A menos que hagas juegos comerciales que se vendan…en ese caso ellos solo piden el 5%


Blender

Blender es un paquete de creación de 3D Open Source. Modelamiento, rigging, animación, simulación, renderizado y un largo etcetera…incluído en la version 2.8, viene EEVEE (Extra Easy Virtual Environment Engine) un motor de renderizado en tiempo real.


HANA Cloud Platform

Base de Datos In-Memory orientada a columnas de SAP que corre en la nube. Que incluye Predictive Analysis, Spatial Data Processing, Text Analysis y mucho más. Además, es extremadamente veloz 😉


Python3

Lenguaje de programación interpretado de alto nivel y multipropósito. Es el lenguaje elegido para programar a Vector.



El Primer Problema


Como quería ver a través de los ojos de Vector…necesitaba mostrar el canal de video de Vector dentro de Unreal Engine…me la pasé buen tiempo pensándo como podía hacer eso…pero al final recordé que cuando utilizar el Microsoft HoloLens y lo enlazas a tu laptop para “streaming”, siempre hay un retrazo de 1 o 2 segundos…luego por supuesto recordé que los videos son solo cientos de imágenes que se muestran en secuencia a una velocidad muy alta…no me importa mucho la velocidad o estar cerca de tiempo real…tener un retrazo de 1 o 2 segundos…no algo tan malo...para nada…

Bueno…los problemas continuaron…Sabía que quería que Vector tome un foto cada 1 o 2 segundos…y luego esa imagen debería llegar a Unreal Engine…y fuera del hecho de que programo en Python en mi Ubuntu de mi Maquina Virtual y Unreal Engine en mi laptop Windows…no me convencía mucho la idea de mandar la imagen como un archivo…así que…Se me ocurrió codificar la imagen en Base 64 (Lo cual, sí…incrementa el tamaño…pero por lo menos te brinda un extremadamete largo String el cual puedes manipular) y enviarlo…pero cómo? Bueno…SAP Cloud Platform es un Base de Datos In-Memory…así que muy rápida…por qué no crear algunos REST APIs para poder manejar la creación , visualización y eliminación de los registros?…


Cada vez que Vector toma una imagen...se convierte a Base 64 y luego se envía a la nube, luego Unreal Engine lee el API, decodifica el Base 64 de nuevo a una imagen y lo muestra…eso me llevó a un segundo problema…

El Segundo Problema


Como codifico una imagen a Base 64 en Unreal Engine? Si bien se utilizar C++ bastante bien…cuando se trata de Unreal Engine por lo general utilizo Blueprints, que es programación visual y aunque en el fondo es C++, no todo está implementado…

Afortunadamente, una rápido paseo por la documentación de Unreal me dió la respuesta en la forma de una función que codifica/decodifica en Base 64…pero en C++ por supuesto…

Pero…lo bueno de Unreal Engine es que puedes crear un proyecto en C++…implementar tu clase de codificación de Base 64 y comenzar a crear Blueprints para consumirla…otro problema resuelto…

Eso fué lo que pensé…pero luego me di cuenta de que no era solo cuestión de tener la imagen de nuevo como una imagen…En realidad necesitaba un material dinámico donde pudiera mostrar las imágenes…busqué en la web y encontré algunos artículos interesántes…pero nada que pudiera ayudarme en realidad…al final…tomé partes de aquí y allá y lo mezcle con mi propia investigación…para finalmente hacerlo funcionar…

Y sí…si le estan preguntándo…había otro problema…

El Tercer Problema


Todo estaba funcionando bien y bonito…probé mi aplicación…inicialmente pasándo algunas imágenes en sequencia a la nube y luego a Unreal y luego utilizándo Vector…con un retrazo de 1 o 2 segundos…se vería como un video corriendo en un celular antiguo con una conexion a Internet barata en medio del desierto…lo suficientemente bueno para mí 😉

Pero…se supone que estamos dentro de Vector, no? Como se suponía que podía simular eso? Luego de no pensarlo por mucho tiempo…decidí utilizar Blender 2.8 el cual está actualmente en Beta 😊 y que viene con EEVEE (Que es un renderizador en tiempo real asombrozo). Hice un pequeño cuarto de control…con algunos botones y paneles llamativos…una silla donde poder sentarse mientras se está controlándo a Vector y una pantalla para poder vez lo que Vector está mirándo….

El Baking no está funcionándo en este momento…o por lo menos soy muy torpe para poder darme cuenta de como se utiliza en 2.8, así que utlizar texturas en Cycles estaba descartado…entonces…hice una prueba en EEVEE utilizándo solo materiales simples…la exporté a .FBX y funcionó perfectamente! Así que, comencé a trabajar y a probarlo en Unreal…por supuesto…no soy un experto en Blender, así que a pesar de que todo se ve bien…no todos los colores fueron renderizados corractement ☹ At least it looks fairly decent 😉

No más problemas


Así es…no es que todo haya funcionado bien y sin errores…pero por lo menos esos fueron los errores o problemas más críticos…así que finalmente podemos empezar con el post -:P 

Blender y El Cuarto de Control


Como ya lo dije…utilicé el renderizado de EEVEE en Blender 2.8 Beta para crear un cuarto de control que pudiera, de alguna manera, dar la impresión de estar dentro de Vector…por supuesto…una versión total y completamente poética porque a) Quién sabe como se ve Vector por adentro? b) No creo que haya suficiente espacio dentro de Vector como para poder acomodar nada más.

Primero, comencé poniendo algunos botones, perillas y teclados a un panel…luego añadí una especie de radares con deslizadores…


Luego, pensé que algunos interruptores y botones de varios colores serían una interesánte adición…


Finalmente, agregué una silla…porque tenemos qu entarnos en algun lugar, no?


La pantalla es simplemente un espacio vacio…casi igual que en un cine…


Miren, muy bonito en Blender, no? Bueno…no mucho en Unreal…no feo…ciertamente no optimizado…probablemente debido al echo de que uní todo y lo exporte como un bloque grande…pero eso está bien…no pienso cambiar eso…soy flojo 😊


Ya ven…no perfecto…pero tampoco mal 😉

Aquí está el archivo .FBX

Creándo las tablas y APIS en HANA Cloud Platform


El siguiente paso…Creé dos tablas en HANA Cloud Platform…Llamé a la primera tabla “VECTOREYES” porque es la tabla que va a contener las imágenes en Base 64. Aquí está el script para crear la tabla…

CREATE COLUMN TABLE "I830502"."VECTOREYES"(
 "TIMESTAMP" LONGDATE CS_LONGDATE NOT NULL,
 "VECTOREYE" CLOB MEMORY THRESHOLD 3000,
 PRIMARY KEY (
  "TIMESTAMP"
 )
) UNLOAD PRIORITY 5 AUTO MERGE;

Para la llave primaria utilicé un TIMESTAMP basicamente porque quería que si algo pasara en terminos de conexión no hubieran errores de llave repetida…

La siguiente tabla se llamará “VECTORCOMMAND” y va a contener…los comandos que vamos a enviar a Vector…

CREATE COLUMN TABLE "I830502"."VECTORCOMMAND"(
 "NID" INTEGER CS_INT,
 "COMMAND" NVARCHAR(50),
 PRIMARY KEY (
  "NID"
 )
) UNLOAD PRIORITY 5 AUTO MERGE;

En este caso…siempre va a existir un solo comando…así que utilicé una llave primaria simple de número entero.

Con las tablas creadas, podemos generar nuestro paquete de XS Engine…y simplemente llamarlo “VectorEyes”.

Debemos crear los siguientes archivos…

.xsaccess
{
     "exposed" : true,  
                  
     "authentication" :                                            
            {
               "method": "Basic"   
            },
  
     "cache_control" : "must-revalidate", 

     "cors" :                      
            {
             "enabled" : true,
             "allowMethods": [
   "GET",
   "POST",
   "HEAD",
   "OPTIONS"
   ]
            }, 
                     
     "enable_etags" : false,

     "force_ssl" : false,
     
     "prevent_xsrf" : false
}


.xsapp


Sip…no es un error…este archivo está total y completamente vacío…


AddVectorEye.xsjs
$.response.contentType = "text/html";

var conn = $.db.getConnection();

var content = $.request.body.asString();
content = JSON.parse(content);

var st = conn.prepareStatement("INSERT INTO \"YourSchema\".\"VECTOREYES\" values(?,?)");

st.setString(1,content.timestamp);
st.setString(2,content.vectoreye);

st.execute();
conn.commit();
st.close();
conn.close();

GetAddVectorEye.xsodata
service namespace "YourSchema"{
 "YourSchema"."VECTOREYES" as "vectoreye";
}

DeleteVectorEye.xsjs
$.response.contentType = "text/html";

var conn = $.db.getConnection();

var st = conn.prepareStatement("DELETE FROM \"YourSchema\".\"VECTOREYES\"");

st.execute();
conn.commit();
st.close();
conn.close();

Con esto, podemos insertar, leer o borrar la tabla VECTOREYES. Continuemos con los archivos para la tabla VECTORCOMMAND

AddVectorCommand.xsjs
$.response.contentType = "text/html";

var nid = $.request.parameters.get("nid");
var command = $.request.parameters.get("command");

var conn = $.db.getConnection();

var st = conn.prepareStatement("INSERT INTO \"YourSchema\".\"VECTORCOMMAND\" values(?,?)");

st.setString(1,nid);
st.setString(2,command);

st.execute();
conn.commit();
st.close();
conn.close();


GetVectorCommand.xsodata
service namespace "YourSchema"{
 "YourSchema"."VECTORCOMMAND" as "vectorcommand";
}


DeleteVectorCommand.xsjs
$.response.contentType = "text/html";

var conn = $.db.getConnection();

var st = conn.prepareStatement("DELETE FROM \"YourSchema\".\"VECTORCOMMAND\"");

st.execute();
conn.commit();
st.close();
conn.close();

Eso es todo 😊 Simplemente debemos activarlos y probarlos…de hecho que Postman es la mejor herramienta para esto 😉

 Creándo nuestro proyecto en Unreal Engine


Como mencioné anteriormente...Creé un proyecto vació de C++ utilizándo Mobile/Tablet, Scalable 3D or 2D y No starter content. Utilicé Unreal Engine versión 4.21.1 y llamé al proyecto “VectorOculusGo”




Cuando el proyecto está abierto, seleccioné “File --> New C++ Class”, y escogí “Actor”.


Llamé a la clase, “ImageParser” y utilicé los siguientes códigos para “ImageParser.h” y “ImageParser.cpp”

ImageParser.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ImageParser.generated.h"

UCLASS()
class VECTOROCULUSGO_API AImageParser : public AActor
{
 GENERATED_BODY()
 
public: 
 // Sets default values for this actor's properties
 AImageParser();

protected:
 // Called when the game starts or when spawned
 virtual void BeginPlay() override;

public: 
 // Called every frame
 virtual void Tick(float DeltaTime) override;

 UFUNCTION(BlueprintCallable, Category = "ImageParser")
  void ParseImage(FString encoded, TArray &decoded);
};

Aquí estamos creándo una función que será llamada desde Blueprints. Va a recibir un String y va a retornar un arreglo de Enteros.

ImageParser.cpp
#include "ImageParser.h"
#include "Misc/Base64.h"

// Sets default values
AImageParser::AImageParser()
{
  // Set this actor to call Tick() every frame.  
        // You can turn this off to improve performance if you don't need it.
 PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AImageParser::BeginPlay()
{
 Super::BeginPlay();
 
}

// Called every frame
void AImageParser::Tick(float DeltaTime)
{
 Super::Tick(DeltaTime);

}

void AImageParser::ParseImage(FString encoded, TArray &decoded)
{
 FBase64::Decode(encoded, decoded);
}

Aquí, simplemente llamamos al método Decode de la librería Base64. Esta va a tomar el String en Base 64 y convertirlo de vuelta a una imagen.

Para poder compilarlo, solo debemos hacer click derecho en el nombre del proyecto y seleccionar “Debug --> Start new instance”.


Una vez que la compilación termino, podemos simplemente para el debugging.

Antes de continuar…debemos descargar una librería para poder manajer APIs…se llama JSONQuery y es impresionante!

Simplemente, debemos cerrar Unreal, ir a la carpeta del proyecto y crear una nueva carpeta llamada “Plugins”, luego descargamos el .zip, lo descomprimos dentro de la carpeta “Plugins” y borramos las carpetas Binaries and Intermediate.

Luego, deberemos cambiar el código fuente un poco…

Dentro de la carpeta “JSONQuery”, debemos ir a “Source --> JSONQuery --> Classes --> JsonFieldData.h” y buscar “GetRequest”.

Luego de const FString& url debemos agregar const FString& auth

Luego debemos abrir “Source --> JSONQuery --> Private --> jsonfielddata.cpp” y volver a buscar “GetRequest”.

Aquí, agregamos el mismo const FString& auth.

Luego de HttpRequest->SetURL(CreateURL(url)); debemos agregar lo siguiente…

HttpRequest->SetHeader(TEXT("Authorization"), auth);

Guardamos ambos archivos y abrimos nuestro proyecto. Vamos a recibir un mensaje diciendo que parte de nuestro código necesita ser recompilado. Así que simplemente acepta y espera un poco hasta que todo haya sido compilado 😊

Para revisar que todo está bien, debemos ir a “Settings --> Plugins” e ir hasta el final hasta encontrar “Project --> Web” y JSON Query debería estar seleccionado. 😉




Excelente, ahora podemos continuar.

Para poder hacer que nuestro proyecto funciones con el Oculus Go, debemos configurar un par de cosas.

Configurándo el Oculus Go


Tu debes querer configurar tu Oculus si es que no lo haz hecho todavía 😊 Aquí le dejo un enlace muy interesánte con toda la explicación necesaria …

Configurándo Unreal para Oculus Go


Necesitamos instalar “Code Works for Android” que en realidad está incluído dentro de la instalación de Unreal. Así que, vamos a “Program Files --> Epic Games --> UE_4.21 --> Engine --> Extras --> AndroidWorks --> Win64” y ejecutamos “CodeWorksforAndroid-1R7u1-windows.exe”.

Nos daremos cuenta de que estamos dentro de la carpeta C++ Classes, así que haz click en el ícono de carpeta que está al lado y selecciona “Content”.



No prestemos atención a las carpetas por el momento.

Primero, grabemos el mapa por defecto y lo llamaremos “MainMap”. Luego vamos a “Edit --> Project Settings”. Busquemos “Maps & Modes” y seleccionemos “MainMap” en ambos “Editor Startup Map” y “Game Default Map”.


Luego debemos bajar y seleccionar “Engine --> Input”. En la sección “Mobile” debemos establecer la Default Touch Interface como None.


Nos movemos más abajo hasta “Platforms” y seleccionamos “Android”. Debemos hacer click en “Configure Now”. Luego nos movemos a “Android”. Y establecemos el minimum y target SDK version a “19”.

También debemos hacer click en “Enable Fullscreen Immersive on KitKat and above devices” para activarlo.

Buscamos “Configure the AndroidManifest for Deployment to Oculus” y lo activamos también.

Ahora, debemos hacer click en “Android SDK” y revisar la configuración. Si no tenemos las Variables del Sistema configuradas, entonces simplemente asignamos las rutas de las carpetas.

Finalmente, nos vamos a “Engine --> Rendering” y nos asegurámos de que “Mobile HDR” no esté seleccionado.

Si algo no está claro, simplemente vayan a este enlace 😉 

Listo, ahora finalmente podemos continuar 😊

Creándo un Material Dinámico


Hacemos click en “Add New --> Material” y lo llamamos “Dynamic_Mat”. Una vez dentro del editor de materiales, hacemos click derecho en un espacio vacio y buscamos “TextureSampleParameter2D”.




Una vez creado, lo llamaremos “Texture_Sample”. Viene con una textura por defecto que puedes cambiar si así lo quieres (Pero al final no importa si la cambias o no). Simplemente conecta el primer output al “Base Color” del nodo de “Dynamic_Mat”.


Grabalo y será aplicado automáticamente. Lo mejor de esta configuraciín es que Param2D es dinámico 😉 

Creándo nuestro primer Blueprint


Debemos crear una nueva carpeta llamada “Blueprints”. Aquí vamos a crear la pantalla donde las imágenes que vienen desde Vector van a ser mostradas.

Presionamos “Add New --> Blueprint Class”.


En vez de escoger “Actor” como la clase padre…vamos hacia abajo a “All Classes” y buscamos “Image Parser” y lo seleccionamos como clase padre.


Lo llamamos “ImageRenderer”.

Una vez creada, nos vamos a la pestaña Viewport y presionamos “Add Component --> Cube”. Simplemente le cambiamos la escala a “0.01, 1.0, 1.0”.



Luego nos vamos a la pestaña “Event Graph”. Aquí es donde vamos a construir nuestros Blueprints.

Pero primero, debemos crear un par de variables.

CubeMaterial --> Material Instance (Object Reference)


Este va a ser el material del cubo que hemos creado.

TempImg --> Texture 2D (Object Reference)


Aquí es donde vamos a almacenar la imagen luego de haber convertido de Base 64.

TempMat --> Material Instance (Object Reference)

Este es el material dinámico que vamos a asignar a nuestro cubo.

ImageJSON --> String

Este es el resultado de llamar al API…el String en Base 64.

Con las variables preparadas, podemos empezar creándo la primera parte del Blueprint.


Aquí, estamos diciendo que una vez que nuestra aplicación inicie (Event BeginPlay) vamos a llamar a una función llamada “Set Timer by Function Name”. Esta función va a llamar a otra función cada 2.0 segundos (puesto que hemos marcado Looping value). La función que llamaremos será “MyEvent”.



Aquí, estamos llamándo a la función “MyEvent”, la cual llamará a “Get JSON Request” pasándole el URL y el Auth. Esto estará enlazado al evento “OnGetResult”. El resultado de la llamada al JSON será extraída utilizándo Get Object Field, Get Object Array Field, un For Loop y finalmente un Get String Field para poder obtener la imagen en Base 64 y guardarla en la variable ImageJSON.


Luego de establecer la variable ImageJSON, llamamos a la API para borrar la tabla. Luego de esto…las cosas se ponen interesántes…


Aquí estamos llamándo a nuestra Clase en C++ “Parse Image” como si fuera otro elemento de Blueprints. Obtenemos el valor almacenado en ImageJSON para que sea decodificado como imagen. El resultado de decodificar en la cadena en Base 64 va a ir hacia “Import Buffer as Texture 2D”, el cual va a ir a la variable TempImg. Luego de esto “Create Dynamic Material Instance” va a aplicar un material dinámico a nuestro material Dynamic_Mat y asignarlo a TempMat que va a ser pásado como el destino de Set Texture Parameter Value, mientras que TempImg va a ser pasado como el valor de parámetro. Finalmente, un nodo Set Material va a establecer el material TempMat al cubo.

Para hacerlo más simple…tomamos la cadena en Base 64…la convertimos en imagen…creámos un material dinámico, lo utilizamos como valor para nuestro material dinámico y finalmente lo asignamos a nuestro cubo. Cada vez que obtengamos un nuevo valor en Base 64, obtendremos una nueva imagen y nuestro cubo podrá mostrárlo 😉

Importándo nuestro Modelo de Blender


Ahora, necesitamos simular que estamos dentro Vector…por lo tanto…debemos importar nuestro de Blender en formato .FBX 😉

Simplemente presionamos Import y seleccionamos el archivo .FBX  que pueden obtener en este enlace. Presionamos Import All y lo tendremos en pantalla.

Cambiemos los siguientes parámetros…


Ahora, agregamos un Point Light con los siguientes parámetros…


Luego, selecionamos el Blueprint “ImageRenderer” y lo arrastramos a la pantalla. Debemos cambiar los parámetros así…


Luego, presionamos “Build” y esperamos a que todo (incluídas las luces) sean construídas.


Una vez que termina…tendremos esto…


Impresionante! Todo está comenzándo a tomar forma 😊 Pero ahora…debemos añadir el verdadero soporte para Oculus Go 😉

Añadiéndo soporte para Oculus Go


Así que, configuramos nuestro proyecto para que trabaje en el Oculus Go…pero eso no es suficiente 😉 Debemos hacer un par de cosas extras…y por supuesto…y más importánte…debemos añadir una forma de controlar a Vector utilizándo el control del Oculus Go 😊

Creamos una nueva carpeta y la llamamos “Modes”. Luego creamos una nueve “Blueprint Class” pero esta vez escogemos “Pawn” y lo llamamos “Pawn Blueprint” (Ingenioso, no?).

Cuando carga, debemos ir a la sección de la izquierda y seleccionar “DefaultSceneRoot”, luego hacer click en “Add Component” y seleccionar “Scene” y cambiarle el nombre a “VRCameraRoot”.

Seleccionamos “VRCameraRoot” y agregamos un componente de “Camera”, y lo llamamos “VRCamera”.

Seleccionamos “VRCameraRoot” y agregamos un componente "Motion Controller", y lo llamamos “OculusGoController”.

Seleccionamos “OculusGoController” y agregamos un componente "Static Mesh", y lo llamamos “OculusGoMesh”.

Para hacer las cosas claras…aquí les dejo una imagen 😊


Con el “OculusGoMesh” seleccionado, vamos a sus propiedades y en la Static Mesh, escogemos el mesh “OculusGoController”.


Luego de esto, debemos crear algunas variables…la primera se llamará “CameraHeight” y será un “Vector” editable.



La segunda será llamada “request” y será un Json Field Data (Object Reference).

Finalmente, creamos una llamada “Lift” de tipo Boolean y una variable String llamada “Var”.



Si están preguntándo por el ojo al lado de “CameraHeight”, eso simplemente significa que es “Pública”, y puedes cambiarlo simplemente haciendo click en el ícono.

Ahora, podemos continuar con la pestaña “Event Graph”.


Aquí, queremos que  cuando la aplicación empiece (Evento BeginPlay) el tracking origin se posicione al nivel de nuestros ojos. El nodo SetRelativeLocation será llamado donde el destino será VRCameraRoot y la nueva ubicación será asignada en CameraHeight. En otras palabras, lo que vamos a ver va a estar al nivel de nuestros ojos.



Cuando presionamos el Thumbstick Arriba o Adelánte, Izquierda o Derecha, asignamos el resultado a nuestra variable Var, luego llamamos a la función Get JSON Request. El URL va a ser la direeción del API más el valor de la variable Var.


Aquí, queremos presionar el boton “Back” del Control del Oculus. La primera vez que lo presionamos, la variable  “Lift” va a ser "Falsa", así que la hacemos “Verdadera”. Si es “Verdadera” entonces enviamos el comando “up”. Si presionamos nuevamente, lo volvemos “Falsa” y pasamos el comando “down”. De esta manera podemos controlar como Vector mueve su brazo.

Listo, compilamos, salvamos y listo 😊 Simplemente debemos agregarlo a nuestra escena. Así que, arrástrenlo y cambiemos estos parámetros.


Además, esto es muy importánte…


Auto Possess Player debería de ser Player 0.

Ahora, presionamos “Build” y esperámos a que todo (incluídas las luces) se compilen.

Luego presionamos Play…y veremos esto…



Por supuesto, si tratan de mover su mouse…nada va a pasar…así que necesitamos enviarlo al Oculus Go 😉

Para hacer esto, simplemente anda a Launch y selecciona tu dispositivo…va a tomar mucho tiempo la primera vez porque todos los shaders, Blueprints y demás, necesitan ser compilados…pero luego de eso, podrás ponerte tu headset y dar un vistazo 😊 Aunque, sin embargo…no va a poder ver nada en la pantalla porque aún necesitamos tener a Vector listo y funcionándo 😉

Instalándo el SDK de Vector


Primero, debemos asegurárnos de que Vector está conectado a Internet utilizándo la aplicación de Vector en nuestro celular…aquí hay un video muy interesánte de como hacerlo…

Una vez que lo haz revisado, corta la aplicación en tu celular…puesto que podría interferir con tu aplicación, la cual va a solicitar el control de Vector…

Puedes instalar el SDK escribiéndo lo siguiente

python3 -m pip install --user anki_vector

Luego…auntenticar tu Vector escribiéndo…

python3 -m anki_vector.configure

Se te pedirá el nombre de Vector’s, su dirección IP y número de serie. Además, se te pedirán tus credenciales del Anki Cloud.

Para obtener esta información, simplemente coloca a Vector en su cargador…y presiona su cabeza dos veces. Esto te dará su nombre, luego sube y baja su brazo para poder obtener el IP. El número de serie está en la parte de abajo de Vector’s.

Creándo el Script de Vector


Este script es la última parte de nuestra aventura 😊 Simplemente debemos crear un nuevo archivo llamado VectorOculusGo.py

VectorOculusGo.py
import anki_vector  #Control Vector
import requests  #Use REST APIs
import json  #Consume JSON
import time  #Manage time
from anki_vector.util import degrees, distance_mm, speed_mmps
import base64 #Encode/Decode images
import datetime  #To get time and data

#URLs to manage upload of Base 64 images and to control Vector using the 
#Oculus Go controller
urlAddEye = "https://YourHANA.ondemand.com/VectorEyes/AddVectorEye.xsjs"
urlGetCommand = "https:// YourHANA.ondemand.com/VectorEyes/
                 GetVectorCommand.xsodata/vectorcommand"
urlDeleteCommand = "https:// YourHANA.ondemand.com/VectorEyes/
                    DeleteVectorCommand.xsjs"
   
def main():
    #We stablish a connection with Vector and enable his camara
    robot = anki_vector.Robot(enable_camera_feed=True)
    #We connect to Vector
    robot.connect()
    i = 0
    #We want this to loop forever…until we close the program
    while i == 0:
        #We instruct Vector to take a pictures
        image = robot.camera.latest_image
        #And save it
        image.save("./img/Temp.png")
        #Once saved, we open it
        with open("./img/Temp.png", "rb") as imageFile:
            #We get the time and create a timestamp
            ts = time.time()
            timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
            #We enconde the picture as an Base 64 string
            strImg = base64.b64encode(imageFile.read())
            #The payload is the parameters that we are sending to the REST API 
            payload = "{\"timestamp\":\"" + timestamp + "\",\"vectoreye\":\"" + 
                        strImg.decode('ascii') + "\"}"  

            #In the headers, we pass the authentication for the REST API        
            headers = {
                'Content-Type': "application/x-www-form-urlencoded",
                'Authorization': "YourAuthorization",
            }

            #We upload the Base 64 string of the image to the DB
            response = requests.request("POST", urlAddEye, data=payload, headers=headers)
            #We put the application to sleep for 2 seconds just not to overload the DB
            time.sleep(2)
            querystring = {"$format":"json"}
            #Right after uploading the Base 64 string, 
            #we want to get any commands coming through
            response = requests.request("GET", urlGetCommand, headers=headers, 
                                        params=querystring)
            #We convert the response to JSON
            json_response = json.loads(response.text)
            #We need to check if there’s any information first and then extract the command
            try:
                json_text = json_response['d']['results'][0]['COMMAND']
            except:
                json_text = ""
            #Depending on the command, we make Vector move forward, backward or 
            #lift his handle. If the lift was already up, we put it down first…
            if (json_text == 'forward'):
                robot.behavior.drive_straight(distance_mm(50), speed_mmps(50))
            elif (json_text == 'backward'):
                robot.behavior.drive_straight(distance_mm(-50), speed_mmps(50))
            elif(json_text == 'right'):
                robot.behavior.turn_in_place(degrees(-90))
            elif(json_text == 'left'):
                robot.behavior.turn_in_place(degrees(90))
            elif(json_text == 'up'):
                robot.behavior.set_lift_height(0.0)
                robot.behavior.set_lift_height(1.0)
            elif(json_text == 'down'):
                robot.behavior.set_lift_height(0.0)
            #After receiving the command, we simply delete it from the DB
            response = requests.request("GET", urlDeleteCommand, headers=headers)
                
if __name__ == '__main__':
    main()

Excelente, el código es bastante sencillo…pero aún así…veamos que sucede con esta aplicación…

Queremos que Vector tome una foto cada 2 segundos…una vez que la foto es tomada, queremos convertirla a una cadena de Base 64 y luego junto con un Timestamp (que es un día con horas, minutos y segundos) enviarlo a la Base de Datos. Una vez que eso está hecho…descanzamos por 2 segundos y revisamos si hay algún comando disponible. Si hay alguno, hacemos que Vector actué de acuerdo al comando…y simplemente para evitar estar repitiéndo el mismo comado…simplemente lo borramos de la Database, y así podemos tranquilamente enviar un nuevo comando.

Juntando todo


Perfecto! Ahora tenemos nuestra aplicación ejecutándose en el Oculus Go y nuestro Vector listo para ejecutar nuestro script.

Entonces…ten listo un CMD o Terminal y escribe lo siguiente…

python3 VectorOculusGo.py

Colocate tu lentes Oculus Go, agarra tu control y presiona “Enter” en tu teclado. Nuestro script comenzará a ejecutarse y verás lo que Vector está mirándo…algo como esto…


Yo lo sé…eso está funcionándo en Unreal Engine y no en el Oculus…pero para eso es el video 😉


Espero que le haya gustado este blog y disfruten controlar a Vector desde el interior! -:D

Saludos,

Blag.
SAP Labs Network.

Wednesday, September 19, 2018

API's de SAP Leonardo Machine Learning en el Go


Trabajar en el d-shop, primero en el Silicon Valley y ahora en Toronto, me permite usar mi creatividad y jugar con cualquier nuevo gadget que llega al mercado.

Esta vez, fué el turno del Oculus Go 😉 y qué es el Oculus Go? Bueno, es un headset de VR independiente, lo cual significa basicamente…no hay cables que se puedan enredar 😉

Para este proyecto tenía la oportunidad de trabajar ya sea con UnityUnreal Engine…Ya había utilizado Unity muchas veces para desarrollar aplicaciones para el Oculus Rift y Microsoft HoloLens…así que pensé que Unreal Engine sería una mejor opción esta vez…aunque nunca lo había utilizado antes para un proyecto grande…especialmente porque nada le gana a Unreal cuando se trata de gráficos…

Con Unreal selecionado…tenía que tomar otra decisión…C++ o Blueprints…bueno…aunque he utilizado C++ en el pasado para un par de aplicaciones en Cinder…Blueprints se veía mejor puesto que quería desarrollar más rápidamente y sin demasiadas complicaciones…y bueno…esa es solo la mitad de la verdad…algunas veces los Blueprints pueden convertirse en algo muy confuso 😊

Solo para que lo sepan, utilicé Unreal Engine 4.20.2 y cree una aplicación Blueprints.



Desde el inicio sabía que quería utilizar las API´s de Machine Learning de SAP Leonardo…puesto que ya las había utilizado antes para mi blog “Cozmo, leeme”  donde utilicé al robot Cozmo, OpenCV y el API de OCR de SAP Leonardo para leer una pizarra con un mensaje escrito a mano y hacer que Cozmo le leyera en voz alta.

La idea

Esta vez, quería demostrar el uso de más de una API…así que tenía que escoger cuales…afortunadamente eso no fué muy dificil…la mayoría de las APIs son más orientadas al uso “Empresarial”…Así que eso me dejaba con “Image Classification, OCR y Language Translation” …

Con todo decidido…aún debía pensar como utilizar las API’s…es decir…el Oculus Go es Realidad Virtual…así que no había la posibilidad de mirar a algo, tomarle una foto y enviarla al API…

Así que, pensé…por qué no utilizo Blender (que es una herramienta Open Source para el desarollo de gráficos generados por computadora) y hacer algunos models…luego podría hacer un render de los models…tomar una foto y enviarlas al API…y tener los model significa…que podía convertirlos a archivos “.fbx” y cargarlos a Unreal para una mejor experiencia…

Con las API's de OCR…era diferente…puesto que necesitaba images con texto…así que decidí utilizar InkScape (que es un software Open Source para editar imágenes vectoriales).

La implementación

Cuando comencé a trabajar en este proyecto…sabía que tenía que ir paso a paso…así que primero hice una version Windows de la aplicación…luego la convertí a Android (Lo cual fué muy sencillo dicho sea de paso) y finalmente una aplicación para el Oculus Go (Lo cual fué un poco doloroso…)

Así qué, lástimosamente no voy a poder poner nada de código fuente en este blog…simplemente porque utilicé Blueprints…y no estoy muy seguro que si quieran reproducirlos a mano ☹ Se darán cuenta de lo que hablo más adelante en el blog…

En fín…continuemos 😊

Cuando pensé en este proyecto, lo primero que me vino a la mente fué…quiero tener un espacio de d-shop…con algunos escritorios…un letrero para cada API…algunas luces no estarían nada mal…



Entonces, no se vé nada mal, no?

Luego, quería trabajar en el API de “Image Classification”…así que quería que fuera bastante parecido…pero con solo un escritorio en el medio…que luego se convirtío en un pedestal…con los objectos 3D rotándo encima…también debería haber un espacio listo para mostrar el resultado de llamar a la API…también…flechas para permitir al usuario cambiar el modelo 3D…y el ícono de una casa para permitir al usario regresar al “Showfloor”…




Se darán cuenta de dos cosas muy rápidamente…primero…qué se supone que esa esfera? Bueno…es solo un contenedor que será reemplazado con los objectos 3D 😊 También…pueden ver un poster negro que dice “SAP Leonardo Output”…eso está escondido y solo se vuelve visible cuando lanzamos la aplicación…

Para las escenas de “Optical Character Recognition” y “Language Translation”…es practicamente lo mismo aunque el último no tiene flechas 😊





Los Problemas

Así que asi es basicamente como las escenas están conectadas…pero por supuesto…me encontré con el primer problema muy rápidamente…como llamar a las API’s utilizándo Blueprints? Busqué online y la mayoría de los plugins son pagados…pero afortunadamente encontré uno gratuito que realmente me sorprendió…UnrealJSONQuery funciona perfectamente y no es tan complicado de utilizar…pero por supuesto…tuve que cambiar algunas cosas en el código fuente (como agregar la cabecera para la llave y cambiar los parámetros para poder subir las imágenes). Luego simplemente recompilé y voila! Tenía JSON en mi aplicación 😉

Pero quieren saber que es lo que cambié, no? Claro 😊Simplemente descomprimí el archivoy me fuí a JSONQuery --> Source --> JSONQuery --> Private y abrí JsonFieldData.cpp

Aquí agregué una nueva cabecera con (“APIKey”, “MySAPLeonardoAPIKey”) y luego busqué el PostRequestWithFile y cambié el parámetro “file” a “files”…

Para compilar el código fuente, simplemente creé una aplicación C++, luego cree una carpeta “plugins” en la carpeta de mi proyecto y puse todos los archivos del zip que descargué…abrí el proyecto…lo dejé compilar y luego recreé todo lo del proyecto anterior…una vez que hice eso…todo comenzó a funcionar perfectamente…

Entonces, veamos parte del Blueprint que utilicé para llamar al API…




Basicamente, necesitamos crear el JSON, llamar al API y después leer el resultado y extraer la información.

Todo estaba yendo de las mil maravillas…hasta que me di cuenta de que necesitaba empaquetar las images 3D generadas por Blender…y no tenía idea de como hacerlo…así que afortunadamente…el Victory Plugin vino al rescate 😉 Victory tiene algunos nodos que nos permiten leer muchos de los directorios que se encuentran dentro de una aplicación compilada…así que finalmente estaba todo listo 😊

Así es como se vé el Victory plugin cuando lo llamamos en un Blueprint…




Los Modelos

Para los models 3D como ya lo dije…utilicé Blender…los modelé utilizándo el “Cycles Render”, hice un bake a los materiales y luego hice el render de las imágenes utilizándo el “Blender Render” para poder generar los archivos .fbx…





Si las manzanas se ven como si fueran de metal o de cera…culpen a mis pobres habilidades para definir la iluminación ☹

Cuando se carga en Unreal…los modelos se ven realmente muy bien…


Ahora…estoy seguro que quieren ver como ve una pantalla completa de Blueprint…está es para los modelos 3D en la escena de Image Classification…



Complicado? Bueno...un poco…usualmente los Blueprints son así…pero son realmente muy poderosos…

Aquí hay otro…esta vez para la "Flecha a la Derecha" que nos permite cambiar los modelos…


Se ve extraño y complejo…pero funciona muy bien 😉



Se darán cuenta de que “Image Classification” y “OCR” tienen ambos flechas a la izquierda y derecha…así que tenía que reutilizar algunas variables y estas debían ser compartidas entre Blueprints…así que…para esto creé un “Game Instance” donde simplemente creé unas cuantas variables públicas que podían ser compartidas y actualizadas.

Si se preguntan para que utilicé Inkscape? Bueno…quería tener una especie de anunción en luces de neon y una imagen escrita a mano…



De Android al Oculus Go

Se preguntarán…por qué cambia de Android al Oculus Go? No están ambos basados en Android? Bueno…sí…pero aún así…gracias a mi experiencia personal…ahora se que las cosas cambian mucho…

Primero…en Android…creé las escenas…y todo estaba perfecto…en el Oculus Go…ninguna escena nueva estaba siendo cargada…cuando hacía click en alguna escena…la primera escena se cargaba a si misma… ☹ Por qué? Porque necesitaba agregarlas al array de escenas que debían ser empaquetadas…

Y lo gracioso es que la carpeta por defecto para proyecto de Unreal es“Documentss”…así que cuanod trataba de agregar las escenas, Unreal se quejaba de que la ruta era demasiado larga…así que tuve que clonar el proyecto y moverlo a una carpeta dentro de C:\

También…cuando cambié de Windows a Android…fué algo tan simple como cambiar el “Click” por “Touch”…pero para el Oculus Go…bueno…necesité crear un “Pawn”…donde puse una camara, un motion controller, y un puntero (actuándo como un puntero laser)…aquí cambie el “Touch” por un “Motion Controller Thumbstick”…y desde ahí tuve que controlar todos los aspectos de la navegación…bastante confuso…

Otra cosas que cambió completamente fué el “SAP Leonardo Output”…veamos como se veía en Android…



Aquí pueden ver que utilicé un “HUD”…así que sin importar a donde miremos…el HUD se mueve con nosotros…

En el Oculus Go…esto no funcionaba para nada…primero tuve que utilizar una imagen negra como fondo…

Luego tuve que crear un actor y luego agrgar el HUD dentro…para convertirlo en un HUD 3D…




El producto final

Cuando todo estaba listo…simplemente empaqueté mi aplicación y la cargué en el Oculus Go…y utilizándo Vysor pude grabar una simple sesión para que puedan ver como se vé en la vida real 😉 Por supuesto…el problema (Primero que nada porque…soy muy flojo como para seguir investigándo y segundo porque parece ser mucho trabajo) es que necesitas correr la aplicación desde la sección “Unknown Sources” en el Oculus Go…pero…está ahí y funciona y eso es lo único que importa 😉

Aquí esta el video para que puedan tener una mejor idea de que lo que se trata el proyecto 😊





Espero que les haya gustado 😉

Saludos,

Blag.
SAP Labs Network.