 | |  |
|
| |
May 22nd, 2008
The final project is complete!
In the attached zip file, you’ll find the processing code for the atp client, the processing code for the atp server, and 5 examples of controllers that adhere to the protocol. By reverse engineering these examples, it is possible to create any sort of controller.
A few notes about the project:
1) The UDP setup is very robust. The server can be started before or after the client and it still hooks up nicely. I really like this approach.
2) I added software override controls so that if the hardware is failing or no Arduino is present, players can simple use the ‘a’ and ’s’ keys on their keyboards to play. The atp client will tell you if hardware controls are available, or if you must use software.
3) After players die, they automatically respawn after a few seconds once they trigger either their left or right command.
4) As I predicted, it should be pretty easy to interface the protocol with any simple digital or analog sensor or set of sensors. However, extending the code as it is now to encompass more of a 3d live action real time lightcycle game would require a significant rewrite. That said, this was my first real, “game,” and once I got the hang of it, it all started to make a lot of sense. It would be exciting to try to integrate real time positional data into something like this.
Here’s the zip file with all of the goodies. Enjoy!
Physical Computing — Final Project
May 22nd, 2008
A good idea for my final project finally descended from the clouds a few weeks ago, but I’ve been so busy, it’s been hard to think about it or implement it. The idea relies on coding a network tron light-cycle game that is controlled by some physical factor. The Light-cycle sequence from tron involves characters each driving a motorcycle in an arena. As they move around, they leave trails behind them that act as walls. Players must avoid other player’s trails (as well as their own) and the walls of the arena. By developing a standard and protocol for how the pieces of this project fit together, I can write instructions for someone else to create a controller for the game. Since the game itself relies on very little skill, I’ve intentionally turned the speed down to focus on the controlling element.
I’ve set it up so that the controller is almost like the place where you get to demonstrate your skill at the game. By creating a reasonable controller and tweaking the variables yourself, you can effectively increase your chance at winning. This raises the importance of the physical element of the project, even though most of the work under the hood is in software.
Another idea I had was to take this even further and build a 3d human controlled lightcycle, where characters run around in a 3d environment and this information gets sent back to a central server that visualizes their movement. This would require more accurate dead reckoning using the 3d accelerometer, as well as a modification to the game mechanics since stopping would be possible. I don’t think that I’m quite there yet, but it’s interesting to think about.
Thus, the curret ATP (Arduino-Tron Protocol) project consists of three parts:
1) The Arduino code that sends turning data (left or right) to an attached computer.
2) The Processing ATP client that runs on the attached client computer and sends turning information to the server
3) The Processing ATP server which runs centrally, and displays the status of the game from all player’s input.
In theory, as long as the Arduino board is sending out the proper signals, it doesn’t matter if it’s being controlled by a Wiimote or a cat over the internet. Your mileage will vary. Code to follow.
April 30th, 2008
I remember back in high school, some friends of mine were really into MUDs. On a whim, I installed a free MUD server on my computer, and lo and behold, the server outputs to STDOUT all the events on the server. It’d be cool to parse this text and visualize the events in the world, in some graphical way; an image or a progression of images that uniquely represents a series of events on the server: Movement, item pickups, deaths of characters, probably even re spawn times. Depending on the level of verbosity of your game server, you could get some pretty interesting input to parse.
I know more modern, even graphical games like Quake server, which I run headless on a dual processor PII box at work, has fairly verbose output. So yeah, this might be a cool idea for a final project…
April 21st, 2008
I was asked to post some interesting links, and while I’ll add some more later, I think this is really interesting:
http://www.cs.unm.edu/~dlchao/flake/doom/
The idea of mapping one environment to another using computers is of essential value to this class. Plus doom is pretty sweet.
http://www.5min.com/Video/How-to-Create-Electronic-Beats-16924863
This is pretty cool too.
April 16th, 2008
My Project Runway is done! Photos and screengrabs to follow, but needless to say, it’s pretty nifty! Code here:
/*
Project Runway: AccelerGlove
Processing Code
Author: Jesse Spielman
Author: Doug Brantner
*/
// For Theremin Audio Output
import ddf.minim.*;
import ddf.minim.signals.*;
AudioOutput out;
SineWave sine;
// Use the built-in Serial library for communicating with the Arduino Board
import processing.serial.*;
Serial port; // Create object from Serial class
int n = 10; //linefeed character \n
// The code runs much faster using OpenGL, so use that to draw
import processing.opengl.*;
// Use the Traer Physics library for the particles that are maniuplated by the
// sms data
import traer.physics.*;
Particle cursor;
Particle[] others;
ParticleSystem physics;
/* --------------------- */
// Max Distance is hypotenous of right triangle of screen.
int MAX_DISTANCE;
// this seems to work really nicely for smoothing!
final int smoothSize = 3;
// Number of background particles...
final int numParticles = 1000;
// These are probably dependant on circumstance...
// it'd be cool to write an auto calibrator.
final int X_CORRECT = -505;
final int Y_CORRECT = -518;
final int Z_CORRECT = -514;
final int MAX_SIZE = 50;
final int MIN_SIZE = 10;
// for converting from acceleration into position
int posx, posy, posz;
// display the current cursor position?
boolean drawCursor = false;
// Variable used for comparing distance from cursor to other particles
int distance = 0;
// An ArrayList of slices for smoothing
java.util.ArrayList Slices = new java.util.ArrayList(smoothSize);
// the vals array, representing the current values from the acceleromer
int vals[] = {
0,0,0};
// A temp slice for use later on
Slice temp = new Slice(0,0,0);
// The opacity of the box drawn over each frame.
int refresher = 15;
void setup() {
size(screen.width,screen.height,OPENGL);
background(0);
// Lets keep this clean...
noCursor();
noStroke();
// Set intial position for posx and posy
posx = width / 2;
posy = height / 2;
// Fill out the Slices array with dummy slices
for (int i = 0; i < smoothSize; i++){
Slices.add(new Slice(0,0,0));
}
// Set up the physics stystem, and cursor particle
physics = new ParticleSystem( 0, 0.1 );
cursor = physics.makeParticle();
cursor.makeFixed();
// Make an array of particles, and set up the attraction to each other particle
others = new Particle[numParticles];
for ( int i = 0; i < others.length; i++ ){
// random mass adds more visual complexity
others[i] = physics.makeParticle( random(0.1,5.0), random( 0, width ), random( 0, height ), 0 );
physics.makeAttraction( cursor, others[i], random(3000,5000), 10 );
}
MAX_DISTANCE = (int)sqrt(pow(screen.width,2) + pow(screen.height,2));
// Sound stuff stolen largely from:
// http://code.compartmental.net/tools/minim/manual-minim/
Minim.start(this);
// get a line out from Minim, default bufferSize is 1024,
// default sample rate is 44100, bit depth is 16
out = Minim.getLineOut(Minim.STEREO);
// create a sine wave Oscillator, set to 440 Hz,
// at 0.5 amplitude, sample rate from line out
sine = new SineWave(440, 0.5, out.sampleRate());
// set the portamento speed on the oscillator to 10 milliseconds
sine.portamento(10);
// add the oscillator to the line out
out.addSignal(sine);
// Connect to Serial:
port = new Serial(this, Serial.list()[0], 9600);
port.bufferUntil(n);
}
void draw() {
// Add to the slices array a new slice with corrected values
// Added some Exceptions logic as this sometimes caused problems at startup.
try{
Slices.add(new Slice(vals[0] + X_CORRECT,vals[1] + Y_CORRECT,vals[2] + Z_CORRECT));
// Delete the oldest value from the smoothing ArrayList
// if we're over the size of samples we want
if (Slices.size() > smoothSize){
Slices.remove(0);
Slices.trimToSize();
}
// Get the average of the array
// I canít use static methods here, so itís awkward.
temp = temp.average(Slices);
}
catch (Exception e){
}
// Mod the pos values by the average of the motion
// Divide by 2 to make the values a little less frantic
// Z is currently unused
posx -= temp.x() / 2;
posy -= temp.y() / 2;
posz -= temp.z() / 2;
// Edge bounding
if (posx > width) posx = width;
if (posx < 0) posx = 0;
if (posy > height) posy = height;
if (posy < 0) posy = 0;
// for nice fading effect that is controlable:
// see keyPressed()
fill(0,refresher);
rect(0,0,width,height);
// Audio Stuff -- Make a theremin!
sine.setFreq(map(posx,0,width,1200,120));
sine.setAmp(map(posy, 0, height, 0.25, 1));
// have the main cursor particle follow the acceleromter position
cursor.moveTo( posx, posy, 0 );
physics.tick();
// Draw all the other particles, with color based on
// a complicated system based on speed and mass
for ( int i = 0; i < others.length; i++ ){
Particle p = others[i];
distance = (distance(cursor,p));
int speed = (int)(p.velocity().x() + p.velocity().y()) / 2;
fill(map(p.mass(),0.1,5.0,0,255),map(speed,-3,3,0,255),128);
ellipse(p.position().x(),p.position().y(),2,2);
}
// Draw the cursor if drawCursor is true
if (drawCursor){
// DUDE! PREDATOR
fill(255,0,0);
ellipse(posx+15,posy+15,10,10);
ellipse(posx-15,posy+15,10,10);
ellipse(posx,posy-15,10,10);
}
}
// Utility function for 2d distance formula
// Return on a scale from 255(closest) to 0(furthest away)
int distance(Particle a, Particle b){
int tempDistance = (int)sqrt(((b.position().x() - a.position().x()) * (b.position().x() - a.position().x()))
+ (b.position().y() - a.position().y()) * (b.position().y() - a.position().y()));
return (int)map(tempDistance,0,MAX_DISTANCE,255,0);
}
// Toggle the cursor with spacebar
void keyPressed(){
if (key == ' '){
drawCursor = !drawCursor;
}
// Change the transparency of the rectangle drawn on top.
else if (key == 'a'){
refresher++;
}
else if (key == 'z'){
refresher--;
// Reset the display to a clean slate
// -- useful as a reset if the rect drawn on top is totally transparent
} else if (key =='x'){
fill(0);
rect(0,0,width,height);
}
}
void serialEvent(Serial port) { //hooray event handling!
String st = port.readStringUntil(n); //read until newline character
if (st != null) {
st = trim(st); //trim whitespace off ends
vals = int(split(st, ',')); //split comma delineated (sweet auto-array!)
}
}
void stop()
{
// always closes audio I/O classes
out.close();
super.stop();
}
import java.util.ArrayList;
public class Slice {
int x;
int y;
int z;
Slice(int x, int y, int z){
this.x = x;
this.y = y;
this.z = z;
//println("X: " + x + ", Y: " + y + ",Z: " + z);
}
int x(){
return x;
}
int y(){
return y;
}
int z(){
return z;
}
Slice average(java.util.ArrayList Slices){
int tempx, tempy, tempz;
tempx = tempy = tempz = 0;
Slice temp;
for (int i = 0; i < Slices.size() ; i++){
temp = (Slice)Slices.get(i);
if (temp != null){
tempx += temp.x();
tempy += temp.y();
tempz += temp.z();
}
}
tempx = tempx / Slices.size();
tempy = tempy / Slices.size();
tempz = tempz / Slices.size();
return new Slice(tempx, tempy, tempz);
}
void output(){
println("X: " + x() + ", Y: " + y() + ", Z: " + z() + ".");
}
}
April 9th, 2008
While unable to complete my project runway project in time, I’ve spent a lot of time drafting the code for my processing component, using the built-in accelerometer to get realistic data. Now that this is mostly set up, I should be able to connect my project to the arduino with ease. Here is the code for my project so far:
// Use the Sudden Motion Sensor Library (and later,
// the acceleromter and arduino) as a data input
import sms.*;
// For sound output, use the totally busted Krister ESS library
import krister.Ess.*;
// The code runs much faster using OpenGL, so use that to draw
import processing.opengl.*;
// Use the Traer Physics library for the particles that are maniuplated by the
// sms data
import traer.physics.*;
// For Krister: http://processing.org/learning/libraries/synthesizer1.html
int numSines = 5; // Number of oscillators to use
AudioStream myStream; // Audio stream to write into
SineWave[] myWave; // Array of sines
FadeOut myFadeOut; // Amplitude ramp function
FadeIn myFadeIn; // Amplitude ramp function
// this seems to work really nicely for smoothing!
final int smoothSize = 5;
// Number of background particles...
final int numParticles = 1000;
// These are probably dependant on circumstance...
// it'd be cool to write an auto calibrator.
final int X_CORRECT = -1;
final int Y_CORRECT = 14;
final int Z_CORRECT = -253;
// for converting from acceleration into position
int posx, posy, posz;
// display the current cursor position?
boolean drawCursor = false;
// For traer
Particle cursor;
Particle[] others;
ParticleSystem physics;
// Variable used for comparing distance from cursor to other particles
int distance = 0;
// An ArrayList of slices for smoothing
java.util.ArrayList Slices = new java.util.ArrayList(smoothSize);
// The vals array returned from the sms
int[] vals;
// A temp slice for use later on
Slice temp = new Slice(0,0,0);
void setup() {
size(screen.width,screen.height,OPENGL);
background(0);
// Lets keep this clean...
noCursor();
noStroke();
// Set intial position for posx and posy
posx = width / 2;
posy = height / 2;
// Fill out the Slices array with dummy slices
for (int i = 0; i < smoothSize ; i++){
Slices.add(new Slice(0,0,0));
}
// Set up the physics stystem, and cursor particle
physics = new ParticleSystem( 0, 0.1 );
cursor = physics.makeParticle();
cursor.makeFixed();
// Make an array of particles, and set up the attraction to each other particle
others = new Particle[numParticles];
for ( int i = 0; i < others.length; i++ ){
others[i] = physics.makeParticle( 1.0, random( 0, width ), random( 0, height ), 0 );
physics.makeAttraction( cursor, others[i], 5000, 20 );
}
// Sound stuff stolen largely from:
// http://processing.org/learning/libraries/synthesizer1.html
Ess.start(this); // Start Ess
myStream = new AudioStream(); // Create a new AudioStream
myStream.smoothPan = true;
myWave = new SineWave[numSines]; // Initialize the oscillators
for (int i = 0; i < myWave.length; i++) {
float sinVolume = (1.0 / myWave.length) / (i + 1);
myWave[i] = new SineWave(0, sinVolume);
}
myFadeOut = new FadeOut(); // Create amplitude ramp
myFadeIn = new FadeIn(); // Create amplitude ramp
myStream.start(); // Start audio
}
void draw() {
// Create a slice from the sms, and add it to the smoothing ArrayList
vals = Unimotion.getSMSArray();
Slices.add(new Slice(vals[0] + X_CORRECT,vals[1] + Y_CORRECT,vals[2] + Z_CORRECT));
// Delete the oldest value from the smoothing ArrayList
// if we're over the size of samples we want
if (Slices.size() > smoothSize){
Slices.remove(0);
Slices.trimToSize();
}
// Get the average of the array
// I can't use static methods here, so it's awkward.
temp = temp.average(Slices);
// Mod the pos values by the average of the motion
posx -= temp.x();
posy += temp.y();
posz += temp.z();
// Edge bounding
if (posx > width) posx = width;
if (posx < 0) posx = 0;
if (posy > height) posy = height;
if (posy < 0) posy = 0;
// for nice fading effect
fill(0,15);
rect(0,0,width,height);
// have the main cursor particle follow the acceleromter position
cursor.moveTo( posx, posy, 0 );
physics.tick();
// Draw all the other particles, with color based on
// a complicated system explained much clearer below in code.
for ( int i = 0; i < others.length; i++ ){
Particle p = others[i];
distance = (int)map(distance(cursor,p),0,500,0,255);
fill(posz+128,map(posx,0,width,0,255)-distance,map(posy,0,height,0,255));
ellipse(p.position().x(),p.position().y(),3,3);
}
// Draw the cursor if drawCursor is true
if (drawCursor){
fill(0,0,255);
ellipse(posx,posy,10,10);
}
}
// Utility function for 2d distance formula
int distance(Particle a, Particle b){
return (int)sqrt(((b.position().x() - a.position().x()) * (b.position().x() - a.position().x()))
+ (b.position().y() - a.position().y()) * (b.position().y() - a.position().y()));
}
// Toggle the cursor with spacebar
void keyPressed(){
if (key == ' '){
drawCursor = !drawCursor;
println(drawCursor);
}
}
// Audio Function, borrowed and tweaked from:
// http://processing.org/learning/libraries/synthesizer1.html
// I HAVE NO IDEA WHAT THIS IS DOING!
void audioStreamWrite(AudioStream s) {
// Figure out frequencies and detune amounts from the mouse
// using exponential scaling to approximate pitch perception
float yoffset = (height - posy) / float(height);
float frequency = pow(1000, yoffset) + 150;
float detune = float(posx) / width - 0.5;
myWave[0].generate(myStream); // Generate first sine, replace Stream
myWave[0].phase += myStream.size; // Increment the phase
myWave[0].phase %= myStream.sampleRate;
myFadeOut.filter(myStream); // Fade down the audio
myWave[0].frequency = round(frequency * sin(frameCount) * (1 + detune));
myWave[0].phase = 0;
myFadeIn.filter(myStream); // Fade up the audio
}
import java.util.ArrayList;
public class Slice {
int x;
int y;
int z;
Slice(int x, int y, int z){
this.x = x;
this.y = y;
this.z = z;
}
int x(){
return x;
}
int y(){
return y;
}
int z(){
return z;
}
Slice average(java.util.ArrayList Slices){
int tempx, tempy, tempz;
tempx = tempy = tempz = 0;
Slice temp;
for (int i = 0; i < Slices.size() ; i++){
temp = (Slice)Slices.get(i);
tempx += temp.x();
tempy += temp.y();
tempz += temp.z();
}
tempx = tempx / Slices.size();
tempy = tempy / Slices.size();
tempz = tempz / Slices.size();
return new Slice(tempx, tempy, tempz);
}
void output(){
println("X: " + x() + ", Y: " + y() + ", Z: " + z() + ".");
}
}
March 19th, 2008
The Blink project is now done. It mostly turned out the way I expected it to. I had some trouble with arrays, and I guess I’ll try to improve that next time. Additionally, the LEDs I found were reversed: ground was the longer end, so that provided some nice confusion for me at the beginning.
I’m glad I diagramed the circuits before I started cutting wire. I definitely feel better with the components now than I did before, so this functioned as a good introduction.
Diagram 1
Diagram 2
I’ve attached two of the diagrams I made for the project. The one that was all red was initial, and a bit rough. The colored one is a bit more sensible and bears a striking resemblance to the board I produced.
I realized from the beginning that the switch circuit, the LED array, and the LDR sensor circuit were largely independent from each other with the Arduino (noted as ARD in the diagram) acting as the brain that connected them all. Thus, the first chart, where the arduino is placed in the first circuit is inaccurate.
March 19th, 2008
/*
* Blink
*
* This is my Code for the first project, Blink. The code features:
*
* Digital in: in the form of a switch.
* Analog in: through a LDR (Light Dependant Resistor (I looked it up!))
*
* and
*
* Digitial Out: through an array of 6 LEDs.
*
* By Jesse Spielman
*/
// Set up global Variables
// Starting pin for the array
int currentPin = 2;
// The direction that the leds are moving
boolean goingUp = true;
// initial delay rate
float delayRate = 100;
// input variable for storing the values obtained through the LDR
int input;
// The pin which has the switch hooked up to,
// and thus needs to be configured for digital in
int switchPin = 12;
void setup(){ // run once, when the sketch starts
Serial.begin(9600);
pinMode(7,OUTPUT);
pinMode(6,OUTPUT);
pinMode(5,OUTPUT);
pinMode(4,OUTPUT);
pinMode(3,OUTPUT);
pinMode(2,OUTPUT);
pinMode(switchPin,INPUT);
}
void loop(){ // run over and over again
// If the circuit is armed (switch is closed)
if (digitalRead(switchPin) == LOW){
// Decide direction of lights by currentPin
if (currentPin == 7){
goingUp = false;
}
else if (currentPin == 2){
goingUp = true;
}
// Based on the above, determine what pin comes next.
if (goingUp){
currentPin = currentPin + 1;
}
else{
currentPin = currentPin - 1;
}
// Turn the pin on...
digitalWrite(currentPin, HIGH);
// Set the delayRate based on the LDR
setSpeed();
// and wait that long...
delay(delayRate);
// turn the pin off.
digitalWrite(currentPin, LOW);
} else {
blinkWarning();
}
}
void setSpeed(){
input = analogRead(0) / 10;
// Use floats here to stop wacky int errors exploding the arduino
delayRate = ((float)input * (float)input * (float)input / 500.0);
}
void blinkWarning(){
// fill in with code to run when the switch is off
// Serial.println("OFF");
}
March 18th, 2008
Ok, I’m sitting in the Lab now ready to start working on my project. I had hoped to run arduino off my laptop, but I’ve forgotten my power adaptor, so I think I’ll switch to one of the workstations here when I start to code. The concept for the project is fairly simple: I’m going to make a simple nightlight. Since we need both Digital and Analog input, I will have an on/off switch that turns the system on. This will be represented by a simple LED switching on when the switch is on. Once on, control will pass to a LDR that will determine the local brightness levels, and respond by turning on a series of LEDs, with inverse intensity. So at full darkness, the lights will be fully on, and when it’s bright out, we’ll make it less bright. To make things interesting, we’ll pulse the lights back and forth light Knight Rider. More details as I have them, but I’m starting work now!
March 18th, 2008
Ok, so ignore that silly wordpress default ‘hello world’ post. This is my actual first one, and I’m just going to kick off this blog. The point of this blog is to document my projects and thoughts regarding my Physical Computing class. In a few minutes, I’ll post my ideas for the first project, “blink.” I guess I’ll try hard to keep this updated with any thoughts I have!
|
| |
 | |  |
|
|
|