Ideation

After comparing our lists of 50 ideas, we narrowed our favorites down to a couple. We thought of doing a robot which would react to taps around it, but chose to adapt that concept to a dog which would respond differently according to how many times it was pet. We believed the dog would be more interesting both for us and for the children.

As we discussed what our dog would do, we decided it would have four different actions: sticking its tongue out, wiggling its tail, shaking its ears, and moving around. As a finishing touch, we thought it would be cool to add some fur after the dog was done in order to make it look and feel more realistic.

Design

We began our design process by sketching what the dog should like, then proceeded to actually draw the parts in SolidWorks. The hardest part of our project was designing the tongue mechanism, since we had to somehow convert a servo’s rotational motion to a translational movement for the tongue to stick out.

SolidWorks image of the interior
Initial test of pet detection and state change

Here is a sketch of the tongue mechanism (unfortunately we did not take any pictures; after repeated petting from the children, it is now broken):

As the servo rotates, the paper rolls back

Circuitry

Our circuits were rather simple, consisting only of servos and photoreceptors. The original concept also had DC motors for movement, but unfortunately we could not drive them along with everything else as our power supply could not provide enough current.

The photoreceptors were responsible for detecting ‘pets’ both to the dog’s head and back, while the servos moved the ears, tongue, and tail accordingly.

Code

Our software was responsible for detecting the ‘petting’ and controlling the servos according to the dog’s state. Whenever idle, the dog slowly wiggled its tail, as if asking for attention. The program is a state machine that changes state when a pet is detected. There is also a watchdog timer of 30s which reverts the dog to an idle state when no petting is found, which would make children have to pet it often.

#include <Servo.h&gt;

const int HEAD_SEN_PIN = A1;
const int BODY_SEN_PIN = A0;

const int EAR_L_SVO_PIN = 8;
const int EAR_R_SVO_PIN = 7;
const int TAIL_SVO_PIN = 4;
const int TONGUE_SVO_PIN = 2;

const int MOT_L_PIN1 = 5;
const int MOT_L_PIN2 = 6;
const int MOT_R_PIN1 = 9;
const int MOT_R_PIN2 = 10;

Servo ear_l_svo, ear_r_svo, tail_svo, tongue_svo;

const int PET_THRES = 500;
const int PET_TIME_THRES = 1000;
const int RESET_TIME_THRES = 30000;

int count = 0;
int pwm = 0;
float counter = 0;

long start_t = millis();
long prev_t = start_t;
long last_pet_t = start_t;

bool idle = true;
bool is_being_petted = false;

void setup() {
  ear_l_svo.attach(EAR_L_SVO_PIN);
  ear_r_svo.attach(EAR_R_SVO_PIN);
  tail_svo.attach(TAIL_SVO_PIN);
  tongue_svo.attach(TONGUE_SVO_PIN);

  pinMode(HEAD_SEN_PIN, INPUT);
  pinMode(BODY_SEN_PIN, INPUT);

  pinMode(MOT_L_PIN1, OUTPUT);
  pinMode(MOT_L_PIN2, OUTPUT);
  pinMode(MOT_R_PIN1, OUTPUT);
  pinMode(MOT_R_PIN2, OUTPUT);
  
  digitalWrite(MOT_L_PIN1, LOW);
  digitalWrite(MOT_L_PIN2, LOW);
  digitalWrite(MOT_R_PIN1, LOW);
  digitalWrite(MOT_R_PIN2, LOW);

  tail_svo.write(140);
  Serial.begin(9600);
}

void loop() {
  long curr_t = millis();

  int head_val = analogRead(HEAD_SEN_PIN);
  int body_val = analogRead(BODY_SEN_PIN);
  
  if ((head_val <= PET_THRES || body_val <= PET_THRES) &amp;&amp; curr_t - last_pet_t &gt; PET_TIME_THRES) {
    count++;
    reset_all();
    if (!is_being_petted) {
      is_being_petted = true;
      start_t = curr_t;
    }
    last_pet_t = curr_t;
  }

  if (curr_t - start_t &gt;= RESET_TIME_THRES) {
    count = 0;
    pwm = 0;
    is_being_petted = false;
    reset_all();
  }

  int state = count % 9;

  Serial.print(state);
  Serial.print("\t");
  Serial.println(head_val);

  if (idle) {
    move_tail_idle(curr_t);
  }

  switch (state) {
    case 1:
      move_ears(curr_t);
      idle = true;
      break;
    case 2:
      move_tail(curr_t);
      move_ears_during_tail(curr_t);
      idle = false;
      break;
    case 3:
      move_tongue(curr_t);
      idle = true;
      break;
    case 4:
      move_tail(curr_t);
      move_ears(curr_t);
      idle = false;
      break;
    case 5:
      move_tongue(curr_t);
      move_tail(curr_t);
      idle = false;
      break;
    case 6:
      move_ears(curr_t);
      move_tongue(curr_t);
      idle = true;
      break;
    case 7:
      move_tail(curr_t);
      move_tongue(curr_t);
      break;
    case 8:
      move_ears(curr_t);
      move_tail(curr_t);
      move_tongue(curr_t);
      break;
    default:
      idle = true;
      break;
  }
  
  prev_t = curr_t;
}

void move_ears(long curr_t) {
  int a = 20;
  ear_l_svo.write(a*sinf((float)curr_t/150) + 90);
  ear_r_svo.write(a*sinf((float)curr_t/150) + 90);
}

void move_ears_during_tail(long curr_t) {
  int a = 10 * sinf((float)curr_t/500) + 5;
  ear_l_svo.write(a*sinf((float)curr_t/150) + 90);
  ear_r_svo.write(a*sinf((float)curr_t/150) + 90);
}

void move_tail(long curr_t) {
  tail_svo.write(20*sinf((float)curr_t/100) + 90);
}

void move_tail_idle(long curr_t) {
  int a = 7 * sinf((float)curr_t/500) + 15;
  tail_svo.write(a*sinf((float)curr_t/100) + 140);
}

void move_tongue(long curr_t) {
  int val = 40*sinf((float)curr_t/200) + 110;
  tongue_svo.write(val);
}

void move_forward(long curr_t) {
  if (pwm <= 150) {
    pwm += 5;
  }
  move_motors(pwm, pwm);
}

void move_backward(long curr_t) {
  if (pwm &gt;= -150) {
    pwm -= 5;
  }
  move_motors(pwm, pwm);
}

void move_motors(int pwm_l, int pwm_r) {
  set_motor_pwm(pwm_r, MOT_L_PIN1, MOT_L_PIN2);
  set_motor_pwm(pwm_l, MOT_R_PIN1, MOT_R_PIN2);
}

void set_motor_pwm(int pwm, int IN1_PIN, int IN2_PIN) {
  if (pwm < 0) {
    analogWrite(IN1_PIN, -pwm);
    digitalWrite(IN2_PIN, LOW);

  } 
  else {
    digitalWrite(IN1_PIN, LOW);
    analogWrite(IN2_PIN, pwm);
  }
}

void reset_motors() {
  set_motor_pwm(0, MOT_L_PIN1, MOT_L_PIN2);
  set_motor_pwm(0, MOT_R_PIN1, MOT_R_PIN2);
}

void reset_all() {
  reset_motors();
  ear_l_svo.write(90);
  ear_r_svo.write(90);
  tongue_svo.write(90);
}

Results and Comments

While we were very pleased with our final version of our dog, the children were not as interested on it as we expected. One of the teachers, however, mentioned that the children liked the dog very much.

Active engagement with our dog lasted for an average of 30 to 45 seconds, with children being generally curious and exploratory, but after about a minute they became more neutral and bored. We acted as facilitators for most of them, while a few started petting the dog as soon as they saw it. During their curious moments, many children asked us what were the sensors since a “normal” dog did not have those.

After our visit, we believe our project had two main problems: it was too subtle and too restrict. First, many children did not understand the moving tail and ears as a symbol of the dog being excited, likely because their motion was slow, which made it seem like the dog was calm and quiet. Second, our interface was rather constrained, as the children had basically one task which was to pet the dog. Both of these factors contributed to our dog being a fun, yet short experience for them.

Even though at first the children were less interested on the dog as we expected, towards the end they came up with a new way to play with it. They took coins from Quincy and Patricia’s dinosaur to feed our dog, which seemed to make the children very engaged. With some sleights of hand, Caio made it seem like the dog was “pooping” the coins, which made them even more excited. Although we had not thought of playing with our dog like that, the children discovered this new, fun way of interacting with our project that made them very happy.