Categories
Interaction Lab

Ezra’s Final Documentation Blog

Is the fare fair?  

Conception and Design

This project was created to shed light on the racial profiling done in New York City and shed light on the issue to an unfamiliar audience. For the past few semesters, I’ve been interested in transit and its accessibility to community members whether it be its efficiency and safety or the social climate of a station. Since coming to Shanghai, I noticed a very stark difference between the city’s Metro systems, being that the one in Shanghai was cleaner, more efficient, and cheaper, whereas the NYC one was grimy, dangerous, and expensive. Another important thing I noticed was the decreased police presence in the stations. In NYC, people often evade the fare because of the cost, because of this many black and Hispanic people are targeted for this crime at a disproportionate level making up the majority of fare evasion arrests.

Our project goal is to simulate this feeling of bias when paying the fare in NYC. To do this we created a replica turnstile that functions similarly to a real one except that the user pays with their demographics instead of money.  We wanted the interaction to be as similar to what is seen at the NYC subway stations as possible, and also include bias in the design.  The interaction is an initial survey, then the user taps the card, and finally, they go through the turnstile or not go through it based on the survey results. Regardless of the survey result, they can also choose to evade by going through the “Emergency Exit”. If they initially receive a negative result and evade, the sirens will blair and a video will play.

During the initial user testing, we got positive feedback on the design of the physical turnstile. People seemed to think it replicated the real one very well. However, one of the main pieces of feedback was that it wasn’t very clear what the purpose of the project was without my explaining it. To fix this, we added a little debrief at the beginning of the project to explain to the user the purpose and the goal of our design. We also added more direction on what to do after the user is finished with the survey.  Another thing was the fare evasion function, we realized because of the size of the project, we’d have to place it in a very isolated location to avoid the sensor’s going off and the siren blaring.

Fabrication and Production

When I first thought of the idea, I thought we’d only do a small-scale model, not a full-sized one. Professor Gottfried explained how creating a lifesized one would better fulfill our goal so we decided to switch. The professor helped us create a prototype with a base that would be mounted to a table and a wooden board with washers and three poles that rotated like a turnstile. We cut a slot in the base and added a solenoid that would act as a locking mechanism. On the rotating base, we cut holes for locking and for the magnets that would be used to lock the turnstile after it touches a magnet sensor. We used clamps to mount it to the table.

The most difficult part of the project was getting everything components to work with each other. Everything from the card reader, motor, magnet sensor, lights, video, and sound were all connected so they used serial communication to work with each other. This interconnectedness of our project gave us a lot of trouble when it came to coding. The survey sent a 0 or 1 value to the Arduino and when the card has been tapped a sound and light would play depending on that value. If you had enough fare you could pass through the turnstile, the motor would unlock and you could pass. If not you would hear a denied sound and if you chose to evade and pass the PIR sensor the lights would flash and a video with sirens would play.


 

It was difficult to understand which data was being read and sent by processing or Arduino, so as our project progressed it became a bit tricky to make sure everything communicated well with each other. For example, when it came time to add the video and siren effects and lights to our design since the values of the sensors consistently changed, it made the effect very inconsistent. To fix this I had to add boolean statements to stabilize the playing of the movie and lights so they would only play consistently when someone who did not have enough fare evaded.

import processing.serial.*;
import processing.sound.*;
import processing.video.*;

SoundFile farepay;
SoundFile farenotpay;
//SoundFile siren;
Serial serialPort;
int NUM_OF_VALUES_FROM_PROCESSING = 1;  /* CHANGE THIS ACCORDING TO YOUR PROJECT */
/* This array stores values you might want to send to Arduino */
int processing_values[] = new int[NUM_OF_VALUES_FROM_PROCESSING];
int NUM_OF_VALUES_FROM_ARDUINO = 3;  /* CHANGE THIS ACCORDING TO YOUR PROJECT */
/* This array stores values from Arduino */
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
int Mode = 0;
int Fare = 0;
boolean pMousePressed;
PFont font; ////!
PImage img; ////!
int Total ;
int pArduinoValue0;
Movie movie;///!!!
MyButton chargeButton;
MyButton q1a;
MyButton q1b;
MyButton q1c;
MyButton q1d;
MyButton q2a;
MyButton q2b;
MyButton q2c;
MyButton q2d;
MyButton q3a;
MyButton q3b;
MyButton q3c;
MyButton q3d;
MyButton q4a;
MyButton q4b;
MyButton q4c;
MyButton q4d;
int b_height = 1440;
int b_width = 900;
boolean armSensor = false;

class MyButton {
  PVector Pos = new PVector(0, 0);
  float Width = 0;
  float Height = 0;
  color Colour;
  String Text;
  Boolean Clicked = false;
  int Value = 0;
  MyButton(int x, int y, int w, int h, String t, int r, int g, int b, int v)
  {
    Pos.x = x;
    Pos.y = y;
    Width = w;
    Height = h;
    Text = t;
    Colour = color(r, g, b);
    Value = v;
  }
  void update()
  {
    if (mousePressed == true && pMousePressed == false)
    {
      if (mouseX>=Pos.x && mouseX<=Pos.x+Width && mouseY>=Pos.y && mouseY<= Pos.y+Height)
      {
        Mode = Mode + 1;
        Fare += Value;
      }
    } else
    {
      Clicked = false;
    }
  }
  void render()
  {
    noFill();
    strokeWeight(7);
    stroke(255, 255, 255);
    rect(Pos.x, Pos.y, Width, Height, 40); ///!!!
    fill(Colour);
    textAlign(CENTER, CENTER);
    text(Text, Pos.x+(Width/2), Pos.y+(Height/2));
  }
  boolean isClicked()
  {
    return Clicked;
  }
}


void setup() {
  size(1440, 900);
  init();
}

void init() {
  if(serialPort != null) {
    serialPort.stop();
    println("Closing serial port, hopefully this will reset the Arduino");
    delay(2000);
  }
  if (movie != null) {
    movie.stop();
  }

  background(255);
  img = loadImage("logo.png");
  farepay= new SoundFile(this, "pay.mp3");
  farenotpay= new SoundFile(this, "nopay.mp3");
  //siren= new SoundFile(this, "siren.mp3");
  movie = new Movie(this, "invasion.mp4");
  chargeButton = new MyButton(b_height/2-250, b_width/2-50, 500, 100, "Add Fare", 255, 255, 255, 0);

  serialPort = new Serial(this, "COM3", 9600);
  q1a = new MyButton(b_height/2-250, b_width/4+35, 500, 100, "Black", 255, 255, 255, 1);
  q1b = new MyButton(b_height/2-250, (b_width/4+35)+150, 500, 100, "Hispanic", 255, 255, 255, 2);
  q1c = new MyButton(b_height/2-250, (b_width/4+35)+300, 500, 100, "Asian", 255, 255, 255, 3);
  q1d = new MyButton(b_height/2-250, (b_width/4+35)+450, 500, 100, "White", 255, 255, 255, 4);
  q2a = new MyButton(b_height/2-250, (b_width/4+35), 500, 100, "18-24", 255, 255, 255, 1);
  q2b = new MyButton(b_height/2-250, (b_width/4+35)+150, 500, 100, "25-34", 255, 255, 255, 2);
  q2c = new MyButton(b_height/2-250, (b_width/4+35)+300, 500, 100, "35-44", 255, 255, 255, 3);
  q2d = new MyButton(b_height/2-250, (b_width/4+35)+450, 500, 100, "45+", 255, 255, 255, 4);
  q3a = new MyButton(b_height/2-350, (b_width/4+35), 700, 100, "<= Middle school", 255, 255, 255, 1);
  q3b = new MyButton(b_height/2-350, (b_width/4+35)+150, 700, 100, "High School", 255, 255, 255, 2);
  q3c = new MyButton(b_height/2-350, (b_width/4+35)+300, 700, 100, "University", 255, 255, 255, 3);
  q3d = new MyButton(b_height/2-350, (b_width/4+35)+450, 700, 100, "Graduate School", 255, 255, 255, 4);
  q4a = new MyButton(b_height/2-250, (b_width/4+35), 500, 100, "Unemployed", 255, 255, 255, 1);
  q4b = new MyButton(b_height/2-250, (b_width/4+35)+150, 500, 100, "Student", 255, 255, 255, 2);
  q4c = new MyButton(b_height/2-250, (b_width/4+35)+300, 500, 100, "Part-time", 255, 255, 255, 3);
  q4d = new MyButton(b_height/2-250, (b_width/4+35)+450, 500, 100, "Full-time", 255, 255, 255, 4);
  font = loadFont("SansSerif-48.vlw");

  for (int i=0; i < NUM_OF_VALUES_FROM_PROCESSING; i=i+1) {
    processing_values[i] = 0;
  }
  for (int i=0; i < NUM_OF_VALUES_FROM_ARDUINO; i=i+1) {
    arduino_values[i] = 0;
  }
  Mode = 0;
  Fare = 0;
  pMousePressed = false;
  Total = 0;
  pArduinoValue0 = 0;
  armSensor = false;
}

void movieEvent(Movie m) {
  m.read();
}

void draw() {
  getSerialData();
  boolean playmov = false;
  String research = "When it comes to fare evasion in NYC, there is racial bias when it comes to who is stopped and fined. According to NYPD data, Black and Hispanic people accounted for nearly 90% of fare-related arrests and nearly 70% of summonses. Moreover, it is reported that up to 20% of New Yorkers struggle to pay the fair which is $2.90 (21 rmb). Our project aims to simulate this bias within the fare system and policing and emulate what people who evade the fare may experience based on their demographics. By doing so, we hope to raise awareness of the inequity that plagues transit policing in NYC.";
  if (Mode == 0) {
    background(255);
    fill(0);
    textFont(font, 70);
    text("Background", b_height/2+200, b_width/10);
    textFont(font, 30);
    textAlign(LEFT);
    text(research, 620, 180, 650, 650);  // Text wraps within text box
    image(img, 0, b_width-750, 600, 600);
    // Draw the circle
    fill(28, 33, 67); // Dark color for the circle
    noStroke();
    float circleX = b_height - 100;
    float circleY = b_width - 100;
    float circleDiameter = 100;
    circle(circleX, circleY, circleDiameter);
    // Set the text properties
    fill(255); // Set the text color to white for contrast
    textSize(16); // Set the text size
    textAlign(CENTER, CENTER); // Center the text horizontally and vertically
    // Draw the text on top of the circle
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 35);
    text("Next", circleX, circleY);
  } else if (Mode == 1) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    noStroke();
    rect(0, -33, b_height, 220, 40); ///!!!!
    fill(255);
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 70);
    text("Welcome to the NYC Subway", b_height/2, b_width/10);
    chargeButton.update();
    chargeButton.render();
  } else if (Mode == 2) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    noStroke();
    rect(0, -33, b_height, 220, 40); ///!!!!
    fill(255);
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 70);
    text("What is your race?", b_height/2, b_width/10);
    q1a.update();
    q1a.render();
    q1b.update();
    q1b.render();
    q1c.update();
    q1c.render();
    q1d.update();
    q1d.render();
  } else if (Mode == 3) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    noStroke();
    rect(0, -33, b_height, 220, 40); ///!!!!
    fill(255);
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 70);
    text("What is your age bracket?", b_height/2, b_width/10);
    q2a.update();
    q2a.render();
    q2b.update();
    q2b.render();
    q2c.update();
    q2c.render();
    q2d.update();
    q2d.render();
  } else if (Mode == 4) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    noStroke();
    rect(0, -33, b_height, 220, 40); ///!!!!
    fill(255);
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 70);
    text("What is your highest-level of education?", b_height/2, b_width/10);
    q3a.update();
    q3a.render();
    q3b.update();
    q3b.render();
    q3c.update();
    q3c.render();
    q3d.update();
    q3d.render();
  } else if (Mode == 5) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    noStroke();
    rect(0, -33, b_height, 220, 40); ///!!!!
    fill(255);
    font = loadFont("SansSerif-48.vlw");
    textFont(font, 70);
    text("What is your employment status?", b_height/2, b_width/10);
    q4a.update();
    q4a.render();
    q4b.update();
    q4b.render();
    q4c.update();
    q4c.render();
    q4d.update();
    q4d.render();
  } else if (Mode == 6) {
    background(28, 33, 67);
    fill(255, 189, 89); ///!!!
    text("Card Loaded", b_height/2, b_width/2);
    text("Tap your card on the reader to pay", b_height/2, b_width/2 + 100);
    if (Fare < 8) {
      Total = 0; // Set Total to false if Fare is greater than 8
      //print("arduinoval ", arduino_values[0]);
      if (pArduinoValue0 != 0 && arduino_values[0] == 0) {
        armSensor = true;
        farenotpay.play();
        print(armSensor);
        print("fuck");
        print(arduino_values[1]);
      }
    } else {
      Total = 1; // Set Total to true otherwise
      print("arduinoval", arduino_values[0]);
      if (pArduinoValue0 != 1 && arduino_values[0] == 1) {
        //armSensor = true;
        farepay.play();
      }
    }
    processing_values[0] = Total;
    //print("processing value",processing_values[0]);
    if (armSensor == true && arduino_values[1] == 1) {
      println("Fare evasion in progress");
      //siren.loop();
      //delay(6000);
      //siren.stop();
      playmov = true;
      print(playmov);
      //delay(8000);
      //movie.stop();
    }
    if (playmov == true) {
      movie.loop();
      image(movie, 0, 0, width, height);
    }
    //keyPressed();
  }
  pMousePressed = mousePressed;
  pArduinoValue0 = arduino_values[0];
  sendSerialData();
}

void getSerialData() {
  while (serialPort.available() > 0) {
    String in = serialPort.readStringUntil( 10 ); // 10 = '\n'  Linefeed in ASCII
    if (in != null) {
      print("From Arduino: " + in);
      String[] serialInArray = split(trim(in), ",");
      if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
        for (int i=0; i<serialInArray.length; i++) {
          arduino_values[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

void sendSerialData() {
  String data = "";
  for (int i=0; i<processing_values.length; i++) {
    data += processing_values[i];
    // if i is less than the index number of the last element in the values array
    if (i < processing_values.length-1) {
      data += ",";  // add splitter character "," between each values element
    }
    // if it is the last element in the values array
    else {
      data += "\n";  // add the end of data character linefeed "\n"
    }
  }
  // write to Arduino
  serialPort.write(data);
  //print("To Arduino: " + data);  // this prints to the console the values going to arduino
}

void mousePressed() {
  // Only proceed if Mode is 0
  if (Mode == 0) {
    float distance = dist(mouseX, mouseY, b_height - 100, b_width - 100);
    float radius = 100 / 2; // The radius is half of the diameter
    // Check if the mouse click was within the circle
    if (distance < radius) {
      Mode = Mode + 1;
      if (Mode == 6) {
        armSensor = false;
      }
      print(Mode);
    }
  }
}

void keyPressed() {
  if (key == ' ') {
    //Mode = 0;
    //Fare = 0;
    init();
  }
}



Conclusion

 

I feel like people understood the general function of the interaction, which was that the survey was used as a way for payment, and based on this people could pass through. I think the main thing that could be improved was the consequences of fare evasion. To make it more realistic, I think we could’ve made it trigger at different frequencies versus having it start all the time when someone who doesn’t have enough money evades. Additionally, I think we could’ve made the setup a bit cleaner and expanded the questions in the survey. I think overall our project accomplished its designated goal which was to inform users about racial profiling in the subway; however, I didn’t take into consideration the cultural differences of the US and China. Our project is very American and it was very hard for some Chinese people to understand the complexity surrounding its meaning. I’m not sure if there was much I could’ve done to make it more accessible to a foreign audience. 

Disassembly

Appendix

 

#include <FastLED.h>

// Motor A connections

int enA = 5;

int in1 = 6;

int in2 = 7;

bool evade = false;

bool lights = false;

int sensorVal = 4;

//magnet sensor

int mag = 2;

#define NUM_LEDS 60  // How many leds on your strip?

#define DATA_PIN 3

CRGB leds[NUM_LEDS];

#include <SPI.h>

#include <MFRC522.h>

#define NUM_OF_VALUES_FROM_PROCESSING 1 /* CHANGE THIS ACCORDING TO YOUR PROJECT */

/* This array stores values from Processing */

int processing_values[NUM_OF_VALUES_FROM_PROCESSING];

int pay = 2;

#define RST_PIN 9  // Configurable, see typical pin layout above

#define SS_PIN 10  // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN);  // Create MFRC522 instance

void setup() {

  Serial.begin(9600);  // Initialize serial communications with the PC

  SPI.begin();         // Init SPI bus

  mfrc522.PCD_Init();  // Init MFRC522 card

  pinMode(enA, OUTPUT);

  pinMode(in1, OUTPUT);

  digitalWrite(in1, HIGH);

  pinMode(in2, OUTPUT);

  digitalWrite(in2, LOW);

  //neopixel

  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);

  FastLED.setBrightness(40);

  //magnet sensor

  pinMode(mag, INPUT);

  fill_solid(leds, NUM_LEDS, CRGB(0, 0, 0));

  FastLED.show();

}

void loop() {

  getSerialData();

  // Turn the first LED on, then pause

  int fareevade = digitalRead(sensorVal);

  int magstate = digitalRead(mag);

  int fare = processing_values[0];

  if (mfrc522.PICC_IsNewCardPresent() && fare == 1) {

    pay = 1;

    //Serial.print(fare);

    //Serial.print(",");  // put comma between sensor values

    Serial.print(pay);

    Serial.print(",");

    Serial.print(fareevade);

    Serial.print(",");

    Serial.print(magstate);

    Serial.println();

    //Serial.print(evade);

    Serial.println();  

    delay(20);

    for (int i = 0; i < NUM_LEDS; i++) {

      leds[i] = CRGB(0, 255, 0);  // Set LED color to red

      FastLED.show();

    }

    digitalWrite(enA, HIGH);

    delay(2000);

    delay(1000);

    for (int i = 0; i < NUM_LEDS; i++) {

      leds[i] = CRGB(0, 0, 0);  

      FastLED.show();

    }

   

    digitalWrite(enA, HIGH);

    //delay(2000);

    if(magstate == 1){

    digitalWrite(enA, LOW);}

   

  }

  else if (mfrc522.PICC_IsNewCardPresent() && fare == 0) {

    pay = 0;

    evade = true;

    Serial.print(pay);

    Serial.print(",");

    Serial.print(fareevade);

    Serial.print(",");

    Serial.print(magstate);

    Serial.println();

   // Serial.print(evade);

    Serial.println();  

    delay(20);

    for (int i = 0; i < NUM_LEDS; i++) {

      leds[i] = CRGB(255, 0, 0);  // Set LED color to red

      FastLED.show();

    }

    delay(5000);

    for (int i = 0; i < NUM_LEDS; i++) {

      leds[i] = CRGB(0, 0, 0);  // Set LED color to red

      FastLED.show();

    }

  }

if (fareevade == 1 && evade == true) {

   Serial.print(pay);

    Serial.print(",");

    Serial.print(fareevade);

    Serial.print(",");

    Serial.print(magstate);

    Serial.println();

  lights = true;

}

  if(lights == true){

   

    fill_solid(leds, NUM_LEDS, CRGB(255, 0, 0));

    FastLED.show();

    delay(100); // One second delay

    // Turn all LEDs blue

    fill_solid(leds, NUM_LEDS, CRGB(0, 0, 255));

    FastLED.show();

    delay(100); // One second

  }

else if (mfrc522.PICC_IsNewCardPresent() == false) {

  pay = 2;




    Serial.print(pay);

    Serial.print(",");

    Serial.print(fareevade);

    Serial.print(",");

    Serial.print(magstate);

    //Serial.print(evade);

    Serial.println();  

    delay(20);

   if(magstate == 1){

    digitalWrite(enA, LOW);}

    if (fare == 2 && fareevade == 1){

        fill_solid(leds, NUM_LEDS, CRGB(0, 0, 0));

        FastLED.show();

  }




}







}

void getSerialData() {

  static int tempValue = 0;  // the "static" makes the local variable retain its value between calls of this function

  static int tempSign = 1;

  static int valueIndex = 0;

  while (Serial.available()) {

    char c = Serial.read();

    if (c >= '0' && c <= '9') {

      // received a digit:

      // multiply the current value by 10, and add the character (converted to a number) as the last digit

      tempValue = tempValue * 10 + (c - '0');

    } else if (c == '-') {

      // received a minus sign:

      // make a note to multiply the final value by -1

      tempSign = -1;

    } else if (c == ',' || c == '\n') {

      // received a comma, or the newline character at the end of the line:

      // update the processing_values array with the temporary value

      if (valueIndex < NUM_OF_VALUES_FROM_PROCESSING) {  // should always be the case, but double-check

        processing_values[valueIndex] = tempValue * tempSign;

      }

      // get ready for the new data by resetting the temporary value and sign

      tempValue = 0;

      tempSign = 1;

      if (c == ',') {

        // move to dealing with the next entry in the processing_values array

        valueIndex = valueIndex + 1;

      } else {

        // except when we reach the end of the line

        // go back to the first entry in this case

        valueIndex = 0;

      }

    }

  }




}
Categories
Interaction Lab

Ezra’s Midterm Project Documentation

The Basketball Claw

Context and Significance 

The Basketball claw was inspired by the servo and potentiometer interaction in class. I was intrigued by how the potentiometer could remotely control the servo. My understanding of interaction stemmed from the idea of manipulating an object and eliciting a reaction from it or another separate thing. An example that comes to mind is a remote control and how you push a button or move a joystick and something changes because of it. We created a project similar to this robot arm, mainly replicating the arm design and changing how the rotation functions within it. I think the idea of automating basketball is very interesting.

Credit: Indian Lifehacker. “How to make robotic arm Using Servo And Adruino | Arduino Project.” YouTube, 26 March 2020, https://www.youtube.com/watch?app=desktop&v=WhcROiZtymQ. Accessed 18 March 2024. 

Although sports will most likely never be automated, I think our project can be seen as a crazy what-if scenario if somehow sports were played by robots. Our project is intended to give sports fans a glimpse into a fictional reality where games are played by controlled robots.

Conception and Design 

Initially, we designed the arm very similar to the video but we want to implement it in a claw machine somehow. We used cardboard to construct essentially the entire thing however things deviated when it came to designing the physical control of it.

Although we used potentiometers, we ended up creating a sort of control panel rather than a lever mechanism for the controller, mainly because the connection between the breadboard and the potentiometers was not the strongest.

Additionally based on the fragile state of the robot for most of its development we pivoted to the basketball design based on feedback from the user testing. We made these concessions during its development to ensure it didn’t break itself and so it could move more fluidly based on user inputs.

Fabrication and Production

I did the coding and circuit for the project and also helped balance the weight of the robot claw.

#include <Servo.h>

Servo servo1;
int serv1;

Servo servo2;
int serv2;

Servo servo3;
int serv3;

Servo servo4;
int serv4;

void setup() {
  servo1.attach(8);
  servo2.attach(9);
  servo3.attach(10);
  servo4.attach(11);
  Serial.begin(9600);
}

void loop() {

  //claw
  serv1 = analogRead(A0);
  serv1 = map(serv1, 0, 1023, 70, 180);
  servo1.write(serv1);
  Serial.println(serv1);
  delay(10);

  //short
  serv2 = analogRead(A1);
  serv2 = map(serv2, 0, 1023, 0, 180);
  servo2.write(serv2);
  //Serial.println(serv2);
  delay(10);

  //long
  serv3 = analogRead(A2);
  serv3 = map(serv3, 0, 1023, 0, 71);
  servo3.write(serv3);
  //Serial.println(serv3);
  delay(10);


  //rotation
  serv4 = analogRead(A3);
  serv4 = map(serv4, 0, 1023, 0, 180);
  servo4.write(serv4);
  //Serial.println(serv4);
  delay(10);
}

/**
* Servo that is controlled by potentiometer 
* This was adapted from a the slideshow found here:
* https://docs.google.com/presentation/d/1yCCIfXoCJEuapk9mgK4SxtSlUBg5GbxcDkBqGK5vkKM/edit#slide=id.g2bb177ec9fa_0_241
*/

Enlarge

circuit

Circuit from 04.2 - Actuators & Mechanisms | Sp24 slide 36

Emberlyn created the arm with cardboard and then we tested it with a servo, usually after this we had to reinforce the servo or fix any damage it caused to our project. When creating the circuit, I had to rewire it several times. At first, the cables were unorganized, and although the robot worked it was impossible to decipher which cable belonged to what. I fixed this issue by using electrical tape to connect each servo’s wires.

However, when my professor recommended I use a 12V power supply for the servos instead I had to reorganize my breadboard and essentially split it in half with one side for servos and one for potentiometers.  

This in turn made them a bit more stable but still very figgidity. Throughout the creation process, we collaborated in creating several design choices like aspects of the basketball court, and also during troubleshooting and testing the functionality of the robot arm.

Another issue that arose was the weight of our claw. The servo and their harsh movements often caused our robot to lose balance. We initially tried to add nuts to the base to weigh it down but that made it too heavy to rotate.

My professor told me instead of using weights to hold it down, I should put a wooden stick in the servo and put a toothpick through it to prevent the base from moving, which ultimately fixed our issue.

User Testing
 
 

During the user testing the main thing that was brought up was this unpredictability of our robot and also the practicality and goal of it. When we presented our prototype it couldn’t lift much of anything; however, we did manage to lift a small cotton ball thus inspiring our desire to create a basketball experience.

 

Looking at the unpredictability of the machine, I realized the issue was with the connection between the cables and the potentiometer Initially, I used 2 F/M cables and linked them to create a longer cable, which was unstable. I fixed this by soldering wires to the potentiometers to create a much stronger connection making the movement very consistent and stable.

Moreover, the design addition of the basketball court gave our project a solid and manageable goal.

Conclusion

 

Our initial goal was to create a remote interaction where users controlled a device to control a separate object. We successfully created this interaction; however, I think the end goal showed me that there is more to interaction than this remote movement. Based on the user testing and seeing other people’s projects, the context, purpose, and goal are also vital parts of an interaction. When it came time to present our project, we had a basketball court with a robot arm controlled by one person, and what felt like an arena of fans cheering them on. I feel like our project captured the feel of what could happen if players were replaced with giant robot claws. If we had more time I feel we could’ve made the claw more sturdy so it could pick up more things and also we could think of a more user-friendly way to control it besides knobs.

Through our setbacks and accomplishments with our interaction, I gained a grasp of the iterative process of creating interaction. Interaction development is very time-consuming and takes many attempts to get the smallest function working. Even though our project wasn’t perfect, I feel as though we achieved our intended purpose and gained more confidence in our ability to create interaction.

Disassembly
Appendix