Summary

Randy the Robot is a self-contained open cube with a personality! He has two IR sensors: one on top and one behind him. Without any human interaction, he likes to turn in circles. However, if you get closer to his top IR sensor, he will react by doing one of three things: move faster, move slower, or wave his hand at you to say hello. If you get closer to his back IR sensor, he will wonder what is there and turn around quickly to see what (or who!) is behind him.

Video

Arduino Code


#include <Servo.h>

// A -> left motor
// B -> right motor
#define MOT_B1_PIN 6
#define MOT_B2_PIN 5
#define MOT_A1_PIN 10
#define MOT_A2_PIN 11
#define LEFT_MOTOR_CIRCLE_SPEED 100
#define RIGHT_MOTOR_CIRCLE_SPEED 130
static int wheel_speed_left = LEFT_MOTOR_CIRCLE_SPEED;
static int wheel_speed_right = RIGHT_MOTOR_CIRCLE_SPEED;

#define SERVO_PIN 9
#define SERVO_UPPER_LIM 60
#define SERVO_LOWER_LIM 5
#define SERVO_INCR_AMOUNT 5

#define IR_BACK_PIN A2
#define IR_TOP_PIN A0
static int back_ir_threshold = int(random(1, 6));
static int back_ir_counter = 0;
static int top_ir_counter = 0;

Servo small_servo;
int servo_pos = 0;
int servo_dir = 1;

#define RANDOM_SEED A1

// https://courses.ideate.cmu.edu/16-223/f2018/text/lib/WheelDrive.html
void set_motor_pwm(int pwm, int IN1_PIN, int IN2_PIN) {
  if (pwm < 0) { // reverse speeds
    analogWrite(IN1_PIN, -pwm);
    digitalWrite(IN2_PIN, LOW);

  } else { // stop or forward
    digitalWrite(IN1_PIN, LOW);
    analogWrite(IN2_PIN, pwm);
  }
}

void incrementServo() {
  int tmp_pos = servo_pos + servo_dir * SERVO_INCR_AMOUNT;

  if (((servo_dir == 1) && (tmp_pos > SERVO_UPPER_LIM)) || 
    ((servo_dir == -1) && (tmp_pos < SERVO_LOWER_LIM))) {
    servo_dir = servo_dir * -1;
    incrementServo();
  }
  else {
    servo_pos = tmp_pos;
    small_servo.write(servo_pos);
  }
}

// check if sensed distance is between specified threshold
// https://acroname.com/articles/linearizing-sharp-ranger-data
bool check_IR(int IR_PIN){
  float vol = analogRead(IR_PIN)*(5.0/1024);
  float dist = 2914/(vol + 5) - 1;
  return (dist < 500);
}

float read_IR(int IR_PIN){
  float vol = analogRead(IR_PIN)*(5.0/1024);
  float dist = 2914/(vol + 5) - 1;
  return dist;
}

void idle_movement(){
// move in figure 8 or circle pattern
  set_motor_pwm(wheel_speed_left, MOT_A1_PIN, MOT_A2_PIN);
  set_motor_pwm(wheel_speed_right, MOT_B1_PIN, MOT_B2_PIN);
}

void wave_hand(){
  unsigned long start_time = millis();
  while ((millis()-start_time) < 1000){
    incrementServo();
    delay(30);
  }
}

void turn_around(){
  unsigned long start_time = millis();
  while ((millis()-start_time) < 1000){
    set_motor_pwm(LEFT_MOTOR_CIRCLE_SPEED, MOT_A1_PIN, MOT_A2_PIN);
    set_motor_pwm(-RIGHT_MOTOR_CIRCLE_SPEED, MOT_B1_PIN, MOT_B2_PIN);
  }
  while ((millis()-start_time) < 1000){
    set_motor_pwm(-LEFT_MOTOR_CIRCLE_SPEED, MOT_A1_PIN, MOT_A2_PIN);
    set_motor_pwm(RIGHT_MOTOR_CIRCLE_SPEED, MOT_B1_PIN, MOT_B2_PIN);
  }
}

void turn_around_fast(){
  unsigned long start_time = millis();
  while ((millis()-start_time) < 1000){
    set_motor_pwm(LEFT_MOTOR_CIRCLE_SPEED*1.5, MOT_A1_PIN, MOT_A2_PIN);
    set_motor_pwm(-RIGHT_MOTOR_CIRCLE_SPEED*1.5, MOT_B1_PIN, MOT_B2_PIN);
  }
  while ((millis()-start_time) < 1000){
  set_motor_pwm(-LEFT_MOTOR_CIRCLE_SPEED*1.5, MOT_A1_PIN, MOT_A2_PIN);
  set_motor_pwm(RIGHT_MOTOR_CIRCLE_SPEED*1.5, MOT_B1_PIN, MOT_B2_PIN);
  }
}

void back_ir_behavior(){
  if (back_ir_counter < back_ir_threshold) {
    turn_around();
  } else {
    turn_around_fast();
    randomSeed(analogRead(RANDOM_SEED));
    back_ir_threshold = int(random(1,6));
    back_ir_counter = 0;
  }
}

void top_ir_behavior(){
  randomSeed(analogRead(RANDOM_SEED));
  int rand_val = int(random(100));
  Serial.println(rand_val % 3);

  if (rand_val % 3 == 0){
    set_motor_pwm(0, MOT_A1_PIN, MOT_A2_PIN);
    set_motor_pwm(0, MOT_B1_PIN, MOT_B2_PIN);
    wave_hand();
  }else if (rand_val % 3 == 1){
    wheel_speed_left = LEFT_MOTOR_CIRCLE_SPEED * 0.8;
    wheel_speed_right = RIGHT_MOTOR_CIRCLE_SPEED * 0.8;
    idle_movement();
    delay(1000);
  }else{
    wheel_speed_left = LEFT_MOTOR_CIRCLE_SPEED * 1.2;
    wheel_speed_right = RIGHT_MOTOR_CIRCLE_SPEED * 1.2;
    idle_movement();
    delay(1000);
  }

  wheel_speed_left = LEFT_MOTOR_CIRCLE_SPEED;
  wheel_speed_right = RIGHT_MOTOR_CIRCLE_SPEED;
}

void setup() {
  // Initialize IR sensors
  // pinMode(IR_TOP_PIN, INPUT);
  // pinMode(IR_BACK_PIN, INPUT);

  // Initialize the motor driver control pins to output drive mode.
  pinMode(MOT_B1_PIN, OUTPUT);
  pinMode(MOT_B2_PIN, OUTPUT);
  pinMode(MOT_A1_PIN, OUTPUT);
  pinMode(MOT_A2_PIN, OUTPUT);

  // Initialize servo pin
  small_servo.attach(SERVO_PIN);

  // Start with drivers off, motors coasting.
  digitalWrite(MOT_B1_PIN, LOW);
  digitalWrite(MOT_B2_PIN, LOW);
  digitalWrite(MOT_A1_PIN, LOW);
  digitalWrite(MOT_A2_PIN, LOW);

  Serial.begin(9600);
}

void loop() {
  static int state_index = 0;

  switch(state_index) {
    case 0:
      Serial.println("State 0");
      idle_movement();
      if (check_IR(IR_BACK_PIN)){
        state_index = 1;
      } else if (check_IR(IR_TOP_PIN)){
        state_index = 2;
      }
      break;
    case 1:
      Serial.println("State 1");
      back_ir_behavior();
      if (check_IR(IR_BACK_PIN)){
        state_index = 1;
      } else if (check_IR(IR_TOP_PIN)){
        state_index = 2;
      } else {
        state_index = 0;
      }
      break;
    case 2:
      Serial.println("State 2");
      top_ir_behavior();
      if (check_IR(IR_BACK_PIN)){
        state_index = 1;
      } else if (check_IR(IR_TOP_PIN)){
        state_index = 2;
      } else {
        state_index = 0;
      }
      break;
  }
  // Serial.println(read_IR(IR_BACK_PIN));
  // Serial.println(check_IR(IR_BACK_PIN));
}

CAD Files

You can find the CAD files here.