Sunday, February 24, 2013

Measuring the distance from speaker to microphone

Introduction

I have worked for a while on a way of telling the distance from a speaker to a microphone. I want to use a number of microphones to localize a sound-source using trilateration.

After a number of failing attempts of using Arduino libraries for FFT and specialized IC´s that can lock on to a specific frequency. I decided to just think over the problem myself and try to do what I can...

Method

The speed of sound is 340 meters per second at sea level 343.2 at 20 degrees Celsius  The aimed distance is 2 meter. The time for the sound to travel the distance I want to cover is 2 /340 = 0,00583 seconds. 

I´d like to take as many samples during that time as possible, after that view the results on the 5150 LCD and identify the incoming sound wave visually and try to just write code that can find the first wave front.


The radio receiver and speaker.
The speaker and microphone is synchronized using 433 Mhz radio. The speaker has a radio receiver and I use the Virtual Wire library.

The code for the speaker:
#include <VirtualWire.h>
int rxpin = 2;
int speakerpin = 8;

void setup() {
  vw_set_rx_pin(rxpin);
  vw_set_ptt_inverted(true);
  vw_setup(2000);
  vw_rx_start();
}
void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
      delay(10);
      sound();
    }
}
void sound() {
    tone(speakerpin, 4800);
    delay(150);
    noTone(speakerpin);
}

The code for the microphone to synchronize
vw_send((uint8_t *)msg, strlen(msg));
vw_wait_tx(); // Wait until the whole message is gone
delay(10);
After that I sample as fast as I can. 

for (int i = 0; i< BUFFERWIDTH; i++) {
    buffer[i] = analogRead(analogPin);
}

In order to sample faster I set the prescale to 16

And I render the result to the LCD
No signal, the sine wave is noise (from the USB connection to the computer I think)

A signal, line is the first wave. identified and the distance is calculated.
In order to distinguish between the signal and the no signal I first identify the valleys.
I do that by comparing a sample with a few of its neighbors.
The code is pretty nasty, but you can see the results as small dots in the lower end of the screen.
   boolean isValley(int index, int numAround) const {
        int valueIndex = getAt(index);
        int totalDiff = 0;
        for (int i = 0; i < numAround; i++) {
          int at = index + i - numAround / 2;
         
          if (at < 0) { //make sure we do not step outside the buffer
            at = 0;
          } else if (at > BUFFERWIDTH) {
            at = BUFFERWIDTH;
          }
          int valueAt = getAt(at);
          int diff = valueAt - valueIndex;
          if (diff < 0) {
            diff = -1;
          } else {
            diff = 1;
          }
          totalDiff += diff;
        }
        return (numAround + totalDiff < 4);
    }
If those valleys exists at the rate of the sound then we have a sound. And I try to identify the first valley of the incoming series...
Test setup, speaker to the left and microphone to the right.

I take a number of samples at different ranges. 50, 75 and 100 cm

Result

I can now measure the distance!
Red line is actual distance, blue is measured, the samples have been ordered . The median errors for all three distances looks similar...


The error is a bit large, but the distance of one wave at 4800 Hz is 7 cm so that was expected... The median error is around +14.8 cm. It sounds reasonable to me that the synchronization takes a little bit of time. And I catch the first valley so we probably have an entire wave before that.

In order to get a proper reading I probably have to make a few different readings and use the median and subtract 14.8,

Future work.¨

I need to clean up the code a bit, experiment with different sampling rates and distances. 

Perhaps I should solder up another microphone circuit and see if I can measure using two microphones next!

But for now I'm really happy, I worked hard on this...  and now I need to spend some time with the family.




No comments:

Post a Comment