Acoustic Vehicle Robot
Authors: Daniel Lewis and Quentin Truncale
Introduction
4180 Final Project for "Acoustic Vehicle Robot". The purpose of this project was to prototype an acoustic modem and use it to control a robotic vehicle. The idea is to send a signal at a certain frequency via a speaker. This signal will then sampled by a microphone and processed by the Goertzel algorithm. After detecting the correct frequency, the microcontroller will then send out a signal to motors in order to drive the car. The frequencies we used for this project were 800 Hz and 1 kHz.
Parts List
The parts required for this project are:
- 2 Mbed NXP LPC1768 microcontrollers
- 2 Small Geared DC Motors
- ROB-14450 (Dual H-Bridge Driver)
- Speaker (PCB Mount, Sparkfun)
- SPH0645 (I2S MEMS Microphone)
- Adafruit Bluefruit LE UART Friend
- Class D Audio Amplifier
- 4 AA Batteries
- 2 Wheels
- Shadow Chassis
Code
There are two sets of code to complete this project. The first set is for the transmitter, which effectively sends out a signal at either 800 Hz or 1 kHz depending on what button is hit on the Adafruit Bluefruit LE Connect App. Hitting '3' on this app interface sends an 800 Hz signal on the speaker which interacts with the left wheel. The '4' button sends a 1 kHz signal out from the speaker which will interact with the right wheel.
Import programTransmitter_publish
This is the transmitter file.
#include "mbed.h" class Speaker { public: Speaker(PinName pin) : _pin(pin) { // _pin(pin) means pass pin to the Speaker Constructor } // class method to play a note based on PwmOut class void PlayNote(float frequency, float duration, float volume) { _pin.period(1.0/frequency); _pin = volume/2.0; wait(duration); _pin = 0.0; } private: // sets up specified pin for PWM using PwmOut class PwmOut _pin; }; BusOut myled(LED1,LED2,LED3,LED4); Serial blue(p28,p27); Speaker mySpeaker(p21); int main() { mySpeaker.PlayNote(400.0,0.05,0.1); char bnum=0; char bhit=0; while(1) { if (blue.getc()=='!') { if (blue.getc()=='B') { //button data packet bnum = blue.getc(); //button number bhit = blue.getc(); //1=hit, 0=release if (blue.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK? myled = bnum - '0'; //current button number will appear on LEDs switch (bnum) { case '1': //number button 1 if (bhit=='1') { mySpeaker.PlayNote(400.0,0.01,0.5); } break; case '2': //number button 2 if (bhit=='1') { mySpeaker.PlayNote(600.0,0.01,0.5); } break; case '3': //number button 3 if (bhit=='1') { mySpeaker.PlayNote(800.0,0.01,0.5); } break; case '4': //number button 4 if (bhit=='1') { mySpeaker.PlayNote(1000.0,0.01,0.5); } break; case '5': //button 5 up arrow if (bhit=='1') { //add hit code here } else { //add release code here } break; case '6': //button 6 down arrow if (bhit=='1') { //add hit code here } else { //add release code here } break; case '7': //button 7 left arrow if (bhit=='1') { //add hit code here } else { //add release code here } break; case '8': //button 8 right arrow if (bhit=='1') { //add hit code here } else { //add release code here } break; default: break; } } } } } }
The second set of code is the receiver. This file samples input from the microphone using interrupts. Once a full block of samples is ready, this data is run through the Goertzel algorithm in order to detect two frequencies. Once the algorithm calculation is complete, it signals to the sampling process that it is ready for another block. Also, depending on the detected frequencies, the motors will turn on and off.
Import programReceiver
This is the receiver code for the 4180 project.
#include "mbed.h" #include "I2S.h" #include "Motor.h" #define sample_freq 8000.0 #define M_PI 3.14159265358979323846 //108 is good bin size // Globals I2S i2srx(I2S_RECEIVE, p17, p16, p15); DigitalOut myled1(LED1); DigitalOut myled2(LED2); DigitalOut myled4(LED4); Motor Lmotor(p21,p6,p5); Motor Rmotor(p22,p7,p8); volatile int updating_data[108] ={0}; //i2s data volatile int i = 0; // counter volatile bool dv = 0; // data valid flag volatile bool gr = 1; // goertzel ready flag volatile float data[108] = {0.0}; //complete data block // Interrupt to read microphone void i2s_isr() { if (i<108){ updating_data[i] = (i2srx.read()>>7);//read value using I2S input dv = 0; i++; } else{ if (gr){ i = 0; for (i=0; i<108; i++) data[i] = float(updating_data[i]); //shift data to constant register dv = 1; } i = 0; updating_data[i] = i2srx.read(); } } // Goertzal function float goertzel_mag(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, volatile float* data) { float realW,imagW,d1,d2,y,resultr,resulti,magnitude; realW = 2.0*cos(2.0*M_PI*TARGET_FREQUENCY/numSamples); imagW = sin(2.0*M_PI*TARGET_FREQUENCY/numSamples); d1=0; d2=0; for (int n=0; n<numSamples; n++) { y = data[n] + realW*d1 - d2; d2=d1; d1=y; } resultr = 0.5*realW*d1 - d2; resulti = imagW*d1; magnitude = sqrtf(resultr*resultr + resulti*resulti); return magnitude; } int main() { //Goertzel function initialize int numSamples = 108; //block size int TARGET_FREQUENCY = 800; //frequency to detect int SAMPLING_RATE = sample_freq; //I2S sampling frequency float mag1 = 0.0; float mag2 = 0.0; //Motor Initialize bool lm_moving = 0; bool rm_moving = 0; //i2s microphone initialize i2srx.stereomono(I2S_MONO);//mono not stereo mode i2srx.masterslave(I2S_MASTER);//master drives clocks i2srx.frequency(sample_freq);//set sample freq i2srx.set_interrupt_fifo_level(1);//interrupt on one sample i2srx.attach(&i2s_isr);//set I2S ISR i2srx.start();//start I2S and interrupts while(1) { if(dv && gr){ gr = 0; mag1 = goertzel_mag(numSamples, TARGET_FREQUENCY, SAMPLING_RATE, data); mag2 = goertzel_mag(108.0,1000.0,SAMPLING_RATE,data); gr = 1; myled4 = !myled4; } //3000000 if (mag1>3000000) { myled1=1; rm_moving = !rm_moving; } else myled1=0; if (mag2>3000000) { myled2=1; lm_moving = !lm_moving; } else myled2=0; if (lm_moving) Lmotor.speed(-0.5); else Lmotor.speed(0); if (rm_moving) Rmotor.speed(0.5); else Rmotor.speed(0); } }
Hardware Setup
The pinouts for the transmitter portion of the hardware setup are connected as follows:
mbed | Audio Amp | Speaker | Bluetooth | Battery |
---|---|---|---|---|
gnd | pwr - | gnd | - | |
Vout | pwr + | Vin | ||
p28 | in+ | RXI | ||
p27 | TXO | |||
gnd | CTS | |||
out + | + | |||
out - | - | |||
Vin | + |
The pinouts for the receiver portion of the hardware setup are connected as follows:
mbed | I2S Mic | H-Bridge | Motor 1 | Motor 2 | Battery |
---|---|---|---|---|---|
Vout | 3 V | Vcc / STBY | - | ||
gnd | gnd | gnd | |||
p15 | BCLK | ||||
p17 | DOUT | ||||
p16 | LRCL | ||||
p5 | AI2 | ||||
p6 | AI1 | ||||
p7 | BI1 | ||||
p8 | BI2 | ||||
p22 | PWMB | ||||
p21 | PWMA | ||||
AO1 | red | ||||
AO2 | black | ||||
BO1 | red | ||||
BO2 | black | ||||
Vin | VM | + |
After setting up the chips, put your breadboard on a connected shadow chassis and it should look somewhat like the image below.
Here is a video of the working robot:
Please log in to post comments.