help with Programming a line following robot

Author
shrishi485
New Member
  • Total Posts : 10
  • Reward points : 0
  • Joined: 2014/12/09 14:05:28
  • Location: 0
  • Status: offline
2014/12/09 14:43:54 (permalink)
0

help with Programming a line following robot

I’m working on programming a line following robot which follows a black line using two sensors and when both sensors senses black at the end of the line which looks like a ’T’ the robot is supposed to turn around and follow the line back to its original spot and do the whole procedure two times. I made the robot follow the line but i need help with making it turn around. Please help.
 

Hardware Notes:
 * PIC used:
 * PIC24FV32KA302 operating at 8MHz)
 * I/O ports used and hardware attached
 * Outputs:
 * RB10, RB11, RB2, RB3, RB8, RB9 connected to SN754410NE
 * RB5 and RB6 connected to left and right LEDs
 * Inputs:
 * RB15 is an analog input connected to the potentiometer
 * RB12 is an analog input connected to the left sensor
 * RB4 is an analog input connected to the right sensor
 *

 ********************************************************************/

/*******************************************************************
    Include Files
 ********************************************************************/

#include "p24FV32KA302.h"
#include "configBits.h"
#include "delay.h"

/*******************************************************************
    Symbolic Constants used by main()
 ********************************************************************/

//leave blank for now

/*******************************************************************
    Local Function Prototypes
 ********************************************************************/

void initialize();
void get_Inputs(void);
void decide(void);
void refresh_PWM(void);
void delayMs(unsigned int ms);
void line(void);

/*** Global Variable Declarations *********************************************/

unsigned int potValue; // Raw Potentiometer setting
unsigned int leftOpto; // Left Opto Sensor Signal
unsigned int rightOpto; // Right Opto Sensor Signal
unsigned int counter = 0; //Counter for when both Sensor senses black

/*** main() Function **********************************************************/

int main(void) {
    initialize();

    //infinite loop
    while (1) {
        get_Inputs();
        decide();
        refresh_PWM();
        delayMs(100);
    }

}

/*******************************************************************************
 * Function: void initialize(void)
 *
 * Overview: Initializes the microcontroller, the peripherals
 * used in the application and any global variables
 * used by multiple functions.
 *
 * Note: None
 ******************************************************************************/

void initialize(void) {
    //TRISB = 0x0000;
    //LATB = 0x0000; // digital outputs
    TRISB = 0x9010; //Initialize RB15(Potentiometer),
                    //RB12(Left Sensor) and RB4(Right Sensor) as inputs
    ANSB = 0x9010; //Initialize RB15(Potentiometer),
                   //RB12(Left Sensor) and RB4(Right Sensor) as analog inputs
    LATBbits.LATB5 = 0; //initialize output port RB5
    LATBbits.LATB6 = 0; //initialize output port RB6


    // Initialize "3-4EN" PWM control output (RB10/OC3)

    TRISBbits.TRISB10 = 0; // Make RB10 digital O/P
    LATBbits.LATB10 = 0; // initialize the pin0 voltage level

    // Initialize Output Compare 3 (OC3) to drive Motor A PWM signal to "3-4EN"
    // We want to create 61.04Hz PWM frequency @ 1024 bits resolution

    OC3CON1bits.OCM = 0b000; // Disable the OC module
    OC3R = potValue; // Write the duty cycle for the 1st PWM pulse
    OC3CON1bits.OCTSEL = 0; // Select Timer 2 as the OC time base
    OC3CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)

    // Initialize "1-2EN" PWM control output (RB11/OC2)

    TRISBbits.TRISB11 = 0; // Make RB11 digital O/P
    LATBbits.LATB11 = 0; // initialize the pin voltage level

    // Initialize Output Compare 2 (OC2) to drive Motor B PWM signal to "1-2EN"
    // We want to create 61.04Hz PWM frequency @ 1024 bits resolution

    OC2CON1bits.OCM = 0b000; // Disable the OC module
    OC2R = potValue; // Write the duty cycle for the 1st PWM pulse
    OC2CON1bits.OCTSEL = 0; // Select Timer 2 as the OC time base
    OC2CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)

    // Initialize and enable Timer 2 to create a 61.04Hz PWM frequency for both
    //PWM channels

    T2CONbits.TON = 0; // Disable Timer
    T2CONbits.TCS = 0; // Select internal instruction clock (Fosc/2)
    // which results in 8MHz/2 = 4MHz
    T2CONbits.TGATE = 0; // Disable Gated Timer Mode
    T2CONbits.TCKPS = 0b10; // Select 1:64 prescale (4MHz/64 = 62.5kHz)
    TMR2 = 0x00; // Clear timer register
    PR2 = 1024; /* Load the period register with 1024
                 * This represents the highest value that can be
                 * loaded from the pot (10-bit converted value)
                 * This forces you to choose a higher prescale.
                 * Using Fosc/2 setting = 4MHz clock
                 * choose a 1:64 prescale = 62.5kHz = .016ms period
                 * Loading the PR2 register with 1024
                 * results in a 61.04Hz control signal
                 * since 1024 * .000016s period = 16.384ms
                 * f = 1/16.384ms = 61.04Hz */

    IFS0bits.T2IF = 0; // Clear Timer 2 interrupt flag
    T2CONbits.TON = 1; // Start timer (starts PWMs)

    AD1CON1bits.ADON = 1; // Turn on ADC

    /***********************SERVO CONTROL*************************************/

  /* Initialize Output Compare 1 (OC1) to drive Servo motor using PWM signal
   * We want to create a 50Hz PWM frequency (20ms period)
   * The servo is limited from 0 to 180 degrees rotation.
   * The pulse width for this servo ranges from .75ms to 2.25ms.
   * Two Servo positions could be:
   * 0 degrees: 3.75% duty cycle (.75ms) * 10,000 = 375
   * 90 degrees: 7.5% duty cycle (1.5ms) * 10,000 = 750 */

  OC1CON1bits.OCM = 0b000; // Disable the OC module
  OC1R = 375; // Write the duty cycle for the
                                 // 1st PWM pulse at 0 degrees
  OC1CON1bits.OCTSEL = 0b001; // Select Timer 3 as the OC time base
  OC1CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)
  OC1CON2bits.SYNCSEL = 0b01101; // Synchronize OC1 to Timer3

  // Initialize and enable Timer 3 to create a 50Hz PWM frequency
  // for PWM channel

  T3CONbits.TON = 0; // Disable Timer
  T3CONbits.TCS = 0; // Select internal instruction clock (Fosc/2)
                                // which results in 8MHz/2 = 4MHz
  T3CONbits.TGATE = 0; // Disable Gated Timer Mode
  T3CONbits.TCKPS = 0b01; // Select 1:8 prescale (4MHz/8 = 500kHz)
  TMR3 = 0x00; // Clear timer register
  PR3 = 10000; /* Load the period register needed for
                                 * for 50Hz (20ms) control signal.
                                 * Using Fosc/2 setting = 4MHz clock
                                 * snd 1:8 prescale = 500kHz = 0.02ms period
                                 * 10,000 clock cycles needed (20ms/0.02ms) */

  IFS0bits.T3IF = 0; // Clear Timer 3 interrupt flag
  T3CONbits.TON = 1; // Start timer (starts PWMs)

}

/*******************************************************************************
 * Function: void get_Inputs(void)
 *
 * Overview: Obtains any input information either on-chip
 * (from internal registers, etc...) or off-chip
 * (pin voltage levels). Uses this information to modify
 * or update special data structures used in the control
 * function "decide()"
 *
 * Note: None
 ******************************************************************************/

void get_Inputs(void) {

    // Sample/convert/save Opto Sensor and Pot Levels

    // Left

    AD1CHS = 0x000C; // Connect AN15(RB4) as CH0 input
    AD1CON1bits.SAMP = 1; // Sample potentiometer value
    delayMs(5); // after 5mS start conversion
    AD1CON1bits.SAMP = 0; // Convert potentiometer value
    while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
    leftOpto = ADC1BUF0; // yes, then save ADC value

    // Right

    AD1CHS = 0x000F; // Connect AN12(RB12) as CH0 input
    AD1CON1bits.SAMP = 1; // Sample potentiometer value
    delayMs(5); // after 5mS start conversion
    AD1CON1bits.SAMP = 0; // Convert potentiometer value
    while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
    rightOpto = ADC1BUF0; // yes, then save ADC value

    // Sample/save potentiometer connected to RA0/AN0

    AD1CHS = 0x0009; // Connect ANSB15(RB15) as CH0 input
    AD1CON1bits.SAMP = 1; // Sample potentiometer value
    delayMs(1); // after 1mS start conversion
    AD1CON1bits.SAMP = 0; // Convert potentiometer value
    while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
    potValue = ADC1BUF0; // yes, then save ADC value

}

/*******************************************************************************
 * Function: void decide(void)

 * Overview: Makes decisions based on the input information
 * gathered in get_Inputs() function to manipulate
 * global output control variables.
 *
 * Note: None
 ******************************************************************************/

void decide(void) {

    if (leftOpto < 145) { //Left sensor senses black, turns left
        LATBbits.LATB5 = 1;
        PORTBbits.RB2 = 0;
    }
    else
    {
        LATBbits.LATB5 = 0;
        PORTBbits.RB2 = 1;
    }
    if (rightOpto < 145) //Right sensor sense black, turns right
    {
        LATBbits.LATB6 = 1;
        PORTBbits.RB8 = 0;
    } else
    {
        LATBbits.LATB6 = 0;
        PORTBbits.RB8 = 1;
    }
    if (leftOpto < 145 && rightOpto < 145) {
        counter++;
        line();
    }
}

/*******************************************************************************
 * Function: void refresh_PWM(void)

 * Overview: Based on the decisions made in the previous function,
 * this function updates the speed of the motors
 *
 * Note: None
 ******************************************************************************/

void refresh_PWM(void) {
    // Update PWM duty cycle registers (motor speed/torque) for both motors
    OC3R = potValue;
    OC2R = potValue;
}

/*******************************************************************************
 * Function: void delayMs(unsigned int ms)
 *
 * PreCondition: Requires Tcyc=250nS (8 MHz Fosc)
 *
 * Input: delay in milliseconds (1-65535)
 *
 * Output: None
 *
 * Side Effects: Blocking Delay (CPU is blocked from doing other tasks)
 * Non-portable (Uses PIC24 Assembly language instructions)
 *
 * Overview: This function implements an in-line delay of up to 65535ms
 *
 * Note: None
 ******************************************************************************/

void delayMs(unsigned int ms) {
    while (ms--) {
        asm("repeat #4000"); // 4000 instruction cycles @ 250nS ea. = 1mS
        asm("nop"); // instruction to be repeated 4000x
    }

}

void line(void) {
    if (counter == 0)
    {
        refresh_PWM();

        PORTBbits.RB2 = 1; //Ignore the first time both sensor senses black and
                           //keep going
        PORTBbits.RB8 = 1;
        delayMs(500);
        counter++;

    }
    else if (counter == 4) {

        PORTBbits.RB2 = 0;//Ignore the second time both sensor senses black and
                          //keep going
        PORTBbits.RB8 = 0;

    }
    else
    {
        PORTBbits.RB2 = 0;//Stop and turn the servo
        PORTBbits.RB9 = 0;
        OC1R = 1125;
// the code below is supposed to turn the robot but it doesnt. the robot keeps turning and doesnt follow the line back

// PORTBbits.RB2 = 1;//Turn around
// PORTBbits.RB9 = 1;
// delayMs(2000);
// do
// {
// PORTBbits.RB2 = 0;//Turn around
// PORTBbits.RB9 = 0;
// decide();
// }
// while(leftOpto < 145 || rightOpto < 145);
// do
// {
// PORTBbits.RB2 = 1;//Turn around
// PORTBbits.RB9 = 1;
// }
// while(leftOpto > 145 && rightOpto > 145);
//
//
//
// do{
// PORTBbits.RB2 = 0;//Turn around
// PORTBbits.RB9 = 0;
// decide();
// }
// while(leftOpto < 145 || rightOpto < 145);
// while (1)
// {
// if(leftOpto > 145 && rightOpto > 145)
// {
// PORTBbits.RB2 = 1;//Turn around
// PORTBbits.RB9 = 1;
// }
// else
// break;
// }
// decide();
// }
}
}

#1

19 Replies Related Threads

    NKurzman
    A Guy on the Net
    • Total Posts : 16123
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 17:00:06 (permalink)
    0
    If Both sensors are Blocked.
    Turn Rights until you find the Line with the right sensor, Then loose the line.
     
     Then Go again.
    #2
    Ian.M
    Super Member
    • Total Posts : 13114
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 17:04:07 (permalink)
    0
    To have any chance of helping you in detail, we'd need the schematic, an accurately dimensioned chassis layout with the sensor positions clearly marked and the minimum and maximum line widths so we could see how the center of rotation during a turn, the line and the sensors interact.
     
     

    --
    NEW USERS: Posting images, links and code - workaround for restrictions.
    I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
    #3
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 17:41:45 (permalink)
    0

    The chassis is a 6” x 61/2” board with two sensors on the bottom front and a breadboard on top with a battery pack. The sensors are about 4mm above the surface and about 2.5cm apart from each other. The black line is the width of electrical tape, so about 2 cm. A picture of the path is attached to this post. Please have a look at it and the code. Thank you very much.

    Attached Image(s)

    #4
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 17:54:36 (permalink)
    0
    Can you please write the code on how you would do it? Thank You
    #5
    NKurzman
    A Guy on the Net
    • Total Posts : 16123
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 18:13:10 (permalink)
    0
    No Thank You.
    #6
    Ian.M
    Super Member
    • Total Posts : 13114
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 18:56:35 (permalink)
    0
    We are happy to explain or suggest algorithms and help you debug any code you write, but as this type of line following robot is frequently used either for competitions or as a student project, we cannot ethically write code for you.   If you are a student, you *REALLY* don't want us to write code for you as your teachers are well aware of this site and if they see code posted by us that resembles yours too closely, you will receive ZERO credit for your code.
     
    It appears that the courses your robot is required to run are based on a 2011 competition:
    http://silver-fox.ca/pdf/Regional_Robotics_Competition_2011_Final.pdf
     
     
    To help with detailed algorithm suggestions we really need that chassis layout so we can see the relationship between the wheel and sensor positions and predict what the sensor response will be as the robot turns 180 degrees.
     
    The basic end-point algorithm would be to count the number of endpoints reached taking the initial endpoint as zero.  Zero and Four are special cases.  For end point counts 1 to 3, the robot should turn on the spot through an angle of  180 degrees then reacquire the line and resume running it.  The exact details of how to perform a 180 degree turn depends on the sensors you have available.  Its fairly easy if you have accurate wheel rotation sensors but considerably more difficult and slower if you only have the two line following sensors. 
     

    --
    NEW USERS: Posting images, links and code - workaround for restrictions.
    I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
    #7
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 19:10:39 (permalink)
    0
    It is an assignment we have to do in class and three of the best robots will take part in that competition. I’m not telling you to write the code for me but just explain how it would work. And i dont have a wheel rotation sensor. only two line following sensors. Thank you for your help.
    #8
    NKurzman
    A Guy on the Net
    • Total Posts : 16123
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 19:24:00 (permalink)
    0
    //Stop and turn the servo

     
     you turn then check for the line.  Right forward & left reverse Or right forward, Left stop. which is best depends on the chassis.
    You need to look for the line while you turn. (rightOpto < 145)
    you need to make sure the sensor crosses the line.(rightOpto >145)
    Assuming a right turn the Right sensor will detect the line.
    Continue turning until it loose the line , or the left sensor picks up the line.
    Which is best you will need to check it depends on motor speed and sensor gap.
     
     
    #9
    vini_i
    Super Member
    • Total Posts : 395
    • Reward points : 0
    • Joined: 2014/01/16 17:51:55
    • Location: Ohio, United States
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 19:24:57 (permalink)
    0
    i can't write the code for you but i can give you some pointers. 
    first in your decide function don't use a counter instead use a state machine. bring in your inputs then decide what state you're in. for example when the robot first powers on then your in state one (start state) drive forward. when both sensors see a black line got to state two (first black line detected) continue to drive forward. when both sensors see white go to state three (line follow) adjust wheels to follow line. when both sensors see black at the same time go to state four (turn state) command the wheels in opposite directions to rotate. 
     
    this is where knowing the size and shape of your robot is needed to know how the sensors will see the line.
     
    after the turn state go back to the line follow state and so on and so on. 
     
    also don't use any delays accept inside the analog routines. (5ms may be a bit too long to sample, see if it's possible to cut that down) you want your main loop to run as fast as possible making as many decisions as possible. 
     
    sample the pot value only once during the initialization and set up the pwm values. (your not going to change the pot while the robot is running the line)
     
     
    #10
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 20:16:14 (permalink)
    0
    Thank you for your reply. I’m not familiar with state machines. by state machines do you mean switch and case statements? Can you show me a sample code maybe? 
    #11
    NKurzman
    A Guy on the Net
    • Total Posts : 16123
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 20:37:48 (permalink)
    0
    a State machine can be constructed with  switch and case
     
    switch(state)
    {
       case 1:
          // Do stuff
          if(something )state= 2;
          break;
      case 2:
          // Do stuff
          if(something )state= 1;
          if(something different )state= 3;
          break;
      case 3:
          // Do stuff
          state= 1;
          break;
       default:
          state = 1;
          break;
    }
     
    State machines can be complicated, but can be simple in your case.
    They can be done using other methods then switch statement. ( you you the switch is fine
    #12
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 20:40:22 (permalink)
    0
    Thanks. That helped a lot.
    #13
    Ian.M
    Super Member
    • Total Posts : 13114
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: help with Programming a line following robot 2014/12/09 20:54:45 (permalink)
    0
    Even so it will be simpler to keep a count of the number of end of lines reached to decide whether the next state is turn around or stop.   Otherwise you would need a separate state for each time it runs the line.
     
    You should also look at the C enum type. Enums let you create a type with named values instead of numbers which lets you esasily use meaningful names for your states.

    --
    NEW USERS: Posting images, links and code - workaround for restrictions.
    I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
    #14
    vini_i
    Super Member
    • Total Posts : 395
    • Reward points : 0
    • Joined: 2014/01/16 17:51:55
    • Location: Ohio, United States
    • Status: offline
    Re: help with Programming a line following robot 2014/12/10 04:08:27 (permalink)
    0 (1)
    Even so it will be simpler to keep a count of the number of end of lines reached to decide whether the next state is turn around or stop. Otherwise you would need a separate state for each time it runs the line.

    you could embed a counter inside the state machine to keep track
     
    to use a previous example by NKurzman
    static unsigned char end_point = 0;

    switch(state)
    {
       case 1:
          // Do stuff
          if(something )state= 2;
          break;
      case 2:
          // Do stuff
          if(something )state= 1;
          if(something different )
          {
              state= 3;
              end_point++; // increment counter every state change from 2 to 3
          }
          break;
      case 3:
          // Do stuff
          state= 1;
          break;
       default:
          state = 1;
          break;
    }

    #15
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/15 22:27:30 (permalink)
    0
    I used your suggestions and used state machines in my code, the robot now follows the line turns around the first time it hits the end of the line(both sensors black), and follows the line back but when it hits black again at the beginning of the line it doesn't turn around again and instead, keeps going forward. Please take a look at my code and help.
     
    //TEJ3M mainCircuitPrototype.c
    /*********************************************************************
    Module:
     main()

    Explain Operation of Program here:
    This program is designed for a robot with two sensors. The sensors senses black
    or white. If one sensor senses black the robot turns in the appropriate
    direction and if both sensor senses black which is at the end of the path
    which looks like a 'T' the robot turns around and follows the line back and does
    this two times and comes to a complete stop after both sensor senses black for
    the 4 time.

    Hardware Notes:
     * PIC used:
     * PIC24FV32KA302 operating at 8MHz)
     * I/O ports used and hardware attached
     * Outputs:
     * RB10(21), RB11(22): connected to SN754410NE
     * RB2(6,left motor), RB3(7, left motor),
     * RB8(17, right motor),RB9(18, right motor) connected to SN754410NE
     * RB5(14) and RB6(15) connected to left and right LEDs
     * Inputs:
     * RB15/ANS9 is an analog input connected to the potentiometer
     * RB12/ANS12 is an analog input connected to the right sensor
     * RB4/ANS15 is an analog input connected to the left sensor
     

     ********************************************************************/

    /*******************************************************************
        Include Files
     ********************************************************************/

    #include "p24FV32KA302.h"
    #include "configBits.h"
    #include "delay.h"

    /*******************************************************************
        Symbolic Constants used by main()
     ********************************************************************/
    #define STATE_START 0
    #define STATE_ROTATE_1 1
    #define STATE_ROTATE_2 2
    #define STATE_ROTATE_3 3
    #define STATE_ROTATE_4 4
    #define STATE_ROTATE_EX 5

    /*******************************************************************
        Local Function Prototypes
     ********************************************************************/

    void initialize();
    void get_Inputs(void);
    void decide(void);
    void refresh_PWM(void);
    void delayMs(unsigned int ms);
    void turnAround(void);
    void leftTurn(void);
    void rightTurn(void);
    void forward(void);
    void stop(void);

    /*** Global Variable Declarations *********************************************/

    unsigned int potValue; // Raw Potentiometer setting
    unsigned int leftOpto; // Left Opto Sensor Signal
    unsigned int rightOpto; // Right Opto Sensor Signal
    unsigned int counter = -1; //Counter for when both Sensor senses black
    unsigned int state = STATE_ROTATE_1; //Set the state to STATE_ROTATE_1

    /*** main() Function **********************************************************/

    int main(void) {
        initialize();

        //infinite loop
        while (1) {
            get_Inputs();
            decide();
            refresh_PWM();
            delayMs(100);
        }

    }

    /*******************************************************************************
     * Function: void initialize(void)
     *
     * Overview: Initializes the microcontroller, the peripherals
     * used in the application and any global variables
     * used by multiple functions.
     *
     * Note: None
     ******************************************************************************/

    void initialize(void) {

        /*Initialize RB15(Potentiometer), RB12(Left Sensor) and RB4(Right Sensor)
                                                                         as inputs*/
        TRISB = 0x9010;

        /*Set RB15(Potentiometer),RB12(Left Sensor) and RB4(Right Sensor) as analog
                                                                            inputs*/
        ANSB = 0x9010;

        LATBbits.LATB5 = 0; //initialize output port RB5 for left led
        LATBbits.LATB6 = 0; //initialize output port RB6 for right led


        // Initialize "3-4EN" PWM control output (RB10/OC3)

        TRISBbits.TRISB10 = 0; // Make RB10 digital O/P
        LATBbits.LATB10 = 0; // initialize the pin0 voltage level

        // Initialize Output Compare 3 (OC3) to drive Motor A PWM signal to "3-4EN"
        // We want to create 61.04Hz PWM frequency @ 1024 bits resolution

        OC3CON1bits.OCM = 0b000; // Disable the OC module
        OC3R = potValue; // Write the duty cycle for the 1st PWM pulse
        OC3CON1bits.OCTSEL = 0; // Select Timer 2 as the OC time base
        OC3CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)

        // Initialize "1-2EN" PWM control output (RB11/OC2)

        TRISBbits.TRISB11 = 0; // Make RB11 digital O/P
        LATBbits.LATB11 = 0; // initialize the pin voltage level

        // Initialize Output Compare 2 (OC2) to drive Motor B PWM signal to "1-2EN"
        // We want to create 61.04Hz PWM frequency @ 1024 bits resolution

        OC2CON1bits.OCM = 0b000; // Disable the OC module
        OC2R = potValue; // Write the duty cycle for the 1st PWM pulse
        OC2CON1bits.OCTSEL = 0; // Select Timer 2 as the OC time base
        OC2CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)

        // Initialize and enable Timer 2 to create a 61.04Hz PWM frequency for both
        //PWM channels

        T2CONbits.TON = 0; // Disable Timer
        T2CONbits.TCS = 0; // Select internal instruction clock (Fosc/2)
        // which results in 8MHz/2 = 4MHz
        T2CONbits.TGATE = 0; // Disable Gated Timer Mode
        T2CONbits.TCKPS = 0b10; // Select 1:64 prescale (4MHz/64 = 62.5kHz)
        TMR2 = 0x00; // Clear timer register
        PR2 = 1024; /* Load the period register with 1024
                     * This represents the highest value that can be
                     * loaded from the pot (10-bit converted value)
                     * This forces you to choose a higher prescale.
                     * Using Fosc/2 setting = 4MHz clock
                     * choose a 1:64 prescale = 62.5kHz = .016ms period
                     * Loading the PR2 register with 1024
                     * results in a 61.04Hz control signal
                     * since 1024 * .000016s period = 16.384ms
                     * f = 1/16.384ms = 61.04Hz */

        IFS0bits.T2IF = 0; // Clear Timer 2 interrupt flag
        T2CONbits.TON = 1; // Start timer (starts PWMs)

        AD1CON1bits.ADON = 1; // Turn on ADC

        /***********************SERVO CONTROL*************************************/

        /* Initialize Output Compare 1 (OC1) to drive Servo motor using PWM signal
         * We want to create a 50Hz PWM frequency (20ms period)
         * The servo is limited from 0 to 180 degrees rotation.
         * The pulse width for this servo ranges from .75ms to 2.25ms.
         * Two Servo positions could be:
         * 0 degrees: 3.75% duty cycle (.75ms) * 10,000 = 375
         * 90 degrees: 7.5% duty cycle (1.5ms) * 10,000 = 750 */

        OC1CON1bits.OCM = 0b000; // Disable the OC module
        OC1R = 375; // Write the duty cycle for the
        // 1st PWM pulse at 0 degrees
        OC1CON1bits.OCTSEL = 0b001; // Select Timer 3 as the OC time base
        OC1CON1bits.OCM = 0b110; // Select the OC mode (Edge PWM)
        OC1CON2bits.SYNCSEL = 0b01101; // Synchronize OC1 to Timer3

        // Initialize and enable Timer 3 to create a 50Hz PWM frequency
        // for PWM channel

        T3CONbits.TON = 0; // Disable Timer
        T3CONbits.TCS = 0; // Select internal instruction clock (Fosc/2)
        // which results in 8MHz/2 = 4MHz
        T3CONbits.TGATE = 0; // Disable Gated Timer Mode
        T3CONbits.TCKPS = 0b01; // Select 1:8 prescale (4MHz/8 = 500kHz)
        TMR3 = 0x00; // Clear timer register
        PR3 = 10000; /* Load the period register needed for
                                     * for 50Hz (20ms) control signal.
                                     * Using Fosc/2 setting = 4MHz clock
                                     * snd 1:8 prescale = 500kHz = 0.02ms period
                                     * 10,000 clock cycles needed (20ms/0.02ms) */

        IFS0bits.T3IF = 0; // Clear Timer 3 interrupt flag
        T3CONbits.TON = 1; // Start timer (starts PWMs)

    }

    /*******************************************************************************
     * Function: void get_Inputs(void)
     *
     * Overview: Obtains any input information either on-chip
     * (from internal registers, etc...) or off-chip
     * (pin voltage levels). Uses this information to modify
     * or update special data structures used in the control
     * function "decide()"
     *
     * Note: None
     ******************************************************************************/

    void get_Inputs(void) {

        // Sample/convert/save Opto Sensor and Pot Levels

        // Left Sensor

        AD1CHS = 0x000F; // Connect AN15(RB4) as CH0 input
        AD1CON1bits.SAMP = 1; // Sample potentiometer value
        delayMs(5); // after 5mS start conversion
        AD1CON1bits.SAMP = 0; // Convert potentiometer value
        while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
        leftOpto = ADC1BUF0; // yes, then save ADC value

        // Right Sensor

        AD1CHS = 0x000C; // Connect AN12(RB12) as CH0 input
        AD1CON1bits.SAMP = 1; // Sample potentiometer value
        delayMs(5); // after 5mS start conversion
        AD1CON1bits.SAMP = 0; // Convert potentiometer value
        while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
        rightOpto = ADC1BUF0; // yes, then save ADC value

        // Sample/save potentiometer connected to RA0/AN0

        AD1CHS = 0x0009; // Connect ANSB15(RB15) as CH0 input
        AD1CON1bits.SAMP = 1; // Sample potentiometer value
        delayMs(1); // after 1mS start conversion
        AD1CON1bits.SAMP = 0; // Convert potentiometer value
        while (!AD1CON1bits.DONE); // conversion done? (takes 12*Tad)
        potValue = ADC1BUF0; // yes, then save ADC value

    }

    /*******************************************************************************
     * Function: void decide(void)

     * Overview: Makes decisions based on the input information
     * gathered in get_Inputs() function to manipulate
     * global output control variables.
     *
     * Note: None
     ******************************************************************************/

    void decide(void) {

        //Left sensor black, right sensor white, turn left
        if (leftOpto < 150 && rightOpto > 150)
        {
            LATBbits.LATB5 = 1; //left led on
            leftTurn();
        }

        //Left sensor white, right sensor black, turn right
        if (leftOpto > 150 && rightOpto < 150)
        {
            LATBbits.LATB6 = 1; //right led on
            rightTurn();
        }

        //Both sensors white, go forward
        if (leftOpto > 150 && rightOpto > 150)
        {
            forward();
        }

        //Both are black, go to turnAround function
        if (leftOpto < 150 && rightOpto < 150) {
            //both leds on
            LATBbits.LATB5 = 1;
            LATBbits.LATB6 = 1;
            counter++; //increment counter
            turnAround();
        }

    }

    /*******************************************************************************
     * Function: void refresh_PWM(void)

     * Overview: Based on the decisions made in the previous function,
     * this function updates the speed of the motors
     *
     * Note: None
     ******************************************************************************/

    void refresh_PWM(void) {
        // Update PWM duty cycle registers (motor speed/torque) for both motors
        OC3R = potValue;
        OC2R = potValue;
    }

    /*******************************************************************************
     * Function: void delayMs(unsigned int ms)
     *
     * PreCondition: Requires Tcyc=250nS (8 MHz Fosc)
     *
     * Input: delay in milliseconds (1-65535)
     *
     * Output: None
     *
     * Side Effects: Blocking Delay (CPU is blocked from doing other tasks)
     * Non-portable (Uses PIC24 Assembly language instructions)
     *
     * Overview: This function implements an in-line delay of up to 65535ms
     *
     * Note: None
     ******************************************************************************/

    void delayMs(unsigned int ms)
    {
        while (ms--) {
            asm("repeat #4000"); // 4000 instruction cycles @ 250nS ea. = 1mS
            asm("nop"); // instruction to be repeated 4000x
        }

    }

    void turnAround(void)
    {
        //First time both sensor senses black, go forward for 250 ms
        if (counter == 0)
        {
            refresh_PWM();
            LATBbits.LATB3 = 1;
            LATBbits.LATB9 = 1;
            delayMs(250); //changed from 100

        }

        //Stop when both sensors senses black the 4th time
        else if (counter == 4)
        {
            stop();

        }
        
        //If both sensor senses black the 1, 2 and 3 time, enter switch(state)
        else if (counter == 1 || counter == 2 || counter == 3)
        {

            switch (state) {

                //STATE_ROTATE_1: Go backwards for 800 ms
                case STATE_ROTATE_1:

                    //Turn the motors counter-clockwise
                    LATBbits.LATB2 = 1;
                    LATBbits.LATB3 = 0;
                    LATBbits.LATB8 = 1;
                    LATBbits.LATB9 = 0;
                    delayMs(800);

                    /*Break the loop and enter STATE_ROTATE_2 if the following is
                                                                              true*/
                    if (leftOpto > 150 && rightOpto > 150) {
                        state = STATE_ROTATE_2;
                        break;
                    }


                //STATE_ROTATE_2: Turn left for 1000 ms
                case STATE_ROTATE_2:

                    //Left motor reverse, right motor forward
                    leftTurn();
                    delayMs(1000);

                    /*Break the loop and enter STATE_ROTATE_3 if the following is
                                                                              true*/
                    if ((leftOpto < 150 && rightOpto > 150)
                            || (leftOpto > 150 && rightOpto < 150)
                            || (leftOpto < 150 && rightOpto < 150)
                            || (leftOpto > 150 && rightOpto > 150)) {
                        state = STATE_ROTATE_3;
                        break;
                    }


                //STATE_ROTATE_3: Turn left for another 50 ms
                case STATE_ROTATE_3:

                    //Left motor reverse, right motor forward, turn left
                    leftTurn();
                    delayMs(50);

                    /*Break the loop and enter STATE_ROTATE_4 if the following is
                                                                              true*/
                    if ((leftOpto < 150 && rightOpto > 150)
                            || (leftOpto > 150 && rightOpto < 150)
                            || (leftOpto < 150 && rightOpto < 150)
                            || (leftOpto > 150 && rightOpto > 150)) {
                        state = STATE_ROTATE_4;
                        break;
                    }
                    

                //STATE_ROTATE_4: Turn left for another 50 ms
                case STATE_ROTATE_4:
                    state = STATE_START;
                    break;

                //STATE_START: Follow the line again
                case STATE_START:
                    decide();
                    break;
            }

        }


    }

    void rightTurn(void) {
        //left motor forward, right motor reverse, turns right
        LATBbits.LATB2 = 0;
        LATBbits.LATB3 = 1;
        LATBbits.LATB8 = 1;
        LATBbits.LATB9 = 0;
    }

    void leftTurn(void) {
        //left motor reverse, right motor forward, turns left
        LATBbits.LATB2 = 1;
        LATBbits.LATB3 = 0;
        LATBbits.LATB8 = 0;
        LATBbits.LATB9 = 1;
    }

    void forward(void) {
        //left motor forward, right motor forward, goes forward, both leds off
        LATBbits.LATB5 = 0;
        LATBbits.LATB6 = 0;
        LATBbits.LATB2 = 0;
        LATBbits.LATB3 = 1;
        LATBbits.LATB8 = 0;
        LATBbits.LATB9 = 1;
    }
    void stop(void)
    {
        LATBbits.LATB2 = 0;
        LATBbits.LATB3 = 0;
        LATBbits.LATB8 = 0;
        LATBbits.LATB9 = 0;
    }
    // PORTBbits.RB2 = 0; //Stop and turn the servo
    // PORTBbits.RB9 = 0;
    // OC1R = 1125;
    // the code below is supposed to turn the robot but it doesnt. the robot keeps turning and doesnt follow the line back

    // PORTBbits.RB2 = 1;//Turn around
    // PORTBbits.RB9 = 1;
    // delayMs(2000);
    // do
    // {
    // PORTBbits.RB2 = 0;//Turn around
    // PORTBbits.RB9 = 0;
    // decide();
    // }
    // while(leftOpto < 145 || rightOpto < 145);
    // do
    // {
    // PORTBbits.RB2 = 1;//Turn around
    // PORTBbits.RB9 = 1;
    // }
    // while(leftOpto > 145 && rightOpto > 145);
    //
    //
    //
    // do{
    // PORTBbits.RB2 = 0;//Turn around
    // PORTBbits.RB9 = 0;
    // decide();
    // }
    // while(leftOpto < 145 || rightOpto < 145);
    // while (1)
    // {
    // if(leftOpto > 145 && rightOpto > 145)
    // {
    // PORTBbits.RB2 = 1;//Turn around
    // PORTBbits.RB9 = 1;
    // }
    // else
    // break;
    // }
    // decide();
    // }

     
     
    #16
    vini_i
    Super Member
    • Total Posts : 395
    • Reward points : 0
    • Joined: 2014/01/16 17:51:55
    • Location: Ohio, United States
    • Status: offline
    Re: help with Programming a line following robot 2014/12/16 04:08:50 (permalink)
    0 (1)
    from what i can tell, inside the turnAround() function there is no sensor updates and with the hefty delays you are using the readings that were taken 1000ms ago are now outdated and are not good for decision making. instead of delay try making traps. 
     
    //First time both sensor senses black, go forward for 250 ms
        if (counter == 0)
        {
            refresh_PWM();
            LATBbits.LATB3 = 1;
            LATBbits.LATB9 = 1;
             //will keep you trapped here until one sensor sees white again.
            while(leftOpto < 150 || rightOpto < 150)
             {
                get_Inputs(); //refresh sensor data
              }
         }

    #17
    shrishi485
    New Member
    • Total Posts : 10
    • Reward points : 0
    • Joined: 2014/12/09 14:05:28
    • Location: 0
    • Status: offline
    Re: help with Programming a line following robot 2014/12/16 08:33:17 (permalink)
    0
    Ok what if i just do the following in my code, let me know if that will work.
     
    else if(both sensors senses black)
    {
          // go back for 500 ms
          while(leftOpto > 150 && leftOpto > 150)
           {
                //getInputs();
                //turn left until one of them hits black
            }
            if(leftOpto < 150 || rightOpto < 150)
            {
               //turn around for another 200 ms
             }
              while(leftOpto > 150 && leftOpto > 150)
               {
                   //getInputs();
                   //keep turning until one of them senses black again
                   //Will it folllow the line itself after this code or do i have to do anything after this?
                }
     

     
     
     
    #18
    Ian.M
    Super Member
    • Total Posts : 13114
    • Reward points : 0
    • Joined: 2009/07/23 07:02:40
    • Location: UK
    • Status: offline
    Re: help with Programming a line following robot 2014/12/16 09:10:18 (permalink)
    0
    No. You haven't updated the sensor readings inside the while loops. 
     
    You also need to centre on the line after completing the turn.   As you still haven't shown us the sensor positions on the chassis, we have no idea whether the turnaround algorithm should carry on turning till the other sensor detects the line then turn back a bit, or whether they are close enough together that they are both right at the line edges and have detection areas that are large enough to allow it to simply turn till their analog levels are equal, or whether its more complex than that.  
     
    Depending on the final maneuvers while following the line before detecting the end of line T which will always tend to leave it at a small angle, simply backing up for a fixed time before turning may put the bot far enough off the centre of the line that a simple turnaround algorithm may fail.

    --
    NEW USERS: Posting images, links and code - workaround for restrictions.
    I also support http://picforum.ric323.com because this forum is sometimes too broken to use!
    #19
    vini_i
    Super Member
    • Total Posts : 395
    • Reward points : 0
    • Joined: 2014/01/16 17:51:55
    • Location: Ohio, United States
    • Status: offline
    Re: help with Programming a line following robot 2014/12/16 09:54:12 (permalink)
    0
    please understand that without knowing the exact dimensions of your robot, what the turning radius is, where the sensors are and how far apart they are... without having the robot in front of me to watch how it operates it's almost impossible for me to examine your logic. that being said what i can do is give you the tools that you need to succeed.
     
    delays are bad (there are exceptions). if the code is in a delay then it can't be reading sensors or making decisions. the while loop trap i showed you is one way to eliminate delays. always update sensor readings if inside a trap. these traps can be strung together. for example, turn left while both sensors read black. when one sensor reads white leave the first trap and enter the second trap. keep turning until both sensors read white. leave the second trap and enter the third trap. keep turning left until the left sensor reads black again. when the left sensor reads black leave the third trap and enter the fourth trap. keep turning until the left sensor reads white again and the right sensor also reads white. now the black line is between the sensors again and the code can go back to line follow. 
    (again, just guessing on the actual logic)
     
     
    #20
    Jump to:
    © 2018 APG vNext Trial Version 4.5