Sensing Part 2: Air bag and comet

Nicholas's picture

 

 

Description

I assembled two mechanical force distributors and built a small Processing application that takes the input from an FSR to change the length of a comet tail as it moves around the screen. Just for fun, I also instrumented the Processing bouncing balls example (Topics -> Motion -> CircleCollision) to allow the user to change the size of one of the balls using the FSR.

Mechanical

I first assembled a Styrofoam ball covered in tissue paper. Although it imparts force reasonably well, its form factor implies that any direction of squeezing will impart the same force. However, since the FSR is flat, the it only works on one axis.

My second construction was sandwiching the FSR between cardboard and an air pocket used in packing. This works better since the interaction is also restricted to one dimension.

Programming

I built a small Processing application in which a ball bounces around the screen leaving a trail. The harder the user pushes on the FSR, the longer the trail becomes. Boundary detection code was taken from the Processing CircleCollision example.

Just for fun, I also instrumented the CircleCollision example to allow the user to make one of the circles larger by pushing on the FSR.

Code

Bouncing ball

 

/*

 * I262 - Sensor 2: Force sensors and photocells
 *
 * Comet whose length is affected by the Arduino potentiometer.
 *
 * @author Nicholas Kong
 */
 
import processing.serial.*;
 
// Serial port variables
String portname = Serial.list()[0]; // Serial port name for Arduino
Serial port;
String buf="";
int cr = 13;  // ASCII return   == 13
int lf = 10;  // ASCII linefeed == 10
float val = 0;
 
// Drawing variables
int backgroundColor = 40;
int backgroundAlpha = 0;
 
// Ball variables
Ball ball;
PVector velocity;
 
void setup() {
  size(500,500);
  noStroke();
  smooth();
 
  float ballRad = 20;
  ball = new Ball(random(ballRad,width-ballRad),
                  random(ballRad,height-ballRad),
                  ballRad);
  velocity = new PVector(2,2);
 
  // Initialize the serial port
  port = new Serial(this, portname, 9600);
  
  background(backgroundColor);
}
 
void draw() {
  backgroundAlpha = 32-int(constrain(val/250*30.0,3.0,30.0));
  println(backgroundAlpha);
  fill(0, backgroundAlpha);
  rect(0, 0, width, height);
  
  // Adjust ball's position based on its velocity
  ball.x += velocity.x;
  ball.y += velocity.y;
  
  fill(91,191,245);
  ellipse(ball.x, ball.y, ball.r*2, ball.r*2);
  checkBoundaryCollision(ball, velocity);
 
  fill(backgroundColor,0);
  rect(width-100,height-100,100,100);
  fill(255);
  text(int(val),width-50,height-50);
}
 
/*
 * Boundary collision function from CircleCollision example in
 * Processing.
 *
 * @author Ira Greenberg
 */
void checkBoundaryCollision(Ball ball, PVector vel) {
  if (ball.x > width-ball.r) {
    ball.x = width-ball.r;
    vel.x *= -1;
  } 
  else if (ball.x < ball.r) {
    ball.x = ball.r;
    vel.x *= -1;
  } 
  else if (ball.y > height-ball.r) {
    ball.y = height-ball.r;
    vel.y *= -1;
  } 
  else if (ball.y < ball.r) {
    ball.y = ball.r;
    vel.y *= -1;
  }
}
 
// called whenever serial data arrives
void serialEvent(Serial p) {
  int c = port.read();
  if (c != lf && c != cr) {
    buf += char(c);
  }
  if (c == lf) {
    val = float(buf);
    buf="";
  }
}
 
Colliding circles
/**
 *
 * INFO 262: Sensor 2: Force sensors and photocells
 *
 * Modified Processing example to process serial input from
 * an Arduino. Takes the input from a potentiometer to modify
 * the size of one of the balls.
 *
 * Original header below:
 *
 * Circle Collision with Swapping Velocities
 * by Ira Greenberg. 
 * 
 * Based on Keith Peter's Solution in
 * Foundation Actionscript Animation: Making Things Move!
 
 */
 
import processing.serial.*;
 
// Serial port variables
String portname = Serial.list()[0]; // Serial port name for Arduino
Serial port;
String buf="";
int cr = 13;  // ASCII return   == 13
int lf = 10;  // ASCII linefeed == 10
float val = 0;
 
Ball[] balls =  { 
  new Ball(100, 400, 20), 
  new Ball(700, 400, 50) 
};
 
PVector[] vels = { 
  new PVector(2.15, -1.35), 
  new PVector(-1.65, .42) 
};
 
void setup() {
  size(640, 360);
  smooth();
  noStroke();
 
  // Initialize the serial port
  port = new Serial(this, portname, 9600);
}
 
void draw() {
 
  balls[0].setRadius(constrain(val/250*100,10,100));  
  println(balls[0].r);
  
  background(51);
  for (int i=0; i< 2; i++){
    if(i == 0)
      fill(255,0,0);
    else
      fill(204);
    
    balls[i].x += vels[i].x;
    balls[i].y += vels[i].y;
    ellipse(balls[i].x, balls[i].y, balls[i].r*2, balls[i].r*2);
    checkBoundaryCollision(balls[i], vels[i]);
  }
  checkObjectCollision(balls, vels);
}
 
void checkObjectCollision(Ball[] b, PVector[] v){
 
  // get distances between the balls components
  PVector bVect = new PVector();
  bVect.x = b[1].x - b[0].x;
  bVect.y = b[1].y - b[0].y;
 
  // calculate magnitude of the vector separating the balls
  float bVectMag = sqrt(bVect.x * bVect.x + bVect.y * bVect.y);
  if (bVectMag < b[0].r + b[1].r){
    // get angle of bVect
    float theta  = atan2(bVect.y, bVect.x);
    // precalculate trig values
    float sine = sin(theta);
    float cosine = cos(theta);
 
    /* bTemp will hold rotated ball positions. You 
     just need to worry about bTemp[1] position*/
    Ball[] bTemp = {  
      new Ball(), new Ball()          };
      
    /* b[1]'s position is relative to b[0]'s
     so you can use the vector between them (bVect) as the 
     reference point in the rotation expressions.
     bTemp[0].x and bTemp[0].y will initialize
     automatically to 0.0, which is what you want
     since b[1] will rotate around b[0] */
    bTemp[1].x  = cosine * bVect.x + sine * bVect.y;
    bTemp[1].y  = cosine * bVect.y - sine * bVect.x;
 
    // rotate Temporary velocities
    PVector[] vTemp = { 
      new PVector(), new PVector()         };
    vTemp[0].x  = cosine * v[0].x + sine * v[0].y;
    vTemp[0].y  = cosine * v[0].y - sine * v[0].x;
    vTemp[1].x  = cosine * v[1].x + sine * v[1].y;
    vTemp[1].y  = cosine * v[1].y - sine * v[1].x;
 
    /* Now that velocities are rotated, you can use 1D
     conservation of momentum equations to calculate 
     the final velocity along the x-axis. */
    PVector[] vFinal = {  
      new PVector(), new PVector()          };
    // final rotated velocity for b[0]
    vFinal[0].x = ((b[0].m - b[1].m) * vTemp[0].x + 2 * b[1].m * 
      vTemp[1].x) / (b[0].m + b[1].m);
    vFinal[0].y = vTemp[0].y;
    // final rotated velocity for b[0]
    vFinal[1].x = ((b[1].m - b[0].m) * vTemp[1].x + 2 * b[0].m * 
      vTemp[0].x) / (b[0].m + b[1].m);
    vFinal[1].y = vTemp[1].y;
 
    // hack to avoid clumping
    bTemp[0].x += vFinal[0].x;
    bTemp[1].x += vFinal[1].x;
 
    /* Rotate ball positions and velocities back
     Reverse signs in trig expressions to rotate 
     in the opposite direction */
    // rotate balls
    Ball[] bFinal = { 
      new Ball(), new Ball()         };
    bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
    bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
    bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
    bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
 
    // update balls to screen position
    b[1].x = b[0].x + bFinal[1].x;
    b[1].y = b[0].y + bFinal[1].y;
    b[0].x = b[0].x + bFinal[0].x;
    b[0].y = b[0].y + bFinal[0].y;
 
    // update velocities
    v[0].x = cosine * vFinal[0].x - sine * vFinal[0].y;
    v[0].y = cosine * vFinal[0].y + sine * vFinal[0].x;
    v[1].x = cosine * vFinal[1].x - sine * vFinal[1].y;
    v[1].y = cosine * vFinal[1].y + sine * vFinal[1].x;
  }
}
 
void checkBoundaryCollision(Ball ball, PVector vel) {
  if (ball.x > width-ball.r) {
    ball.x = width-ball.r;
    vel.x *= -1;
  } 
  else if (ball.x < ball.r) {
    ball.x = ball.r;
    vel.x *= -1;
  } 
  else if (ball.y > height-ball.r) {
    ball.y = height-ball.r;
    vel.y *= -1;
  } 
  else if (ball.y < ball.r) {
    ball.y = ball.r;
    vel.y *= -1;
  }
}
 
// called whenever serial data arrives
void serialEvent(Serial p) {
  int c = port.read();
  if (c != lf && c != cr) {
    buf += char(c);
  }
  if (c == lf) {
    val = float(buf);
    buf="";
  }
}
 
Ball class
/*
 * Modified Processing example Ball class
 * with a setRadius helper method.
 *
 * @author Ira Greenberg
 * @author Nicholas Kong
 */
 
class Ball{
  float x, y, r, m;
 
  // default constructor
  Ball() {
  }
 
  Ball(float x, float y, float r) {
    this.x = x;
    this.y = y;
    this.r = r;
    m = r*.1;
  }
  
  void setRadius(float rad) {
    this.r = rad;
    this.m = rad*.1;
  }
}
 
mechanical_constructions.jpg
comet.png
colliding_balls.png
0
Your rating: None