stefano_maugeri
Published © GPL3+

Autonomous Line Follower with Seeed Shield Bot 1.2 and ARTe

A simple rover for line following made exploiting the ARTe Arduino REal-Time eXtension.

IntermediateShowcase (no instructions)2,192
Autonomous Line Follower with Seeed Shield Bot 1.2 and ARTe

Things used in this project

Hardware components

Arduino Due
Arduino Due
×1
Seeed Studio Shield Bot 1.2
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

firmware motors

C/C++
With this software you can use this shieldbot easily:
http://wiki.seeedstudio.com/Shield_Bot_V1.2/
/*
 *  Motor control.
*/

#ifndef DRIVERMOTORS_H
#define DRIVERMOTORS_H

#include <Arduino.h>


#define RIGHT1              8 
#define SPEEDPINRIGHT       9 
#define RIGHT2              11
#define LEFT1               12
#define SPEEDPINLEFT        10
#define LEFT2               13

#define FINDER1             A0
#define FINDER2             A1
#define FINDER3             A2
#define FINDER4             A3
#define FINDER5             4       /*This pin is 4, not A4*/

#define MAXSPEED            255
#define MINSPEED            100
#define MINSPEED            0
#define MAXSPEEDREVERSE     -255

#define FORWARD             1
#define STOP                0
#define BACKWARDS           -1

#define print               Serial.print
#define println             Serial.println
#define PRINTDEBUG          print("\t[DEBUG] motordx: "); println(speedmotorRight);            \
                            print("\t[DEBUG] motorsx: "); println(speedmotorLeft);

//****************************************************************************************
//          Global variables
//****************************************************************************************

int speedmotorLeft;                     //Power of the left engine. This value will be given to the analogWrite function. The range is: [0:1:255]
int speedmotorRight;                    //Power of the right engine. This value will be given to the analogWrite function. The range is: [0:1:255]
int initValueMotorLeft;                 //Initial and minimum values of the engine. Is not possible to exceed these ranges of values
int initValueMotorRight;
int directionMotorLeft;
int directionMotorRight;


//****************************************************************************************
//          Prototypes
//****************************************************************************************

int readS1();
int readS2();
int readS3();
int readS4();
int readS5();
int getSpeed();
void goLeft();
void goRight();
void initialize(int min_speed_left, int min_speed_right);
void increaseSpeed(int powerOffset);
void decreaseSpeed(int powerOffset);	
void increaseSpeedLeft(int powerOffset);
void increaseSpeedRight(int powerOffset);
void decreaseSpeedLeft(int powerOffset);
void decreaseSpeedRight(int powerOffset);
void rightMotor(int direction);
void leftMotor(int direction);
void drive(int leftDirection, int rightDirection);
void forward();
void backwards();
void stop();

//****************************************************************************
//                  HIGH LEVEL FUNCTION
//****************************************************************************

void initialize(int min_speed_left, int min_speed_right){
    pinMode(RIGHT1,OUTPUT);
    pinMode(RIGHT2,OUTPUT);
    pinMode(SPEEDPINRIGHT,OUTPUT);
    pinMode(LEFT1,OUTPUT);
    pinMode(LEFT2,OUTPUT);
    pinMode(SPEEDPINLEFT,OUTPUT);
    pinMode(FINDER1,INPUT);
    pinMode(FINDER2,INPUT);
    pinMode(FINDER3,INPUT);
    pinMode(FINDER4,INPUT);
    pinMode(FINDER5,INPUT);
    directionMotorRight = directionMotorLeft = STOP;
    initValueMotorLeft  = speedmotorLeft  = min_speed_left;
    initValueMotorRight = speedmotorRight = min_speed_right;
}

int getSpeed() {
    if (speedmotorLeft != speedmotorRight)
        return -1;
    return speedmotorLeft;
}

void goLeft(){
    if (speedmotorRight< MINSPEED) speedmotorRight = initValueMotorRight;
    drive(STOP, FORWARD);
}

void goRight(){
    if (speedmotorLeft < MINSPEED) speedmotorLeft = initValueMotorLeft;
    drive(FORWARD, STOP);
}

void goForward() {
    if (speedmotorLeft < MINSPEED) speedmotorLeft = initValueMotorLeft;
    if (speedmotorRight< MINSPEED) speedmotorRight = initValueMotorRight;
    drive(FORWARD, FORWARD);
}

void goBackwards() {
    drive (BACKWARDS, BACKWARDS);
}

void stop() {
    drive (STOP, STOP);
}

void decreaseSpeed(int powerOffset){
    decreaseSpeedLeft(powerOffset);
    decreaseSpeedRight(powerOffset);
    drive(directionMotorLeft, directionMotorRight);
}

void increaseSpeed(int powerOffset){
    increaseSpeedLeft(powerOffset);
    increaseSpeedRight(powerOffset);
    drive(directionMotorLeft, directionMotorRight);
}
int readS1(){
    return digitalRead(FINDER1);
}

int readS2(){
    return digitalRead(FINDER2);
}

int readS3(){
    return digitalRead(FINDER3);
}

int readS4(){
    return digitalRead(FINDER4);
}

int readS5(){
    return digitalRead(FINDER5);
}



//****************************************************************************
//          LOW LEVEL FUNCTION
//****************************************************************************


void decreaseSpeedLeft(int powerOffset) {
    powerOffset %= MAXSPEED + 1;

    if (speedmotorLeft - powerOffset > initValueMotorLeft) {
        speedmotorLeft -= powerOffset;
    } else {
        speedmotorLeft = initValueMotorLeft;
    }
}

void decreaseSpeedRight(int powerOffset) { 
    powerOffset %= MAXSPEED + 1;

    if (speedmotorRight - powerOffset > initValueMotorRight) {
        speedmotorRight -= powerOffset;
    } else { 
        speedmotorRight = initValueMotorRight;
    }
}

void increaseSpeedLeft(int powerOffset) {
    powerOffset %= 256;

    if (powerOffset + speedmotorLeft < MAXSPEED) {
        speedmotorLeft += powerOffset;
    } else speedmotorLeft = 260;        //Trust me, is not an error. This makes some calculations easier
}

void increaseSpeedRight(int powerOffset) {
    powerOffset %= 256;
    if (powerOffset + speedmotorRight < MAXSPEED) {
        speedmotorRight += powerOffset;
    } else speedmotorRight = 260;
}


void drive(int left, int right) {
    directionMotorLeft = left;
    directionMotorRight = right;
    rightMotor(right);
    leftMotor(left);
}

/**
 * Value: 
 *  if FORWARD     ==> go forward
 *  if STOP        ==> stop engine
 *  if BACKWARDS   ==> go backwards
 */
void rightMotor(int direction) {
    if (direction == FORWARD) {
        analogWrite(SPEEDPINRIGHT, speedmotorRight >= MAXSPEED ? 255 : speedmotorRight);
        digitalWrite(RIGHT1, HIGH);
        digitalWrite(RIGHT2,LOW);
    } else if (direction == STOP) {
        analogWrite(SPEEDPINRIGHT, 0);
    } else {
        analogWrite(SPEEDPINRIGHT, speedmotorRight >= MAXSPEED ? 255 : speedmotorRight);
        digitalWrite(RIGHT1,LOW);
        digitalWrite(RIGHT2,HIGH);
    }
}

/**
 * Value: 
 *  if FORWARD     ==> go forward
 *  if STOP        ==> stop engine
 *  if BACKWARDS   ==> go backwards
 */
void leftMotor(int direction){
    if(direction == FORWARD) {
        analogWrite(SPEEDPINLEFT, speedmotorLeft >= MAXSPEED ? 255 : speedmotorLeft);
        digitalWrite(LEFT1,HIGH);
        digitalWrite(LEFT2,LOW);
    } else if(direction == STOP) {
        analogWrite(SPEEDPINLEFT, 0);
    } else { 
        analogWrite(SPEEDPINLEFT, speedmotorLeft >= MAXSPEED ? 255 : speedmotorLeft);
        digitalWrite(LEFT1,LOW);
        digitalWrite(LEFT2,HIGH);
    }
}
#undef print
#undef println 
#undef PRINTDEBUG
#endif

Sketch Autonomous line follower with ARTe

Arduino
This sketch, written for arduino due, allows to use the shieldbot v1.2 with two modes, that is in automatic mode and in manual mode. The rover will then be controlled via serial.
/*  
 *  The goal of this firmware is to control the engines of the rover through sensors or via commands that arrive via serial.
    
 *  We have two main modalities
        AUTOMATIC. In this mode the sensors are read on board the rover and this will try to follow it.
        MANUAL. In this mode instead, rover receives commands from the serial and we don't care About the values of the sensors, therefore the sensors will not be checked.
    
 *  Technically we have another mode, that is STOPPED. Rover enters in this modality only if we were on the AUTOMATIC mode before.
        In this mode the rover locks the engines and waits to recive the CMD_RESTART command to restart in AUTOMATIC mode.
    
 *  In any of the three modes we find ourselves, it is always possible to receive the CHANGEMODALITY command, so we switch:
        If we are in AUTOMATIC mode, then go to MANUAL mode
        If we are in MANUAL mode, then go to AUTOMATIC mode
        If we are in STOPPED mode, ther go to MANUAL mode

        
 * MANUAL command (recived from users via serial)
     It is possible to make the rover go forward, go back, go left and go right.
     It is possible to modify the speed, at any time
        To go forward send key '8'
        To go backwards send key '2'
        To go right send key '6'
        To go left send key '4'

 * Modify speed
     Only in STOPPED mode it is not possible to modify the speed.
        To increase speed send key '7'
        To decrease speed send key '1'
*/
#include "DriverMotors.h"

#define SENSORS         loop1
#define MOTORS          loop2
#define COMMUNICATION   loop3


#define sprint          Serial.print
#define sprintln        Serial.println

//modes:
#define AUTOMATIC       '1'
#define MANUAL          '2'
#define STOPPED         '3'

//commands:
#define CHANGEMODALITY  'c'
#define CMD_STOP        '5'
#define RESTART         'r'

#define CMD_FORWARD     '8'
#define CMD_BACKWARDS   '2'
#define CMD_LEFT        '4'
#define CMD_RIGHT       '6'
#define CMD_MOTORUP     '7'
#define CMD_MOTORDOWN   '1'

//We decide to change the speed of these values. Range is: [0, 255]. Values outside this range will be changed automatically from "DriverMotors.h"
#define INCREASE_MOTOR  20
#define DECREASE_MOTOR  20


/**
 *  On "S1, S2, .., S5" we save the values read by the sensors.
 *  On "mode" the mode is saved
 *  On "actualCommand" we save the last command received
*/
static int S1, S2, S3, S4, S5;
static char mode;
static char actualCommand;



void setup() {
    Serial.begin(9600);
    arteLock();
    S1 = S2 = S3 = S4 = S5 = HIGH;
    arteUnlock();
    actualCommand = CMD_STOP;

    mode = getCommand();
    mode = mode == AUTOMATIC ? AUTOMATIC : MANUAL;
    initialize(140, 140);
}

/*
 * Read the serial, and stands still until we receive a command
*/
char getCommand() {
    char c;
    while (1){
        while (Serial.available()<=0);      //Block the execution
        c = Serial.read();
        if (c != '\n') break;
    }
    return c;
}

/*
 * This function moves the rover along the path (the path is a black line) and sends to the serial the direction in which it's going.
 * It tries to move in such a way the S3 sensor detects LOW, and the other sensors detect HIGH.
 * 
 *            SENSORS                                             Eg. Ope path                                     Eg. Close path
 *              ||                                                  . (END)                                        _______(START)/(END)
 *              S3                                                 /                                                 |       |
 *           S2 || S4                                              \__                                               |       |
 *         S1   ||   S5                                               \__.  (START)                                  |_______| 
 *
 * If the sensor don't detect any path (they don't find black under themselves) the rover will try to find a path backwards
 * This because:
 *  If we are in an open path (see above) then the rover will not run away, but will go back and forth cyclically
 *  If during a difficult curve the rover goes too far because it is going too fast, it will come back to find the lost line
*/
void follow_line() {
    int s1, s2, s3, s4, s5;
    arteLock();             //Start atomic section
    s1 = S1;
    s2 = S2;
    s3 = S3;
    s4 = S4;
    s5 = S5;
    arteUnlock();           //Finish atomic section

    if ((s3==LOW) && (s1==HIGH && s2==HIGH && s4==HIGH && s5==HIGH)){
        sprintln(CMD_FORWARD);
        goForward();
    } else {
        if (s1==LOW || s2==LOW) {
            sprintln (CMD_RIGHT);
            goRight();

        } else if (s5==LOW || s4==LOW){
            sprintln (CMD_LEFT);
            goLeft();

        } else if (s1 == HIGH && s2 == HIGH && s3 == HIGH && s4 == HIGH && s5 == HIGH) {
            goBackwards();
            sprintln (CMD_BACKWARDS);

        } else {
            Serial.print("We will never enter in this condition\n");
            Serial.print("-1\n");
            
            stop();
            while (1) delay(100);
        }
    }
}

/*
 * This function controls the rover
 * Sends the command executed on the serial line
*/
char driveRover(char cmd) {
    switch(cmd) {
        case CMD_FORWARD:
            sprintln (CMD_FORWARD);
            goForward();
            break;
        case CMD_RIGHT:
            sprintln (CMD_RIGHT);
            goRight();
            break;
        case CMD_LEFT:
            sprintln (CMD_LEFT);
            goLeft();
            break;
        case CMD_BACKWARDS:
            sprintln (CMD_BACKWARDS);
            goBackwards();
            break;
        case CMD_MOTORUP:
            sprintln (CMD_MOTORUP);
            increaseSpeed(INCREASE_MOTOR);
            return ' ';
        case CMD_MOTORDOWN:
            sprintln (CMD_MOTORDOWN);
            decreaseSpeed(DECREASE_MOTOR);
            return ' ';
        case CMD_STOP:
            sprintln (CMD_STOP);
            stop();
            break;
        default:
            sprint("[driveRover] default: <"); sprint(cmd); sprint("#>\n");
            return ' ';
    }
    return cmd;
}

/* 
 * This function is only called if we are in MANUAL mode. Sends via serial the current command that is running
*/
void sendAction(char cmd) {
    switch(cmd) {
        case CMD_FORWARD:
            sprintln (CMD_FORWARD);
            break;
        case CMD_RIGHT:
            sprintln (CMD_RIGHT);
            break;
        case CMD_LEFT:
            sprintln (CMD_LEFT);
            break;
        case CMD_BACKWARDS:
            sprintln (CMD_BACKWARDS);
            break;
        case CMD_STOP:
            sprintln (CMD_STOP);
            break;
        default:    /*Not verifiable*/
            sprintln("-1");
    } 
}


/*
 * Read and store the values of sensors
 */
void SENSORS(50) {
    if (mode == AUTOMATIC) {
        arteLock();               //Start atomic section
        S1 = readS1();
        S2 = readS2();
        S3 = readS3();
        S4 = readS4();
        S5 = readS5();
        arteUnlock();             //Finish atomic section
    }
}

/*
 * Thread MOTORS: change its action based on the mode
 * It must be able to move the rover only if we are in AUTOMATIC mode or MANUAL mode, else keep still the rover. 
 */
void MOTORS(100) {
    char cmd;
    switch (mode) {
        case STOPPED:
            stop();
            sprint(CMD_STOP);
            break;
        case AUTOMATIC:
            follow_line();
            break;
        case MANUAL:
            arteLock();
            cmd = actualCommand;
            arteUnlock();
            sendAction(cmd);
            break;
        default:   /*Not verifiable*/
            sprintln("-1");
    }
}

/* 
 * This task receive command from the serial and will call the corresponding management routine
 */
void COMMUNICATION(200) {
    char cmd = getCommand();
    switch (mode) {
        case AUTOMATIC:
            if      (cmd == CMD_STOP) {
                mode = STOPPED;

            } else if (cmd == CHANGEMODALITY) {
                mode = MANUAL;
                stop();

            } else if (cmd == CMD_MOTORUP) {
                increaseSpeed(20);

            } else if (cmd == CMD_MOTORDOWN) {
                decreaseSpeed(20);

            }
            break;
        case MANUAL:
            if (cmd == CHANGEMODALITY) {
                mode = AUTOMATIC;
            } else {
                cmd = driveRover(cmd);
                if (cmd != ' ') {
                    arteLock();                 //Start atomic section
                    actualCommand = cmd;
                    arteUnlock();               //Finish atomic section
                }
            }
            break;
        case STOPPED:
            if (cmd == CHANGEMODALITY)
                mode = MANUAL;
            else if (cmd == RESTART)
                mode = AUTOMATIC;
            break;
        default: /*Not verifiable*/
            sprintln("-1");
    }
}

void loop() {
}

Credits

stefano_maugeri

stefano_maugeri

0 projects • 0 followers

Comments