Lag(?) issues

I'm having problems with a robot I am making. I am making a follower robot using an Arduino Mega 2560, 2 DC brush motors for drive motors, a PING))), and a servo. I have a sketch written, but when I go to run it I seem to be getting massive processing lag or something because the servo will stop moving and whatever the last PWM values were are sustained until the robot kind of Lucy and Ethal's itself by like trying to run through as many commands that it missed as fast as possible until it gets stuck again. I'm not a programmer/ CS major or anything, so my code may be highly unoptimized (I'm told many if statements and nested if statements are very processing intensive). Does anyone have any advice?

As kind of a run down with what I'm trying to do, I'm trying to limit the 40ish degree FOV of the PING))) by taking 2 readings that are 10-15 degrees (still experimenting) plus and minus the direction of the person the robot is to follow. The way I have my sensor mounted is good; it sees what I want it to see and gets readings. What I want from the 2 readings is just a binary output to give the robot an idea of where the person is (ie 11 right in front - go forward, 10 to the left - turn left, 01 to the right - turn right, and 00 no where in sight - stop and wait for person). I'm not super worried about my steering stuff at the moment, I can hammer that out with experimentation, and don't worry about the base stuff, that's a contrivance I've added to slowly speed up the robot instead of just lyrching it to full blast (again I can hammer that out with experimentation). I'm also not worried about my void setup(){}, the stuff commented out is from an older code I was working with that I may, but probably will not keep. The problem I have is with the main loop. If I just turn on the robot and don't stand in front of it (so the readings come back 00) the robot is fine, the servo goes back and forth from 75-105 degrees at regular speed. The second I stand in front of it it sees me from the left, the servo turn sees me on the right, pwm value are written and the thing pretty much stays like that, even if i walk out of its sight (I have the robot on a crate since right now it just likes to crash into walls) it will sustain this for a couple of seconds and then go back into its happy no detect state. It seems like controller is getting bogged down somewhere, and I know I have a TON of if statements. I've tried adding some delays here and there because i notice the Arduino seems to process its backlogged stuff during delays, but I haven't had much success.

O and another little note: As part of my experimentation I noticed the PING))) didn't always detect on the first attempt at the ranged I am looking to do (~2-7 feet), so I had a for loop to give it a few "chances."

#include <Servo.h>

Servo servo;
//Servo Position and Steering Value values
int servoPos = 90;
int steerVal = 0;
// Motor control values (PWM values)
int rMot = 0;
int lMot = 0;
// Sensor reading and accepted viewing range. Viewing range in 
// milliseconds. Sensor measures the time for an ultrasonic pulse
// to travel from the sensor to the nearest object. Sound travels
// at approximately 74 microseconds per inch, so a sensor reading
// 1776 microseconds corresponds to a distance of 1 foot away.
// "inc" in the increment for servo steps, "n" is a counter.
int sensorRead = 0;
int farthest = 9000;
int shortest = 5000;
int n;
boolean detectR;
boolean detectL;
int scan = 15;
// Base values for PWM values. Base values used to gradually speed
// up and slow down robot to make robot motion less jumpy and to
// draw current more eficiently when speeding the robot up.
int base = 50;
int baseInc = 20;
int baseMax = 220;
int baseMin = 180;
// Pin Values
int sensorPin = 15;
int servoPin = 14;
int rMotPin = 2;
int lMotPin = 3;
int hBdg1 = 16;
int hBdg2 = 17;
int hBdg3 = 18;
int hBdg4 = 19;

void setup()
{
  servo.attach(servoPin);
  servo.write(servoPos);
  pinMode(rMotPin, OUTPUT);
  pinMode(lMotPin, OUTPUT);
  pinMode(hBdg1, OUTPUT);
  pinMode(hBdg2, OUTPUT);
  pinMode(hBdg3, OUTPUT);
  pinMode(hBdg4, OUTPUT);
  forward();
  base = baseMin;
// Initially locates to runner before juping into the main loop.
/*  getRange();
  if(sensorRead > shortest && sensorRead < farthest)
  {
    detectR = true;
  }
  else
  {
    detectR = false;
  }
    while(detectR == false)
  {
    servoPos += inc;
    servo.write(servoPos);
    getRange();
    if(servoPos <= servoMin || servoPos >= servoMax)
    {
      inc *= -1;
    }
    delay(100);
  }*/
}
// **Main Loop**
void loop()
{
//  if((!detectR && detectL) || (detectR && !detectL))
//  {
//    delay(100);
//  }
    servoPos += scan;
  if(servoPos > 180)
    {
      servoPos = 180;
    }
  servo.write(servoPos);
  delay(300);
  detectR = getRange();
  
  servoPos -= 2 * scan;
  if(servoPos < 0)
  {
    servoPos = 0;
    
  }
  servo.write(servoPos);
  delay(300);
  detectL = getRange();
  
  servoPos += scan;
  delay(100);
  
  
  if(detectR && detectL)
  {
    steerVal = (servoPos - 90) / 2;
    if(steerVal > 0)
    {
      rMot = base - steerVal;
      lMot = base;
      
    }
    else
    {
      rMot = base;
      lMot = base - steerVal;
      
    }
    base += baseInc;
    if(base > baseMax)
    {
      base = baseMax;
      
    }
  }

   
  if(detectR && !detectL) 
  {
    servoPos += scan;
    if(servoPos > 180)
    {
      servoPos = 180;
      
    }
    steerVal = (servoPos - 90) / 2;
    if(steerVal > 0)
    {
      rMot = base - steerVal;
      lMot = base;
      
    }
    else
    {
      rMot = base;
      lMot = base - steerVal;
      
    }
  }
 
 
  if(!detectR && detectL)
  {
    servoPos -= scan;
    if(servoPos < 0)
    {
      servoPos = 0;
      
    }
    steerVal = (servoPos - 90) / 2;
    if(steerVal > 0)
    {
      rMot = base - steerVal;
      lMot = base;
      
    }
    else
    {
      rMot = base;
      lMot = base - steerVal;
      
    }
  }
  
  if(!detectR && !detectL)
  {
    servoPos = 90;
    rMot = 0;
    lMot = 0;
    base = baseMin;
    
  }
  rMot -= 10;
  if(rMot < 0)
  {
    rMot = 0;
  }
  analogWrite(rMotPin, rMot);
  analogWrite(lMotPin, lMot);
  delay(300);
}                                                          
// **Subroutines**
// Sensor detection process The sensor uses the same pin to
// recieve intiation pulse and output result. The sensor is
// initiated with a 5 microsecond digital high pulse and outputs a
// short digital high pulse after recieving its echo.
// Experimentation showed the process does not always yield a
// value so the process is looped 10 times or until a value is
// obtained to weed out false negatives.
boolean getRange()                    
{                                 
  for(n = 1; n < 5; n++)         
  {                               
    pinMode(sensorPin, OUTPUT);   
    digitalWrite(sensorPin, LOW); 
    delayMicroseconds(2);          
    digitalWrite(sensorPin, HIGH);
    delayMicroseconds(5);         
    digitalWrite(sensorPin, LOW); 
    pinMode(sensorPin, INPUT);    
    sensorRead = pulseIn(sensorPin, HIGH); 
    delay(100);                   
    if(sensorRead >= shortest && sensorRead <= farthest)
    {  
      return true;         
      break;
    }
  }
  return false;
}
// While this program does not use it the robot's circuity does
// include H-bridges that allow the wheel motors to spin
// backwards and forwards with proper digital inputs. Each motor
// has 2 pins (right has hBdg3 and hBdg4, left has hBdg1 and 
// Hbdg2), and the high low for those pins (in respective order)
// is forward, low high is backwards, high high is documented as a
// "fast stop" which will draw a lot of current and risks damaging
// the H-bridges so should be used with caution, and low low is a
// free spin stop. 
void forward()               
{                             
  digitalWrite(hBdg1, HIGH);  
  digitalWrite(hBdg2, LOW);  
  digitalWrite(hBdg3, HIGH); 
  digitalWrite(hBdg4, LOW);  
}                            
void reverse()               
{                            
  digitalWrite(hBdg1, LOW);   
  digitalWrite(hBdg2, HIGH); 
  digitalWrite(hBdg3, LOW);  
  digitalWrite(hBdg4, HIGH); 
}

Get rid of calls to"delay".
When you call delay, your device is blind.

(Before posting, it would be polite to remove all commented-out code)

Well the delays in the getRange() function have to be there for the device to work (ref. http://arduino.cc/en/Tutorial/Ping?from=Tutorial.UltrasoundSensor). The thing gets bogged down immediately if I don't have the long 100 ms delay at the end of it. And If I take the delays out after the servo position changes, the indicator light on the ping will start firing before the servo gets to the right angle. All the other delays are kind of hap hazard.

The delays in getRange are of the order of microsecond and can be ignored.
Delays of hundreds of milliseconds cannot.

Lol the servo literally goes slower (well the servo goes the same speed, its just now when the program starts it goes left, the indicator light on the ping doesn't go off at all, and it stays in this position for a bout 3 seconds, then goes to the left, again not indicator lights and stays here for 3-8 seconds and kind of repeats. And it comes to a long stop if i stand in front of it.) now after removing the delay(100); from the getRange function, the delays after the servo movements, and the delay at the very end. And this is without even tracking anything, just it's kind of 00 detection. The second I step in front of it, it locks up.

And if I let it kind of stay at 00 its finding ways to bog it self down for like little 5-20 second hissy fits

I can assure you that servos are are fully immunised against "hissy fits" before they leave the factory
Your code, however, needs some remedial work.

Lol the servo literally goes slower

I don't see which of your servos is called "Lol"

Have you ever had a problem with an arduino board just looking like its processing to much? Is this like a memory problem? Is there a way to clear memory? Or is the servo library really hard on the board? Because it looks like the board is trying to do too much.

Also there is no servo called "Lol", there's only one servo called servo (the other two motors are just DC brush motors that I control with PWM), I was just laughing at the strangeness of the robot doing things much slower when delays were removed.

No, just about any software I've written to drive anything mechanical will have been running at least ten times as fast as it needs to.
I don't understand what you mean by clearing memory - What purpose would that serve?
And no, I don't think you have a memory problem.

Comments about what you think the code is supposed to do would be useful.
Maybe Google "state machine"

I want the code to:

  • start with some kind of "heading"(intiatially this will be straight forward, or 90 for the servo with the sensor on it, but in subsequent loops this can change depending on where the person the robot is following is detected)
  • turn the servo "heading" + scan degrees
  • engage the sensor
  • return a boolean reading (detectL) on whether the target is within a minimum and maximum ranges
  • turn the servo "heading" - 2 * scan degrees
  • engage the sensor
  • return a second boolean reading (detectR) on whether the target is within the minimum and maximum ranges
  • based on "heading" produce some kind of steering value
    based on these two readings:
    a) both are true (person is in front of the robot)
  • set the rMot and lMot to the base speed value
  • increment the base speed value (yes after setting rMot and lMot so the next loop will be faster)
    b) detectR: true, detectL: false (person is to the right of the robot/ right front)
  • decrease the "heading" by scan (set the heading more rightward)
  • decrease base speed value (before writing motor pwm values)
  • set lMot to base value, set rMot to base value -steering value (to turn the robot right)
    c) detectR: false detectL: true (person is to the left/ left front of the robot)
  • increase "heading" by scan (set heading more left ward)
  • decrease base speed value (before writing motor pwm values)
  • set rMot to base value, set lMot to base value minus steering value
    d) both false (person not detected)
  • set rMot and lMot to 0 (stop the robot)
  • set base back to min value (robot is expect to stop, so it will need to be brought back up to speed when the person is detected again)
  • analoge write motor values
    loop

caveats:

  • the sensor doesn't always detect on the first try, so the sensing procedure is put in a loop to give it a few chances to detect
  • operating the sensor by itself, the sensor performs fastest with a delay of 100ms on the end of the sensing procedure as show in http://arduino.cc/en/Tutorial/Ping?from=Tutorial.UltrasoundSensor. I have tried shortening and removing entirely the large delay, but the device performs fast with that delay.
  • the steering logic/ method is very subject to change, I just want some kind of steering related to the heading of the robot
  • the base speed value logic/ method is very subject to change, I just don't want to robot to jump or lyrch when starting from a stop, slow down in turns, etc.

That seems like a reasonable algorithm. I assume the subject would walk in front of the sensor to attract its attention, and the device would pursue that person until it reached the minimum range, or were outside the maximum range, or managed to get outside the scanning arc.

AWOL:
Get rid of calls to"delay".
When you call delay, your device is blind.

(Before posting, it would be polite to remove all commented-out code)

yeah, that way you can get ridiculed for not commenting your code! But then I just hadn't gone to the trouble to write the comments just so I could remove them, it's much quicker and easier. :smiley:

PS - damned if you do, damned if you don't, that's all I'm sayin

Chiken:

  • operating the sensor by itself, the sensor performs fastest with a delay of 100ms on the end of the sensing procedure

Bad move when you're trying to drive and sense at the same time.

You can instead set up some variables and fill loop() with task blocks in priority order. When a secondary task runs, unless it is very short it should have a return at the end to run the priority task ASAP.
--a block that checks the sensor every 100ms, code to able to schedule multiple countdowns.
--the next most important block, if it executes, it returns when it's done and loop() runs again
--ditto as needed
it's simple, it can do simple things in simple ways but it is to spaghetti code what macaroni pasta is to spaghetti pasta. It breaks it up. :wink: Besides, it's easy to write.

You need to do more/better; look into the finite state machine mentioned in Majenko's Reply #2 here:
http://arduino.cc/forum/index.php/topic,120827.0.html

GoForSmoke:
yeah, that way you can get ridiculed for not commenting your code!

Big difference between commenting the code, and using comments to disable chunks of code while leaving them in the source.

Ah, commented-out not commented. Yup, not the same but that one part does say where he tried cutting the delay.

I think I am kind of not understanding this FSM concept (I am a rookie programmer at best T.T). Can I make these seperate "machines" on the same arduino? Like What I am getting from all of this is use switch case instead of if() statements, is that correct? I have read this:

http://hacking.majenko.co.uk/finite-state-machine

and maybe I'm trying to cookie cutter this solution too much. Should I like nest switch statements? Can that be done? Or should my code just be kind of like one be gigantic switch statement with a WHOLE bunch of cases? I kind of see where this FSM concept is going, but I'm having trying understanding how it would be implemented in the context of this project.

EDIT: Also I apologize for a bit of a necro here, I went on a vacation for labor day (America).

Like What I am getting from all of this is use switch case instead of if() statements, is that correct?

No. Whether to use a switch statement or a series of if statements will become abundantly clear when you understand what each does. If you are not comfortable with the switch statement, nothing it does can not be done with a series of if/else if/else statements.

The converse is not necessarily true.

Can I make these seperate "machines" on the same arduino?

Yes. The concept of a state machine is pretty simple. Imagine a state machine involving you and your house. You are in the living room OR you are in the bathroom OR you are in the bedroom. All you need to do is keep track of what state you are in (in the living room, in the bathroom, in the bedroom).

For some states, in the bedroom, for example, there are other states - in bed, for example.

Even there, there are possibilities - asleep or awake or doing something, alone or with someone else.

So, you can have any number of states, some of which make sense when another state is true, and some of which do not. Asleep, having sex, in the kitchen is not a really plausible set of states. But, asleep, in bed, in the bedroom is a perfectly reasonable set of states.

The Arduino is perfectly capable of keeping track of all the states, for all the state machines mentioned.

Should I like nest switch statements?

You can. Whether you should, or not, depends on what you are trying to accomplish.

Or should my code just be kind of like one be gigantic switch statement with a WHOLE bunch of cases?

How hard are you trying to make the code to maintain? Separating the states into different state machines, and using separate functions managing each state, would make it easier to maintain the code.

Chiken:
Can I make these seperate "machines" on the same arduino?

A finite state machine is a concept. Yes you can implement multiple ones if you need to.

No. Whether to use a switch statement or a series of if statements will become abundantly clear when you understand what each does. If you are not comfortable with the switch statement, nothing it does can not be done with a series of if/else if/else statements.

The converse is not necessarily true.

Then I don't understand what is wrong with the code I have written. Am I exceeding the limitations of the capabilities of the arduino or something? I think I have a pretty good idea what a switch statement does from reading this: switch...case - Arduino Reference and the previous majenko link, but is there a more important difference that isn't stated here? Like from what I understand of the language, my code should work, but when I run the device it just locks up. It seems the delays are a problem (?), something about interrupts (which I don't really know anything about) but I see how switch statements (or if statements) could be used to work around those and (I guess?) work around the interrupts? Perhaps I should write a new code implementing some of these things, see if that works, and if it doesn't come back here again and ask about more specific fixes?

What I would do is add some Serial.print() statements to see what the robot is doing. After deleting all the commented out code, that is.

In the IDE, commented out code changes color, and is easy to skip. On the forum, working code and commented out code look the same.

Perhaps I should write a new code implementing some of these things, see if that works, and if it doesn't come back here again and ask about more specific fixes?

This is not a bad idea. You learned some stuff writing the first code. You've learned some stuff as a result of this thread. Any new code you develop is bound to be better/easier to debug/understand.

Perhaps when you get done, either the problem will have gone away, you will have had an "oh, shit, why was I doing that" moment, or the problem will still exist in code with Serial.print() statements that show what is actually happening.

If the first two possibilities don't pan out, do post the new code, and we'll try again.

Alright so I have rewritten my code:

#include <Servo.h>

Servo servo;

int servoCase = 1;
int sensorCase = 1;
int steeringCase = 1;

int heading = 90;
const int scan = 15;
const int shortest = 3000;
const int farthest = 11000;
boolean detectR;
boolean detectL;
unsigned long sensorRead;
int steerVal;

int rMot;
int lMot;
int servoPos = 90;
int base = 255;

unsigned long tus;
unsigned long tms;
int n = 1;

int rMotPin = 2;
int lMotPin = 3;
int servoPin = 14;
int sensorPin = 15;
int hBdg1 = 16;
int hBdg2 = 17;
int hBdg3 = 18;
int hBdg4 = 19;

void setup()
{
  Serial.begin(9600);
  servo.attach(servoPin);
  servo.write(servoPos);
  pinMode(rMotPin, OUTPUT);
  pinMode(lMotPin, OUTPUT);
  pinMode(hBdg1, OUTPUT);
  pinMode(hBdg2, OUTPUT);
  pinMode(hBdg3, OUTPUT);
  pinMode(hBdg4, OUTPUT);
  forward();
  servoCase = 2;
}

void loop()
{
  //servo FSM
  switch (servoCase)
  {
    case 1:
      break;
    case 2:
      servoPos = heading - scan;
      if (servoPos < 0)
      {
        servoPos = 0;
      }
      servo.write(servoPos);
      servoCase = 3;
      tms = millis();
      break;
    case 3:
      if (millis() - tms >= 200)
      {
        sensorCase = 2;
        servoCase = 1;
        break;
      }
      break;
    case 4:
      servoPos = heading + scan;
      if (servoPos > 180)
      {
        servoPos = 180;
      }
      servo.write(servoPos);
      servoCase = 3;
      break;
    case 5:
      steeringCase = 4;
      servoCase = 1;
  }
  //Sensor FSM
  switch (sensorCase)
  {
    case 1:
      break;
    case 2:
      pinMode(sensorPin, OUTPUT);
      digitalWrite(sensorPin, LOW);
      delayMicroseconds(2);
      digitalWrite(sensorPin, HIGH);
      delayMicroseconds(5);
      digitalWrite(sensorPin, LOW);
      pinMode(sensorPin, INPUT);
      sensorRead = pulseIn(sensorPin, HIGH);
      tms = millis();
      if ((sensorRead < shortest || sensorRead > farthest) && n <=5)
      {
        n += 1;
        sensorCase = 2;
        break;
      }
      if (servoPos < heading)
      {
        steeringCase = 2;
      }
      else
      {
        steeringCase = 3;
      }
      sensorCase = 5;
      n = 1;
      break;
    case 5:
      if (tms >= 100)
      {
        sensorCase = 1;
        if (servoPos < heading)
        {
          servoCase = 4;
        }
        else
        {
          servoCase = 5;
        }
        break;
      }
      break;
  }
  //steering FSM
  switch (steeringCase)
  {
    case 1:
      break;
    case 2:
      if (sensorRead > shortest && sensorRead < farthest)
      {
        detectR = true;
      }
      else
      {
        detectR = false;
      }
      steeringCase = 1;
      break;
    case 3:
      if (sensorRead > shortest && sensorRead < farthest)
      {
        detectL = true;
      }
      else
      {
        detectL = false;
      }
      steeringCase = 1;
      break;
    case 4:
      steerVal = abs(heading - 90);
      if (detectR && detectL)
      {
        steeringCase = 5;
      }
      if (!detectR && detectL)
      {
        steeringCase = 6;
      }
      if (detectR && !detectL)
      {
        steeringCase = 7;
      }
      if (!detectR && !detectL)
      {
        steeringCase = 8;
      }
      break;
    case 5:
      rMot = 255;
      lMot = 255;
      servoCase = 2;
      steeringCase = 1;
      break;
    case 6:
      rMot = base;
      lMot = 0;
      tms = millis();
      if(millis() - tms >= steerVal)
      {
        lMot = base;
        heading += scan;
        if (heading > 180)
        {
          heading = 180;
        }
        servoCase = 2;
        steeringCase = 1;
      }
      break;
    case 7:
      rMot = 0;
      lMot = 255;
      tms = millis();
      if (millis() - tms > steerVal)
      {
        rMot = base;
        heading -= scan;
        if(heading < 0)
        {
          heading = 0;
        }
        servoCase = 2;
        steeringCase = 1;
      }
      break;
    case 8:
      rMot = 0;
      lMot = 0;
      heading = 90;
      servoCase = 2;
      steeringCase = 1;
      break;
  }
  analogWrite(rMotPin, rMot);
  analogWrite(lMotPin, lMot);
  Serial.print(detectR);
  Serial.print(" ");
  Serial.print(detectL);
  Serial.print(" ");
  Serial.println(millis());
}
void forward()
{
  digitalWrite(hBdg1, HIGH);
  digitalWrite(hBdg2, LOW);
  digitalWrite(hBdg3, HIGH);
  digitalWrite(hBdg4, LOW);
}
void reverse()
{
  digitalWrite(hBdg1, LOW);
  digitalWrite(hBdg2, HIGH);
  digitalWrite(hBdg3, LOW);
  digitalWrite(hBdg4, HIGH);
}

Initially I was still experiencing the same lag issues I was having. When I plug the serial monitor in I seem to get HUGE bursts of information. The robot will stop doing things for a second, the suddenly things will go grazy for about half a second and like 80 loops worth of Serial.print will show up in the serial monitor, which is really odd because the servo the sensor is mounted on will only go a short distance in one direction; there's just no way my code is being executed. Does any one see any specific problems with my code or anything? Am I doing this FSM thing right?

One thing that seemed strange to me was the case 4 of the steering FSM switch structure. Perhaps a switch structure isn't the way this should be happening? Am I asking this arduino to do too much?