Need Help with Line Following Code

Ok, I’ve been working on this for quite some time and ready to throw in the towel…

It follows the line pretty well, but once it leaves the line (i.e. when the track starts to loop around), it just keeps going in that direction and fails to find the line again.

I have no prior programming experience, but have been attempting to program an autonomous line following robot in EasyC and here is my code so far:

Constants

#ifndef main_h
#define main_h

#include “UserAPI.h”

#define TAPE 100
#define CARPET 400
#define LEFT_MOTOR_SPEED_FORWARD 165
#define RIGHT_MOTOR_SPEED_FORWARD 96
#define MOVE_FORWARD 0
#define STEER 1
#define ROBOT_MOVE_FORWARD {SetPWM( 1, RIGHT_MOTOR_SPEED_FORWARD ); SetPWM( 2, LEFT_MOTOR_SPEED_FORWARD);}
#define RIGHT_MOTOR 1
#define LEFT_MOTOR 2
#define VEER_RIGHT {SetPWM(1, 105); SetPWM(2, 154);}
#define VEER_LEFT {SetPWM(1, 107); SetPWM(2, 127);}
#define STEER_RIGHT {SetPWM(1, 127); SetPWM(2, 167);}
#define STEER_LEFT {SetPWM(1, 87); SetPWM(2, 127);}

#endif // main_h

Main Program

#include “Main.h”

void main ( void )
{
int right_sensor_value;
int center_sensor_value;
int left_sensor_value;
int previous_right;
int previous_left;
int toggle_movement = STEER;

  while ( 1 )
  {
        left_sensor_value = GetAnalogInput ( 1 ) ;//get value for left sensor
        center_sensor_value = GetAnalogInput ( 2 ) ;//get value for center sensor
        right_sensor_value = GetAnalogInput ( 3 ) ;//get value for right sensor
        if ( center_sensor_value <= TAPE  )
        {
              ROBOT_MOVE_FORWARD 
              previous_right = right_sensor_value  ;
        }
        else if ( left_sensor_value <= TAPE  )
        {
              STEER_LEFT 
              previous_left = left_sensor_value  ;
        }
        else if ( right_sensor_value <= TAPE  )
        {
              STEER_RIGHT 
        }
        else if ( previous_right <= TAPE  )//if robot leaves the line and if right sensor was last to see tape, steer right and then move forward to hunt for line
        {
              if ( toggle_movement == STEER  )
              {
                    STEER_RIGHT 
                    toggle_movement = MOVE_FORWARD  ;
              }
              else
              {
                    ROBOT_MOVE_FORWARD
                    toggle_movement = STEER  ;
              }
        }
        else if ( previous_left <= TAPE  )//if robot leaves the line and if left sensor was last to see tape, steer left and then move forward to hunt for line
        {
              if ( toggle_movement == STEER  )
              {
                    STEER_LEFT 
                    toggle_movement = MOVE_FORWARD  ;
              }
              else
              {
                    ROBOT_MOVE_FORWARD 
                    toggle_movement = STEER  ;
              }
        }
        else
        {
              if ( toggle_movement == STEER  )//if all else fails, the robot can hunt for line by moving left and then right
              {
                    STEER_LEFT 
                    toggle_movement = MOVE_FORWARD  ;
              }
              else
              {
                    STEER_RIGHT 
                    toggle_movement = STEER  ;
              }
        }
  }

}

Thanks in advance for any help you can give!!!

I haven’t gone through it in great detail, but at first glance I think you may not be keeping previous_right & previous_left updated properly. You only update previous_right when moving forward, and previous_left when steering left. You should make sure they both get updated in the three “main” cases where tape is visible to one of the sensors.

Hope that helps,

  • Dean

Based on the behavior, what branch is your code in when this happens?
It seems like it is in the “if all else fails” routine.
Here it alternates steering right, forward,left, alternating every 1ms(every time through the loop), so it goes wiggly straight away from the line.

You probably entered that branch because the right sensor skipped over the line between reads, and the right sensor was previously not on the line because you were going straight.

If right_sensor_value<=TAPE, you should always set previous_right,
but Dean’s advice (might as well always save previous sensor data) is more general.

Other alternative idea brainstorms for organizing the code:

  • Instead of comparing the sensor values to TAPE, compare them to each other. When you are off the line, try turning toward the sensor with the lowest value.

  • Think of it as SPA: Sense Plan Act
    The existing code is mostly SR: Sense React, but has a little planning with the toggle and previous variables.

  • A stack of nested elses are hard to follow, try “next” or “break” when you have decided to do something and are done with this branch, like this:

while(1){
  if (cond1) {do1;next;}
  if (cond2) {do2;next;}
  do_all_else_fails;
}

It isn’t just general advice, it is necessary for the algorithm to work. If you only save off each previous_ values when they are seeing tape, then they will always hold low values and will be effectively useless. You have to save both of them when either side sensor sees tape so that you know which side last saw tape.

I agree, though, that toggling quickly between STEER_LEFT and STEER_RIGHT is not likely to find the line - but that only fires if both previous_left and previous_right are > TAPE, which is pretty unlikely.

The line trackers I’ve made in the past just hunt by spinning to the one side that last saw the line. I let that go on long enough for about a 360° turn, and then have the program give up and go back to radio control for manual repositioning.

Cheers,

  • Dean

Right, my bad. When I thought about saving the previous values every time like this, I realized that when the first iteration of backup didn’t work, we just lost the record of which way we needed to go.

while(1){
  prev = sensor_value;          // save previous every time, (bad!)
  sensors = GetAnalog;  // get new sensors
  if (sensors) { do stuff ; break }
  if (prev) { do recovery stuff; break} //backup plan
} // wend

You comment "save both of them when either side sensor sees tape " can be implemented like this, and so it works to avoid that problem.

while(1){
  sensors = GetAnalog;  // get new sensors
  if (left or right sensor) { save sensors in prev } // good! 
  if (sensors) { do stuff ; break }
  if (prev) { do recovery stuff; break}// backup plan
} // wend

I hope I got it right that time.

The one question I do have about these is how fast they work… I know the new brain is supposed to be many factors faster… Does this affect how fast it can move while following a line?

I’ve not done it, but cleverness in programming and sensing beats raw cpu speed like practice driving beats the best robot finished yesterday.
Your post reminded me of something, so I searched and found this reference to a RobotC PID line follower by **** Swan.
http://www.google.com/url?q=http://www.robotc.net/forums/viewtopic.php%3Ff%3D10%26t%3D1767&ei=uQ3qS86qGcGBlAfgkeDeCg&sa=X&oi=forum_cluster&resnum=1&ct=result&cd=2&ved=0CBYQrAIoATAA&usg=AFQjCNF8iEFagWFIVEiQCdK-PCdYyjTk-Q

link quoted below:
The Youtube videos have links to even faster ones.

Is there any way I can get the code for that robot? That was amazing!!! And extraordinarily fast!!!

Here’s another fast VEX line-follower. It posted the highest Programming Skills Challenge score in the world last year (playing Elevation: 37 points at World Championships). http://www.youtube.com/watch?v=rqovGM8Tl7Q

I would have to look around to see if I still have the code. It was written by a 9th grader. :slight_smile:

For some objects of the pointer “that robot” the answer is Yes! And it only takes a little reading between the lines of the post your are responding to.

So if you go to Robotc.net and download RobotC, you can find a bunch of line follower code at Program Files/Robotics Academy/ROBOTC for IFI/Sample Programs/VEX/Advanced

RobotC has a large number of sample programs. Balancing robots, sensor readings, serial port control, etc. Looks like exellent reading, even after your trial RobotC download expires.

Thanks so much for your help all! I think that I made my code a little more complicated than it needed to be. I took out the “if all else fails” piece, and I did change the updating of the previous sensors to always update if one of them sees tape and it works much better.

My robot accomplishes the task, but it isn’t very pretty. I am finding that once it gets off of the line, it comes back, but tends to over-rotate and then it ends up always overshooting the line all the way around the track. Any suggestions on how to make the line tracking a little smoother?

Brainstorm suggestions:

  • overshoot vs undershoot is standard control theory, read about that and tune the amount you turn. This is the simplest change.
  • instead of converting the analog reading to digital (>TAPE or not) use the analog value to proportionally set the feedback to all or nothing. This should let you track the white/black edge of one side of the tape.
  • how long does it take to get a sensor reading, vs to change the heading of the robot?
  • Download RobotC and look at the example programs for line tracking, as listed in a previous post. There are some that use PID control to set movement reponse that is a weighted sum of Proportional error, Integrated error, Derivative error;