Making Things Interactive

March 10, 2008

Lightchaser v5 [part 2]

Filed under: 7: Mid-Term Project, Gaku Sato — ponkotsu @ 8:02 pm

Schematic:
lightchaser-schematic.pdf

Pics:
lightchaser1.jpg
Top (left to right): 9V battery pack, 6V battery pack, Tamiya Twin-Motor Gearbox
Center: H-bridge circuitry
Bottom: Arduino, photoresistor circuitry
lightchaser2.jpg

BB Gun:
  The lightchaser was initially designed with a range weapon to be fired when it reached the target, but since the main goal is to chase the target, it was left out of the final code.
  For optional implementation, the construction of a solenoid-actuated rapid-fire BB gun is photographed below.  The wiring schematic is included in the pdf attached above.  The code would have been simply to ON/OFF the transistor input (drawn as digital 12 pin output) intermittently.  The current code includes a “display I/O variables every second” portion at the bottom, so this can be modified to send a HIGH output every 1/10 second (–> 600rpm) or something around there.

bbgun.jpg

Lightchaser v5

Filed under: 7: Mid-Term Project, Gaku Sato — ponkotsu @ 3:32 pm

Description:
  This is a motorized light-chasing trike with the left and right motors having independent motor controls for the ability to turn/rotate in place.  It takes inputs from three light sensors on its left, right, and back sides.
  The front left/right sensors are the main inputs used to control the motors.  There are two main control algorithms:

1. ABSOLUTE
   This takes sensor inputs and compares them individually to a constant threshold to get the desired motor output.  For instance, if the left sensor detects low levels of light, the right motor should run at a low speed, and vice versa.  So if the right sensor detects high levels, it would run the left motor at high speed, allowing the trike to steer towards the light.  If they are both low, the trike would drive straight forward but slowly, so as to not wildly run after slight differences in ambient light, etc.  Also, there are MIN/MAX thresholds set so that the trike will stop running when the sensors read above MAX (sensors/trike is close enough to light source) or when they read below MIN (light source too far away or nonexistant).

2. RELATIVE
   This takes sensor inputs and compares them to each other.  For instance if the front inputs vary greatly, this means that the trike is not facing the light source.  For example, if the left input reads 700 and the right input reads 500, both of which are above the specified MIN threshold, the initial ABSOLUTE algorithm would tell the right motor to run HIGH and the left motor to run LOW.  However, since the difference is 200 (quite large), this means that the light source is hitting the sensors from the left side of the trike, so in fact, the left motor should not run at all and allow the trike to pivot without moving forwards (i.e. away from the light).  Also, while the sensor inputs have been calibrated by a coefficient to read on matching light scales, they will still have variations, especially with noise.  Relative input parsing can account for some of this and eliminate twitch motions by allowing the motors to run at the same speed if the inputs are only slightly different.

  The algorithm was the most challenging part.  Below is the motion logic from L/R sensor inputs to L/R motor outputs.  The MIN/MAX input conditions are the absolute thresholds mentioned above, and the L/H conditions are LOW<—>HIGH analog controls.

Input  Output
L      R      L      R
min min   0      0
min L       L      0
min H      H      0
min max  H     0
L     min   0      L
L     L       L      L
L     H       H     L
L     max  H      0
H    min    0     H
H     L       L      H
H     H      H      H
H     max  L      0
max min  0       H
max L      0       H
max H      0      L
max max 0      0

  The rear sensor is used to turn the trike around if it is facing the wrong way.  If the input here is significantly higher (difference threshold specified) than the front inputs, it rotates the trike towards around by almost 180degrees in the direction of the higher front input.  So if the left is slightly higher than the right when the light hits the back of the trike, this probably means that the light source is slightly more to the right, so when it turns around, it doesn’t do a full 180; it rotates slightly less to face more towards what is probably the location of the light source.

Pictures:
  Here is my current setup.  I’m working on making it fully operational, but it’s difficult to fit all the parts onto the chassis for remote operation.  So far the I/O works perfectly under various ambient conditions and light sources.  Once I get all the parts onto the chassis, it should just be a matter of tweaking the constant coefficients and thresholds in the code.
  [I'm having trouble with the camera so I will have this up soon!]

Code:

int LightL = 2;   // input analog pin - photoresistor [left]
int LightR = 3;   // input analog pin - photoresistor [right]
int LightB = 4;   // input analog pin - photoresistor [back]
int MotorL1 = 6;  // output digital pin - motor [left] - direction 1
int MotorL2 = 5;  // output digital pin - motor [left] - direction 2
int MotorL = 9;  // output analog pin - motor [left] - speed
int MotorR1 = 8;  // output digital pin - motor [right] - direction 1
int MotorR2 = 7;  // output digital pin - motor [right] - direction 2
int MotorR = 10;  // output analog pin - motor [right] - speed

int Lin;  // input - photoresistor [left]
int Rin;  // input - photoresistor [right] * coefficient
int Bin;  // input - photoresistor [back] * coefficient

long Lout;  // output - motor [left]
long Rout;  // output - motor [right]

int LightMin = 450;    // light input MIN for motion
int LightMax = 800;    // light input MAX for motion

int SpeedLow = 70;    // motor output LOW (MIN for motion)
int SpeedHigh = 255;  // motor output HIGH

int RotateDiff = 100;  // light input different threshold to rotate
int RotateTime = 1500; // motor ON time (ms) to rotate <180deg
int msecond = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(LightL, INPUT);
  pinMode(LightR, INPUT);
  pinMode(LightB, INPUT);
  pinMode(MotorL, OUTPUT);
  pinMode(MotorL1, OUTPUT);
  pinMode(MotorL2, OUTPUT);
  pinMode(MotorR, OUTPUT);
  pinMode(MotorR1, OUTPUT);
  pinMode(MotorR2, OUTPUT);
  analogWrite(MotorL, 0);
  digitalWrite(MotorL1, HIGH);  // forward: HIGH
  digitalWrite(MotorL2, LOW);   // forward: LOW
  analogWrite(MotorR, 0);
  digitalWrite(MotorR1, HIGH);  // forward: HIGH
  digitalWrite(MotorR2, LOW);   // forward: LOW
  Lin = analogRead(LightL);
  Rin = analogRead(LightR) *1.1;
  if(max(Lin,Rin)>LightMin)
  {LightMin = max(Lin, Rin);}  // calibrate light input MIN
}

void loop()
{
  Lin = analogRead(LightL);
  Rin = analogRead(LightR) *1.1;
  Bin = analogRead(LightB) *1.0;
  Lout = 0;
  Rout = 0;

  if(Bin>Lin+RotateDiff && Bin>Rin+RotateDiff)
  {  // rotate when light sensed from behind > front + constant
    if(Lin>Rin)
    {  // rotate left
      digitalWrite(MotorL1, LOW);
      digitalWrite(MotorL2, HIGH);
    }
    else
    {  // rotate right
      digitalWrite(MotorR1, LOW);
      digitalWrite(MotorR2, HIGH);
    }
    int ms = millis();
    while(millis() < ms+RotateTime)
    {
      analogWrite(MotorR, SpeedLow);
      analogWrite(MotorL, SpeedLow);
    }
    digitalWrite(MotorL1, HIGH);
    digitalWrite(MotorL2, LOW);
    digitalWrite(MotorR1, HIGH);
    digitalWrite(MotorR2, LOW);
    analogWrite(MotorL, 0);
    analogWrite(MotorR, 0);
  }

  if(Lin<LightMin)
  {
    Rout=0;
    if(Rin>LightMax) {Lout=255;}
    else if(Rin>LightMin)
    {  // Lout = (SpeedHigh-SpeedLow)*(Lin-LightMin)/(LightMax - LightMin)+SpeedLow
      Lout = SpeedHigh-SpeedLow;
      Lout *= Lin-LightMin;
      Lout /= LightMax - LightMin;
      Lout += SpeedLow;
    }
  }
  if(Lin>LightMax)
  {
    Lout=0;
    if(Rin>LightMin && Rin<LightMax)
    {  // Rout = (SpeedHigh-SpeedLow)*[1-(Rin-LightMin)/(LightMax - LightMin)]+SpeedLow
      Rout = SpeedHigh-SpeedLow;
      Rout *= LightMax - Rin;
      Rout /= LightMax - LightMin;
      Rout += SpeedLow;
    }
  }
  if(Rin<LightMin)
  {
    Lout=0;
    if(Lin>LightMax) {Rout=255;}
    else if(Lin>LightMin)
    {
      Rout = SpeedHigh-SpeedLow;
      Rout *= Rin-LightMin;
      Rout /= LightMax - LightMin;
      Rout += SpeedLow;
    }
  }
  if(Rin>LightMax)
  {
    Rout=0;
    if(Lin>LightMin && Lin<LightMax)
    {
      Lout = SpeedHigh-SpeedLow;
      Lout *= LightMax - Lin;
      Lout /= LightMax - LightMin;
      Lout += SpeedLow;
    }
  }

  if(Lin>LightMin && Lin<LightMax && Rin>LightMin && Rin<LightMax)
  {
    Lout = SpeedHigh-SpeedLow;
    Lout *= Rin-LightMin;
    Lout /= LightMax - LightMin;
    Lout += SpeedLow;
    Rout = SpeedHigh-SpeedLow;
    Rout *= Lin-LightMin;
    Rout /= LightMax - LightMin;
    Rout += SpeedLow;
  }
 
  if(Lin>Rin+200) {Lout=0;}
  if(Rin>Lin+200) {Rout=0;}
  if(abs(Lin-Rin)<50) {Rout=Lout;}

  analogWrite(MotorL, Lout);
  analogWrite(MotorR, Rout);

  msecond = millis()/1000;
  if(millis()-msecond*1000 == 0)  // display every second
  {
    Serial.print("L:i");
    Serial.print(Lin);
    Serial.print("-o");
    Serial.print(Lout);
    Serial.print("   R:i");
    Serial.print(Rin);
    Serial.print("-o");
    Serial.print(Rout);
    Serial.print("   B:");
    Serial.println(Bin);
   }
}

Parts List:
3x photoresistors (preferably all same model)
3x 1Kohm resistors (for photoresistor circuit)
1x 22uF capacitor (for H-bridges)
1x 9V battery pack (for Arduino)
1x 6V battery pack (for motors)
2x quad half H-bridges (for motor control)
1x Tamiya Twin-Motor Gearbox (or 2x DC motors & gearboxes + 1x chassis)
1x breadboard (currently on 2 separate in pic, but need to condense)
?x 22awg hookup wires

References:
  http://itp.nyu.edu/physcomp/Labs/DCMotorControl

Physical Pixel Representation – Bridging the gap between my Computer and the Physical World

Filed under: 5: Making Motion, Joshua Smith — jssmith44 @ 3:05 pm

For this project, I wanted to investigate bridging the gap between my computer and the physical world. I split my computer screen into 6 white squares ( a 3 x 2 matrix). I then created a physical representation of these squares with a 3 x 2 matrix of Red Led’s.  The physical matrix represents which square my mouse is within the screen. As the mouse moves into a new square, that square turns red on the screen and the corresponding LED turns on. This system consisted of a Processing Application to determine where the mouse was on the screen, run the screen display, and pass that location on to the Arduino Program using serial communication. The Arduino program then controlled the physical pixel matrix. Video is below: (Video Here) Processing Code:  

import processing.serial.*; 

Serial port;
String colorO = "#FFFFFF";
String colorC = "#FF0000";

void setup()
{
  size(900, 600);
  noStroke();
  frameRate(10); 

  // List all the available serial ports in the output pane.
  // You will need to choose the port that the Arduino board is
  // connected to from this list. The first port in the list is
  // port #0 and the third port in the list is port #2.
  println(Serial.list()); 

  // Open the port that the Arduino board is connected to (in this case #0)
  // Make sure to open the port at the same speed Arduino is using (9600bps)
  port = new Serial(this, Serial.list()[0], 9600);
} 

// function to test if mouse is over square
boolean mouseOverRectA()
{
  return ((mouseX >= 0)&&(mouseX = 0)&(mouseY = 301)&&(mouseX = 0)&(mouseY = 601)&&(mouseX = 0)&(mouseY = 0)&&(mouseX = 301)&(mouseY = 301)&&(mouseX = 301)&(mouseY = 601)&&(mouseX = 301)&(mouseY <= 600));
} 

void draw()
{
  background(#FFFFFF);
  stroke(255);
  line(0, 295, 700, 295);
  strokeWeight(2);

  if(mouseOverRectA())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('A');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('B');       // send an 'L' otherwise
  }
  rect(0, 0, 300, 300);  // draw square 

  if(mouseOverRectB())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('C');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('D');       // send an 'L' otherwise
  }
  rect(300, 0, 300, 300);  // draw square 

  if(mouseOverRectC())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('E');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('F');       // send an 'L' otherwise
  }
  rect(600, 0, 300, 300);  // draw square

  if(mouseOverRectD())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('G');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('H');       // send an 'L' otherwise
  }
  rect(0, 300, 300, 300);  // draw square

  if(mouseOverRectE())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('I');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('J');       // send an 'L' otherwise
  }
  rect(300, 300, 300, 300);  // draw square

if(mouseOverRectF())      // if mouse is over square
  {
    fill(#FF0000);         // change color
    port.write('K');       // send an 'H' to indicate mouse is over square
  } else {
    fill(#FFFFFF);         // change color
    port.write('L');       // send an 'L' otherwise
  }
  rect(600, 300, 300, 300);  // draw square
}

Arduino Code

int outputPin = 13;
int val;

void setup()
{
  Serial.begin(9600);
  pinMode(outputPin, OUTPUT);
}

void loop()
{
  if (Serial.available()) {
    val = Serial.read();
    if (val == 'A') {
      digitalWrite(2, HIGH);
    }
    if (val == 'B') {
      digitalWrite(2, LOW);
    }
     if (val == 'C') {
      digitalWrite(3, HIGH);
    }
    if (val == 'D') {
      digitalWrite(3, LOW);
    }
     if (val == 'E') {
      digitalWrite(4, HIGH);
    }
    if (val == 'F') {
      digitalWrite(4, LOW);

    }
    if (val == 'G') {
      digitalWrite(5, HIGH);
    }
    if (val == 'H') {
      digitalWrite(5, LOW);

    }
    if (val == 'I') {
      digitalWrite(6, HIGH);
    }
    if (val == 'J') {
      digitalWrite(6, LOW);

    }
  }
}

Hardware: Each LED is directly connected to Digital Pins 1-6 and Ground.

Midterm Project: Fireflies in a Jar

Filed under: 7: Mid-Term Project, Assignments, Gee Kim — gskim @ 2:00 am

Fireflies in a Jar is attaching tool for children who can’t sleep in the dark. This tool is going to help the child learn to adjust their pupils in the dark, and eventually get used to the darkenss of the room. Hopefully this will take away their fear of the dark.

_______________________

Here is how I set up the wiring from the arduino to the bread board to the jar:

img_0573.jpg

And here is the circuit diagram:

circuit3.jpg

As you can see from the image above, there is a master/mercury switch that is attached to the jar. There are also three LEDs that correspond to three different switches.

How will it work? Each firefly will have an LED attached to it. The fireflies will sit on its own on/off switch. Removing the firefly from the switch will let us know which firefly is being put into the jar and need to be on once the jar is closed. The jar will also has a mercury switch, attached to the lid, that will tell us when the jar is closed. Once a firefly is placed into the jar, and the jar is closed, the program for that firefly will run as long as the jar is closed. If the jar opens and closes, with the same firefly in the jar, the program of that firefly will restart. This works with all the fireflies.

(for more info on this project, read “Midterm Proposal: Fireflies in a Jar”)

_______________________

Parts List: Arduino, 3 LEDs (white), 3 on/off switch, mercury switch, jar/box with lid, circuit wire, wiring tape

_______________________

Here are the links to the video demonstration of how it works:

  • http://www.youtube.com/watch?v=oRv7qrKfryM (how switch corresponds to the LED)
  • http://www.youtube.com/watch?v=Jvm_uhmg7YA (what happens when you place an LED into the jar)
int switchPin0 = 7;    // connect switch 0 to pin 0
int switchPin1 = 2;    // connect switch 1 to pin 1
int switchPin2 = 3;    // connect switch 2 to pin 2
int switchPin3 = 4;    // connect switch 3 to pin 3
int ledPin1 = 9;       // connect led 1 to pin 9
int ledPin2 = 10;      // connect led 2 to pin 10
int ledPin3 = 11;      // connect led 3 to pin 11
int brightness = 0;    //

void setup ()
{
  pinMode (switchPin0, INPUT);  // set switch 0 as digital input
  pinMode (switchPin1, INPUT);  // set switch 1 as digital input
  pinMode (switchPin2, INPUT);  // set switch 2 as digital input
  pinMode (switchPin3, INPUT);  // set switch 3 as digital input
  pinMode (ledPin1, OUTPUT);    // set led 1 as analog output
  pinMode (ledPin2, OUTPUT);    // set led 2 as analog output
  pinMode (ledPin3, OUTPUT);    // set led 3 as analog output
  //Serial.begin(9600);
}

void loop ()
{
 //Serial.println(digitalRead(switchPin0));
 if (digitalRead (switchPin0) == LOW){         // when switch 0 is low
    if(digitalRead (switchPin1) == HIGH){      // and when switch 1 is high
      { for (brightness = 0; brightness < 255; brightness ++)
         { analogWrite (ledPin1, brightness);  // make LED get bright
           delay (5);                          // delay for 0.5 seconds
         }
      }
      { for (brightness = 255; brightness > 0; brightness --)
         { analogWrite (ledPin1, brightness);  // then make LED dim
          delay (5);                           // delay for 0.5 seconds
         }
      }
   }
    if (digitalRead (switchPin2) == HIGH){     // and when switch 2 is high
      { for (brightness = 0; brightness < 255; brightness ++)
         { analogWrite (ledPin2, brightness);  // make LED get bright
           delay (5);                          // delay for 0.5 seconds
         }
      }
      { for (brightness = 255; brightness > 0; brightness --)
         { analogWrite (ledPin2, brightness);  // then make LED dim
          delay (5);                           // delay for 0.5 seconds
         }
      }
    }
   if (digitalRead (switchPin3) == HIGH){      // and when switch 3 is high
      { for (brightness = 0; brightness < 255; brightness ++)
         { analogWrite (ledPin3, brightness);  // make LED get bright
           delay (5);                          // delay for 0.5 seconds
         }
      }
      { for (brightness = 255; brightness > 0; brightness --)
         { analogWrite (ledPin3, brightness);  // then make LED dim
          delay (5);                           // delay for 0.5 seconds
         }
      }
   }
}
}

Blog at WordPress.com.