Pages: [1] 2 3   Go Down
Author Topic: Sound Detection Circuit - Following Handclaps  (Read 7314 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have made 3 identical circuits for sound detection using some LM741's, and they will function as frontal sound sensors for detecting the position (left, left center, center, right center, right) of a handclap. The robot will then turn to that heading and roam towards the source of the sound. I am having a problem, though. Distinguishing between a handclap coming from dead left and dead right is easy, but determining of it is left center, center, and right center is not so easy. I have tried different methods, but none of them seemed to work. I need help with the software. The code is below (ignore any commented out sections. Also, the code below only has the 'detect left' and 'detect right' parts worked out.) Any suggestions?
Code:
//int txPin = 7;
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin);
//  delay(3);
//  Serial.println("VALS");
  //Serial.println(val1);
  //Serial.println(val2);
  //Serial.println(val3);
  //delay(500);
  if(val1 > 500 || val2 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     if(val2 > val1)
        Serial.println("Right Detect!");
        
}
 
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is some pseudocode that I hope helps solve your problem. I have renamed the values read from the analog ports for clarity

Code:
if (leftval > centerval) {
       // here if left is louder than center
       if(leftval > rightval)
          // leftval is loudest
       else
           // righttval is loudest
}
else {
        // here if center is louder then left;
       if(centerval > rightval)        
           // centerval is loudest
        else
            // rightval is loudest
}
You may want to add a threshold in the comparison to make sure there is a meaningful difference in the levels.

  
« Last Edit: February 03, 2008, 01:30:00 pm by mem » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oooh...I know why I was screwing a lot up; I was not using the 'else' statement. That messed EVERYTHING up...
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wait - I appreciate the help, but I don't think you understand what I m getting at. Here's the code I've gotten thus far (read on after the code):
Code:

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin);
  
  if(val1 > 500 || val2 > 500 || val3 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     else if(val2 > val1)
        Serial.println("Right Detect!");
     //else if()
        //Serial.println("Center Detect!");
     //else if()
        //Serial.println("Left Center Detect!");
     //else if()
        //Serial.println("Right Center Detect!");
}

The commented out areas are what I haven't yet figured out. The triggering amplitude for a handclap, represented as a decimal after A/D conversion is ~500 or more. Therefore, I use that value [500] in the if...then statements. These microphones will be spaced ~6" apart. Maybe I should use a conical fixture on each to make it more accurate?   :-/
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I assumed you wanted to detect the loudest of three inputs that is over 500? If so, then you can add the following  to the fragment I posted earlier:
put your check:
   if(val1 > 500 || val2 > 500 || val3 > 500)
before the if statements.  And replace the comments:
     //xxx val is the loudest  with the appropriate
     Serial.println("xxx Detect!");

And you can stick with your variable naming or mine but it needs to be consistent.

anyway, I hope that helps. Or did you want to do something completly different?
« Last Edit: February 03, 2008, 05:43:29 pm by mem » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hmmm...I still don't quite see what you are trying to tell me  :-[. Yes, I know how to get it to register which value is the highest over 500. And no, I don't need to put that statement before every if statement. The thing is, the circuits are relatively sensitive, so if I were to stand across the room (say, 8 feet away), it's not like only one of the three microphones is going to register the handclap; all three most likely will exceed the 500 threshold, the closest mic being the highest, the adjacent one being the second highest, and the third one probably registering just above 500 (usually if you clap more towards the extremes of right and left, the opposite one will not register the handclap. for example, if you clapped way off to the right, the mic on the far left would not exceed the 500 value). The problem I am having is distinguishing a clap from directly in front of the array, between the left and center microphones (left-center), and between the right and center microphones (right-center). I am HOPING that I can get that kind of resolution (just left-center and right-center; I know I can pick out a clap from the dead center...), but maybe it can't be done?
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Aha, I think I am clearer on what you want to do.

Here is a simple way you could use the output from the louder two of the three mics to determine intermediate sound sources.

First determine which two mics  are the loudest . This should be two adjacent mics, if all three are about the same then you can assume the source is coming from the center. If the left and right are louder than the center then you either have two sound sources or a faulty mic.

If it's the left and center mics that are loudest, then multiply the value you get from the left mic by minus one so the sign is inverted and add it to the right mic value. A resultant value close to 0 will be centered between the mics, values more positive will be closer to the center, more negative closer to the left.

You can do the same thing with the center and right mics, and it may  be more intuitive if in this case you invert the center value so positive values always indicate sound sources to the right.

I hope that makes sense.
« Last Edit: February 03, 2008, 09:11:57 pm by mem » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yay!  ;D I figured it out!

Quote

"Here is a simple way you could use the output from the louder two of the three mics to determine intermediate sound sources.
 
First determine which two mics  are the loudest . This should be two adjacent mics, if all three are about the same then you can assume the source is coming from the center."

Those couple first words from you gave me an idea. I figured out that the Arduino had a MAX function, which worked well. Now I am going to move on to doing the more complex logic (invert the values, add them, see which is closer or farther from zero...) and see if I can pull that off...  :-/
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh yeah, and here's the code:

Code:
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int maximumVal = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin);
  
  if(val1 > 500 || val2 > 500 )//|| val3 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     else if(val2 > val1)
        Serial.println("Right Detect!");
     //else if
        //Serial.println("Center Detect!");
     //else if()
        //Serial.println("Left Center Detect!");
     //else if()
        //Serial.println("Right Center Detect!");
  if(val3 > 500)
     maximumVal = max(val1, val2);
        if(maximumVal == val1)
           if(maximumVal > 500)
              Serial.println("Left Center Detect!");
        if(maximumVal == val2)
           if(maximumVal > 500)
              Serial.println("Right Center Detect!");
}
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

...And here's the more advanced one. But there's one problem with it: for some reason, the right value (when the center mic value is subtracted from the right value/inverted and added) sometimes returns negative values. Why? It shouldn't, right? I don't want that; that will mess up logic if you know what I mean. BTW, thanks a million for the help.

Code:
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int maximumVal = 0;
int directionVal = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);                 //Read all the sound sensor outputs
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin);
  
  if(val3 > 500)                                 //If the center sensor has been tiggered
     maximumVal = max(val1, val2);               //Find the highest value out of the left and right sensors
        if(maximumVal == val1)                   //If the result is the left sensor, continue statement
           if(maximumVal > 500)                  //If the sensor has been triggered, continue statement
             {directionVal = val2 - val1;        //Determine how far; more negative = more to the left; 0 = at absolute center
               Serial.println("Left");
               Serial.println(directionVal);}  
        if(maximumVal == val2)                   //If the result is the right sensor, continue statement
           if(maximumVal > 500)                  //If the sensor has been triggered, continue statement
             {directionVal = val2 - val3;        //Determine how far; more positive = more to the right; 0 = at absolute center
               Serial.println("Right");
               Serial.println(directionVal);}        
}
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
"So...What does this thing do?"
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I take that back - the values for the right mic are wrong altogether. When the handclaps come nearer to the center (and therefore farther left from the far right mic), they go even further into the negatives (like -175 for example), and when you move towards the right away from the center mic, and closer towards the far right mic, the value eventually reaches zero. What is wrong? Remember the values in the code are:

'val1' - The left mic
'val3' - The center mic
'val2' - The right mic

I have not 'negated' the values and then added them, I simply subtracted. Be sure not to mix that up. Thanks!
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

because there are no brackets after the first if statement, the code does not correctly handle cases where val3 <= 500 ( the statement maximumVal = max(val1,val2) will not be executed)

using if statements is a little cumbersome for what you want to do so perhaps something like the following is a better approach.
Code:
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;

int values[3];   // this holds readings from the three mics
int loudest[3];  // this holds indexes to the above values, after sorting, the loudest will be first (i.e. loudest[0]);  
char *Positions[3] = {"Left", "Center", "Right"};  // string used to display the calculated position

void setup() {
   Serial.begin(9600);      
}

void readValues(){     // function to load the value array with sensor data and initialise the loudest array so it can later be sorted
  for(int i=0; i < 3; i++){
     values[i] = analogRead(i);
     loudest[i] = i; // this stores the index to the above values, ready for sorting
  }  
}

void sortValues(){   // this function orders indexes in the loudest array with the loudest first
   if(values[0] < values[1])
      swap(0,1);
   if(values[1] < values[2])
      swap(1,2);
   if(values[0] < values[1])
      swap(0,1);    
}

void swap(int index1, int index2){  // utility function to swap the values in the values array at the given indexes
   int temp;
   temp = values[index1];
   values[index1] = values[index2];
   values[index2] = temp;  
}

void processLoudest(int index){
    Serial.print("the sound is coming from the ");
    Serial.println(Positions[index]);  
}

void processBetween(int index1, int index2){
    Serial.print("the sound is coming from the ");
    Serial.print(Positions[index1]);  
    Serial.println(Positions[index2]);  
    // TODO: you could add code to calculate values[index1] - values[index2] to get the relative position if required
}


void loop()
{
   readValues();                 //Read all the sound sensor outputs
   sortValues();                     // order the array of indexes with the loudest first
   if(values[loudest[0]] > 500){      // only process output if at least one value is greaer than 500
      if(values[loudest[1]] > 500)    // check if the first and second loudest are greater than 500
         processBetween(loudest[0], loudest[1]);   // process the two loudest values
      else
         processLoudest(loudest[0]);  // only one value greater than 500
   }                
}

I have not tested the code so it may need some work but I hope it helps.
« Last Edit: February 05, 2008, 04:58:43 am by mem » Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
&quot;So...What does this thing do?&quot;
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow. Thanks! I just tested it though, and something must be written wrong because when any single microphone detects a sound, it always says "...from the left"; The "LeftCenter" part works correctly, but the "Center", "Right" and "RightCenter" parts do not. I am sorry to say that I am just now building my knowledge of the Arduino code, so what you have written for me looks like German. Whether you have the patience or not to explain it to me, that's up to you. I would love to understand how it works, if you have the time. Here's what I DO know:

-The 'void setup () {} part is only run once.
-the 'for(int i=x; i _ y; i ++ or --) behavior  
NOTE: I noticed the "int i" part. Does that indicate that you declared the variable 'i' right there? Or is 'i' and operator and does not have to be declared??

What I DON'T know or am confused about:
-all the rest
-also, I noticed these statements:

void processLoudest(int index){
    Serial.print("the sound is coming from the ");
    Serial.println(Positions[index]);  
}

void processBetween(int index1, int index2){
    Serial.print("the sound is coming from the ");
    Serial.print(Positions[index1]);  
    Serial.println(Positions[index2]);  

I can understand why you have the first one, but why do you need the second? What is the difference between the two? Also, I have never been able to get the Arduino to output a value obtained from a sensor on the same line as some works (e.g "LDR Reads: -value-). Is that, in a sense, what is being done in the above statements? And lastly, what do you mean by 'the if statements are cumbersome? How should they be any different? Maybe I should just build on that for now...If I wanted to improve the code I have already written, how do I fix the improper 'right' values? Put the brackets in? Where? Thank you for all your help.
« Last Edit: February 05, 2008, 03:42:07 pm by quackmaster7000 » Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I see what the problem is with the code I posted but lets debug it together so you can get a better idea of what is happening in the program.

Below is the sketch with some debug code added that will show how the program is manipulating the data as it runs. I think you will see that the loudest[] array never gets sorted. Do you see why it doesn't and what change you need to make to fix the program?

Code:
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;

int values[3];   // this holds readings from the three mics
int loudest[3];  // this holds indexes to the above values, after sorting, the loudest will be first (i.e. loudest[0]);  
char *Positions[3] = {"Left", "Center", "Right"};  // string used to display the calculated position

void setup() {
   Serial.begin(9600);      
}

void readValues(){     // function to load the value array with sensor data and initialise the loudest array so it can later be sorted
  for(int i=0; i < 3; i++){
     values[i] = analogRead(i);
     loudest[i] = i; // this stores the index to the above values, ready for sorting
  }  
  debugPrintValues() ;
  debugPrintLoudest();  // this is the order before sorting

}

void sortValues(){   // this function is supposed to order indexes in the loudest array with the loudest first, it doesn't, do you see why?
   if(values[0] < values[1])
      swap(0,1);
   if(values[1] < values[2])
      swap(1,2);
   if(values[0] < values[1])
      swap(0,1);    
   debugPrintLoudest();    // this is the order of the loudest array ofter a single pass through sort
}

void swap(int index1, int index2){  // utility function to swap the values in the values array at the given indexes
   int temp;
   temp = values[index1];
   values[index1] = values[index2];
   values[index2] = temp;  
}

void processLoudest(int index){
    Serial.print("the sound is coming from the ");
    Serial.println(Positions[index]);  
}

void processBetween(int index1, int index2){
    Serial.print("the sound is coming from the ");
    Serial.print(Positions[index1]);  
    Serial.println(Positions[index2]);  
    // TODO: you could add code to calculate values[index1] - values[index2] to get the relative position if required
}


void loop()
{
   readValues();                 //Read all the sound sensor outputs
   sortValues();                     // order the array of indexes with the loudest first
   if(values[loudest[0]] > 500){      // only process output if at least one value is greaer than 500
      if(values[loudest[1]] > 500)    // check if the first and second loudest are greater than 500
         processBetween(loudest[0], loudest[1]);   // process the two loudest values
      else
         processLoudest(loudest[0]);  // only one value greater than 500
   }                
}

void debugPrintValues(){  // show whats happening on the serial monitor
    // Show the three values we collected
    // this should print: "Left=x, Center=y, Right=z" where x,y&z are values previously read
    for(int i=0; i < 3;i++) {
       Serial.print(Positions[i]);   // i is the index, Position contains an array of strings
       Serial.print("=");
       Serial.print(values[i]);     // print the value
       Serial.print(", ");      
     }
     Serial.println();
}

void debugPrintLoudest(){  // show the array with sorted positions on the monitor
    // Show the three readings in order of loudest to quitest
    // This shows how the indexes are moved as they get sorted
    // this should print: "Left, Center, Right" the first time, the order should change as the array gets sorted
    for(int i=0; i < 3;i++) {
       Serial.print(loudest[i]);   // i is the index, loudest contains an index to the actual value haled in values[]
       Serial.print(", ");      
     }
     Serial.println();
}  

To answer your other questions:

> 'for(int i=x; i _ y; i ++ or --) behavior ,  Does that indicate you declared the variable 'i' right there?

Yes, int i= is a declaration of the variable i that has the benefit of only being visible within the scope of the enclosing curly brackets  {} of the for loop. This construct only works in source files compiled as C++ such as the main sketch file.

>    Serial.print("LDR Reads:  ");
>    Serial.println(value);    

Serial.print can display strings, and numbers in various bases but it can only do one of those things in each call. So to display a string of characters followed by a number we need to call it twice.  The second call to println sends the end of line character as well so the next print will be on another line.  There are more examples of this usage in the debug functions just added
    
>what do you mean by 'the if statements are cumbersome? How should they be any different?

The first versions of the sketch (before introducing the sorting function) would have required if statements detecting the six possible cases of loudness. That's a lot 'if else if' stuff and I thought that the sorting solution would be easier to follow. But the sketch can certainly be implemented using just if statements and don't let me stop you if you are more comfortable doing it that way.

Anyway, run the sketch with the debugging code and let me know if you have difficulty seeing what change you need to make to get it to work.  Feel free to add any of your own Serial.print statements to further clarify what the program is doing.
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 171
&quot;So...What does this thing do?&quot;
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok. I'll run it later today. Going back to what I said before, I REALLY don't understand that code, but when I run it tonight, I'll see if I can figure it out. I was sticking to all the if...then statements before because that's easier for me; I have only been using the Arduino since Christmas. But one thing I want to know is: is the if...then method SLOWER than yours? If you could explain to me how that program works, that would be great. I would love to know how to write mine more efficiently.
Logged

Pages: [1] 2 3   Go Up
Jump to: