David's dead reckoning code for the LVBots competition on March 6th. Uses the mbed LPC1768, DRV8835, QTR-3RC, and two DC motors with encoders.

Dependencies:   PololuEncoder Pacer mbed GeneralDebouncer

main.cpp

Committer:
DavidEGrayson
Date:
2014-03-04
Revision:
30:84be2d602dc0
Parent:
29:cfcf08d8ac79
Child:
31:739b91331f31

File content as of revision 30:84be2d602dc0:

#include <mbed.h>
#include <Pacer.h>
#include <GeneralDebouncer.h>
#include <math.h>

#include "main.h"
#include "motors.h"
#include "encoders.h"
#include "leds.h"
#include "pc_serial.h"
#include "test.h"
#include "reckoner.h"
#include "buttons.h"
#include "line_tracker.h"

Reckoner reckoner;
LineTracker lineTracker;

const int16_t drivingSpeed = 300;

void setLeds(bool v1, bool v2, bool v3, bool v4)
{
   led1 = v1;
   led2 = v2;
   led3 = v3;
   led4 = v4;
}

int __attribute__((noreturn)) main()
{
    pc.baud(115200);
    
    // Enable pull-ups on encoder pins and give them a chance to settle.
    encodersInit();
    motorsInit();
    buttonsInit();

    // Test routines
    //testMotors();
    //testEncoders();
    //testLineSensors();
    //testReckoner();
    //testButtons();
    //testDriveHome();
    //testFinalSettleIn();
    //testCalibrate();
    //testLineFollowing();
    //testAnalog();
    testSensorGlitches();

    // Real routines for the contest.
    loadCalibration();
    
    setLeds(1, 0, 0, 0);
    waitForSignalToStart();
    setLeds(0, 1, 0, 0);
    
    findLine();
    //findLineAndCalibrate();
    
    //setLeds(1, 1, 0, 0);
    //turnRightToFindLine();
    setLeds(0, 0, 1, 0);
    followLineToEnd();
    setLeds(1, 0, 1, 0);
    driveHomeAlmost();
    setLeds(0, 1, 1, 0);
    finalSettleIn();
    setLeds(1, 1, 1, 1);
    while(1){}
}

void loadCalibration()
{
    lineTracker.calibratedMinimum[0] = 34872;
    lineTracker.calibratedMinimum[1] = 29335;
    lineTracker.calibratedMinimum[2] = 23845;
    lineTracker.calibratedMaximum[0] = 59726;
    lineTracker.calibratedMaximum[1] = 60110;
    lineTracker.calibratedMaximum[2] = 58446;   
}

void updateReckonerFromEncoders()
{
    while(encoderBuffer.hasEvents())
    {
        PololuEncoderEvent event = encoderBuffer.readEvent();
        switch(event)
        {
        case ENCODER_LEFT | POLOLU_ENCODER_EVENT_INC:
            reckoner.handleTickLeftForward();
            break;
        case ENCODER_LEFT | POLOLU_ENCODER_EVENT_DEC:
            reckoner.handleTickLeftBackward();
            break;
        case ENCODER_RIGHT | POLOLU_ENCODER_EVENT_INC:
            reckoner.handleTickRightForward();
            break;
        case ENCODER_RIGHT | POLOLU_ENCODER_EVENT_DEC:
            reckoner.handleTickRightBackward();
            break;
        }
    }
}

float magnitude()
{
    return sqrt((float)reckoner.x * reckoner.x + (float)reckoner.y * reckoner.y);   
}

float dotProduct()
{
    float s = (float)reckoner.sin / (1 << 30);
    float c = (float)reckoner.cos / (1 << 30);
    float magn = magnitude();
    if (magn == 0){ return 0; }    
    return ((float)reckoner.x * c + (float)reckoner.y * s) / magn;
}

// The closer this is to zero, the closer we are to pointing towards the home position.
// It is basically a cross product of the two vectors (x, y) and (cos, sin).
float determinant()
{
    // TODO: get rid of the magic numbers here (i.e. 30)
    float s = (float)reckoner.sin / (1 << 30);
    float c = (float)reckoner.cos / (1 << 30);
    return (reckoner.x * s - reckoner.y * c) / magnitude();
}

int16_t reduceSpeed(int16_t speed, int32_t reduction)
{
    if (reduction > speed)
    {
        return 0;   
    }
    else
    {
        return speed - reduction;
    }
}

// Returns true if each line sensor has one third of a volt of difference between the
// maximum seen value and the minimum.
bool calibrationLooksGood()
{
    for(uint8_t s = 0; s < LINE_SENSOR_COUNT; s++)
    {
        uint16_t contrast = lineTracker.calibratedMaximum[s] - lineTracker.calibratedMinimum[s];
        if (contrast < 9929)  // 0xFFFF*0.5/3.3 = 9929
        {
            return false;
        }
    }
    return true;
}

void waitForSignalToStart()
{
    while(!button1DefinitelyPressed())
    {
        updateReckonerFromEncoders();
    }   
    reckoner.reset();
}

// Keep the robot pointing the in the right direction (1, 0).
// This should basically keep it going straight.
void updateMotorsToDriveStraight()
{
    const int32_t straightDriveStrength = 1000;
    int16_t speedLeft = drivingSpeed;
    int16_t speedRight = drivingSpeed;
    int32_t reduction = reckoner.sin / (1<<15) * straightDriveStrength / (1 << 15);
    if (reduction > 0)
    {
        speedRight = reduceSpeed(speedRight, reduction);
    }
    else
    {
        speedLeft = reduceSpeed(speedLeft, -reduction);
    }
    motorsSpeedSet(speedLeft, speedRight);   
}

void updateMotorsToFollowLine()
{
    const int followLineStrength = 300;

    int16_t speedLeft = drivingSpeed;
    int16_t speedRight = drivingSpeed;
    int16_t reduction = (lineTracker.getLinePosition() - 1000) * followLineStrength / 1000;
    if(reduction < 0)
    {
        speedLeft = reduceSpeed(speedLeft, -reduction);   
    }
    else
    {
        speedRight = reduceSpeed(speedRight, reduction);
    }
    
    motorsSpeedSet(speedLeft, speedRight);      
}

void findLine()
{   
    GeneralDebouncer lineStatus(10000);
    while(1)
    {
        lineTracker.read();
        lineTracker.updateCalibration();       
        updateReckonerFromEncoders();
        updateMotorsToDriveStraight();        
        lineStatus.update(lineTracker.getLineVisible());       

        if(lineStatus.getState() == true && lineStatus.getTimeInCurrentStateMicroseconds() > 100000)
        {
            break;
        }
    }
}

// THIS IS DEPRECATED.  Instead we are using loadCalibrationAndFindLine.
void findLineAndCalibrate()
{
    Timer timer;
    timer.start();
    
    Timer goodCalibrationTimer;
    bool goodCalibration = false;
    
    while(1)
    {
        lineTracker.read();
        lineTracker.updateCalibration();       
        updateReckonerFromEncoders();        
        updateMotorsToDriveStraight();
        
        if (goodCalibration)
        {
            if(goodCalibrationTimer.read_ms() >= 300)
            {
                // The calibration was good and we traveled for a bit of time after that,
                // so we must be a bit over the line.
                break;
            }
        }
        else
        {
            if(calibrationLooksGood())
            {
                goodCalibration = true;
                goodCalibrationTimer.start();
            }
        }
    }
}

void turnRightToFindLine()
{   
    while(1)
    {
        lineTracker.read();
        lineTracker.updateCalibration();
        updateReckonerFromEncoders();
        
        if(lineTracker.getLineVisible())
        {
            break;   
        }
        
        motorsSpeedSet(300, 100);        
    }
}

void followLineToEnd()
{
    Timer timer;
    timer.start();
    
    GeneralDebouncer lineStatus(10000);
    const uint32_t lineDebounceTime = 100000;
    
    while(1)
    {
        lineTracker.read();
        updateReckonerFromEncoders();

        lineStatus.update(lineTracker.getLineVisible());
        
        bool lostLine = lineStatus.getState() == false &&
          lineStatus.getTimeInCurrentStateMicroseconds() > lineDebounceTime;        
        if(lostLine && timer.read_us() >= 2000000)
        {
            break;   
        }
        
        updateMotorsToFollowLine();     
    }
}

void driveHomeAlmost()
{
    Timer timer;
    timer.start();
    
    while(1)
    {
        updateReckonerFromEncoders();
        
        float magn = magnitude();
        
        if (magn < (1<<17))  
        {
            // We are within 8 encoder ticks, so go to the next step.
            break;
        }
        
        float det = determinant();
        
        int16_t speedLeft = drivingSpeed;
        int16_t speedRight = drivingSpeed;
        if (magn < (1<<20)) // Within 64 encoder ticks of the origin, so slow down.
        {
            int16_t reduction = (1 - magn/(1<<20)) * drivingSpeed;
            speedLeft = reduceSpeed(speedLeft, reduction);
            speedRight = reduceSpeed(speedRight, reduction);
        }
        
        if (det > 0)
        {
            speedLeft = reduceSpeed(speedLeft, det * 1000);
        }
        else
        {
            speedRight = reduceSpeed(speedRight, -det * 1000);               
        }
        motorsSpeedSet(speedLeft, speedRight);
    }
    
    motorsSpeedSet(0, 0);
}

void finalSettleIn()
{
    const int16_t settleSpeed = 300;
    const int16_t settleModificationStrength = 150;
    
    Timer timer;
    timer.start();
    
    // State 0: rotating
    // State 1: Trying to get into final orientation: want [cos, sin] == [1<<30, 0].
    uint8_t state = 0;
    
    Pacer reportPacer(200000);
    Pacer motorUpdatePacer(10000);   

    float integral = 0;
    
    motorsSpeedSet(-settleSpeed, settleSpeed); // avoid waiting 10 ms before we start settling
    
    while(1)
    {
        led1 = (state == 1);
        
        updateReckonerFromEncoders();
        
        float dot = dotProduct();
        int16_t speedModification = -dot * settleModificationStrength;
        if (speedModification > settleModificationStrength)
        {
            speedModification = settleModificationStrength;    
        }
        else if (speedModification < -settleModificationStrength)
        {
            speedModification = -settleModificationStrength;
        }
        
        if (state == 0 && timer.read_ms() >= 2000 && reckoner.cos > (1 << 29))
        {
            // Stop turning and start trying to maintain the right position.
            state = 1;
        }
        
        if (state == 1 && timer.read_ms() >= 5000)
        {
            // Stop moving.
            break;   
        }
        
        if (motorUpdatePacer.pace())
        {
            int16_t rotationSpeed;
            if (state == 1)
            {
                float s = (float)reckoner.sin / (1 << 30);
                integral += s;
                rotationSpeed = -(s * 2400 + integral * 20);
                
                if (rotationSpeed > 300)
                {
                    rotationSpeed = 300; 
                }
                if (rotationSpeed < -300)
                {
                    rotationSpeed = -300; 
                }
            }
            else
            {
                rotationSpeed = settleSpeed;
            }
            
            int16_t speedLeft = -rotationSpeed + speedModification;
            int16_t speedRight = rotationSpeed + speedModification;
            motorsSpeedSet(speedLeft, speedRight);
        }
        
        if (state == 1 && reportPacer.pace())
        {
            pc.printf("%11d %11d %11d %11d | %11f %11f\r\n",
              reckoner.cos, reckoner.sin, reckoner.x, reckoner.y,
              determinant(), dotProduct());
        }
    }
    
    // Done!  Stop moving.
    motorsSpeedSet(0, 0);
}