PENGUINPET COMPANION – Lucas Lin – ERIC

PENGUINPET COMPANION – Lucas Lin – ERIC

CONCEPTION AND DESIGN:

For my project, I decided to focus on creating a fun and companionable robotic pet that can be interacted with as if it were alive. I aimed to make it unique by building it from scratch and using sensors to enable user interaction. Although my project was not significantly different from existing robotic pets, I wanted to create a successful interactive experience. During user testing, I found that users were not aware of all the interaction features due to overheating of the stepper motors, and they suggested ventilating and adding more sensors to enhance interaction. However, due to time constraints, I focused on fixing the robot by hot gluing the arms and head and making the control interface bigger. Although the adaptations were effective, the controls were not as intuitive as expected.

FABRICATION AND PRODUCTION:

The longest and most time-consuming step of my project was waiting for the 3D printing process to complete. Each object took at least 10 hours to be printed, but I felt it was worth the wait. When it was time to assemble the pieces as intended in the Tinkercad Model, I encountered a significant setback. The servomotors were too weak, causing the gears to be pushed to the sides and rendering the robot inoperable. To address this issue, I decided to switch to stepper motors. This required me to rework the internal mechanism of the penguin robot body to accommodate the new motor and make it function properly. During this process, I also attempted to build a capacitive sensor. Unfortunately, my initial attempts were unsuccessful due to poor soldering, and it took me three tries before I achieved a functional sensor. However, the sensor was weak and did not work reliably. On the other hand, setting up the LED light did not present any significant difficulties. Another issue that emerged was the head’s inability to rotate correctly due to the hard wire. This was a significant problem that required considerable effort to overcome. In the end, I opted to use only one capacitive sensor and action as a trade-off for time and space constraints. As a result, I used all the pins on my Arduino. Despite the challenges and setbacks I encountered, I was able to complete the project successfully to some extent.

 

CONCLUSIONS:

Initially, my goal for this project was simply to experiment with building a functional robot. Although I didn’t include many actions due to my focus on making it work, the end result was a successful creation. While the interaction between the robot and users could be improved, it was still functional and many users found it fun and interesting to interact with. Although some audiences found the eyes to be scary, they found the overall design to be cute. My project aligns with my definition of interaction, which involves communication and action between the user and the device. Given more time, I would have liked to remodel and 3D print another interior robot part, add more sensors, make the coding clearer, and improve the processing control interface to make it more intuitive. During the process of building this robot, I faced many problems, which I learned to solve and improvise. I also learned when to give up on certain problems. Overall, this was a valuable experience in building a robot mechanism and coding it.

The project not only provided me with technical skills, but also taught me valuable lessons on how to approach complex problems and the importance of not giving up. Despite the challenges I faced, the end result was a successful and functional robot that aligned with my definition of interaction. While there is room for improvement in terms of the robot’s design and functionality, the project was a valuable learning experience and a testament to the power of creativity and persistence in the face of challenges. I am excited to take these skills and apply them to future projects in the field of robotics.

APPENDIX

Final Project All Process and Material Document

Tinkercad ModelSTLUltimaker S5 & Ultimaker S3

Images/Videos

Code

Task 4: Coding

Arduino:

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

#include <CapacitiveSensor.h>

#include <AccelStepper.h>

/* This array stores values from Processing */

int processing_values[NUM_OF_VALUES_FROM_PROCESSING];

/* Head Stepper Motor */

int DIR_PIN1 = 1;

int STEP_PIN1 = 2;

int EN_PIN1 = 3;

AccelStepper stepper1(AccelStepper::DRIVER, STEP_PIN1, DIR_PIN1);

/* Left Arm Stepper Motor */

int DIR_PIN2 = 4;

int STEP_PIN2 = 5;

int EN_PIN2 = 6;

AccelStepper stepper2(AccelStepper::DRIVER, STEP_PIN2, DIR_PIN2);

/* Right Arn Motor */

int DIR_PIN3 = 7;

int STEP_PIN3 = 8;

int EN_PIN3 = 9;

AccelStepper stepper3(AccelStepper::DRIVER, STEP_PIN3, DIR_PIN3);

/* Captivitive Sensor */

CapacitiveSensor  cs_11_10 = CapacitiveSensor(11,10);

/* LEDs */

int led1 = 13;                        // the PWM pin the LED is attached to

int led2 = 12;                        // the PWM pin the LED is attached to

int brightness = 0;                   // how bright the LED is

int fadeAmount = 5;                   // how many points to fade the LED by

int turn_cond = 0;

void setup() {

 Serial.begin(9600);

  // Declare pin 13 & 12 to be an output:

 pinMode(led1, OUTPUT);

 pinMode(led2, OUTPUT);

 // Capticitive sensor

 cs_11_10.set_CS_AutocaL_Millis(0xFFFFFFFF);

 // Enable the stepper driver 1

 pinMode(EN_PIN1, OUTPUT);

 digitalWrite(EN_PIN1, LOW);

 // Set initial speed & acceleration

 stepper1.setMaxSpeed(1000);

 stepper1.setAcceleration(1000);

  // Enable the stepper driver 2

 pinMode(EN_PIN2, OUTPUT);

 digitalWrite(EN_PIN2, LOW);

 // Set initial speed & acceleration

 stepper2.setMaxSpeed(1000);

 stepper2.setAcceleration(1200);

 // Enable the stepper driver 3

 pinMode(EN_PIN3, OUTPUT);

 digitalWrite(EN_PIN3, LOW);

 // Set initial speed & acceleration

 stepper3.setMaxSpeed(1000);

 stepper3.setAcceleration(500);

}

void loop() {

 // Getting Data

 getSerialData();

 /* testing out the capacivitive sensor*/

   Serial.println(cs_11_10.capacitiveSensor(30));

   delay(100);

  /* Conditon for when capitivitive sensor is touched eyes will blink */

   if(cs_11_10.capacitiveSensor(30) > 0 ){

     blink();

   } else if(cs_11_10.capacitiveSensor(30) == 0){

     /* keep LED on */

     digitalWrite(led1, HIGH);

     digitalWrite(led2, HIGH);

     stepper1.runToNewPosition(0);     // Return to original Postition  

   }

 /* Conditions from processing switch to blink */

   if (processing_values[2] == 1){

     blink();

     shakeHead();

   } else {

     stepper1.runToNewPosition(0);     // Return to original Postition  

   }

 /* Lifting Left Arm*/

 if(processing_values[4] == 1){

   stepper3.runToNewPosition(45);

 } else {

   stepper3.runToNewPosition(0);

 }

 /* Lifting Right Arm*/

 if(processing_values[5] == 1){

   stepper2.runToNewPosition(720);

 } else {

   stepper2.runToNewPosition(0);

 }

 /* Dance */

 if(processing_values[6] == 1){

   dance();

 } else if (processing_values[6] == 0){

   stepper2.runToNewPosition(0);

   stepper3.runToNewPosition(0);

 }

 /* Conditions from processing switch to fade */

   if (processing_values[0] == 1){

       if ((processing_values[1] == 1) && (processing_values[3] == 0)){

         analogWrite(led2, 255);

         analogWrite(led1, brightness);

         fade();

       } else if ((processing_values[1] == 0) && (processing_values[3] == 1)){

         analogWrite(led1, 255);

         analogWrite(led2, brightness);

         fade();

       }else if ((processing_values[1] == 1) && (processing_values[3] == 1)){

         analogWrite(led1, brightness);

         analogWrite(led2, brightness);

         fade();

       } else {

         analogWrite(led2, 255);

         analogWrite(led1, 255);

       }

   }

}

// From example code: Fade

void fade(){

 // change the brightness for next time through the loop:

 brightness = brightness + fadeAmount;

 // reverse the direction of the fading at the ends of the fade:

 if (brightness <= 0 || brightness >= 255) {

   fadeAmount = -fadeAmount;

 }

 // wait for 30 milliseconds to see the dimming effect

 delay(30);

}

void shakeHead(){

 stepper1.runToNewPosition(0);

 delay(1000);

 stepper1.runToNewPosition(50);

 delay(1000);

}

void dance(){

 stepper2.runToNewPosition(0);

 stepper3.runToNewPosition(45)

 delay(1000);

 stepper2.runToNewPosition(720);

 stepper3.runToNewPosition(0)

 delay(1000);

}

// From example code: Blink

void blink(){

 /* turn the LED on by making the voltage HIGH for 50 milliseconds */

 digitalWrite(led1, HIGH);

 digitalWrite(led2, HIGH);

 delay(50);

 /* turn the LED off by making the voltage LOW for 50 milliseconds*/

 digitalWrite(led1, LOW);

 digitalWrite(led2, LOW);

 delay(50);

}

/* Receive serial data from Processing */

/* You won’t need to change this code */

void getSerialData() {

 static int tempValue = 0;

 static int valueIndex = 0;

 while (Serial.available()) {

   char c = Serial.read();

   // switch – case checks the value of the variable in the switch function

   // in this case, the char c, then runs one of the cases that fit the value of the variable

   // for more information, visit the reference page: https://www.arduino.cc/en/Reference/SwitchCase

   switch (c) {

     // if the char c from Processing is a number between 0 and 9

     case ‘0’ … ‘9’:

       // save the value of char c to tempValue

       // but simultaneously rearrange the existing values saved in tempValue

       // for the digits received through char c to remain coherent

       // if this does not make sense and would like to know more, send an email to me!

       tempValue = tempValue * 10 + c – ‘0’;

       break;

     // if the char c from Processing is a comma

     // indicating that the following values of char c is for the next element in the values array

     case ‘,’:

       processing_values[valueIndex] = tempValue;

       // reset tempValue value

       tempValue = 0;

       // increment valuesIndex by 1

       valueIndex++;

       break;

     // if the char c from Processing is character ‘n’

     // which signals that it is the end of data

     case\n‘:

       // save the tempValue

       // this will b the last element in the values array

       processing_values[valueIndex] = tempValue;

       // reset tempValue and valueIndex values

       // to clear out the values array for the next round of readings from Processing

       tempValue = 0;

       valueIndex = 0;

       break;

   }

 }

}

Processing:

import processing.serial.*;

Serial serialPort;

int NUM_OF_VALUES_FROM_PROCESSING = 7;  /* 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];

boolean s = false;

boolean t = false;

boolean u = false;

boolean v = false;

boolean w = false;

void setup() {

  size(1400, 700);

  printArray(Serial.list());

  // put the name of the serial port your Arduino is connected

  // to in the line below – this should be the same as you’re

  // using in the “Port” menu in the Arduino IDE

  serialPort = new Serial(this, “/dev/cu.usbmodem101”, 9600);

}

void draw() {

  background(100);

  textSize(30);

  penguin(width/2, height/2, 2);

  

  /* TOP RIGHT*/

  push();

    translate(3 * width/4, height/4);

    penguin(0,0,1);

    text(“PENGUIN REJECTS”,-105,-105);

    

    /* Switch */

    noFill();

    stroke(255);

    strokeWeight(1);

    rect(125,-20,10,40);

    if(t == false){

      fill(255,0,0);

      circle(130,-10, 5);

      text(“OFF”, 105,-35);

    } else {

      fill(0,255,0);

      circle(130,10, 5);

      text(“ON”, 105, 60);

    }

  pop();

  

  /* TOP LEFT*/

  push();

    translate(width/4, height/4);

    penguin(0,0,1);

    

    /* Arrow + Text */

    text(“CHOOSE FADE SIDE”,-115,-105);

    

    /* Switch */

    noFill();

    stroke(255);

    strokeWeight(1);

    rect(125,-20,10,40);

    if(s == false){

      fill(255,0,0);

      circle(130,-10, 5);

      text(“OFF”, 105,-35);

    } else {

      fill(0,255,0);

      circle(130,10, 5);

      text(“ON”, 105, 60);

    }

  pop();

    

    /* Conditions for Right or Left Rotation */

    push();

      translate(width/2,height/2);

      fill(255);

      if(s == true){

        text(“R”, 135 , -150);

        text(“L”, -150 , -150);

        text(“BOTH”, -45 , -190);

        stroke(255);

        

        if(mouseX < width/2 – 50){

          text(“CLOSE LEFT EYE …”,-150,250);

          processing_values[1] = 0;

          processing_values[3] = 1;

          

          fill(255,0,0);

          circle(30,-50,20);              // Right eye

          fill(0);

          circle(-30,-50,20);              // Left eye

        } else if(mouseX > width/2 + 50) {

          text(“CLOSE RIGHT EYE …”,-150,250);

          processing_values[1] = 1;

          processing_values[3] = 0;

          

          fill(255,0,0);

          circle(-30,-50,20);              // Left eye

          fill(0);

          circle(30,-50,20);              // Right eye

        } else{

          text(“CLOSE BOTH EYES …”,-150,250);

          processing_values[1] = 1;

          processing_values[3] = 1;

          

          fill(0);

          circle(-30,-50,20);              // Left eye

          circle(30,-50,20);              // Right eye

        } 

      }

    pop();

  

  /* BOTTOM LEFT */

  push();

    translate(width/4, 3 * height/4);

    penguin(0,0,1);

    text(“LIFT LEFT ARM”,-85,-105);

    

    /* Switch */

    noFill();

    stroke(255);

    strokeWeight(1);

    rect(125,-20,10,40);

    if(u == false){

      fill(255,0,0);

      circle(130,-10, 5);

      text(“OFF”, 105,-35);

    } else {

      fill(0,255,0);

      circle(130,10, 5);

      text(“ON”, 105, 60);

    }

  pop();

  

   /* BOTTOM RIGHT */

  push();

    translate(3 * width/4, 3 * height/4);

    penguin(0,0,1);

    text(“LIFT RIGHt ARM”,-85,-105);

    

    /* Switch */

    noFill();

    stroke(255);

    strokeWeight(1);

    rect(125,-20,10,40);

    if(v == false){

      fill(255,0,0);

      circle(130,-10, 5);

      text(“OFF”, 105,-35);

    } else {

      fill(0,255,0);

      circle(130,10, 5);

      text(“ON”, 105, 60);

    }

  pop();

  

  /* MID LEFT*/

  push();

    translate(width/8, height/2 + 10);

    penguin(0,0,1);

    text(“PENGUIN DANCE”,-105,-105);

    

    /* Switch */

    noFill();

    stroke(255);

    strokeWeight(1);

    rect(125,-20,10,40);

    if(w == false){

      fill(255,0,0);

      circle(130,-10, 5);

      text(“OFF”, 105,-35);

    } else {

      fill(0,255,0);

      circle(130,10, 5);

      text(“ON”, 105, 60);

    }

  pop();

  

  // send the values to Arduino

  sendSerialData();

}

void mouseClicked(){

 /*TOP LEFT*/

 if(  dist(mouseX ,mouseY, width/4, height/4) < 160){

    if(s == false) {

      s = true;

      t = false;                   //deactivate other LED Condititon

      processing_values[0] = 1;

    } else {

      s = false;

      processing_values[0] = 0;

      processing_values[1] = 0;

    }

  }

  

  /* BOTTOM LEFT*/

  if( dist(mouseX ,mouseY, 3 * width/4, height/4) < 160){

    if(t == false){

      t = true;

      processing_values[2] = 1;

      s = false;                   //deactivate other LED Condititon

    } else {

      t = false;

      processing_values[2] = 0;

    }

  }

  

  /* BOTTOM LEFT*/

  if( dist(mouseX ,mouseY, width/4, 3 * height/4) < 160){

    if(u == false){

      u = true;

      processing_values[4] = 1;

      w = false;                   //deactivate other arm Condititon

    } else {

      u = false;

      processing_values[4] = 0;

    }

  }

  

  /* BOTTOM RIGHT */

  if( dist(mouseX ,mouseY, 3 * width/4, 3 * height/4) < 160){

    if(v == false){

      v = true;

      processing_values[5] = 1;

      w = false;                   //deactivate other arm Condititon

    } else {

      v = false;

      processing_values[5] = 0;

    }

  }

  

  /* MID RIGHT */

  if( dist(mouseX ,mouseY, width/8, height/2) < 160){

    if(w == false){

      w = true;

      processing_values[6] = 1;

      u = false;                   //deactivate other arm Condititon

      v = false;                   //deactivate other arm Condititon

    } else {

      w = false;

      processing_values[6] = 0;

    }

  }

}

void penguin(float x, float y, float s) {

  push();

    translate(x,y);

    scale(s);

    

    /* circle around pet*/

    noFill();

    stroke(255);

    strokeWeight(4);

    circle(0,0,180);

    fill(0);

    noStroke();

    strokeWeight(1);

    

    push();

      rect(-65,0,125,20);

      ellipse(-70,0,30,75);         // Left Arm

      ellipse(70,0,30,75);          // Right Arm

    pop();

    

    noStroke();

    circle(0,0,100);                // Head

    rect(-50,0,100, 50);            // Body

    stroke(0);

    fill(255);

    rect(-50,-5,100,5);             // White Strip

    arc(0,50,50,60,-PI, 0, CHORD);  // White Belly

    

    push();

      translate(0,-20);

      fill(255,0,0);

      circle(-15,-5,10);             // Left eye

      circle(15,-5,10);              // Right eye

      rotate(PI/4);

      fill(255,255,0);

      square(0,0,10);                // Nose

    pop();

  pop();

}

// the helper function below sends the variables

// in the “processing_values” array to a connected Arduino

// running the “serial_PtoA_arduino” sketch

// (You won’t need to change this code.)

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

}

Leave a Reply

Your email address will not be published. Required fields are marked *