Dies ist eine alte Version des Dokuments!
Die Aufgabe unseres Roboters ist es, selbstständig einen Menschen zu erkennen und diesem mit einem bestimmten Abstand zu folgen. Währenddessen er dies tut, soll er ein Lied abspielen. Sobald die verfolgte Person dem Roboter ein „High-Five“ gibt, wird dies durch einen Phototransistor registriert. Darauf reagiert der Roboter, indem er eine zuvor aufgezeichnete Tonaufnahme abspielt.
Momentan erkennt unser Roboter bereits Menschen und schickt die Daten der Person an ein Arduino-Programm, dass die Steuerung und Bewegung des Roboters steuert.
Das Erkennen eines Menschen wird durch eine Kinect-Kamera realisiert, welche die karthesischen Koordinaten des Massenschwerpunkts (center of mass) der zu folgenden Person ausgibt. Abbildung 1 veranschaulicht das Koordinatensystem, welches die Kamera nutzt. Anschließend sendet das Programm die X-Koordinate der Person an die serielle Schnittstelle des Computer. Dies wird durch das nachfolgende Processing-Programm ermöglicht:
// Erkennen vom Centre of Mass und Senden der x-Koordinate an Ardino import SimpleOpenNI.*; //importiert fremdes Programm Simple... import processing.serial.*; //importiert Programm zum Senden von Werten aus dem Processing an Arduino Serial myPort; // Create object from Serial class // The Number we will send to the Arduino SimpleOpenNI context; // zentrale Dings, wo die Kinect-Daten rauskommen PVector com = new PVector(); void setup () { size(640,480); context = new SimpleOpenNI(this); //startet Kinect if(context.isInit() == false) { println("Can't init SimpleOpenNI, maybe the camera is not connected!"); exit(); return; } // enable depthMap generation context.enableDepth(); // enable skeleton generation for all joints context.enableUser(); background(200,0,0); // (rot, grün, blau) AB HIER: stroke(0,0,255); strokeWeight(3); //liniendicke smooth(); //FÜR BILDSCHRIMEINSTELLUNG /*// Open whatever port is the one you're using. String portName = Serial.list()[Serial.list().length-1]; myPort = new Serial(this, portName, 9600); //open a connection with 115200Baud - this has to match the Baudrate in your Arduino sketch! frameRate(10);*/ } void draw () { // update the cam context.update(); // holt die Daten aus von der Camera ein (immer wieder, denn ist in void draw) image(context.userImage(),0,0); //Wichtig! Bild malen // draw the skeleton if it's available int[] userList = context.getUsers(); // userList = array, das die daten der User for(int i=0;i<userList.length;i++) // userList.length = Anzahl der User { if(context.isTrackingSkeleton(userList[0])) // falls Gelenkkoordinaten für den User nummer i: { drawSkeleton(userList[0]); // Funktion!, malt die Person } context.getCoM(userList[0],com); println(com.x, com.y, com.z); // druckt den x-,y-, und z-wert des Vektors von der Kinect zum CoM vom user nr 1 ; com = Vektor (siehe oben)!!!! // myPort.write(com.x); //send a line containing the name and value separated by a space to the Serial. } } void onNewUser(SimpleOpenNI curContext, int userId) { println("onNewUser - userId: " + userId); println("\tstart tracking skeleton"); curContext.startTrackingSkeleton(userId); } void onLostUser(SimpleOpenNI curContext, int userId) { println("onLostUser - userId: " + userId); } void onVisibleUser(SimpleOpenNI curContext, int userId) { //println("onVisibleUser - userId: " + userId); } // draw the skeleton with the selected joints void drawSkeleton(int userId) { // to get the 3d joint data /* PVector jointPos = new PVector(); context.getJointPositionSkeleton(userId,SimpleOpenNI.SKEL_NECK,jointPos); println(jointPos); */ context.drawLimb(userId, SimpleOpenNI.SKEL_HEAD, SimpleOpenNI.SKEL_NECK); context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_LEFT_SHOULDER); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_LEFT_ELBOW); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_ELBOW, SimpleOpenNI.SKEL_LEFT_HAND); context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_RIGHT_SHOULDER); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_RIGHT_ELBOW); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW, SimpleOpenNI.SKEL_RIGHT_HAND); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_TORSO); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_TORSO); context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_LEFT_HIP); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_HIP, SimpleOpenNI.SKEL_LEFT_KNEE); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_KNEE, SimpleOpenNI.SKEL_LEFT_FOOT); context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_RIGHT_HIP); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_HIP, SimpleOpenNI.SKEL_RIGHT_KNEE); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_KNEE, SimpleOpenNI.SKEL_RIGHT_FOOT); }
Darauf folgend liest ein Arduino-Programm die serielle Schnittstelle aus und erhält so die X-Koordinate. Da der Ursprung des Koordinatensystems mittig auf der Kinect ist, die negativen Werte rechts und die positiven Werte links davon liegen, weiß man nun, je nachdem wie groß der abgelesene X-wert ist, wohin sich der Roboter drehen muss. Ist die Koordinate größer oder kleiner als 0, dreht sich der Roboter so lange, bis der Wert gleich 0 ist, also die Person sich nun geradeaus vor dem Roboter befindet. Nachdem dies erreicht ist, fährt er gerade auf die Person zu.
Durch das Senden unterschiedlicher Geschwindigkeiten an die Räder des Roboters wird das Drehen ermöglicht:
// Motorbewegung in Abhängigkeit von der X-Koordinate // Definiere weißen Punkt als "rechts vorne" // dimmen der geschwindigkeit mit analorwrite (pwm modulation) // Einbetten der Library ArduPar #include <ArduPar.h> #include <avr/eeprom.h> int vwL = 13; int rwL = 12; int gasL = 11; int gasR = 10; int vwR = 8; int rwR = 9; int a = 80; //Geschwindigkeit für die äußeren Räder beim Fahren einer Kurve int b = 60; // Geschwindigkeit f. d. inneren Räder IntArduPar xWert; // deklarieren der Variable "xWert" vom Typ IntArduPar void setup () { Serial.begin(9600); //stellt alle Pins für die Motoren auf Output pinMode (8, OUTPUT); // In1 pinMode (9, OUTPUT); // In2 pinMode (10, OUTPUT); // EnA pinMode (11, OUTPUT); // EnB pinMode (12, OUTPUT); // In3 pinMode (13, OUTPUT); // In4 xWert.setup(F("xWert"), -1200 , 1200); } void loop () { //liest daten über die serielle Schnittstelle aus. updateParametersFromStream(&Serial,10); if (xWert.value==0){ // wenn x-Wert=0, dann Person geradeaus vor der Kinect --> Roboter soll vorwärts fahren // Fährt vorwärts digitalWrite (vwR, HIGH); // In1 = 1 digitalWrite (rwR, LOW); // In2 = 0 analogWrite (gasR, a); // E analogWrite (gasL, a); digitalWrite (vwL, HIGH); digitalWrite (rwL, LOW); } if (xWert.value<0){ // dann Person rechts: Roboter soll Rechtskurve fahren, bis x=0 digitalWrite (vwR, HIGH); digitalWrite (rwR, LOW); analogWrite (gasR, a); // zum dimmen der geschwindigkeit: analogwirte (pwm-modulation) analogWrite (gasL, b); digitalWrite (vwL, HIGH); digitalWrite (rwL, LOW); } if (xWert.value>0) { // dann Person links: Roboter soll Linkskurve farhren, bis x=0 digitalWrite (vwR, HIGH); digitalWrite (rwR, LOW); analogWrite (gasR, b); analogWrite (gasL, a); digitalWrite (vwL, HIGH); digitalWrite (rwL, LOW); } }
Die hauptsächliche Herausforderung bis jetzt war, die zwei Programme, Arduino und Processing, miteinander zu verbinden, was wir nun aber durch die Library „ArduPar“ geschafft haben.
Bisweilen hatten wir auch einige Probleme mit der Hardware des Roboters. Die Räder starteten nicht von alleine, sondern benötigten anfangs eine Drehung von Hand und der Motor überhitzte schon nach kurzer Zeit. Da wir nicht herausfinden konnten, was daran schuld war, entschieden wir uns, andere Motoren zu nutzen, welche wir nun noch anbauen müssen. Außerdem muss unser Processing-Programm noch soweit geändert werden, dass es nur eine Person erkennt und nicht mehrere wie im Moment.
Des Weitern müssen wir noch die Interaktion des Roboters mit dem Menschen programmieren und steuern.
Ist der Roboter nun nah genug an der Person, soll er stoppen und auf ein High Five der Person warten. Das Erkennen des High-Fives wird durch eine Lichtschranke ermöglicht. Ein Phototransistor wird eingebaut, auf dem ein ständiger Lichteinfall durch Tageslicht herrscht. Verdeckt man diesen mit der Hand soll die Änderung der Lichtverhältnisse die Reaktion des Roboters, eine Tonaufnahme abzuspielen, auslösen.
Am 13.01.14 versuchten wir, ein Programm zu erstellen, mit welchem wir die Übertragung von Daten vom Processing- ins Arduino-Programm zu überprüfen. Dieses soll die übermittelten Zahlen auf dem Monitor ausdrucken:
//Empfangen der X-Koordinate von Processing // Einbetten der Libary ArduPar #include <ArduPar.h> #include <avr/eeprom.h> IntArduPar xWert; // deklarieren der Variable "xWert" vom Typ IntArduPar void setup () { Serial.begin(9600); xWertSetting.setup( F("xWert"), -1200 , 1200); } void loop () { //liest daten über die serielle Schnittstelle aus. //Empfangen der X-Koordinate von Processing // Einbetten der Libary ArduPar #include <ArduPar.h> #include <avr/eeprom.h> IntArduPar xWert; // deklarieren der Variable "xWert" vom Typ IntArduPar void setup () { Serial.begin(9600); xWert.setup(F("xWert"), -1200 , 1200); } void loop () { updateParametersFromStream(&Serial,10); Serial.println(xWert.value); }
// Erkennen vom Centre of Mass und Senden der x-Koordinate an Ardino import SimpleOpenNI.*; //importiert fremdes Programm Simple... import processing.serial.*; //importiert Programm zum Senden von Werten aus dem Processing an Arduino Serial myPort; // Create object from Serial class // The Number we will send to the Arduino SimpleOpenNI context; // zentrale Dings, wo die Kinect-Daten rauskommen PVector com = new PVector(); void setup () { size(640,480); context = new SimpleOpenNI(this); //startet Kinect if(context.isInit() == false) { println("Can't init SimpleOpenNI, maybe the camera is not connected!"); exit(); return; } // enable depthMap generation context.enableDepth(); // enable skeleton generation for all joints context.enableUser(); background(200,0,0); // (rot, grün, blau) AB HIER: stroke(0,0,255); strokeWeight(3); //liniendicke smooth(); //FÜR BILDSCHRIMEINSTELLUNG /*// Open whatever port is the one you're using. String portName = Serial.list()[Serial.list().length-1]; myPort = new Serial(this, portName, 9600); //open a connection with 115200Baud - this has to match the Baudrate in your Arduino sketch! frameRate(10);*/ } void draw () { // update the cam context.update(); // holt die Daten aus von der Camera ein (immer wieder, denn ist in void draw) image(context.userImage(),0,0); //Wichtig! Bild malen // draw the skeleton if it's available int[] userList = context.getUsers(); // userList = array, das die daten der User for(int i=0;i<userList.length;i++) // userList.length = Anzahl der User { if(context.isTrackingSkeleton(userList[0])) // falls Gelenkkoordinaten für den User nummer i: { drawSkeleton(userList[0]); // Funktion!, malt die Person } context.getCoM(userList[0],com); println(com.x, com.y, com.z); // druckt den x-,y-, und z-wert des Vektors von der Kinect zum CoM vom user nr 1 ; com = Vektor (siehe oben)!!!! curnumber = com.x while (curnumber< 10000) { myPort.write("someInt "+curnumber+"\n"); //send a line containing the name and value separated by a space to the Serial. } } void onNewUser(SimpleOpenNI curContext, int userId) { println("onNewUser - userId: " + userId); println("\tstart tracking skeleton"); curContext.startTrackingSkeleton(userId); } void onLostUser(SimpleOpenNI curContext, int userId) { println("onLostUser - userId: " + userId); } void onVisibleUser(SimpleOpenNI curContext, int userId) { //println("onVisibleUser - userId: " + userId); } // draw the skeleton with the selected joints void drawSkeleton(int userId) { // to get the 3d joint data /* PVector jointPos = new PVector(); context.getJointPositionSkeleton(userId,SimpleOpenNI.SKEL_NECK,jointPos); println(jointPos); */ context.drawLimb(userId, SimpleOpenNI.SKEL_HEAD, SimpleOpenNI.SKEL_NECK); context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_LEFT_SHOULDER); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_LEFT_ELBOW); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_ELBOW, SimpleOpenNI.SKEL_LEFT_HAND); context.drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_RIGHT_SHOULDER); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_RIGHT_ELBOW); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW, SimpleOpenNI.SKEL_RIGHT_HAND); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_TORSO); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_TORSO); context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_LEFT_HIP); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_HIP, SimpleOpenNI.SKEL_LEFT_KNEE); context.drawLimb(userId, SimpleOpenNI.SKEL_LEFT_KNEE, SimpleOpenNI.SKEL_LEFT_FOOT); context.drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_RIGHT_HIP); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_HIP, SimpleOpenNI.SKEL_RIGHT_KNEE); context.drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_KNEE, SimpleOpenNI.SKEL_RIGHT_FOOT); }