Go Down

Topic: Sound Detection Circuit - Following Handclaps (Read 8493 times) previous topic - next topic

mem

#30
Feb 08, 2008, 03:50 pm Last Edit: Feb 08, 2008, 03:51 pm by mem Reason: 1
If your mics are not highly directional, I wonder if you would be better off just using two mics angled 180 degrees from each other. Then you could use the idea we talked about before:  when at least one of the values is above your threshold, invert one of the values (say from the left mic) and add it to the right.  If the sum is very negative, its coming from the left, very positive from the right, zero from the center. If the sum is around half the negative value its coming from left center, if half the positive value then center right. (The actual position will be proportional to the sum of the two values).

quackmaster7000

WOW.  ;D Who thought it could be as simple as that, and I practically tried to re-invent the wheel; it works! That method provides enough resolution, too, so I'm set. Thank you for your patience and effort.  

quackmaster7000

Here's the final code:

Code: [Select]

int leftmicPin = 0;
int rightmicPin = 1;
int val1 = 0;
int val2 = 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);
 
 if(val1 >= 500 || val2 >= 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 - val1;        //Determine how far; more positive = more to the right; 0 = at absolute center
              Serial.println("Right");
              Serial.println(directionVal);}        
}

mem

#33
Feb 08, 2008, 11:30 pm Last Edit: Feb 08, 2008, 11:32 pm by mem Reason: 1
I am really glad you got it going. Well done.

I wonder if the following does a little more of what you want with a little less code

Code: [Select]
void loop()
{
 val1 = analogRead(leftmicPin);                 //Read all the sound sensor outputs
 val2 = analogRead(rightmicPin);
 
 if(val1 >= 500 || val2 >= 500)         //If the center sensor has been tiggered
    directionVal = val2 - val1;   //Determine how far; more negative = more to the left; 0 = at absolute center
    if(directinval < -250 )   // you may need to play with the value of this threshold.
         Serial.println("Left");
    else if( directionval > 250)
          Serial.println("Right";
    else
        Serial.println("Center");      

    Serial.println(directionVal);}  
}

quackmaster7000

Hm. That works. I edited it a bit, because I want more resolution, but even this code needs more tweaking. I'll eventually hit the sweet spot; there always is one, you just have to keep on messing with the values.

Code: [Select]

int leftmicPin = 0;
int rightmicPin = 1;
int val1 = 0;
int val2 = 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);
 directionVal = 0;
 
 if(val1 >= 500 || val2 >= 500)        
     directionVal = val2 - val1;   //Determine how far; more negative = more to the left; 0 = at absolute center; more positive = more to the right
     if(directionVal < -70 )  
         Serial.println("Left");
       else if(directionVal > -70 && directionVal < -35)
           Serial.println("Left Center");
     else if(directionVal > 70)
         Serial.println("Right");
       else if(directionVal < 70 && directionVal > 35)
           Serial.println("Right Center");
     else if(directionVal > -70 && directionVal < 70 && directionVal > 5 || directionVal < 0)
        Serial.println("Center");
 //if(directionVal > 50 || directionVal < -50)
   //Serial.println(directionVal);            
}

mem

#35
Feb 09, 2008, 10:20 am Last Edit: Feb 09, 2008, 11:00 am by mem Reason: 1
Ok, now that you have it working you may want to consider ways of further simplifying the code as well as interfacing to your motors.

Here is how I would design it. I will explain how it works but don't hesitate to do your own thing.

The code below calculates an index of steps from max left to max right. You set the number of left/right steps you want by defining the value for INDEX_STEPS. The example has three steps on each direction (left, center left and center).  Note there is a theoretical value of 'hard left' and 'hard right' that could occur if one mic was above the threshold and the other was zero. This can't happen in the real world but we include it in the code so that test values are handled correctly.

The raw index is calculated by multiplying directionVal by the number of steps and dividing this by the loudest mic value  i.e.:   (directionVal * INDEX_STEPS) / valRight ;
The index will be negative when the left mic is louder  so by adding the number of steps we get the index to range from 0 to the total number of steps  (i.e. 6)  :  directionIndex = INDEX_STEPS + ((directionVal * INDEX_STEPS) / valRight);
This enables us to use the index to access the correct action for the current position: Serial.print( directionText[directionIndex]);

The index could also be used to access constants for your steering control. If you used a servo for steering you could have an array of servo angles where 0 degrees was full left, 90 degrees center and 180 degrees full right, for example:  int ServoAngles[] = {0, 0,45,90,135,180,180};
 
Or if you wanted to use differential wheel speeds to steer you could have an array of PWM steering values for each motor, for example:
Int leftMotor[] = {255,255,127,0,0,0,0} ;
Int rightMotor[] = {0,0,0,0,127,255,255};

anyway, the explanation is longer than the code:

Code: [Select]
int leftmicPin = 0;
int rightmicPin = 1;
int valLeft = 0;
int valRight = 0;
int directionVal = 0;
int directionIndex = 0;
#define INDEX_STEPS    3  // this is the number of steps we want in our index of directions
// below are the text strings indicating sound direction.
// Note that hard left and hard right should never occur in the real world
// because this index is only returned when the sound from one mic is greater than 500 and the other mic is 0
char* directionText[] = {"hard Left", "left", "center left", "center", "center right", "right", "hard right"};  

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

void loop()
{
 valLeft = analogRead(leftmicPin);                 //Read all the sound sensor outputs
 valRight = analogRead(rightmicPin);
 
 if(valLeft >= 500 || valRight >= 500)      {
     directionVal = valRight - valLeft;   //Determine how far; more negative = more to the left; 0 = at absolute center; more positive = more to the right
     if(directionVal >= 0)               // left is smaller so get positive index
         directionIndex = INDEX_STEPS + ((directionVal * INDEX_STEPS) / valRight);   //  3 if center, 4 if center right, 5 if right
     else
         directionIndex = INDEX_STEPS + ((directionVal * INDEX_STEPS) / valLeft);   //  1 if left, 2 if center left, 3 if center
         
     Serial.print( directionText[directionIndex]);  
 }      
}


p.s. if you do want to stay with a version that uses if statements instead of an index, you need to use curley brackets after your first if statment testing if either value >= 500.

quackmaster7000

Thanks! That works better than the if...then code! I wish I knew how it worked, so I don't feel guilty when I finish my robot and say "Yup, I did it all myself!"  ::) . Have you heard of LOOKUP and LOOKDOWN? Is that how the INDEX command works? I used LOOKUP and LOOKDOWN with my Basic Stamp II. Could you explain the code a little further? Thanks.

mem

#37
Feb 09, 2008, 05:09 pm Last Edit: Feb 09, 2008, 05:41 pm by mem Reason: 1
No commands are used in that calculation, its just some arithmetic to calculate  the average level (the sum of the two mics) as a ratio of higher of the two levels. This ratio is divided by the number of steps so that you end up with one of three states you are looking for (center, midway, and maximum).

To help visualise what is happening, get a piece of paper and write down some test cases. Choose a representative range of value pairs for the left and right mics. For example:
100,100  - both left and right are below threshold
500,400  - left above threshold and right below
1000, 400 - as above (but more to the left )
Etc.  (do enough to get a few examples of each of the six cases)

Then run through the math using a calculator for each case calculating:

the sum of the right minus the left
divide that number by the larger of the two values. This gives the ratio of the sum to the highest value.

You want to know if that ratio is close to the highest value (i.e. above 2/3 of the total)

Or if its below 1/3 of the total and therefore close to 0 (center)

Or if its above 1/3 but below 2/3 then its midway.  

If you do that on calculator you will get floating point answers.
However the arduino is happier doing integer math and by doing the  multiplication before the division it avoids the use of floating point.

Anyway, try some worked examples and see if that makes things clearer

edit: if the expression:
   #define INDEX_STEPS    3
seems odd, it is a shorthand way C has to define a constant. You can replace if with:
  int INDEX_STEPS = 3;
if that seems more comfortable




quackmaster7000

Ooooooh. I see. Then what does this do: "char* directionText[] ="?

mem

"char* directionText[] ={"hard Left", "left", "center left", "center", "center right", "right", "hard right"};  


creates an array of pointers to strings. The array is called directionText and it is initialised to the values in the curley braces.

directionText[0] is the first element and its value is "hard Left"
directionText[1] is the second element and its value is "Left"
and so on to the last element, directionText[6] whose value is "hard right"

so, Serial.print(directionText[1]) sends the string "left" to the serial port


quackmaster7000


midna

Hi! I hope someone still reads this thread. I'm a person who has no previous experience of electric circuits, but enough in programming. I am trying to make my arduino detect sounds. SO far I am the happy owner of an electret microphone (omnidirectional, which is completely ok by me), and of a piezo microphone.

My piezo only reacts to knocks, the other one just sends insane chains of values even if I am quiet as a mouse. I understand I need an amplifier. I have NO IDEA how to connect it to the piezo/electret and I do not understand the strange drawings on the websites. I only understand pictures or literally colourful explanations. Can you guys help me? I have the LEDs down and working, I can connect them and make them be bright and so on, but can't get a value from the mikes. Please share some wisdom with me, it should be elementary for you from what I hear.

Midna

mem

Hi midna,

The best advice I can give is to encourage you to read up on audio amplification in an elementary electronics book.  A visit to your local library should provide you with access to better explanations then I could give in a few sentences here.
And I am sure you can find resources on the net for people just coming into electronics that explain concepts like microphone amplification in terms you can understand.

I hope this is the start of a very enjoyable learning experience you.

Have fun!

midna

Hello to you too!

Sadly for all of us, I have just spent my last two evenings in search of an easy to do, DIY tutorial, even on youtube. However I found nothing that looked doable. There is a gap between the same rotten tutorials that you can find even on the arduino page, and the supercool-supercomplicated stuff. The kind-of-easy things are completely omitted. I just need the simplest picture of a mike connected to an amplifier, and back to arduino. Apparently it is one of the most omitted things in the history of electronics. But thanks for your advice!

Midna

Go Up