En esta página se describe un demo de tecnología usando el sistema de captura de movimiento PhaseSpace para capturar el movimiento de la mano derecha de un humano. Al final de la página encontrará un link con los archivos necesarios para correr la demo.
En este link puede encontrar un video demostrativo de la aplicación.
El objetivo de este demo, es usar el sistema de captura de movimiento PhaseSpace junto con un guante y 8 marcadores del sistema, para controlar una mano virtual. La disposición de los marcadores sobre el guante es la siguiente:
La siguiente es una imagen del guante.
Dada la disposición de los marcadores, los movimientos reflejados en la mano virtual no incluyen flexiones en los dedos. A continuación se muestra una imagen del modelo utilizado para la mano virtual.
El demo está hecho para utilizar un guante que se use en la mano derecha de una persona.
El demo usa diferentes tecnologías para cumplir su objetivo. Para la visualización y lógica se utiliza Blender, la comunicación entre el dispositivo y Blender se hace usando la versión Java de VRPN y por último se utilizan las herramientas que provee el sistema PhaseSpace para la calibración y configuración de PhaseSpace.
Primero se deben posicionar las cámaras de tal forma que se tenga una buena cobertura del espacio en donde se va a utilizar el sistema. Una vez las cámaras se encuentren en una buena posición, se debe realizar la calibración del sistema. En el siguiente video se muestra el proceso de calibración del sistema con las cámaras posicionadas de una buena forma.
http://www.youtube.com/watch?v=uWETQxn1fgQ
Una vez calibrado el sistema, se procede a configurar un controlador de LED del sistema para que se puedan utilizar los marcadores del guante. Para esto se debe crear un perfil en el sistema de configuración que provee PhaseSpace.
Se le da un nombre al perfil y el número de slots debe ser 4.
Se selecciona la opción mostrada en la imagen.
Dado que el guante tiene 8 marcadores, se especifíca la opción de 8 LEDs y se da clic en “Add this String” y luego en “Save this driver”.
Finalmente se guarda el perfil. Una vez se encuentre creado el perfil, se procede a configurar el controlador de LEDs con el perfil que se creó.
Por ultimo se codifica el controlador de LEDs con la configuración creada
Una vez se ha realizado todo el proceso, se puede comenzar la captura de movimiento. Para esto se debe usar el programa “Master” ingresando como parametro la ip del servidor PhaseSpace
Con el sistema PhaseSpace listo para usar con el guante, se debe configurar el ambiente VRPN que viene integrado con el servidor de PhaseSpace.
Se debe usar el siguiente archivo de configuración (vrpn.cfg) para el servidor VRPN.
vrpn_Tracker_PhaseSpace TrackerPhase 157.253.192.178 120.0 1 1 <owl> 0 : pt 0 1 : pt 1 2 : pt 2 3 : pt 3 4 : pt 4 5 : pt 5 6 : pt 6 7 : pt 7
Para arrancar el servidor VRPN, se debe tener “Master” abierto y conectado con toda la configuración descrita anteriormente. Posteriormente, se debe ejecutar el comando que inicia el servidor VRPN en la máquina.
$ <ruta al servidor VRPN>/vrpn_server
Una vez se ha ejecutado este comando, el servidor VRPN comienza su funcionamiento y envía la información capturada.
Para que Blender reciba la información de PhaseSpace, utilizamos la versión Java de VRPN como cliente. Se debe contar con las librerías previamente compiladas para poder conectarse de esta forma. Al final de esta página encontrará un archivo comprimido con los archivos necesarios para correr esta demo. en la carpeta vrpn, encontrará un archivo llamado java_vrpn.dll
, este archivo de debe copiar en la carpeta System32 de su equipo (para ambientes diferentes a windows se debe compilar la librería para la versión Java de vrpn). Adicionalmente encontrará un archivo llamado vrpn.jar
, este archivo debe incluirse en el classpath del ambiente java que desee utilizar. Por último encontrará 2 archivos: Cliente.java
y ThreadConexion.java
. Debe modificar el archivo Cliente.java
en la línea 45 para ingresar la dirección ip del servidor PhaseSpace.
Una vez hecho esto, puede correr el archivo Cliente.java
y si el servidor VRPN de PhaseSpace se encuentra configurado y corriendo, el programa imprimirá por consola la información de tracking capturada.
Para iniciar la demo, se debe abrir el archivo Demo.blend
que se encuentra en la carpeta Blender
. Antes de iniciar, se debe tener corriendo el programa Java descrito anteriormente junto con el sistema PhaseSpace configurado correctamente con VRPN.
Para correr el programa se debe estar en el modo “Game” dentro de Blender.
Una vez dentro de este modo, se selecciona la opción “Start Game Engine” o se presiona la tecla “P” con el mouse dentro de la vista 3D.
La función del código Java mostrado anteriormente, es conectarse con el servidor VRPN del sistema PhaseSpace para capturar la información de movimiento que arroja el sistema. Por cada punto capturado, el programa en java replica los datos capturados y los envía como cadenas de caracteres por medio de un socket en el puerto 40001. Esto se hace por que la integración VRPN con Blender es difícil y no se logró encontrar una librería Python que logre integrar Blender con VRPN directamente.
Una vez que el programa Java envía los datos por ese puerto, Blender puede capturar esos datos por medio de otro socket. El siguiente código es un script en Python que sirve para establecer la conexión entre el programa Java y Blender.
import bge import threading from socket import * import GameLogic as gl import mathutils as math gl.conectado=False scene = bge.logic.getCurrentScene() sock = socket(AF_INET, SOCK_STREAM) sock.setsockopt(SOL_SOCKET, SO_REUSEADDR,1) #sock.bind((HOST,PORT)) sock.connect(("localhost",40001)) #sock.connect(("localhost",40001)) sock.setblocking(0) #Dejar el socket como NO Blocking sock.settimeout(1/100) gl.conexionVrpn=sock gl.conectado=True print ("se creo la conexion")
En esta sección se explican las partes más importantes del código en Blender del demo. Consta de 4 scripts en Blender:
A continuación se explica como se logra el movimiento de la mano virtual. El código está dividido en 3 secciones: traslación de la mano, rotación de la mano y movimiento de los dedos.
Para la posición de la mano se toma el marcador mostrado en la imagen. Se utiliza una constante llamada PUNTO_IZQ para identificar este punto.
Primero se hace un chequeo para ver si existen coordenadas que se puedan usar para el movimiento. Después, se toma la última y la penúltima coordenada enviada por PhaseSpace para ese marcador. Se calculan los vectores que van desde la coordenada inicial del marcador (coordenada al momento de iniciar el programa) hasta la última y la penúltima coordenada. A estos vectores se les aplica una escala para que concuerden los movimientos reales con los movimientos en pantalla. Finalmente se calcula la diferencia entre estos dos vectores y se aplica la traslación con la función applyMovement().
if(bge.puntosGuanteActual[bge.PUNTO_IZQ]!=None and bge.puntosGuanteInicio[bge.PUNTO_IZQ]!=None): posAnterior = bge.escalaMapeo*(bge.puntosGuanteAnterior[bge.PUNTO_IZQ]-bge.puntosGuanteInicio[bge.PUNTO_IZQ]) pos = bge.escalaMapeo*(bge.puntosGuanteActual[bge.PUNTO_IZQ]-bge.puntosGuanteInicio[bge.PUNTO_IZQ]) bge.mano.applyMovement(pos-posAnterior)
Para calcular la orientación de la mano, se toman los 2 marcadores que se encuentran en la parte superior de la mano, formando un vector de orientación.
Para que las rotaciones sean fluidas, se utilizan coordenadas en donde la diferencia en el tiempo de lectura entre la penúltima y la última coordenada no sea mayor a un tiempo determinado. Luego se calcula el vector entre los puntos mostrados en la imagen anterior. Para calcular el cuaternión de la nueva orientación, se toma el vector inicial (vector calculado al inicio del programa), se calcula el ángulo de diferencia entre el vector inicial y el nuevo vector y se arma el cuaternión tomando el vector inicial como eje de rotación. En cada frame se calcula esta orientación, por lo tanto se guarda el cuaternión calculado con anterioridad y se calcula la diferencia con el nuevo cuaternión, esto nos arroja un cuaternión que representa la rotación que se debe aplicar a la orientación de la mano. Teniendo esto, se utiliza la función rotate() con el cuaternión de diferencia como parámetro. Para evitar cambios tan bruscos de orientación cuando PhaseSpace pierde la posición de los marcadores, se verifica que el ángulo de rotación del cuaternión sea menor a 45 grados, o 1.570796 radianes.
if bge.tiemposUltimaLectura[bge.PUNTO_DER]-bge.tiemposLecturaAnterior[bge.PUNTO_DER]<bge.tiempoMaxLectura and bge.tiemposUltimaLectura[bge.PUNTO_IZQ]-bge.tiemposLecturaAnterior[bge.PUNTO_IZQ]<bge.tiempoMaxLectura: vectDif=(bge.puntosGuanteActual[bge.PUNTO_DER]-bge.puntosGuanteActual[bge.PUNTO_IZQ]) axis=bge.vectIniMano.cross(vectDif) angle=bge.vectIniMano.angle(vectDif,0) quat=math.Quaternion(axis,-angle) if bge.quatAnteriorMano!=None: quatTemp=quat quat=quat.rotation_difference(bge.quatAnteriorMano) bge.quatAnteriorMano=quatTemp elif bge.quatAnteriorMano==None: bge.quatAnteriorMano=math.Quaternion((1,0,0,0)) if angle<1.570796: bge.mano.worldOrientation.rotate(quat)
El vector de orientación que se utiliza para cada dedo es el vector que se forma entre el marcador más cercano a la punta del dedo, y el marcador de la punta del dedo.
Básicamente se cambia la orientación de cada uno de los dedos de la misma forma en la que se cambia la orientación de la mano pero no se hace el chequeo de los 45 grados.
if bge.tiemposUltimaLectura[bge.PUNTO_MEDIO]-bge.tiemposLecturaAnterior[bge.PUNTO_MEDIO]<bge.tiempoMaxLectura and bge.tiemposUltimaLectura[bge.PUNTO_DER]-bge.tiemposLecturaAnterior[bge.PUNTO_DER]<bge.tiempoMaxLectura: vectDif=(bge.escalaMapeoDedos*(bge.puntosGuanteActual[bge.PUNTO_MEDIO]-bge.puntosGuanteActual[bge.PUNTO_DER])) axis=bge.vectsIniDedos[bge.PUNTO_MEDIO].cross(vectDif) angle=bge.vectsIniDedos[bge.PUNTO_MEDIO].angle(vectDif,0) quat=math.Quaternion(axis,angle*multAng) bge.medio.rotation_quaternion=quat
Este archivo contiene los archivos necesarios para correr la demo.