Idag får det bli svenska.
När jag kom till lektionen, hade alla redan börjat. Jag tog min dator (hur skulle jag klara mig utan dator) och började pyssla med phidget-servona. Jag insåg att jag aldrig någonsin ägnat mig åt kontrollerade stegmotorer. Det är en ganska dyr alternativt krånglig teknik för hemmapulare, eftersom att man behöver både DACar och mikrokontrollers, och om man ska implementera dem från scratch behövs dessutom ett hum om reglerteknik och induktiva laster, vilket inte är så enkelt.
Nåväl, tack och lov fick jag igång ett java-program, som SimpleServo.java, som kunde ställa ut motorn och vrida den. Programmet krashade en smula då det försökte ställa motorn för långt.
Jag kom att tänka på att det vore roligt att bygga en hypnosmaskin, Austin Powers och sedan den absurda karaktären Percy Nilegård kom upp i mitt inre:
Och titta vad han har i handen! En hypnosmaskin! Man kunde helt enkelt göra liknande spiralmönstrade plattor och sätta på servomotorn. När någon kom i närheten kunde den börja snurra och datorn som styrde den kunde spela upp hypotiserande Sirensång och datorn kunde genom en lugn röst styra människor i sin omgivning. Bra idé!
Johan och Anna anslöt. Eftersom att datorn var min och ingen av dem kan Mac särskilt bra fortsatte jag att vara datoroperatör. Jag är ganska bra på det också, det är väl inget konstigt, men jag insåg att det var ganska svårt att programmera med som sitter bredvid. Ofta ville de kolla referenser, och så, när jag ville programmera. Nåja, det gick bra, även om det måste varit väldigt tråkigt för dem att titta på när andra jobbar.
Det gick i alla fall framåt. Vi diskuterade min idé, hypnotisören, och hur man skulle kunna kontrollera människor - till exempel genom att titta hur mycket de rörde sig genom att filma dem med en kamera. Om de rörde sig skulle de sägas åt att stå stilla. Datorn som befallare.
Interaktionsdesignern kom förbi och pratade om hjärnvågor och så, för att genom dem kunna beräkna vad en människa höll på med. Jag har inte gått vidare med den idén riktigt, eftersom att jag inte har någon bra idé på hur man skulle göra.
Ett problem var att vi både behövde se till att personen stod still framför kameran, men samtidigt inte "försvann" och maskinen fortsatte sin hypnos i onödan. En IR-sensor, värme eller en ljusstråle som bröts skulle kunna lösa problemet.
Diskussionsgenomgång med hela klassen. Intressant att höra de andras idéer, men extra roligt att höra att hypnosmaskinen var något helt nytt. Anna berättade om pendeln, vilket var en fullkomligt lysande idé. Anna var tvungen att gå på något ärende, och jag och Johan implementerade pendeln, med hjälp av ett bit sladd och en vikt.
Genom formeln 2*pi*sqrt(L/g) fick vi fram periodtiden, vilken vi implementerade i servot, så att den slog fram och tillbaka i rätt takt. Inget utjämnande, den går från 10° till 30° i dagsläget, vilket var tillräckligt bra för att vikten skulle börja pendla bra.
Intressant var när vi hängde upp pendeln i taket, och satte igång maskineriet. Folk tittade förbluffat på pendeln. Uppenbarligen fungerar maskinen bättre än vi vågade hoppas.
Vi hittade en algoritm för motion detection i processing på
http://www.learningprocessing.com/examples/chapter-16/example-16-13/ som vi blev väldigt nöjda med, vi lade till en räknare som räknade alla svarta pixlar för varje bildruta, och det blev bara svarta pixlar när det var en stor förändring i bilden. Det blev tillräckligt bra. Genom en finurlig medelvärdesalgoritm kunde vi säga när personen rörde sig för mycket.
Det återstod bara att koppla ihop webkamera-applikationen (processing) och servokontrollen (java). Efter att försökt använda processing-kärnan i java men gett upp, lyckades vi, genom att Johan förstod att vi behövde fånga exceptions, och Anna som förstod att vi var tvugna att bygga allt som objekt och jag som förstod att vi behövde threads för att inte bli knäppa vid implementationen. Till slut fungerade det! Vi kopplade det till en början bara till att servot rörde sig när man rörde på sig i kameran, och bara det var en verkligt rolig applikation.
När man satt framför datorn kunde man alltså gunga och så började pendeln också gunga. Fantastiskt roligt. Så långt kom vi idag.
Här är några bilder:
Och en film:
Man ser i tur och ordning datorn med en skymt av motion-detection-programmet och sedan pendeln.
Här är källoden för programmet också (fungerar på macbook, annars måste man göra något åt kamera-raden, antagligen):
import processing.video.*;
import com.phidgets.*;
import com.phidgets.event.*;
float lengthofpendulum = 0.55; // meter!
int periodtime = int( 1000 * 2 * PI * sqrt ( lengthofpendulum / 9.82)); // milliseconds!
Capture video; // Variable for capture device
PImage prevFrame; // Previous Frame
float threshold = 70; //// How different must a pixel be to be a "motion" pixel
int movtresh = 50; //how many frames that need motion detected to be able to move
int movement=0; //increases when a videoframe have a lot of black dots (have been moved)
int blackthreshold = 4000; //how many black pixels are nescessary befor
// Create the object with the run() method
Runnable runnable = new PendelThread();
// Create the thread supplying it with the runnable object
Thread pendelthread = new Thread(runnable);
//Thread controlthread = new Thread(controller);
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com
// Example 16-13: Simple motion detection
void setup() {
size(320,240);
video = new Capture(this, width, height, 30);
// Create an empty image the same size as the video
prevFrame = createImage(video.width,video.height,RGB);
pendelthread.start();
}
void draw() {
// Capture video
if (video.available()) {
// Save previous frame for motion detection!!
prevFrame.copy(video,0,0,video.width,video.height,0,0,video.width,video.height); // Before we read the new frame, we always save the previous frame for comparison!
prevFrame.updatePixels();
video.read();
}
loadPixels();
video.loadPixels();
prevFrame.loadPixels();
long blackamt = 0; //reset the black-counter between every frameupdate
// Begin loop to walk through every pixel
for (int x = 0; x <>
for (int y = 0; y <>
int loc = x + y*video.width; // Step 1, what is the 1D pixel location
color current = video.pixels[loc]; // Step 2, what is the current color
color previous = prevFrame.pixels[loc]; // Step 3, what is the previous color
// Step 4, compare colors (previous vs. current)
float r1 = red(current);
float g1 = green(current);
float b1 = blue(current);
float r2 = red(previous);
float g2 = green(previous);
float b2 = blue(previous);
float diff = dist(r1,g1,b1,r2,g2,b2);
// Step 5, How different are the colors?
// If the color at that pixel has changed, then there is motion at that pixel.
if (diff > threshold) {
// If motion, display black
pixels[loc] = color(0);
blackamt++;
}
else {
// If not, display white
pixels[loc] = color(255);
}
}
if (blackamt > blackthreshold) {
movement++; //if we hade over
}
}
updatePixels();
//println(blackamt); //DEBUG: how many black pixels are there?
}
// Start the thread
class PendelThread implements Runnable {
void run() {
println("Pendel: Pendel starta");
// This method is called when the thread runs
//här ska koden ligga
try { //because listeners can throw phidgetexception, which must handled by processing. //simple solution
ServoPhidget servo;
System.out.println(Phidget.getLibraryVersion());
servo = new ServoPhidget();
servo.addAttachListener(new AttachListener() {
public void attached(AttachEvent ae) {
System.out.println("attachment of " + ae);
}
}
);
servo.addDetachListener(new DetachListener() {
public void detached(DetachEvent ae) {
System.out.println("detachment of " + ae);
}
}
);
servo.addErrorListener(new ErrorListener() {
public void error(ErrorEvent ee) {
System.out.println("error event for " + ee);
}
}
);
servo.addServoPositionChangeListener(new ServoPositionChangeListener()
{
public void servoPositionChanged(ServoPositionChangeEvent oe)
{
System.out.println(oe);
}
}
);
servo.openAny();
System.out.println("waiting for Servo attachment...");
servo.waitForAttachment();
System.out.println("Serial: " + servo.getSerialNumber());
System.out.println("Servos: " + servo.getMotorCount());
servo.setPosition(0,0);
Thread.sleep(1000);
int pos=10;
while (true) //isch ok
{
println("Gungar!");
if (pos==10 && movement > movtresh){
pos=30;
servo.setPosition(0,90);
}
else if (movement > movtresh) {
pos=10;
servo.setPosition(0,10);
}
movement = 0;
//servo.setPosition(0, i);
Thread.sleep(periodtime);
System.out.println("Position: " + servo.getPosition(0));
}
/*System.out.println("Outputting events. Input to stop.");
System.in.read();
servo.setPosition(0, 0);
System.out.print("closing...");
servo.close();
servo = null;
System.out.println(" ok");*/
}
catch (Exception e) {
println("fel!");
}
}
}