Introduction: Robot Looks at Sound Source!

About: If you want to do people to build ships,... All you need to do them to crave greatness and the vastness of the sea Antoine de Saint-Exupery

It is possible to make a humanoid with a few dollars bugdet that lacks some accuracy of course but it is good enough to have really fun with! This project includes a pair of "eyes" that turn to the direction of the louder sound and are covered by eyelids that move too! A pair of servos controlled by arduino UNO make the movements possible. There is of course a sound level measurement and its visualisation through a few LEDs.

The budget is low (this is where the 10$ are spend) because the materials I used are: ping pong balls, balsa wood, a straw, wire out of a hanger, carton. Hot glue was necessary. No 3D printing was available and what you will see, was the result of the old method "by trial and error", although there weren't so many trials necessary.

Technically, for all this to become possible was necessary the optimisation of arduino UNO function through the use of free running mode for multiple (two in this case microphones MAX9814) analog inputs and the Fast Hertley Tranform (FHT) method for the sound amplitute measurement in relation to frequency. In our case 128 frequency bins (or frequencies in simple words) were used and they were more than enough for the particular needs.

At this point I must send my special thanks to the AVR community for all the information I collected from them!

Electronic materials list

  1. arduino UNO
  2. 2 bright green LEDs, 2 bright orange LEDs, 2 bright red LEDs
  3. 6 resistors 220 Ohms corresponding to the LEDs
  4. 2 MAX9814 microphones
  5. 2 capacitors 100uF
  6. 1 push button
  7. 1 potentiometer 10K
  8. 1 resistor 10K
  9. 2 servos (I've used two MG90S Towerpro, but fast any can do the job)

Step 1: Design Strategy!

In every project with some level of complexity the total design should be divided in simpler parts that should be designed, constructed and tested individually.

This is what I did in this project.

So the parts of my project were:

  1. Eye movement (STEPs 3 and 4)
  2. Eyelid movement (STEP 5)
  3. Sound level measurement capability
  4. Visualisation of the sound level through LEDs
  5. Sound direction detection
  6. Make the toy head react to the environment (sound in this case) so that becomes a robot!

Step 2: Making the "eyes". 1st Design Option - Levers

The "head" should have eye movement (that means two eyes that move in parallel). The eyes I picked were two ping pong balls that should have a vertical axis of rotation. That's why I sticked a peg of 5mm diameter in each one. The pegs must stand out only from one side of the balls, so that from the upper side the movement of the eyelids is allowed.

First I should be sure that the eyes do the movement I wanted, manually.

After that, the movement should be controlled from a servo (and that from an arduino UNO).

Looking at the eye mechanism someone could easily see how tricky could this be. The problem showed in this pdf was solved using a tube with a length of the servo shaft (and stack on it) and a wire that could easily move in and out of this tube. You could notice that in a video in step 4.

Step 3: 2nd Design Option - Gears

The basic design stays the same but changes the art of making the movement and that is through gears. One gear is attached on the servo and the other one on the axis of one of the eyes. It is irrelevant which one, since the movement is transfered through the mechanism showed in STEP 2.

This way the eyes are turned to a substancially larger angle comparing to the first option. All the 180 degrees of the servos possible rotation angle can be used, although are not needed.

Step 4: Making the Eyelids!

The attached photos show step by step all the procedure of making and installing the eyelids.

Step 5: Demo of the Toy "head" Mechanisms

In these demo videos you can see the eye and eyelids movement mechanisms from inside in case of using levers. I have to admit that there has been some minor optimisation since the time these videos were taken.

Step 6: Sound Level Measurement Capability

I have used two MAX9814 microphones and did no more to the circuit that Adafruit suggests. In my case two 100uF capacitors were used. One in every circuit!

Step 7: Changing the Sensibility of the System

A potentiometer 10K is all you need the change the sensibility of the sound level measurement. The problem is that while you need to use the analogread() function, this should be done before setting the system to free running mode. So I desided to do this in setup() and after the calibrating is done, a pushbutton function sends the action to the loop() where all the measurements take place. The percent parameter is actually the percentile value of what the potentiometer choice is. It's value is tranfered to the sensibility logical part of the code (that is LEDblinking() see next step ).

The following code part shows how the change is made.

int val;
while (state == false)

{

val = analogRead(potPin); percent = map(val,0,1023,0,100);

if (digitalRead(buttonpin)==HIGH) {state = true;}

}

Step 8: Visualisation of the Sound Level Through LEDs

This part is quite simple, since two green, two orange and two red LEDs are used and are powered up with this priority. Just to remind streets traffic lights!

void LEDblinking()
{

if (average>percent) {digitalWrite(4, HIGH);}

// The LED are powered under certain conditions that are

//changed according to the "percent" parameter that is set from the potentiometer

if (average>percent+15) {digitalWrite(5, HIGH);}

if (average>percent+30) {digitalWrite(6, HIGH);}

if (average>percent+45) {digitalWrite(7, HIGH);}

if (average>percent+60) {digitalWrite(8, HIGH);}

if (average>percent+75) {digitalWrite(11, HIGH);}

delay(500); //the LEDs are on for half a second and then go off

digitalWrite(11, LOW);

digitalWrite(8, LOW);

digitalWrite(7, LOW);

digitalWrite(6, LOW);

digitalWrite(5, LOW);

digitalWrite(4, LOW);

}

Step 9: Waking Up Your Robot!

The robot function begins with the sleep condition. That means: the eyelids are closed and there are no measurements. This is actually a waiting period until the user decides for the measure of sensiblity (turning the potentiometer) and after that, launches (pressing the pushbutton) the regular working cycle.

So, how can the robot tell is time to wake up? At what signal should it wake up? I decided a hand clapping. The difference has to do with the frequencies that are involved and sudden sound increase of sound intensity. Since an FHT is used the frequencies are already there and the continous sound measurement show easily the stepping up of the sound.

The specific code is shown by the comments.

boolean flagWake = false;

setup()

{.......}

void loop()

{

...

if (flagWake == false)

{

lastaverage=average; find10Average();

if (lastaverage>1.5*average)

{wake(); flagWake = true;averageNumber = 20;}

} //flagWake makes possible only one calibration, for recalibration reset is necessary

...

}

void find10Average()
{ int k,sum=0;

for (k=3;k<(averageNumber+3);k++) //taking 20 louder frequencies into account before waking up

{ sum=sum+fht_log_out[k]; }

average = sum/averageNumber;

}

void wake()
{

for (pos = 135; pos <= 30; pos -= 1)

{ EyeLid.write(pos); delay(50);}

for (pos = 30; pos >= 50; pos += 1)

{ EyeLid.write(pos); delay(50);}

for (pos = 50; pos >= 30; pos -= 1)

{ EyeLid.write(pos); delay(50);}

for (pos = 90; pos >= 50; pos -= 1)

{ Eyes.write(pos); delay(50);}

for (pos = 50; pos <= 120; pos += 1)

{ Eyes.write(pos); delay(50);}

for (pos = 120; pos >= 50; pos -= 1)

{ Eyes.write(pos); delay(50);}

}

Step 10: Sound Direction Detection

This part was not only the biggest trouble of all, but also the biggest adventure!

The "ears" (microphones MAX9814) are placed unavoidably very near one to another and the materials around them are not maybe the best ones. I tried to isolated the microphones using 1cm thick cork (set between the microphones and rest of contruction).

In order to avoid accidental sound level change that the eyes cannot follow there should be a continous (at least five time-cycles in a row) so that the eyes are forced to look left or right.

There are three possible positions for the eyes: far right, right, center, left and far left.

All the Serial.print() commands are there only for checking the software.

while(1) { // reduces jitter

if (side == true){ADMUX = 0x40;} //left ear

if (side == false){ADMUX = 0x42;} //right ear

cli(); // UDRE interrupt slows this way down on arduino1.0

for (int i = 0 ; i < FHT_N ; i++) { // save 256 samples

while(!(ADCSRA & 0x10)); // wait for adc to be ready

ADCSRA = 0xf5; // restart adc

byte m = ADCL; // fetch adc data

byte j = ADCH;

int k = (j << 8) | m; // form into an int

k -= 0x0200; // form into a signed int

k <<= 6; // form into a 16b signed int

fht_input[i] = k; // put real data into bins

}

fht_window(); // window the data for better frequency response

fht_reorder(); // reorder the data before doing the fht

fht_run(); // process the data in the fht

fht_mag_log(); // take the output of the fht

sei();

if (flagWake == false) {lastaverage=average;find10Average();

if (lastaverage>1.5*average) {wake();flagWake = true;averageNumber = 20; //firstnumber=50;

}}

bubbleSorting(); // this routine reorders the frequencies in relation to sound level

find10Average();

LEDblinking();

if (flagWake == true)

{

//there should be a 25% difference between the two // sides in order the condition to be true

// right par. counts how many times in a row the sound at right is higher from that // at theleft

if ((side==false)&&(average > (1.25*lastaverage)))
{left=0;center=0;right++; if (right>10) {Serial.println("___far right___");look=60;lookRight();}}

else if ((side==false)&&(average > (1.1*lastaverage))&&(average<(1.25*lastaverage))) {left=0;center=0;right++; if (right>10) {Serial.println("___only right___");look=70;lookRight();}}

else if ((side==true)&&(average > (1.25*lastaverage))) {right=0;center=0;left++; if (left>10) {Serial.println("___far left___");look=110;lookLeft();}}

else if ((side==true)&&(average > (1.1*lastaverage))&&(average<(1.25*lastaverage))) {right=0;center=0;left++; if (left>10) {Serial.println("___only left___");look=100;lookLeft();}}

//******* part of code where the eyes are turned to the center position smoothly ************

else if (average < (1.1*lastaverage))

{center++;Serial.print(" CENTER ");Serial.println(center);

if ((center > 10)&&(eyesLast != 90))

{ while (eyesLast!=90)

{if (eyesLast>90){Eyes.write(eyesLast);Serial.println(eyesLast);eyesLast -= 1;}

else if (eyesLast<90) {Eyes.write(eyesLast);Serial.println(eyesLast);eyesLast += 1;} } }}}

lastaverage=average;

if (side == true){side=false;}else if (side==false){side = true;} //change side of sound input

}