Blender vs. Kinect

Aus toolbox_interaktion
Wechseln zu: Navigation, Suche
Blender vs. Kinect
Blender vs Kinect blenderLogo.pngKinect Desc.png
Teammitglieder: Alexander Freund
Lionel Key
Veit-Georg Hartung
Kevin Ittner

Mit Blender vs. Kinect ist ein 3D-Spiel realisiert, das mit dem Körper gesteuert werden kann. Dabei treten zwei Spieler gegeneinander an. Es gibt einen Fänger und einen Gejagten. Als Referenzpunkt gilt die rechte Hand beider Spieler.


Verwendete Technik

Kommunikation:

Software (Installationslinks s. "Projektverlauf"):

Hardware:

  • 2 Kinect Kameras (XBOX 360)
  • 3 Laptops

Betriebssystem:

  • Microsoft Windows 7


Spielbeschreibung

Jeder Spieler wird mit einer Kinect-Kamera erkannt und erhält in Processing ein Skelett. Hierfür sind also zwei Kinects mit je einem Laptop notwendig. Von diesem Skelett werden die xyz-Koordinaten der rechten Hand ausgelesen und per OSC Protokoll an einen dritten Laptop versendet, um die Latenz gleich zu halten und ein faieres Spiel zu garantieren (zu Präsentationszwecken werden wir der Einfachheit halber auf den dritten Laptop verzichten).
Auf dem dritten Rechner werden die OSC Daten in Blender mittels eines Pythonskriptes empfangen und an die Blender Game Engine weitergegen. Hier bewegen sich die virtuellen Spieler, indem die Koordinaten permanent neu gesetzt werden. Wenn der Fänger den Gejagten im virtuellen Raum berührt, hat er gewonnen.


Spielregeln

Es gibt zwei Spieler. Jeder Spieler hat eine feststehende Base (Quadrat) und eine sich bewegende Spielfigur (Kreis). Diese Spielfigur wird durch die rechte Hand der Spielperson gesteuert.
Um Punkte erzielen zu können, muss man mit seiner Spielfigur die Base des Mitspielers berühren. Ob man einen Punkt erzielen kann, wird dann durch einen farbigen Marker in der rechten oberen Ecke angezeigt.
Kann man nun einen Punkt erzielen, weil man sich freigeschaltet hat, muss man nur noch mit der eigenen Spielfigur die des Mitspielers fangen. Dadurch gewinnt man einen Punkt und der Zähler erhöht sich um eins. Die Freischaltung, Punkte zu erzielen, wird wieder zurückgesetzt.
Zu jedem Zeitpunkt, kann immer nur ein Spieler freigeschaltet sein, Punkte zu erzielen.
Sollte der andere Spieler freigeschaltet sein, kann man ihm diese Möglichkeit wieder entziehen. Dazu berührt man mit der eigenen Spielfigur die eigene Base. Die Freischaltung des Mitspielers wird dadurch zurückgesetzt.
Gewonnen hat derjenige, der am Ende des Spiels, die meisten Punkte hat. Die Spieldauer legen die Spieler fest.


Projektverlauf

Im Folgenden sind unsere Aktivitäten dokumentiert. Dabei wird auch auf die Installation der notwendigen Software- und Treiberpakete eingegangen und gezeigt, worauf unser Programmcode basiert.

Aller Anfang ist schwer: Einbindung der Kinect (EyesWeb und Processing)

Eyesweb screenshot.gif ProcessingLogo.png Kinect Desc.png

  • Anfangs wurden verschiedene Versuche unternommen, die Kinect zusammen mit EyesWeb 3.3.0 und 5.5.0 zum Laufen zu bringen. Dies war zunächst erfolglos, da die Kinect nicht erkannt wurde.
  • Zwei neue Möglichkeiten ergeben sich:
    • Einen Farbhandschuh verwenden und über das Kinect-RGB-Bild die Handkoordinate (Mittelpunkt) sowie über das Graustufenbild die Tiefenkoordinate ermitteln.
    • Skelettkoordinaten der Kinect auslesen (besser mit Processing als mit Eyesweb).
  • EyesWeb 3.3.0 und 5.5.0 wurden deinstalliert und wir testeten EyesWeb XMI 5.2.1 (Kinect-Support) inklusive des OpenNI Adapters, dem Kinect Driver und der NITE Middleware aus dem internen Bereich. Die Verbindung mit der Kinect funktioniert nun.
  • Parallel haben wir Processing inklusive der SimpleOpenNI Bibliotek installiert. SimpleOpenNI ermöglicht die Einbindung der Kinect in Processing. Dies geschieht, indem man in Processing unter Sketch > Import Library... > Add Library... im Suchfeld "SimpleOpenNI" eingibt und den entsprechenden Eintrag installiert. Zusätlich muss Windows Kinect SDK 1.7 installiert werden.
    • Eine genaue Anleitung zur Installation und Verwendung gibt es hier.
    • Unsere Codegrundlage zur Einbindung der Kinect mit SimpleOpenNI gibt es hier.
  • Die Variante mit Processing setzt sich durch und wir verwenden jetzt kein EyesWeb mehr.

Virtuelle Welt: 3D Spielumgebung (Blender)

Blender vs Kinect blenderLogo.png Blender vs Kinect pythonLogo.png

  • Es wird ein Prototyp einer 3D-Welt in Blender 2.72b erstellt.
  • Einarbeitung in die Möglichkeiten, wie die Blender Game Engine xyz-Koordinaten aufnehmen kann. Hierfür gibt es relativ einfache Befehle, die die Position setzten (Stichwort „bge“ für Blender Game Engine und der Hinweis "bpy.data.scenes[0].objects['Cube'].location[0] = x").

Processing - Finetuning und OSC-Versand

  • Das Kinect-Skeletttracking läuft jetzt gut mit Processing. Deshalb wird die Funktion implementiert, dass nur noch ein Skelett vergeben wird. Damit wird verhindert, dass eine weitere Person mitspielen kann.
  • Die oscP5 Bibliothek wird in Processing eingebunden, um das Senden und Empfangen von OSC-Paketen zu ermöglichen. Dies geschieht, indem man in Processing unter Sketch > Import Library... > Add Library... im Suchfeld "oscP5" eingibt und den entsprechenden Eintrag installiert.
    • Unsere Codegrundlage und eine Anleitung findet man hier.
  • Wir passen den Code so an, dass nur die Daten der rechten Hand aus Processing per OSC verschickt werden.
  • An den Rändern der Kinect-Aufzeichnung wird das Skelett verloren. Somit müssen die Koordinaten beim Verlassen des Spielfeldes auf einen Maximalwert gesetzt werden. Dies wird durch Maximalwerte für |x| und |y| realisiert. Dies wurde wieder ausgebaut, da es letzten Endes nicht notwendig war.
  • Da die Spieler verschiedene Körpergrößen haben, hat der kleinere Spieler einen Nachteil beim Fangen. Die Umrechnung auf eine neutrale Basis wurde aus Zeitgründen abgebrochen.
  • Wir stellen fest, dass die xyz-Koordinaten, die direkt von der Kinect kommen, nicht dem echten metrischen System entsprechen. Die Methode „ConvertProjectiveToRealWorld“ löst das Problem. Jedoch haben wir diese wieder ausgebaut und die Umrechnung lieber in Blender direkt gemacht.
  • Der Empfang von zwei OSC Clients funktionierte in Blender nicht, daher lassen wir das eine Processing die Daten an den anderen Laptop schicken. Somit wird von einem Processing alles an Blender übertragen.

Blender - Finetuning und OSC-Empfang

  • Einarbeitung in Python, um die Möglichkeiten der OSC-Schnittstelle in Blender herauszufinden. Hierbei werden die folgenden Bibliotheken näher betrachtet: SimpleOSC für Blender, PyOSC, Pyliblo.
  • Das Empfangen von OSC-Daten funktioniert bei uns nur mit SimpleOSC richtig. Deshalb entscheiden wir uns dafür.
  • Das Skript wird so umgeschreiben, dass die xyz-Daten empfangen werden und der Prototyp mit der rechten Hand gesteuert werden kann. Für die Steuerung wird der z-Wert fix gesetzt, da für die Präsentation ein 2D-Spiel ausreicht (Zeitmangel).
  • Es entsteht aus dem Prototypen der Spielewelt eine mit schickerem Design (In der endgültigen Variante aus zeitgründen leider nicht eingebaut).
  • Es wird eine optische Anzeige eingebaut, die anzeigt welcher Spieler an der Reihe ist.
  • Wenn ein Spieler einen Punkt erzielt, wird ein Soundfile (WAV) mittels Pythonskript abgespielt.


Der Code unter der Haube

Processing

Kinect verbinden:

import SimpleOpenNI.*;

SimpleOpenNI kinect;
                          
void setup()
{
  kinect = new SimpleOpenNI(this);

  // enable depthMap generation
  kinect.enableDepth();
   
  // enable skeleton generation for all joints
  kinect.enableUser();
}


OSC einbinden:

Processing empfängt testweise OSC Daten.
import oscP5.*;
import netP5.*;
 
OscP5 oscP5;
NetAddress myRemoteLocation;

void setup()
{
  // start oscP5, telling it to listen for incoming messages at port 12345 */
  oscP5 = new OscP5(this, 12345);
 
  // set the remote location to be the localhost on port 12344
  myRemoteLocation = new NetAddress("127.0.0.1", 1234);
}


Skelett für einen Spieler zeichnen und updaten:

Processing zeigt die Spieler an und ordnet ein Skelett zu.
void draw()
{
  // update the cam
  kinect.update();
 
  image(kinect.userImage(),0,0);
 
  // draw the skeleton if it's available
  int[] userList = kinect.getUsers();
  for(int i=0;i<userList.length;i++)
  {
    if(i==0){
      if(kinect.isTrackingSkeleton(userList[i]))
      {
        drawSkeleton(userList[i]);
      }   
    }   
  }    
}


Koordinaten der rechten Hand in eine OSC Message verpacken und versenden (Linke Hand im Programm wird nur für Testzwecke mitübergeben):

void drawSkeleton(int userId)
{
  // to get the 3d joint data
  PVector jointPosR = new PVector();
  
  kinect.getJointPositionSkeleton(userId,SimpleOpenNI.SKEL_RIGHT_HAND,jointPosR);
  kinect.getJointPositionSkeleton(userId,SimpleOpenNI.SKEL_LEFT_HAND,jointPosL);

  // create an osc message
  OscMessage myMessage = new OscMessage("/blender");
 
  float x = worldJointPos.x;
  float y = worldJointPos.y;
  float z = worldJointPos.z;
  
  // add floats and user ID to the osc message
  myMessage.add(jointPosR.x);
  myMessage.add(jointPosR.y);
  myMessage.add(jointPosR.z);
  myMessage.add(0);

  // send the message
  oscP5.send(myMessage, myRemoteLocation);
}


Blender

Pythonskript, um den Socket für den OSC-Empfang zu erstellen:

from bge import logic as gl

gl.ip_in = "127.0.0.1"
gl.port_in = 1234

Pythonskript, um OSC-Daten in Blender zu empfangen:

Übersicht über die Blender Programmoberfläche und die Einstellungen.
from bge import logic as gl
import socket
import OSC

#init global var
ip = gl.ip_in
port = gl.port_in
buffer_size = 1024
data = gl.data #Only if no player connected

#sens.positive is used only to run this script once: sens is positive only 0 to 1, not 1 to 0
cont = gl.getCurrentController()
sens = cont.sensors["Get OSC"]

if sens.positive:
    #Connect Blender
    if not gl.connected :
        try:
            gl.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            gl.sock.bind((ip, port))
            gl.sock.setblocking(0)
            gl.sock.settimeout(0.005)
            gl.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, buffer_size)
            print()
            print("Socket :")
            print(' Blender Receive OSC data \n on IP = {} \n Port = {} \n with Buffer = {} \n'.format(ip, port, buffer_size))
            gl.connected = True
        except:
            print('Connection to IP={} Port={}'.format(ip, port), "failed. \n")
            pass

    #If Blender connected
    if gl.connected :
        try:
            data = OSC.decodeOSC(gl.sock.recv(buffer_size))

        except socket.error:
            pass
    #Save
    gl.data = data

Pythonskript, um empfangene OSC-Daten zwischen zu speichern:

from bge import logic as gl

#Received OSC data
gl.data = []

Pythonskript, um Objekte in Blender zu bewegen:

Einstellungen der roten Spielfigur (Sensors, Controllers, Actuators, Properties).
import OSC
from bge import logic as gl

scene = gl.getCurrentScene()

try:
    dec = gl.data
    if dec[5]==0:
        try:
            scene.objects['Player1_1'].localPosition = (dec[2]/40, dec[3]/40, 1.2)
            scene.objects['Player1_2'].localPosition = (-15.0, 5.0, 1.2)
        except:
            print('set new position Player1 failed')
    elif dec[5]==1:
        try:
            scene.objects['Player2_1'].localPosition = (dec[2]/40, dec[3]/40, 1.2)
            scene.objects['Player2_2'].localPosition = (15.0, 5.0, 1.2)
        except:
            print('set new position Player2 failed')
except:
    print('no readout')

Pythonskript für die Punktezählung und die Soundwiedergabe:

Das Spielfeld: Links oben die Punktestände, rechts oben die Freischaltungsmarkierungen.
import bge
from bge import logic as gl
import aud

cont = bge.logic.getCurrentController()
own = cont.owner
scene = gl.getCurrentScene()
sound = aud.Factory.file(gl.expandPath("//_audio/laser.wav"))

if(own['turn'] == True):
    own['points'] = own['points'] + 1
    aud.device().play(sound)
    own['turn'] = False


Projektausschluss (mögliche Projektweiterführungsmöglichkeit)

  • Die z-Koordinate ist nicht aktiv. Sie wird mit OSC übermittelt und empfangen, jedoch nicht an die Spielfiguren weitergegeben (erschwert das Spielen).
  • Die neu entworfene Spieleumgebung müsste eingebaut werden.


Weblinks

Interner Bereich

Weitere Quellen