Boe-bot for arduino roaming with ping on servo turret

I have been trying to find out why the boe-bot jacks the turret to the left and starts turning in circles to the left. When the boe-bot encounters a corner. If I put an object in front of the sensor while its in its retard turn it snaps out of it and starts to move around like normal. Knowing C++ fairly well I can see in the sketch the author may have had a major lack of concentration because I see a lot of first grader mistakes and "WTF's?".

Others have had this issues and their fix was ether new battery's or a bad ground. Well that's not the problem with this bot, all fresh batteries, connections to the circuit are fine and everything works.

I have tried to find help on the parallax forum and other parts of their site but it seems to me their company or users have a major bias towards arduino and do not support the arduino shield as well as their own micro controller even though they said they would.

I think its programing. Am I wrong?

Here is a link to the sketch:
http://learn.parallax.com/project/let-it-roam

One thing that I noticed without going far into the code is this

// Declare array to store that many cm distance elements using pre-calculated
// number of elements in array.
const int elements = sizeof(sequence);
int cm[elements];

Despite what it says in the comment

sizeof(sequence);

does not return the number of elements in the array but rather the number of bytes in the array. It probably does not matter because sequence[] is an int array so the cm[] array will have twice as many elements in it as needed.

Interestingly in the very next section of code sizeof() is used again but incorrectly in a different way.

const int degreesTurret = 180/(sizeof(sequence)/sizeof(int)-1);

degreesTurret is going to end up wrong because the number of elements is not actually sizeof(sequence)/sizeof(int)-1 so the turret will presumably try to move too far.

Neither of these observations may actually cause any problems but it does not bode well for the rest of the program.

I see "detach" calls for the servos, but no re-attach.
Make sure you never call "maneuver" with -1 for the argument "msTime".

Check which pins your using for servo timing and motor control. For example, the standard servo.h lib is not compatible with the standard motor shield.

Mark

holmes4:
Check which pins your using for servo timing and motor control. For example, the standard servo.h lib is not compatible with the standard motor shield.

Mark

The bot uses two servos to move it one for the left and one for the right wheel. Those two servos have some modification that makes them a continuous rotation servo motor, they also have a potentiometer for calibrating the speed so they both move the bot strait. But it can be a pain to keep the two servos even and calibrated.

You need some rotation sensors so that you can compare the rotational speed of the wheels and adjust the speed of the motors accordingly. There are even clever ways to do it with only one rotation sensor but it would be easy to do it with two.

AWOL:
I see "detach" calls for the servos, but no re-attach.
Make sure you never call "maneuver" with -1 for the argument "msTime".

yes, that's one spot that made me wonder why a detach is ever needed and if that sketch uses that part of the function.

It also made me wonder how delay() would handle a negative number or if it defaults to one or zero.

It also made me wonder how delay() would handle a negative number or if it defaults to one or zero.

The argument to delay() is an unsigned long. You need to determine for yourself what -1 as an unsigned long looks like. It will mean a lot more to you if you figure that out that if we tell you.

A hint, though. No, it does not cause delay() to think that you meant 0 or 1, not be a long shot. (Or an unsigned long shot).

UKHeliBob:
You need some rotation sensors so that you can compare the rotational speed of the wheels and adjust the speed of the motors accordingly. There are even clever ways to do it with only one rotation sensor but it would be easy to do it with two.

Can you tell me what rotation sensors would be good enough, easy to get and and fairly cheap?

I've used IR proximity sensors and bits of white card stuck to the tyre in the past.

PaulS:

It also made me wonder how delay() would handle a negative number or if it defaults to one or zero.

The argument to delay() is an unsigned long. You need to determine for yourself what -1 as an unsigned long looks like. It will mean a lot more to you if you figure that out that if we tell you.

A hint, though. No, it does not cause delay() to think that you meant 0 or 1, not be a long shot. (Or an unsigned long shot).

Either Im going blind or the banks should start storing everyone's negative debt in an unsigned long.

S_Flex:

UKHeliBob:
You need some rotation sensors so that you can compare the rotational speed of the wheels and adjust the speed of the motors accordingly. There are even clever ways to do it with only one rotation sensor but it would be easy to do it with two.

Can you tell me what rotation sensors would be good enough, easy to get and and fairly cheap?

I can't recommend a particular one but Google will find you plenty, or ask in the Robotics section of this forum for more specific advice.

What value is in a byte (unsigned) when you try to store -1 in it?
What value is in an int (unsigned) when you try to store -1 in it?
What value is in a long (unsigned) when you try to store -1 in it?

Write a sketch to find out.

PaulS:
What value is in a byte (unsigned) when you try to store -1 in it?
What value is in an int (unsigned) when you try to store -1 in it?
What value is in a long (unsigned) when you try to store -1 in it?

Write a sketch to find out.

  1. error: invalid combination of multiple type-specifiers
  2. 65,535
  3. 4,294,967,295

I maybe wrong on the exact number for three and two because I didnt have time to copy it. I sure didnt think it would return the highest number it can hold...

well seeing that makes truely think the boe-bot sketch is broke and its not a hardware issue.

I sure didnt think it would return the highest number it can hold...

Which is why delay(-1) is not a good idea. That will take a few weeks to complete.

as an alternative I been working on this sketch so i at least have some thing to work with.

The only lesson I have learned from this bot is to beware of what shield you buy for arduino. Unfortunately I do not own the boe-bot to return it and was helping a friend understand how it works and why it has this flaw.

Maybe time will tell if parallax is trying to just make a buck or rely wants to support the products they sell.

I hope this code helps others.

/* 
 Current Version: 2.0 (2/13/2013) Not tested
 BOE Shield code from "Robotics with the BOE Bot" 
 Roaming With Whiskers source, modified to use a PING)))
 sensor on the PING))) servo bracket.
 Using code from the Ping))) example sketch.
 http://thoughtfix.com/blog/2012/3/25/arduino-boe-shield-ping-and-a-servo.html
 
 Ping view angles, 125 or maybe 115 degree's, 45 can be wrong too maybe 35 will work
                 centerDist
  leftDist2=125      90       45=rightDist2
              ^       ^       ^
               \      |      /
 leftDist=160 <---------------> 20=rightDist
 
 Flex - changes (2/13/2013) v2.0
 - added 45 degree turns
 - made the ping() function move the turret to the angle with delay then return the ping
 - Removed Look forward function since ping() does the work now
 - LookAround() has 2 new angles so 45 degree turns can be done and added the new ping method
 - Added 45 degree turn logic to loop() before the 90 degree turning logic so small turns can be done first
 - Fixed the code so it doesnt go forward if the safe distance is equal to the center distance.
 - Removed some variables that where not used
 - Removed microsecondsToInches() function and moved the math in ping.
 - Removed forward(),turnLeft(),turnRight() and backward() functions
 - Added function move_or_turn(time, task) this will do all the movement.
 - Added LengthConversion variable with notes to set length to inches or centimeters 
 - Changed logic so the code does not favor one direction then another and turns to the true max distance
 
 Flex: Found code, did some small changes and am starting the version number at v1.0
*/

// Servo calibration values, Not used, can be if needed.
const int max_LeftSpeed = 100;
const int max_RightSpeed = 200;

const int servoLeftPin = 13;                 // I/O Pin constants
const int servoRightPin = 12;
const int servoTurretPin = 11;
const int pingPin = 10;
const int piezoPin = 4;

const int LengthConversion = 74;             // Length: 74 = Inches or 29 = Centimeters 
const int minSafeDist = 11;                  // Minimum safe distance of object 

#include <Servo.h>                           // Include servo library
Servo servoLeft;                             // Declare left, right and Ping))) servos 
Servo servoRight; 
Servo PingServo;

// remember to look around after a 45 degree turn
int DoLook = 0;

// Define distance variables
int centerDist, leftDist, leftDist2, rightDist, rightDist2;
// Define variables for Ping)))
long duration, moveTime;                       

void setup() {                               // Built-in initialization block    
  tone(piezoPin, 3000, 1000);                // Play tone for 1 second 
  delay(1000);                               // Delay to finish tone

  servoLeft.attach(servoLeftPin);            // Attach left signal to pin 13  
  servoRight.attach(servoRightPin);          // Attach right signal to pin 12 
  PingServo.attach(servoTurretPin);          // Attach turret signal to pin 11
}  

void loop() { 
  if (DoLook == 0) {
    // Look Ahead get center distance
    centerDist = ping(90, 175);
  }
  else {
    DoLook = 0; // set for forward
    // Look around to see if last turn was enough
    LookAround();
    // set to 1 to skip moving forward so it can turn again
    // To-Do: test if this logic works
    if (rightDist2 < minSafeDist 
      || centerDist < minSafeDist 
      || leftDist2 < minSafeDist) DoLook = 1;  
  }
  
  /* If the inches in front of an object is greater than 
   the minimum safe distance (11 inches), move forward */
  if(centerDist > minSafeDist && DoLook == 0) {
    move_or_turn(121, 1);
    delay(110); // Wait 0.11 seconds 
  }
  else {
    // Stop moving
    servoLeft.writeMicroseconds(1500);
    servoRight.writeMicroseconds(1500);

    // Check your surroundings for best route
    if (DoLook == 0) LookAround(); // scan
    else DoLook = 0; // scan was done

    // Default move time
    moveTime = 250;

    // Check witch direction is the most so we dont faivor one direction then another
    if (rightDist > minSafeDist && rightDist > leftDist && rightDist2 > leftDist2) {
      // Right side has more distance 
      if (rightDist2 > centerDist && rightDist2 > minSafeDist) {
        DoLook = 1; // remember to look around next loop
        moveTime = 125;
        move_or_turn(moveTime, 3); // do a 45 turn right
      }
      else {
        // do a 90 Turn Right  
        move_or_turn(250, 3); 
      }
   // maybe need to put backward here also and ask a question for 90 degree turns   
    }
    else if (leftDist > minSafeDist && leftDist > rightDist && leftDist2 > rightDist2) {
      // Left side has more distance
      if (leftDist2 > centerDist && leftDist2 > minSafeDist) {      
        DoLook = 1; // remember to look around next loop 
        moveTime = 125;
        move_or_turn(moveTime, 2); // do a 45 turn left
      }
      else {
        // do a 90 Turn Right  
        move_or_turn(250, 3); 
      } 
    // maybe need to put backward here also and ask a question for 90 degree turns  
    }
    else {
      // Go Backward
      move_or_turn(250, 4); 
    } 

    delay(moveTime);
  }
}
/*
Ping function moves turret to angle, pings then 
 converts time to length and returns the number
 
 Distance_in_length = ping(angle, delayMS);
 */
int ping(int angle, int delayMS) {
  PingServo.write(angle);      // move turret to angle 
  delay(delayMS);              // wait time for turret

  pinMode(pingPin, OUTPUT);    // Set pin to OUTPUT
  digitalWrite(pingPin, LOW);  // Send a low pulse
  delayMicroseconds(2);        // wait for two microseconds
  digitalWrite(pingPin, HIGH); // Send a high pulse
  delayMicroseconds(5);        // wait for 5 micro seconds
  digitalWrite(pingPin, LOW);  // send a low pulse
  pinMode(pingPin,INPUT);      // switch the Pingpin to input 
  //listen for echo with 25000 time out
  duration = pulseIn(pingPin, HIGH, 25000);    
  // converts time to a distance then return the length
  return duration / LengthConversion / 2;
}
/*
Look around at 5 angles
 */
void LookAround() {
  //get the two right distances
  rightDist = ping(20, 320);
  rightDist2 = ping(45, 275); // TO-DO: check if 35 is better then 45
  // get the two left distances
  leftDist2 = ping(125, 320); // TO-DO: check if 115 is better then 125
  leftDist = ping(160, 275); 
  // move turret back to center
  centerDist = ping(90, 320); 
}

/*
move and turn function
 time - Time to wait for movement to complete
 task - move or turn command number
Number   Task
 1    =  Forward
 2    =  Turn left
 3    =  Turn right
 4    =  Backwards
 */
void move_or_turn(int mttime, int mttask) {
  if (mttask == 1 || mttask == 3) servoLeft.writeMicroseconds(1700);
  else if (mttask == 2 || mttask == 4) servoLeft.writeMicroseconds(1300);

  if (mttask == 3 || mttask == 4) servoRight.writeMicroseconds(1700);
  else if (mttask == 1 || mttask == 2) servoRight.writeMicroseconds(1300);

  if (mttime <= 0) mttime = 1; 
  delay(mttime);
}