This code contains all of the robot drive functions.
#ifndef DRIVE_H #define DRIVE_H #include "constants.h" void driveUntilTime(int heading, float power, int time, bool stop); void driveWhileRotate(int startHeading, float turnDegree, float power, float turnPower, int time); void driveUntilBump(int heading, float power, int bumpSide); int driveUntilBumpTimeout(int heading, float power, int bumpSide, int timeout); void driveUntilCds(int heading, float power, float cdsvalue, int timeout); void driveUntilRPSx(float desiredX, float power, int faultHeading, int timeout); void driveUntilRPSy(float desiredY, float power, int faultHeading, int timeout); void driveUntilRPSxRange(float desiredX, float power, int faultHeading, float range, int timeout); void driveUntilRPSyRange(float desiredY, float power, int faultHeading, float range, int timeout); void turnUntilTime(float power, int time); void turnUntilRPS(int desiredHeading, int power, int timeout); #endif // DRIVE_H
// Required FEH libraries #include <FEHLCD.h> #include <FEHBuzzer.h> #include <FEHUtility.h> #include <FEHMotor.h> #include <FEHIO.h> #include <FEHRPS.h> #include <FEHSD.h> // Required library to calculate motor ratios based on heading #include <math.h> // Required custom libraries #include "constants.h" #include "worldstate.h" #include "drive.h" //Declare global motor ratios float motor1ratio; float motor2ratio; float motor3ratio; float motor4ratio; /* This function calculates the motor ratios for a given heading. The calculation is derived from vector quantities. * The calculated ratios are assigned to each individual motor ratio. The ratios range from -1 to 1. * If the heading is a cardinal direction (N/S/E/W), the ratio is 0.707. * Last Modified: 3/14/2016 JKL */ void ratios(int heading) { LCD.WriteRC("Calculating motor ratios", 5,3); float x, y; x = cos( (45-heading) * PI / 180.0 ); y = sin( (45-heading) * PI / 180.0 ); LCD.WriteRC(x, 6, 3); LCD.WriteRC(y, 7, 3); motor1ratio = x; motor2ratio = -y; motor3ratio = -x; motor4ratio = y; } // end ratios function /* This function drives the robot with a given heading at a given power for a given time in milleseconds. * If bool stop is false, the robot does not stop the motors. There will not be a pause in between the next drive function. * Last Modified: 3/15/2016 JKL */ void driveUntilTime(int heading, float power, int time, bool stop) { ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilTime start"); LCD.WriteRC("Time:", 5, 3); LCD.WriteRC(time, 5, 9); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); Sleep(time); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilTime stop"); if (stop) { motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(100); } } // end driveUntilTime function /* This function drives the robot while rotating it. * startHeading: the initial heading the robot drives. * turnDegree: the total degrees the robot rotates during the drive * power: the power the robot drives at; changing this does not affect the total turn degree or drift angle * turnPower: the constant power added to turn the robot; this is dependent upon turnDegree and time * time: the duration the robot drives in milleseconds * Last modified: 4/8/2016 JKL */ void driveWhileRotate(int startHeading, float turnDegree, float power, float turnPower, int time) { // x is a fraction of the turnDegree used to add to the increment float x = turnDegree / (time / 50.0); for( int increment = 0; increment <= turnDegree; increment += x) { ratios(startHeading + increment); motor1.SetPercent(power * motor1ratio + turnPower); motor2.SetPercent(power * motor2ratio + turnPower); motor3.SetPercent(power * motor3ratio + turnPower); motor4.SetPercent(power * motor4ratio + turnPower); Sleep(50); } motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); } /* This function drives the robot with a given heading at a given power until a side is bumped (or not). * int bumpSide ranges from -4 to 4. The sides are numbered in a clockwise fashion. The front of the robot is side 1. * A positive value allows for a robot drive until that side is bumped. * A negative value allows for the robot to ride along that side until a microswitch is released. * Last Modified: 3/14/2016 */ void driveUntilBump(int heading, float power, int bumpSide) { ratios(heading); LCD.WriteRC("Bump:", 5, 3); LCD.WriteRC(bumpSide, 5, 9); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilBump start"); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // if any of the microswitches are released on the specified side if (bumpSide == -1) while( 2 != (microswitch1.Value() + microswitch2.Value()) ); if (bumpSide == -2) while( 2 != (microswitch3.Value() + microswitch4.Value()) ); if (bumpSide == -3) while( 2 != (microswitch5.Value() + microswitch6.Value()) ); if (bumpSide == -4) while( 2 != (microswitch7.Value() + microswitch8.Value()) ); // if both of the microswitches are depressed on the specified side if (bumpSide == 1) while( 0 != (microswitch1.Value() + microswitch2.Value()) ); if (bumpSide == 2) while( 0 != (microswitch3.Value() + microswitch4.Value()) ); if (bumpSide == 3) while( 0 != (microswitch5.Value() + microswitch6.Value()) ); if (bumpSide == 4) while( 0 != (microswitch7.Value() + microswitch8.Value()) ); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilBump stop"); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(100); } // end driveUntilBump function /* This function drives the robot with a given heading at a given power until a side is bumped (or not). * int bumpSide ranges from -4 to 4. The sides are numbered in a clockwise fashion. The front of the robot is side 1. * A positive value allows for a robot drive until that side is bumped. * A negative value allows for the robot to ride along that side until a microswitch is released. * int timeout limits the duration the robot drives in milleseconds * The return value is 0 is the function fulfills bumpSide or 1 if the function times out. * Last Modified: 3/31/2016 */ int driveUntilBumpTimeout(int heading, float power, int bumpSide, int timeout) { ratios(heading); LCD.WriteRC("Bump:", 5, 3); LCD.WriteRC(bumpSide, 5, 9); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilBumpTimeout start"); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); int returnValue = 0; int startTime = TimeNowMSec(); // if any of the microswitches are released on the specified side if (bumpSide == -1) while( 2 != (microswitch1.Value() + microswitch2.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == -2) while( 2 != (microswitch3.Value() + microswitch4.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == -3) while( 2 != (microswitch5.Value() + microswitch6.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == -4) while( 2 != (microswitch7.Value() + microswitch8.Value()) && TimeNowMSec() - startTime < timeout ) {}; // if both of the microswitches are depressed on the specified side if (bumpSide == 1) while( 0 != (microswitch1.Value() + microswitch2.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == 2) while( 0 != (microswitch3.Value() + microswitch4.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == 3) while( 0 != (microswitch5.Value() + microswitch6.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (bumpSide == 4) while( 0 != (microswitch7.Value() + microswitch8.Value()) && TimeNowMSec() - startTime < timeout ) {}; if (TimeNowMSec() - startTime > timeout) returnValue = 1; worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilBumpTimeout stop"); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(100); return returnValue; } // end driveUntilBumpTimeout function /* This function drives the robot with a given heading at a given power until the cdsvalue is reached. * If the CdS sensor reads a value less than float cdsvalue, the function stops the robot. * Last Modified: 3/25/2016 JKL */ void driveUntilCds(int heading, float power, float cdsvalue, int timeout) { ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilCds start"); LCD.WriteRC("CdS:", 5, 3); LCD.WriteRC(cdsvalue, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); int startTime = TimeNowMSec(); while( cdscell.Value() > cdsvalue && TimeNowMSec() - startTime < timeout); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilCds stop"); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(150); } // end driveUntilCds function /* This function corrects the x-position of the robot using RPS. * The robot will drive until the desiredX coordinate with a given power or until the timeout is exceeded. * If the RPS is -1 or -2 (off the course or dead zone), the robot will pulse in the direction of faultHeading. * Use driveUntilRPSxRange if a custom range/tolerance is needed. * Last Modified: 3/28/2016 JKL */ void driveUntilRPSx(float desiredX, float power, int faultHeading, int timeout) { float startTime = TimeNowMSec(); // the tolerance for the X coordinate is +/- 0.25 inches while((RPS.X() < desiredX - 0.25 || RPS.X() > desiredX + 0.25) && TimeNowMSec() - startTime < timeout) { // pulse toward faultHeading if (RPS.X() == -1 || RPS.X() == -2) { driveUntilTime(faultHeading, power, 500, true); } else if (RPS.X() < desiredX) { int heading = RPS.Heading() + 90; ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSx"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredX, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // continuously drive the motors while the robot is greater than 2 inches away the desiredX while( fabs( desiredX - RPS.X() ) >= 2); // pulse the motors if the robot is within 2 inches of the desiredX if ( fabs( desiredX - RPS.X() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } else if (RPS.X() > desiredX) { int heading = RPS.Heading() - 90; ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSx"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredX, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // continuously drive the motors while the robot is greater than 2 inches away the desiredX while( fabs( desiredX - RPS.X() ) >= 2); // pulse the motors if the robot is within 2 inches of the desiredX if ( fabs( desiredX - RPS.X() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } } // end while loop } // end driveUntilRPSx function /* This function corrects the y-position of the robot using RPS. * The robot will drive until the desiredY coordinate with a given power or until the timeout is exceeded. * If the RPS is -1 or -2 (off the course or dead zone), the robot will pulse in the direction of faultHeading. * Use driveUntilRPSyRange if a custom range/tolerance is needed. * Last Modified: 3/28/2016 JKL */ void driveUntilRPSy(float desiredY, float power, int faultHeading, int timeout) { float startTime = TimeNowMSec(); // the tolerance for the Y coordinate is +/- 0.25 inches while((RPS.Y() < desiredY - 0.25 || RPS.Y() > desiredY + 0.25) && TimeNowMSec() - startTime < timeout) { // pulse toward faultHeading if (RPS.Y() == -1 || RPS.Y() == -2) { driveUntilTime(faultHeading, power, 200, true); } else if (RPS.Y() < desiredY) { int heading = RPS.Heading(); ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSy"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredY, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // continuously drive the motors while the robot is greater than 2 inches away the desiredY while( fabs( desiredY - RPS.Y() ) >= 2); // pulse the motors if the robot is within 2 inches of the desiredY if ( fabs( desiredY - RPS.Y() ) < 2) Sleep(250); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } else if (RPS.Y() > desiredY) { int heading = RPS.Heading() + 180; heading-= 360 * (heading < 360); ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSy"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredY, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // continuously drive the motors while the robot is greater than 2 inches away the desiredY while( fabs( desiredY - RPS.Y() ) >= 2); // pulse the motors if the robot is within 2 inches of the desiredY if ( fabs( desiredY - RPS.Y() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } } // end while loop } // end driveUntilRPSy function /* This function corrects the x-position of the robot using RPS. * The robot will drive until the desiredX coordinate with a given power or until the timeout is exceeded. * If the RPS is -1 or -2 (off the course or dead zone), the robot will pulse in the direction of faultHeading. * float range is the distance error from the desiredX the robot can be within * Last Modified: 4/2/2016 JKL */ void driveUntilRPSxRange(float desiredX, float power, int faultHeading, float range, int timeout) { float startTime = TimeNowMSec(); while((RPS.X() < desiredX - range || RPS.X() > desiredX + range) && TimeNowMSec() - startTime < timeout) { if (RPS.X() == -1 || RPS.X() == -2) { driveUntilTime(faultHeading, power, 500, true); } else if (RPS.X() < desiredX) { int heading = RPS.Heading() + 90; ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSx"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredX, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // to pulse or not to pulse while( fabs( desiredX - RPS.X() ) >= 2); if ( fabs( desiredX - RPS.X() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } else if (RPS.X() > desiredX) { int heading = RPS.Heading() - 90; ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSx"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredX, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // to pulse or not to pulse while( fabs( desiredX - RPS.X() ) >= 2); if ( fabs( desiredX - RPS.X() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } } // end while loop } // end driveUntilRPSxRange function /* This function corrects the y-position of the robot using RPS. * The robot will drive until the desiredY coordinate with a given power or until the timeout is exceeded. * If the RPS is -1 or -2 (off the course or dead zone), the robot will pulse in the direction of faultHeading. * float range is the distance error from the desiredY the robot can be within * Last Modified: 4/2/2016 JKL */ void driveUntilRPSyRange(float desiredY, float power, int faultHeading, float range, int timeout) { float startTime = TimeNowMSec(); while((RPS.Y() < desiredY - range || RPS.Y() > desiredY + range) && TimeNowMSec() - startTime < timeout) { if (RPS.Y() == -1 || RPS.Y() == -2) { driveUntilTime(faultHeading, power, 500, true); } else if (RPS.Y() < desiredY) { int heading = RPS.Heading(); ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSy"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredY, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); // continuously drive the motors while the robot is greater than 2 inches away the desiredY while( fabs( desiredY - RPS.Y() ) >= 2); // pulse the motors if the robot is within 2 inches of the desiredY if ( fabs( desiredY - RPS.Y() ) < 2) Sleep(250); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } else if (RPS.Y() > desiredY) { int heading = RPS.Heading() + 180; heading-= 360 * (heading < 360); ratios(heading); worldState(true, heading, power, motor1ratio, motor2ratio, motor3ratio, motor4ratio); SD.Printf("driveUntilRPSy"); LCD.WriteRC("RPS:", 5, 3); LCD.WriteRC(desiredY, 5, 8); motor1.SetPercent(power * motor1ratio); motor2.SetPercent(power * motor2ratio); motor3.SetPercent(power * motor3ratio); motor4.SetPercent(power * motor4ratio); while( fabs( desiredY - RPS.Y() ) >= 2); // to pulse or not to pulse if ( fabs( desiredY - RPS.Y() ) < 2) Sleep(300); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(250); } } // end while loop } // end driveUntilRPSyRange function /* This function turns the robot at a given power for a given time. * float power (0-100): positive = clockwise rotation, negative = counter-clockwise rotation * Last Modified: 4/2/2016 JKL */ void turnUntilTime(float power, int time) { worldState(true, 0, power, 1, 1, 1, 1); SD.Printf("turnUntilTime start"); LCD.WriteRC("Time:", 5, 3); LCD.WriteRC(time, 5, 9); motor1.SetPercent(power); motor2.SetPercent(power); motor3.SetPercent(power); motor4.SetPercent(power); Sleep(time); worldState(true, 0, power, 1, 1, 1, 1); SD.Printf("turnUntilTime stop"); motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); Sleep(100); } // end turnUntilTime function /* This function corrects the heading of the robot using RPS. * Power should be less than 30. * Last Modified: 4/2/2016 JKL */ void turnUntilRPS(int desiredHeading, int power, int timeout) { int startTime = TimeNowMSec(); // tolarance/range is +/- 1.5 degrees // probably could have implemented a custom range while( ( fabs(desiredHeading-RPS.Heading())>1.5 && fabs(desiredHeading-RPS.Heading())<358.5 ) && (TimeNowMSec() - startTime < timeout)) { worldState(true, desiredHeading, power, 1,1,1,1); SD.Printf("turnUntilRPS"); LCD.WriteRC("Desired H:", 5, 3); LCD.WriteRC(desiredHeading, 5, 13); // if within +/- 10 degree of desiredHeading, start to pulse the motors if ( fabs(desiredHeading-RPS.Heading())<10 || fabs(desiredHeading-RPS.Heading())>350) { motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); worldState(true, desiredHeading, power, 0,0,0,0); SD.Printf("turnUntilRPS"); Sleep(150); } // if within +/- 1.5 degrees, stop everything and break out of the function // necessary to check within the loop right after the robot has stopped from the pulsing if (fabs(desiredHeading-RPS.Heading())<1.5 || fabs(desiredHeading-RPS.Heading())>358.5) { motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); worldState(true, desiredHeading, power, 0,0,0,0); SD.Printf("turnUntilRPS"); return; } if (RPS.Heading() - desiredHeading >= 0 ) { // turn clockwise if (RPS.Heading() - desiredHeading <= 180) { motor1.SetPercent(power); motor2.SetPercent(power); motor3.SetPercent(power); motor4.SetPercent(power); } // turn counter-clockwise else if (desiredHeading - RPS.Heading() < 180) { motor1.SetPercent(-power); motor2.SetPercent(-power); motor3.SetPercent(-power); motor4.SetPercent(-power); } } if (RPS.Heading() - desiredHeading < 0 ) { // turn counter-clockwise if (desiredHeading - RPS.Heading() <= 180) { motor1.SetPercent(-power); motor2.SetPercent(-power); motor3.SetPercent(-power); motor4.SetPercent(-power); } // turn clockwise else if (RPS.Heading() - desiredHeading < 180) { motor1.SetPercent(power); motor2.SetPercent(power); motor3.SetPercent(power); motor4.SetPercent(power); } } } // end while loop motor1.SetPercent(0); motor2.SetPercent(0); motor3.SetPercent(0); motor4.SetPercent(0); worldState(true, desiredHeading, power, 0,0,0,0); SD.Printf("turnUntilRPS"); } // end turnUntilRPS function