Part 2 – Sound Following Robot

Having finished the first project, the secret box, and learned how to use sensors to control other components, I started on designing the sound following robot. Initially the idea was quite simple, have two microphones on either side of the robot and when one spikes, the robot turns one way and vice versa. However, my dream for this project was to design a robot that can be left to it’s own devices and not have any issues, essentially making it self sufficient without necessarily needing any human input and the design at the time would have gone in a straight line until there was a significant increase in volume on one side and so it would have crashed every now and again when it was faced with a wall.

I found this video online…

I’m pretty sure he used different code from what I ended up doing but it gave me the idea to have a distance sensor on top of the robot that sits on a servo and scans the environment before moving forward for a few seconds and then scanning again. I therefore combined this with the previous inspiration from the ‘photovore’ that I had found from the previous project.

I started to play around with the components that I had bought to try to understand how they worked. At the start of the first project (before I had realised that making this robot in three weeks wouldn’t work) I had bought two electret microphones and tried wiring them up as if they were an LDR just to see if I could get them to work as ‘sound dependent resistor’.

IMG_3161_2

I soon found that this wouldn’t work very well and did some more research when it came to the start of this project.
I found that I would need an op amp to go with the microphone which I could either wire up myself in a make shift sort of way or I could buy the two already combined. Given that I wanted to have these microphones to stick up like antennae out of the robot, the idea of having multiple components wired up so that they could achieve this ‘antennae’ look seemed less feasible than simply buying them all together.

I started by buying the Adafruit elecret microphones with an op amp…

photo

…but found that they were didn’t respond to sound as well I was looking for and so I explored some more and found some better microphones for what I was trying to achieve.

Screen Shot 2015-04-18 at 11.16.19 AM

From experimenting with these microphones and the sample code that came with it, I found that they would sit at about 16 when there was no sound and spike to about 300 which made them very sensitive but great for controlling components at a more specific sound level.

Before getting the microphones to control motors, I needed to understand how to use an h-bridge. this is a component that has four ‘switches’ on either side so that it can close one set and open another to make the motor spin one way and vice versa. I learned that you can use a dual h-bridge to control just one motor as well as two. So used the DC motor that came in my kit as well as a push button to change the motors direction.

I started trying to use the microphones with the DC motor from the kit I had been given, however, I found that the motor produced too much noise for the microphones to pick up anything else. This wasn’t a huge issue as the microphones would be far away from the motors on the robot and facing a different direction but I realised they might need a covering.

I was now able to start putting together my robot. Once I had the main frame built, I started to look into what motors to use. I used the drive motor sizing tool from robotshop.com to help with selecting motors with the correct torque for my robot and started the search for motors with the correct specs. i knew that they would need to be geared because a normal DC motor would not produce enough torque on its own to move a robot and soon I found two motors with a torque of 0.13Nm which was plenty, an input voltage 12V and a required current of about 100mA. They were also low noise motors which was perfect for me.

Now that everything was built and I had gotten each component to work by itself, it was time to try to combine all the individual code to create the code for the whole robot. Once I had everything together, this is the code I came up with and the fritzing below…

#define PIN_GATE_IN1 13
#define IRQ_GATE_IN1 0
#define PIN_LED_OUT1 13
#define PIN_ANALOG_IN1 A1

#define PIN_GATE_IN2 4
#define IRQ_GATE_IN2 0
#define PIN_LED_OUT2 13
#define PIN_ANALOG_IN2 A2

const int trigPin = 10;
const int echoPin = 9;

const int motor1pin1 = 2;
const int motor1pin2 = 3;
const int enablepin1 = 5;
const int motor2pin1 = 7;
const int motor2pin2 = 8;
const int enablepin2 = 6;

int check_sound_levels(int value1 = analogRead(PIN_ANALOG_IN1), int value2 = analogRead(PIN_ANALOG_IN2)) {
for(int repeat = 0; repeat < 100; repeat++) {
Serial.println(value1);
Serial.println(value2);
delay(10);
if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
return value1, value2;
}
}
}

#include <Servo.h>

Servo myservo;
int pos = 0;

void soundISR()
{
int pin_val1;

pin_val1 = digitalRead(PIN_GATE_IN1);
digitalWrite(PIN_LED_OUT1, pin_val1);

int pin_val2;

pin_val2 = digitalRead(PIN_GATE_IN2);
digitalWrite(PIN_LED_OUT2, pin_val2);
}

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

pinMode(PIN_LED_OUT1, OUTPUT);
pinMode(PIN_LED_OUT2, OUTPUT);

pinMode(PIN_GATE_IN1, INPUT);
attachInterrupt(IRQ_GATE_IN1, soundISR, CHANGE);
pinMode(PIN_GATE_IN2, INPUT);
attachInterrupt(IRQ_GATE_IN2, soundISR, CHANGE);

pinMode(motor1pin1, OUTPUT);
pinMode(motor1pin2, OUTPUT);
pinMode(enablepin1, OUTPUT);
pinMode(motor2pin1, OUTPUT);
pinMode(motor2pin2, OUTPUT);
pinMode(enablepin2, OUTPUT);

digitalWrite(enablepin1, HIGH);
digitalWrite(enablepin2, HIGH);

Serial.println(“Initialized”);

myservo.attach(12);
}

void loop()

{

int value1;
int value2;

value1 = analogRead(PIN_ANALOG_IN1);
value2 = analogRead(PIN_ANALOG_IN2);
if ((value1 >= (value2 + 10))) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, HIGH);
digitalWrite(motor2pin2, LOW);
delay(1000);
}

else if ((value2 >= (value1 + 10))) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, HIGH);
delay(1000);
}

else {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

while (((value1 – value2) > -5) && ((value1 – value2) < 5)) {

int value1;
int value2;

value1 = analogRead(PIN_ANALOG_IN1);
value2 = analogRead(PIN_ANALOG_IN2);

long duration, inches, cm;

pinMode(trigPin, OUTPUT);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

check_sound_levels();

if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
break;
}

delay(1000);
for (pos = 0; pos < 25; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(1000);
}
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

check_sound_levels();

if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
break;
}

delay(1000);
for (pos = 25; pos < 50; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, HIGH);
digitalWrite(motor2pin2, LOW);
delay(1000);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(1000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

check_sound_levels();

if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
break;
}

delay(1000);
for (pos = 50; pos < 75; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(1000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

check_sound_levels();

if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
break;
}

delay(1000);
for (pos = 75; pos < 100; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(1000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

check_sound_levels();

if (((value1 – value2) < -5) || ((value1 – value2) > 5)) {
break;
}

delay(1000);
for (pos = 100; pos > 0; pos -= 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, HIGH);
delay(2000);
}

else if (cm < 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(1000);
}

}

}

long microsecondsToCentimeters(long microseconds)
{
return microseconds / 29 / 2;
}

Screen Shot 2015-04-14 at 8.10.36 PM

The idea for this code was that the microphone values are read and if either microphone is more that 5 above the other then one motor will run and vice versa and if not then the robot goes into the while loop of the servo turning, the distance sensor being read it moving forward. This way of writing the if statement was due to the fact that both microphones did up the sound that would make it turn so it couldn’t just be if one goes above a fixed value. It also helped the robot adjust to different environments. There was a ‘break’ after each time the servo moved in the ‘check_sound_levels’ function to make sure that the robot was alway able to get out of the while loop whenever one microphone spiked. This is the code where I did most of my learning. I learned how to write my own functions, which help to simplify code and avoid errors, by writing ‘check_sound_levels’ and the difference between long, void and int when writing these, void returning nothing to the previous function and int returning data. I learned about how to write ‘and’, ‘or’, etc. when it came to declaring functions. I also learned about ‘while’ loops which were key in switch between the distance sensor moving and the sound levels being checked to turn the robot. Subsequently I found out about the function ‘break’ as well as ‘switch’ cases. I also found that, in a loop, if you only serial.print data, it will always print the same value, you have to tell it to take a new reading each time.

However, while this code did work, it was surprisingly the servo motor that was too loud for the microphones to pick anything else up  and given that the servo made a constant noise there was no way to code for the microphones to ignore it. I therefore had to have two modes that the user could switch between by a toggle switch on the side. One mode was sound following and the other, obstacle avoiding.  Here is the code…

#define PIN_GATE_IN1 13
#define IRQ_GATE_IN1 0
#define PIN_LED_OUT1 13
#define PIN_ANALOG_IN1 A1

#define PIN_GATE_IN2 4
#define IRQ_GATE_IN2 0
#define PIN_LED_OUT2 13
#define PIN_ANALOG_IN2 A2

const int trigPin = 10;
const int echoPin = 9;

int buttonpin = 13;
int buttonstate = 0;

const int motor1pin1 = 2;
const int motor1pin2 = 3;
const int enablepin1 = 5;
const int motor2pin1 = 7;
const int motor2pin2 = 8;
const int enablepin2 = 6;

#include <Servo.h>

Servo myservo;
int pos = 0;

void soundISR()
{
int pin_val1;

pin_val1 = digitalRead(PIN_GATE_IN1);
digitalWrite(PIN_LED_OUT1, pin_val1);

int pin_val2;

pin_val2 = digitalRead(PIN_GATE_IN2);
digitalWrite(PIN_LED_OUT2, pin_val2);
}

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

pinMode(PIN_LED_OUT1, OUTPUT);
pinMode(PIN_LED_OUT2, OUTPUT);

pinMode(PIN_GATE_IN1, INPUT);
attachInterrupt(IRQ_GATE_IN1, soundISR, CHANGE);
pinMode(PIN_GATE_IN2, INPUT);
attachInterrupt(IRQ_GATE_IN2, soundISR, CHANGE);

pinMode(motor1pin1, OUTPUT);
pinMode(motor1pin2, OUTPUT);
pinMode(enablepin1, OUTPUT);
pinMode(motor2pin1, OUTPUT);
pinMode(motor2pin2, OUTPUT);
pinMode(enablepin2, OUTPUT);

digitalWrite(enablepin1, HIGH);
digitalWrite(enablepin2, HIGH);

Serial.println(“Initialized”);

myservo.attach(12);
}

void loop()

{

buttonstate = digitalRead(buttonpin);

while (buttonstate == HIGH) {

myservo.write(50);

int value1;
int value2;

value1 = analogRead(PIN_ANALOG_IN1);
value2 = analogRead(PIN_ANALOG_IN2);

long duration, inches, cm;

pinMode(trigPin, OUTPUT);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

Serial.println(value1);
Serial.println(value2);

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, HIGH);
if ((value1 >= (value2 + 30))) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

else if ((value2 >= (value1 + 30))) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

else if ((value1 > 300) && (value2 > 300)) {

digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(500);
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(500);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(500);
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(500);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);

}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, HIGH);
digitalWrite(motor2pin2, LOW);
delay(1000);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

}

long duration, inches, cm;

pinMode(trigPin, OUTPUT);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

delay(1000);
for (pos = 0; pos < 25; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

delay(1000);
for (pos = 25; pos < 50; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, HIGH);
digitalWrite(motor2pin2, LOW);
delay(1000);
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

delay(1000);
for (pos = 50; pos < 75; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

delay(1000);
for (pos = 75; pos < 100; pos += 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
}

else if (cm < 40) {
digitalWrite(motor1pin1, HIGH);
digitalWrite(motor1pin2, LOW);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

cm = microsecondsToCentimeters(duration);

delay(1000);
for (pos = 100; pos > 0; pos -= 1)
{
myservo.write(pos);
delay(10);
}

Serial.print(cm);
Serial.print(“cm”);
Serial.println();

if (cm > 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, HIGH);
delay(2000);
}

else if (cm < 40) {
digitalWrite(motor1pin1, LOW);
digitalWrite(motor1pin2, HIGH);
digitalWrite(motor2pin1, LOW);
digitalWrite(motor2pin2, LOW);
delay(2000);
}

}

long microsecondsToCentimeters(long microseconds)
{
return microseconds / 29 / 2;
}

This then worked perfectly and I was able to have the robot running around on my kitchen floor and give him a name, Jeff, as instructed by my sister. I also added a bit of code to make the robot do a little dance if both microphones spiked above 300.

Here are videos of the two modes…

https://www.youtube.com/watch?v=vzWa-b-b5ng&feature=youtu.be 

https://www.youtube.com/watch?v=uB3n8R1eUjI&feature=youtu.be

IMG_3593 IMG_3595       IMG_3592

IMG_3598IMG_3597 

IMG_3596

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s