Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:98e98e01a6ce, committed 2014-04-20
- Comitter:
- redxeth
- Date:
- Sun Apr 20 03:33:03 2014 +0000
- Commit message:
- Initial version
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FRDM-TFC.lib Sun Apr 20 03:33:03 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/emh203/code/FRDM-TFC/#b34924a05d60
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Spices/Spices.cpp Sun Apr 20 03:33:03 2014 +0000
@@ -0,0 +1,1270 @@
+#include "mbed.h"
+
+#include "Spices.h"
+#include "common.h"
+#include "TFC.h"
+
+// camera params
+#define NUM_LINE_SCAN 128
+#define MAX_LINE_SCAN NUM_LINE_SCAN-1
+#define MIN_LINE_WIDTH 0
+#define MAX_LINE_WIDTH 15
+#define FILTER_ENDS 0 // # of pixels at end of camera data to ignore; set to 0 for now, later make 15
+#define RANGE (NUM_LINE_SCAN - (2 * FILTER_ENDS)) // range of camera pixels to consider
+#define ERR_RATIO 0.85 // ratio of max possible error to pixels (have to measure!)
+#define DER_RATIO 0.5 // ratio for der threshold level (was 0.5 initially, may put back)
+
+// steer/servo params
+#define MAX_STEER_LEFT -0.51 // value determined by demo mode 1 measure (have to be adjusted with every servo horn attach)
+#define MAX_STEER_RIGHT 0.39 // value determined by demo mode 1 measure
+#define DT 0.02 // # MS of time between intervals (doesn't really matter)
+
+// logging parameters
+#define NUM_LOG_FRAMES 700 // # of frames to log (when logging active) ~14 sec worth!
+
+// ****** for debug tuning ******
+#define TUNE_SPEED 0.7
+#define TUNE_KP 0.008
+#define TUNE_KI 0
+#define TUNE_KD 0
+#define MIN_POWER 60 // percent min power (estimating for a 2-ft turn => 24" / (24" + 6" car) = 4/5; speed of inner wheel is 20% lower worst case
+#define SPEED_ADJUST 4
+#define ABS_ERROR_THRESH 10 // number of pixels line position offset before changing KP value
+#define CONTROL_METHOD 2 // which control method to use
+
+
+// Drive/motor params
+// 0.4 way too slow!! need to charge battery
+#define SUB_LIGHT_SPEED 0.5 // moderate speed (value 0 to 1 sent to motors)
+#define LIGHT_SPEED 0.6 // fast...
+#define RIDICULOUS_SPEED 0.7 // faster...
+#define LUDICROUS_SPEED 0.9 // faster still!
+#define MAX_POWER 100 // percent max power (for speed adjustments)
+
+// algo params
+#define UNKNOWN_COUNT_MAX 50 // max value to allow for unknown track conditions before killing engine
+#define STARTGATEFOUNDMAX 0 // max value to allow for finding starting gate before killing engine
+#define STARTGATEDELAY 50 // Delay before searching for starting gate to kill engine
+
+#define MMA8451_I2C_ADDRESS (0x1d<<1) // address for accelerometer?
+
+
+/* CAR INTERFACE
+
+ DIP SWITCH:
+ -----------------------------------------------------------------
+ 1 - ON: Run MCP below; OFF: Run Demo program (see main.cpp)
+ 2 - ON: Log frame data to array
+ 3 - ON: Risky race option; OFF: Conservative race option
+ 4 - ON: Start Gate Kill Switch Active
+
+ POTS
+ -----------------------------------------------------------------
+ 0 - controls nothing at the moment
+ 1 - controls nothing at the moment
+
+ PUSHBUTTONS
+ -----------------------------------------------------------------
+ A - START car race!
+ B - END CAR RACE / (while holding down when 'log frame data' active will also output terminal data)
+
+*/
+
+// LEARNING CAR CLUB 9/10/13:
+// IP need to test-- get around U turns -- more aggressive proportional control? Or derivative? Have camera look farther ahead but not too far ahead
+// IP need to test -- fix lighting for tunnels (if good algo doesn't need lighting!!)
+// -- increase power up to get up hills, brake down hills (accel: http://mbed.org/users/SomeRandomBloke/code/MMA8451Q/#)
+// -- add speed control? (hall effect sensor)
+// DONE -- make sure steering won't go past limits!!
+// 9/12/13 - adjust camera exposure time based on maximum light intensity!!
+// 9/14/13 - DONE -- make derivative threshold related to maximum light intensity!
+// 9/16/13 - crash at car club blew out resistor R8, replaced it and getty twitching when powering servo from USB only. Ok when powering from battery.
+// 9/17/13 - experiments show that derivative control just doesn't work very well-- the tinest error delta causes huge drastic changes, need to use non-linear proportional
+// control instead... parabolic??
+// DONE -- Also need to slow down on curves
+// speed up hills and slow down on downhill...
+// measure speed?
+// 9/18/13 - Test track work: doesn't appear to be sampling camera fast enough-- not able to handle the wiggly track and sometimes not able to handle the curves!
+// TODO: Need to increase rate at which camera sampled and decisions are made!! Look to codewarrior code
+// Need to cut off left/right ends of camera data-- seems to not read line properly in well lit rooms
+// DONE Speed adjust as you go round; Need to have it slow down "into" curve, speed up again "out of curve"
+// 9/23/13 - Definitely not processing camera fast enough-- seems to not react well when speed up the car. Goes slow just fine all the way 'round.
+// TODO: Need to see how often TFC_LineScanImageReady gets updated. If update camera sample freq how will that impact exposure? Light adjustment algos should
+// be able to handle it. Need to be able to measure the 'processing time' required. Also wondering if 20mS is fast enough for updating the servo?? Need
+// to calculate how fast servo needs to really go based on track curves and speed.
+// TODO: Need a way to measure speed I think as well.
+// TODO: Need to control speed differentially across the different motors!! See Eli video!!
+// TODO: Use this to get excel feedback quicker: http://strokescribe.com/en/serial-port-download.html (Doesn't work too well-- prefer my own excel method)
+// TODO: Use PID control for steering!
+// TODO: Use Speed control (PID?) --- add speed sensor!
+// 10/8/13 Reduced speed control to 90% (was 85%)-- seems to go off track and lose line at high speed-- mainly on the U turns
+// Need to figure out why derivative control in steering doesn't work well. Add integral control.
+// Latest track times (no hill, no bumps, no tunnel, only squiggles) = 11.8sec at high speed with speed control
+// Losing time on the curves-- need to optimize!!
+// Worry about hill later-- need to get track times down around 8sec first.
+// *****************************
+// ** Implement some method to acount for U-TURNS on track-- and to help even go beyond what camera can see as far as estimating line position
+// ** (U-TURNS take the longest time out of track)
+// ** Method to help find the line even when not really visible
+// *****************************
+// 10/17/13
+// NEED CURVE AND WIGGLE DETECT!!
+// 10/20/13 Added logging capability and 'algo time' detect
+// It is not finding the line on the edges when on a curve-- likely because my line detect algo requires both edges
+// TODO-- need method that can use only one edge!
+
+// TODO LIST
+// - Speed Control via Sensor WAITING ON SENSOR
+// - Differential drive around curves DONE -- still trying to figure out what %age to drop on turns
+// - Full PID steering control IN PROGRESS
+// - Starting gate kill engine debug IN PROGRESS -- need to add delay
+// - Off track kill engine debug -- IN PROGRESS-- seems to kill car prematurely
+
+// image processing vars
+uint16_t GrabLineScanImage0[NUM_LINE_SCAN]; // snapshot of camera data for this 'frame'
+float DerivLineScanImage0[NUM_LINE_SCAN]; // derivative of line scan data
+float NegEdges[NUM_LINE_SCAN]; // array-- set of where in line scan data negative edges found
+float PosEdges[NUM_LINE_SCAN]; // array-- set of where in line scan data positive edges found
+uint16_t numNegEdges = 0, numPosEdges = 0; // max value of valid neg and positive indices (also serves as a count of # edges found)
+uint16_t MaxLightIntensity = 0; // max measured light intensity -- to account for lighting differences
+uint16_t MinLightIntensity = (1 << 12); // min measured light intensity -- to account for lighting differences
+float maxDerVal = 0; // max deriv value
+float minDerVal = (float) (1 << 12); // min deriv value
+float aveDerVal = 0; // average deriv value
+float DerivThreshold = (1 << 9); // Derivative Threshold (default)
+float PosDerivThreshold = (1 << 9); // Pos Edge Derivative Threshold (default)
+float NegDerivThreshold = (1 << 9); // Neg Edge Derivative Threshold (default)
+
+
+// Steering control variables
+float CurrentLinePosition; // Current position of track line (in pixels -- 0 to 127)
+float LastLinePosition; // Last position of track line (in pixels -- 0 to 127)
+float CurrentLinePosError = 0; // Current line position error (used for derivative calc)
+float AbsError;
+float LastLinePosError = 0; // Last line position error (used for derivative calc)
+float SumLinePosError = 0; // Sum of line position error (used for integral calc)
+float DerivError = 0; // Derivative of error
+float CurrentSteerSetting = (MAX_STEER_RIGHT + MAX_STEER_LEFT) / 2; // drive straight at first
+float CurrentLeftDriveSetting = 0; // Drive setting (left wheel)
+float CurrentRightDriveSetting = 0; // Drive setting (right wheel)
+
+// Speed control vars
+float MaxSpeed; // maximum speed allowed
+
+uint16_t startRaceTicker; // ticker at start of race1
+
+// Custom Data Types
+typedef enum TrackStatusType {Unknown,
+ LineFound,
+ StartGateFound,
+ LineJustLeft} TrackStatusType;
+
+TrackStatusType CurrentTrackStatus; // current track status
+TrackStatusType LastTrackStatus; // last track status
+
+/* typedef enum TrackType {NotSure,
+ Straight,
+ Curve,
+ Wiggle,
+ Bumps,
+ StartGate,
+ UpHill,
+ DownHill} TrackType;
+
+TrackType CurrentTrack; */
+
+
+struct LogData {
+ float linepos;
+ float steersetting;
+ float leftdrivesetting;
+ float rightdrivesetting;
+};
+
+LogData frameLogs[NUM_LOG_FRAMES]; // array of log data to store
+int logDataIndex; // index for log data
+
+
+int StartGateFoundCount = 0; // how many times start gate has been found
+int UnknownCount = 0; // how many times nothing has been found (to help with kill switch implementation)
+bool go = false; // Car can go! Should be set to false to start.
+
+// EXTRA CONTROL PARAMETERS
+bool debugFakeMode = false; // if true, ignores real camera and uses fake camera input instead; used for data processing debug
+int terminalOutput = 0; // set debug level for terminal output
+ // 0 : no terminal output, race!
+ // 1 : output just to measure frame rate
+ // 2 : output for measuring time of operations
+ // 3 : output with delay
+bool doLogData = false; // whether to capture log data to output later on
+bool killSwitch = false; // whether to enable Kill Switch (allow engine to stop after not finding track)
+bool startGateStop = false; // whether to stop or not depending on starting gate reading
+bool doRisky = false; // race style-- whether conservative or risky
+
+// timer stuff
+Timer timer;
+int after_time, before_time, start_time, last_start_time;
+bool run_once = false;
+
+void MasterControlProgram()
+{
+
+ // put here all things want to run only once after reset
+ if (!run_once){
+ if ((terminalOutput == 1) || (terminalOutput == 2)){
+ timer.start();
+ }
+ run_once = true;
+ }
+
+ // read DIP switches and Pots for data
+ readSwitches();
+
+ // Every 4s (or Terminal Output is off, i.e. race mode!)
+ // AND line scan image ready (or fake mode where image is always ready)
+ // (ticker updates every 2ms) (Line scan image ready very 20mS?)
+ if((TFC_Ticker[0]>2000 || (terminalOutput != 3)) && (TFC_LineScanImageReady>0 || debugFakeMode))
+ {
+
+ // stuff that needs to be reset with each image frame
+ if (terminalOutput == 1) {
+ last_start_time = start_time;
+ start_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:Between frames:%d:uSec\r\n", start_time - last_start_time);
+ before_time = timer.read_us();
+ }
+ TFC_Ticker[0] = 0;
+ TFC_LineScanImageReady=0; // must reset to 0 after detecting non-zero
+ MaxLightIntensity = 0; // reset
+ MinLightIntensity = (1 << 12); // reset
+
+ // grab camera frame
+ grabCameraFrame();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER grabCameraFrame:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // calcalate derivative of linescandata, filter starttime data
+ derivativeLineScan(&GrabLineScanImage0[0], &DerivLineScanImage0[0]);
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER derivativeLineScan:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // adjust deriv threshold based on max lighting value
+ // has to be called before find edges
+ adjustLights();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER adjustLights:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ //find edges from derivative data
+ findEdges_v2(&DerivLineScanImage0[0]);
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER findEdges_v2:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // turn on terminal output if line not found -- FOR DEBUG
+ //if (CurrentTrackStatus == Unknown)
+ // terminalOutput = 1;
+
+ //review edge data and set position or starting flag appropriately
+ reviewEdges();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER reviewEdges:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ if (terminalOutput == 3) {
+ // print data to Terminal for camera 0
+ printLineScanData(&GrabLineScanImage0[0]);
+
+ // print deriviative of linescandata, filter starttime data
+ printDerivLineScanData(&DerivLineScanImage0[0]);
+
+ printAdjustLightsData();
+
+ printEdgesFound();
+
+ }
+
+ // ** Track Status available at this point **
+
+
+ // test out accelerometer
+ // accelTest();
+
+ // Update things based on latest track status
+ // e.g. change steering setting, stop car, ...
+ ActOnTrackStatus();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER ActOnTrackStatus:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+
+ //give LED feedback as to track status
+ feedbackLights();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER feedbackLights:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // control max power (speed)
+ SpeedControl();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER SpeedControl:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // Drive!!
+ Drive();
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME:TO AFTER Drive:%d:uSec\r\n", after_time - before_time);
+ before_time = timer.read_us();
+ }
+
+ // wait_ms(1);
+
+ // Capture Log data while driving
+ if (go && doLogData) {
+ captureData();
+ }
+
+ // Dump Log data to Terminal while stopped and holding B button
+ if (!go && doLogData && TFC_PUSH_BUTTON_1_PRESSED) {
+ dumpData();
+ }
+
+ if (terminalOutput == 2) {
+ after_time = timer.read_us();
+ TERMINAL_PRINTF("TIME: ENTIRE FRAME (include prints):%d:uSec\r\n", after_time - start_time);
+ before_time = timer.read_us();
+ }
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("\r\n**************************END********************************\r\n");
+ }
+
+ }
+}
+
+void dumpData()
+{
+ TERMINAL_PRINTF("INDEX,LINEPOS,STEERSETTING,LEFTDRIVESETTING,RIGHTDRIVESETTING\r\n");
+ for(logDataIndex=0;logDataIndex<NUM_LOG_FRAMES;logDataIndex++) {
+ TERMINAL_PRINTF("%d,%6.2f,%6.2f,%6.2f,%6.2f\r\n",logDataIndex,frameLogs[logDataIndex].linepos,frameLogs[logDataIndex].steersetting,frameLogs[logDataIndex].leftdrivesetting,frameLogs[logDataIndex].rightdrivesetting);
+ }
+}
+
+void captureData()
+{
+ frameLogs[logDataIndex].linepos = CurrentLinePosition;
+ frameLogs[logDataIndex].steersetting = CurrentSteerSetting;
+ frameLogs[logDataIndex].leftdrivesetting = CurrentLeftDriveSetting;
+ frameLogs[logDataIndex].rightdrivesetting = CurrentRightDriveSetting;
+
+ // increment index
+ logDataIndex++;
+ if (logDataIndex > NUM_LOG_FRAMES)
+ logDataIndex = 0;
+}
+
+void readSwitches()
+{
+
+ // ********* GATHER DIP SWITCH INPUTS *********
+ if(TFC_GetDIP_Switch()&0x02) // SWITCH 2
+ doLogData = true; // Log data to array
+ else
+ doLogData = false; // normal operation
+
+ if(TFC_GetDIP_Switch()&0x04) // SWITCH 3
+ doRisky = true;
+ else
+ doRisky = false;
+
+ if(TFC_GetDIP_Switch()&0x08) // SWITCH 4 control start stop gate
+ startGateStop = true;
+ else
+ startGateStop = false;
+
+
+}
+
+void grabCameraFrame()
+{
+ uint32_t i = 0;
+ uint8_t fake_type = 4; // type of fake data if used
+
+ for(i=0;i<NUM_LINE_SCAN;i++) // print one line worth of data (128) from Camera 0
+ {
+
+ if (debugFakeMode) { // use fake camera data
+ switch (fake_type) {
+ case 0: // ideal track -- line in center
+ if (i<57 || i > 70)
+ GrabLineScanImage0[i] = 0xFFF; // no line
+ else
+ GrabLineScanImage0[i] = 0x4B0; // line
+ break;
+ case 1: // ideal track -- line to the left
+ if (i<27 || i > 40)
+ GrabLineScanImage0[i] = 0xFFF; // no line
+ else
+ GrabLineScanImage0[i] = 0x4B0; // line
+ break;
+ case 2: // ideal track -- line to the right
+ if (i<87 || i > 100)
+ GrabLineScanImage0[i] = 0xFFF; // no line
+ else
+ GrabLineScanImage0[i] = 0x4B0; // line
+ break;
+ case 3: // ideal track -- starting gate!
+ // TBD
+ break;
+ case 4: // less than ideal track -- debug multi-edge issue!
+ if (i<54)
+ GrabLineScanImage0[i] = 4000; // no line
+ if (i == 54)
+ GrabLineScanImage0[i] = 3370; // neg edge
+ if (i == 55)
+ GrabLineScanImage0[i] = 3309; // neg edge
+ if (i == 56)
+ GrabLineScanImage0[i] = 2016; // neg edge
+ if (i == 57)
+ GrabLineScanImage0[i] = 711; // neg edge
+ if (i == 58)
+ GrabLineScanImage0[i] = 696; // neg edge
+ if ((i>58) && (i<69))
+ GrabLineScanImage0[i] = 500; // line
+ if (i == 69)
+ GrabLineScanImage0[i] = 1800; // pos edge
+ if (i > 69)
+ GrabLineScanImage0[i] = 4000; // no line
+ default:
+ break;
+ }
+
+ } else { // use real camera data
+ GrabLineScanImage0[i] = TFC_LineScanImage0[i];
+ }
+ }
+
+
+}
+
+void printLineScanData(uint16_t* LineScanData)
+{
+ uint32_t i = 0;
+ float Val;
+
+ TERMINAL_PRINTF("LINE SCAN DATA:,");
+
+ for(i=0;i<NUM_LINE_SCAN;i++) // print one line worth of data (128) from Camera 0
+ {
+ if (1 == 1) { // use float to print
+ Val = (float) LineScanData[i];
+ TERMINAL_PRINTF("%9.3f",Val);
+ if(i==MAX_LINE_SCAN) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n");
+ else
+ TERMINAL_PRINTF(",");
+ } else {
+ TERMINAL_PRINTF("0x%X",LineScanData[i]);
+ if(i==MAX_LINE_SCAN) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n",LineScanData[i]);
+ else
+ TERMINAL_PRINTF(",",LineScanData[i]);
+ }
+ }
+
+}
+
+void printDerivLineScanData(float* derivLineScanData)
+{
+ uint32_t i, minCnt = 0, maxCnt = 0;
+
+ minCnt = FILTER_ENDS;
+ maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
+
+ TERMINAL_PRINTF("DERIVATIVE DATA:,");
+
+ for(i=minCnt;i<maxCnt;i++) // print one line worth of data (128) from Camera 0
+ {
+ TERMINAL_PRINTF("%9.3f",derivLineScanData[i]);
+ if(i==maxCnt-1) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n",derivLineScanData[i]);
+ else
+ TERMINAL_PRINTF(", ",derivLineScanData[i]);
+ }
+
+}
+
+void derivativeLineScan(uint16_t* LineScanDataIn, float* DerivLineScanDataOut)
+{
+
+ uint32_t i, minCnt = 0, maxCnt = 0;
+ float DerVal, upperDerVal, lowerDerVal = 0;
+
+ maxDerVal = 0;
+ minDerVal = (float) (1 << 12);
+ aveDerVal = 0;
+
+ minCnt = FILTER_ENDS;
+ maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
+
+ // TERMINAL_PRINTF("i, upperDerVal, lowerDerVal, DerVal\r\n");
+
+ for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
+ {
+
+ // store max light intensity value
+ if (LineScanDataIn[i] > MaxLightIntensity)
+ MaxLightIntensity = LineScanDataIn[i];
+
+ // store min light intensity value
+ if (LineScanDataIn[i] < MinLightIntensity)
+ MinLightIntensity = LineScanDataIn[i];
+
+
+ // Central Derivative
+ if (i==minCnt) { // start point
+ upperDerVal = (float)(LineScanDataIn[i+1]);
+ lowerDerVal = (float)(LineScanDataIn[i]); // make same as start point
+ } else if (i==maxCnt - 1){ // end point
+ upperDerVal = (float)(LineScanDataIn[i]); // make same as end point
+ lowerDerVal = (float)(LineScanDataIn[i-1]);
+ } else { // any other point
+ upperDerVal = (float)(LineScanDataIn[i+1]);
+ lowerDerVal = (float)(LineScanDataIn[i-1]);
+ }
+ DerVal = (upperDerVal - lowerDerVal) / 2;
+ // TERMINAL_PRINTF("%d,%9.3f,%9.3f,%9.3f\r\n", i, upperDerVal, lowerDerVal, DerVal);
+
+ if (DerVal > maxDerVal) {
+ maxDerVal = DerVal;
+ }
+ if (DerVal < minDerVal) {
+ minDerVal = DerVal;
+ }
+ aveDerVal = aveDerVal + DerVal; //get sum
+ DerivLineScanDataOut[i] = DerVal;
+ }
+ aveDerVal = (float) aveDerVal / (maxCnt - minCnt);
+}
+
+//Not reliable for finding edges!
+void findEdges(float* derivLineScanData)
+{
+ // search for edges in deriviative data using a threshold
+ // need to store in a hash if that's possible...
+ // combine edges that are a pixel apart
+
+ int i, minCnt = 0, maxCnt = 0;
+ int multiNegEdgeCnt = 1, multiNegEdgeSum = 0;
+ int multiPosEdgeCnt = 1, multiPosEdgeSum = 0;
+
+ minCnt = FILTER_ENDS;
+ maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
+
+ numNegEdges = 0;
+ numPosEdges = 0;
+ for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
+ {
+ if (derivLineScanData[i] <= NegDerivThreshold) // NEGATIVE EDGE FOUND!
+ {
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("NEG EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
+ }
+
+ if ((numNegEdges > 0) && (NegEdges[numNegEdges - 1] + 1 == i )) // if no multi edges
+ { // edge actually across multiple pixels
+ multiNegEdgeCnt++;
+ multiNegEdgeSum = multiNegEdgeSum + i;
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("MULTIEDGE FOUND! MultiNegEdgeCnt: %d; MultiNegEdgeSum: %d\r\n", multiNegEdgeCnt, multiNegEdgeSum);
+ }
+ } else { // not a multi-pixel edge known at this time, store negative edge index value
+ numNegEdges++;
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("NEG EDGE STORED WITH INDEX %d. NUM NEG EDGES = %d\r\n", i, numNegEdges);
+ }
+ NegEdges[numNegEdges - 1] = (float) i;
+ multiNegEdgeSum = i;
+ }
+
+
+ } else if (derivLineScanData[i] > PosDerivThreshold) { // POSITIVE EDGE FOUND!
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("POS EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
+ }
+
+ if ((numPosEdges > 0) && (PosEdges[numPosEdges - 1] + 1 == i ))
+ { // edge actually across multiple pixels
+ multiPosEdgeCnt++;
+ multiPosEdgeSum = multiPosEdgeSum + i;
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("MULTIEDGE FOUND! MultiPosEdgeCnt: %d; MultiPosEdgeSum: %d\r\n", multiPosEdgeCnt, multiPosEdgeSum);
+ }
+ } else { // not a multi-pixel edge known at this time, store Posative edge index value
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("POS EDGE STORED WITH INDEX %d. NUM POS EDGES = %d\r\n", i, numPosEdges);
+ }
+ numPosEdges++;
+ PosEdges[numPosEdges - 1] = (float) i;
+ multiPosEdgeSum = i;
+ }
+
+ } else { // NO EDGE FOUND
+ // combine multi-edges if there are any
+ if (multiNegEdgeCnt > 1)
+ {
+ NegEdges[numNegEdges - 1] = (float) multiNegEdgeSum / multiNegEdgeCnt;
+ multiNegEdgeCnt = 1; multiNegEdgeSum = 0;
+ }
+ if (multiPosEdgeCnt > 1)
+ {
+ PosEdges[numPosEdges - 1] = (float) multiPosEdgeSum / multiPosEdgeCnt;
+ multiPosEdgeCnt = 1; multiPosEdgeSum = 0;
+ }
+
+ }
+ }
+
+}
+
+
+void findEdges_v2(float* derivLineScanData)
+{
+ // search for edges in deriviative data using a threshold
+ // need to store in a hash if that's possible...
+ // combine edges that are a pixel apart
+
+ int i;
+
+ int NegEdgeBufCnt = 0, NegEdgeBufSum = 0; // serves as buffer to store neg edges found next to each other
+ int PosEdgeBufCnt = 0, PosEdgeBufSum = 0; // serves as buffer to store pos edges found next to each other
+
+ int minCnt = FILTER_ENDS;
+ int maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
+
+
+
+ numNegEdges = 0; // count of neg edges found thus far
+ numPosEdges = 0; // count of pos edges found thus far
+ for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
+ {
+
+ if (derivLineScanData[i] <= NegDerivThreshold) // NEGATIVE EDGE FOUND!
+ {
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("NEG EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
+ }
+
+ NegEdgeBufCnt++; // add value to neg edge buffer
+ NegEdgeBufSum = NegEdgeBufSum + i;
+
+ } else if (derivLineScanData[i] > PosDerivThreshold) { // POSITIVE EDGE FOUND!
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("POS EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
+ }
+
+ PosEdgeBufCnt++; // add value to pos edge buffer
+ PosEdgeBufSum = PosEdgeBufSum + i;
+
+ } else { // NO EDGE FOUND
+
+ // POP EDGE BUFFERS IF NON-EMPTY AND STORE TO EDGE "STACK" (i.e. edges found)
+
+ if (NegEdgeBufCnt > 0) {
+ // store edge value
+ numNegEdges++;
+ NegEdges[numNegEdges - 1] = (float) NegEdgeBufSum / NegEdgeBufCnt;
+
+ // clear edge buffer
+ NegEdgeBufSum = 0; NegEdgeBufCnt = 0;
+ }
+
+ if (PosEdgeBufCnt > 0) {
+ // store edge value
+ numPosEdges++;
+ PosEdges[numPosEdges - 1] = (float) PosEdgeBufSum / PosEdgeBufCnt;
+
+ // clear edge buffer
+ PosEdgeBufSum = 0; PosEdgeBufCnt = 0;
+ }
+
+ }
+
+ }
+
+}
+
+void printEdgesFound()
+{
+ int i;
+
+ // Check that neg edges captured ok
+ TERMINAL_PRINTF("NEGATIVE EDGES FOUND:,");
+ for(i=0;i<=numNegEdges-1;i++)
+ {
+ TERMINAL_PRINTF("%9.3f",NegEdges[i]);
+ if(i==numNegEdges-1) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n");
+ else
+ TERMINAL_PRINTF(", ");
+ }
+
+
+ // Check that pos edges captured ok
+ TERMINAL_PRINTF("POSITIVE EDGES FOUND:,");
+ for(i=0;i<=numPosEdges-1;i++)
+ {
+ TERMINAL_PRINTF("%9.3f",PosEdges[i]);
+ if(i==numPosEdges-1) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n");
+ else
+ TERMINAL_PRINTF(", ");
+ }
+
+}
+
+void reviewEdges()
+{
+ LastTrackStatus = CurrentTrackStatus;
+
+ if ((numPosEdges == 1) && (numNegEdges == 1)) // only one negative and positive edge found (LINE)
+ {
+ if (((PosEdges[0] - NegEdges[0]) >= MIN_LINE_WIDTH) && ((PosEdges[0] - NegEdges[0]) <= MAX_LINE_WIDTH)) // has proper expected width
+ {
+ CurrentTrackStatus = LineFound; // report line found!
+ UnknownCount = 0; // reset unknown status count
+ LastLinePosition = CurrentLinePosition;
+ CurrentLinePosition = (PosEdges[0]+NegEdges[0]) / 2; // update line position
+ }
+ } else if ((numPosEdges == 1) && (numNegEdges == 0)) { // 1 pos edge found (POSSIBLE LINE)
+ if ((PosEdges[0] <= MAX_LINE_WIDTH) && (LastLinePosError < 0)) // pos edge is within line width of edge of camera (LEFT)
+ {
+ CurrentTrackStatus = LineFound; // report line found!
+ UnknownCount = 0; // reset unknown status count
+ LastLinePosition = CurrentLinePosition;
+ CurrentLinePosition = PosEdges[0] - ( MAX_LINE_WIDTH / 2); // update line position
+ // TERMINAL_PRINTF("*** SINGLE POSEDGE LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
+ }
+ } else if ((numNegEdges == 1) && (numPosEdges == 0)) { // 1 neg edge found (POSSIBLE LINE)
+ if ((NegEdges[0] >= (MAX_LINE_SCAN - MAX_LINE_WIDTH)) && (LastLinePosError > 0)) // neg edge is within line width of edge of camera (RIGHT)
+ {
+ CurrentTrackStatus = LineFound; // report line found!
+ UnknownCount = 0; // reset unknown status count
+ LastLinePosition = CurrentLinePosition;
+ CurrentLinePosition = NegEdges[0] + ( MAX_LINE_WIDTH / 2); // update line position
+ // TERMINAL_PRINTF("*** SINGLE NEGEDGE LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
+ }
+ } else if ((numPosEdges == 2) && (numNegEdges == 2)) { // 2 negative and 2 positive edges found (STARTING/FINISH GATE)
+
+ if ((((NegEdges[0] - PosEdges[0]) >= MIN_LINE_WIDTH) && ((NegEdges[0] - PosEdges[0]) <= MAX_LINE_WIDTH)) && // white left 'line'
+ (((NegEdges[1] - PosEdges[1]) >= MIN_LINE_WIDTH) && ((NegEdges[1] - PosEdges[1]) <= MAX_LINE_WIDTH)) && // white right 'line'
+ (((PosEdges[1] - NegEdges[0]) >= MIN_LINE_WIDTH) && ((PosEdges[1] - NegEdges[0]) <= MAX_LINE_WIDTH)) // actual track line
+ )
+
+
+ if (startRaceTicker > STARTGATEDELAY) { // only start counting for starting gate until after delay
+ StartGateFoundCount++;
+ }
+
+ CurrentTrackStatus = StartGateFound;
+ UnknownCount = 0; // reset unknown status count
+
+ } else if ((numPosEdges > 1) && (numNegEdges > 1)) { // more than 1 negative edge and positive edge found (but not 2 for both) (STARTING / FINISH GATE)
+
+ // remove edges that aren't close to center TBD DDHH
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("***************************************** \r\n");
+ TERMINAL_PRINTF("********** NOT SURE FOUND ********** \r\n");
+ TERMINAL_PRINTF("***************************************** \r\n");
+ }
+ CurrentTrackStatus = Unknown;
+
+ } else { // no track or starting gate found
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("***************************************** \r\n");
+ TERMINAL_PRINTF("*** !!!!!!!!!! LINE NOT FOUND !!!!!!! *** \r\n", CurrentLinePosition);
+ TERMINAL_PRINTF("***************************************** \r\n");
+ }
+
+ CurrentTrackStatus = Unknown;
+ UnknownCount++;
+ }
+
+
+
+
+}
+
+void ActOnTrackStatus()
+{
+ // Decide what to do next based on current track status
+
+ if (CurrentTrackStatus == LineFound) { // LINE FOUND!
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("***************************************** \r\n");
+ TERMINAL_PRINTF("*** LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
+ TERMINAL_PRINTF("***************************************** \r\n");
+ }
+
+ // Update steering position
+ SteeringControl();
+
+ // Apply to servo
+ Steer();
+
+ } else if (CurrentTrackStatus == StartGateFound) { // STARTING GATE FOUND
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("***************************************** \r\n");
+ TERMINAL_PRINTF("********** STARTING GATE FOUND ********** \r\n");
+ TERMINAL_PRINTF("********** count = %d ********** \r\n", StartGateFoundCount);
+ TERMINAL_PRINTF("***************************************** \r\n");
+ }
+
+ // END RACE!
+ if (startGateStop) {
+ if (StartGateFoundCount > STARTGATEFOUNDMAX)
+ {
+ go = false; // STOP!!
+ }
+ }
+
+ }
+
+
+
+}
+
+void SteeringControl()
+{
+
+ float targetPosition = (float)( (NUM_LINE_SCAN / 2) - 0.5); // target to achieve for line position
+
+ float KP; // proportional control factor
+ float KI; // integral control factor
+ float KD; // derivative control factor
+
+ float Pout, Iout, Dout; // PID terms
+
+ // Calculate error
+ // make error to the right positive
+ // i.e. if LINE to the right-- then CurrentLinePosError > 0
+ // if LINE to the left -- then CurrentLinePosError < 0
+ CurrentLinePosError = CurrentLinePosition - targetPosition;
+
+ // Get absolute error
+ if (CurrentLinePosError >= 0)
+ AbsError = CurrentLinePosError;
+ else
+ AbsError = -1 * CurrentLinePosError;
+
+ // CHOOSE SET OF PID CONTROL PARAMETERS
+ switch (CONTROL_METHOD) {
+ case 0:
+ // Pure proportional control based on range of steering values vs. range of error values
+ KP = (float) ( MAX_STEER_RIGHT - MAX_STEER_LEFT ) / ( NUM_LINE_SCAN - (2*FILTER_ENDS) - MIN_LINE_WIDTH );
+ KD = 0;
+ KI = 0;
+ break;
+ case 1:
+ // Proportional control with 50% bit more oomph --- a bit more aggressive around the bends
+ KP = (float) ( MAX_STEER_RIGHT - MAX_STEER_LEFT ) / ( NUM_LINE_SCAN - (2*FILTER_ENDS) - MIN_LINE_WIDTH );
+ KP = KP * 1.5;
+ KD = 0;
+ KI = 0;
+ break;
+ case 2: // MANUAL TUNING CASE 1 (use pot to help determine tuning parameters)
+ KP = TUNE_KP;
+ KI = TUNE_KI;
+ KD = TUNE_KD;
+ case 3:
+ if (AbsError < ABS_ERROR_THRESH) {
+ KP = 0.003; // when relatively straight, keep KP gain low
+ } else {
+ KP = 0.010; // when curve begins or off track, increase KP gain
+ }
+ KI = 0;
+ KD = 0;
+
+ default:
+ break;
+ }
+
+ /* Pseudocode
+ previous_error = 0
+ integral = 0
+ start:
+ error = setpoint - measured_value
+ integral = integral + error*dt
+ derivative = (error - previous_error)/dt
+ output = Kp*error + Ki*integral + Kd*derivative
+ previous_error = error
+ wait(dt)
+ goto start
+ */
+
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("KP = %6.4f\r\n", KP);
+ TERMINAL_PRINTF("TARGET %6.3f\r\n", targetPosition);
+ }
+
+
+
+ // Update integral of error
+ // i.e. if LINE stays to the right, then SumLinePosError increases
+ // i.e. if LINE stays to the left, then SumLinePosError decreases
+ SumLinePosError = SumLinePosError + ( CurrentLinePosError * DT );
+
+ DerivError = (CurrentLinePosError - LastLinePosError) / DT;
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("CURRENT LINE POSITION %9.3f\r\n", CurrentLinePosition);
+ TERMINAL_PRINTF("CURRENT LINE POSITION ERROR %9.3f\r\n", CurrentLinePosError);
+ }
+
+ // SECOND- calculate new servo position
+
+ // proportional control term
+ Pout = KP * CurrentLinePosError;
+
+ // integral control term
+ Iout = KI * SumLinePosError;
+
+ // Derivative control term
+ Dout = KD * DerivError;
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("KP = %6.4f\r\n", KP);
+ TERMINAL_PRINTF("KI = %6.4f\r\n", KI);
+ TERMINAL_PRINTF("KD = %6.4f\r\n", KD);
+ TERMINAL_PRINTF("Pout = %6.4f\r\n", Pout);
+ TERMINAL_PRINTF("Iout = %6.4f\r\n", Iout);
+ TERMINAL_PRINTF("Dout = %6.4f\r\n", Dout);
+ }
+
+ // Finally add offset to steering to account for non-centered servo mounting
+ // CurrentSteerSetting = Pout + Iout + Dout + ( (float) (MAX_STEER_LEFT + MAX_STEER_RIGHT) / 2 );
+ CurrentSteerSetting = Pout + ( (float) (MAX_STEER_LEFT + MAX_STEER_RIGHT) / 2 );
+
+ // store for next cycle deriv calculation
+ LastLinePosError = CurrentLinePosError;
+
+ // for tuning control algo only
+ if (1 == 0) {
+ TERMINAL_PRINTF("*** ******************************** \r\n");
+ TERMINAL_PRINTF("*** LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
+ TERMINAL_PRINTF("*** ERROR %9.3f *** \r\n", CurrentLinePosError);
+ TERMINAL_PRINTF("*** INTEGRAL ERROR %9.3f *** \r\n", SumLinePosError);
+ TERMINAL_PRINTF("*** DERIVATIVE ERROR %9.3f *** \r\n", DerivError);
+ TERMINAL_PRINTF("*** P STEER SETTING %9.3f *** \r\n", CurrentSteerSetting);
+ TERMINAL_PRINTF("*** PI STEER SETTING %9.3f *** \r\n", (CurrentSteerSetting + Iout));
+ TERMINAL_PRINTF("*** ******************************** \r\n");
+ wait_ms(1000);
+ }
+
+}
+
+void Steer()
+{
+
+ // make sure doesn't go beyond steering limits
+ if (CurrentSteerSetting > MAX_STEER_RIGHT)
+ {
+ CurrentSteerSetting = MAX_STEER_RIGHT;
+ } else if (CurrentSteerSetting < MAX_STEER_LEFT)
+ {
+ CurrentSteerSetting = MAX_STEER_LEFT;
+ }
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("APPLYING SERVO SETTING %5.3f\r\n", CurrentSteerSetting);
+ }
+ TFC_SetServo(0,CurrentSteerSetting);
+
+}
+
+void SpeedControl()
+{
+
+ // Get max speed setting from reading pot0
+ // then adjust
+
+ float ErrLimit;
+ float LeftDriveRatio, RightDriveRatio;
+
+ // set maximum speed allowed
+ switch (1)
+ {
+ case 0:
+ // read value off pot0
+ MaxSpeed = TFC_ReadPot(0);
+ break;
+ case 1:
+ if (doRisky)
+ MaxSpeed = TUNE_SPEED + 0.1;
+ else
+ MaxSpeed = TUNE_SPEED;
+ break;
+ case 2:
+ MaxSpeed = SUB_LIGHT_SPEED;
+ break;
+ case 3:
+ MaxSpeed = LIGHT_SPEED;
+ break;
+ case 4:
+ MaxSpeed = RIDICULOUS_SPEED;
+ break;
+ case 5:
+ MaxSpeed = LUDICROUS_SPEED;
+ break;
+ default:
+ break;
+ }
+
+ switch (SPEED_ADJUST)
+ {
+ case 0:
+ // SPEED ADJUST METHOD 0
+ // no speed adjust
+ LeftDriveRatio = MAX_POWER;
+ RightDriveRatio = LeftDriveRatio;
+ case 1:
+ // SPEED ADJUST METHOD 1
+ // High speed when error is low, low speed when error is high
+ // lower speed when more than third outside of center
+ ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO * 0.33;
+ if (AbsError > ErrLimit) {
+ LeftDriveRatio = MIN_POWER;
+ } else {
+ LeftDriveRatio = MAX_POWER;
+ }
+ RightDriveRatio = LeftDriveRatio;
+ break;
+ case 2:
+ // SPEED ADJUST METHOD 2
+ // Have max/min speed adjust proportional to absolute value of line error
+ ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO;
+ LeftDriveRatio = MAX_POWER - ((MAX_POWER - MIN_POWER) * (AbsError / ErrLimit));
+ RightDriveRatio = LeftDriveRatio;
+ break;
+ case 3:
+ // SPEED ADJUST METHOD 3
+ // have wheel relative speed proportional to absolute value of line error
+ ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO;
+ if (CurrentLinePosError > 0) { // heading right
+ LeftDriveRatio = MAX_POWER;
+ RightDriveRatio = (MIN_POWER - MAX_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) ) + MAX_POWER;
+ } else if (CurrentLinePosError < 0) { // heading left
+ RightDriveRatio = MAX_POWER;
+ LeftDriveRatio = (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) ) + MAX_POWER;
+ } else {
+ LeftDriveRatio = MAX_POWER;
+ RightDriveRatio = MAX_POWER;
+ }
+ break;
+ case 4:
+ // SPEED ADJUST METHOD 4
+ // have wheel relative speed proportional to absolute value of line error
+ // only when above a certain error
+ ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO * 0.1;
+ if (CurrentLinePosError > ErrLimit) { // heading right
+ LeftDriveRatio = MAX_POWER - (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) );
+ RightDriveRatio = MIN_POWER;
+ } else if (CurrentLinePosError < (-1 * ErrLimit)) { // heading left
+ RightDriveRatio = MAX_POWER - (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) );
+ LeftDriveRatio = MIN_POWER;
+ } else {
+ LeftDriveRatio = MAX_POWER;
+ RightDriveRatio = MAX_POWER;
+ }
+ break;
+ case 5:
+ // SPEED ADJUST METHOD 5
+ // High speed when error is low, low speed when error is high
+ // lower speed when more than third outside of center
+ ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO * 0.2;
+ if (AbsError > ErrLimit) {
+ LeftDriveRatio = MIN_POWER;
+ } else {
+ LeftDriveRatio = MAX_POWER;
+ }
+ RightDriveRatio = LeftDriveRatio;
+ break;
+ case 6:
+ // SPEED ADJUST METHOD 6
+ // High speed when error is low, low speed when error is high
+ // lower speed when more than third outside of center
+ if (AbsError > ABS_ERROR_THRESH) {
+ LeftDriveRatio = MIN_POWER;
+ } else {
+ LeftDriveRatio = MAX_POWER;
+ }
+ RightDriveRatio = LeftDriveRatio;
+ break;
+ default:
+ break;
+
+ }
+ // TBD-- add speed adjust based on Xaccel sensor!
+
+
+ // currently no control mechanism as don't have speed sensor
+ CurrentLeftDriveSetting = (float) (LeftDriveRatio / 100) * MaxSpeed;
+ CurrentRightDriveSetting = (float) (RightDriveRatio / 100) * MaxSpeed;
+
+
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("Abs Error: %4.2f\r\n", AbsError);
+ TERMINAL_PRINTF("Error Limit: %4.2f\r\n", ErrLimit);
+ TERMINAL_PRINTF("MAX SPEED = %5.2f\n", MaxSpeed);
+ TERMINAL_PRINTF("Current Left Drive Setting: %5.2f\r\n", CurrentLeftDriveSetting);
+ TERMINAL_PRINTF("Current Right Drive Setting: %5.2f\r\n", CurrentRightDriveSetting);
+ }
+ if (1 == 0) {
+ TERMINAL_PRINTF("Current Left Drive Setting: %5.2f\r\n", CurrentLeftDriveSetting);
+ TERMINAL_PRINTF("Current Right Drive Setting: %5.2f\r\n", CurrentRightDriveSetting);
+ }
+
+}
+
+void Drive()
+{
+
+ // START!
+ // if not going, go when button A is pressed
+ if (!go) {
+ if(TFC_PUSH_BUTTON_0_PRESSED) {
+ go = true;
+ UnknownCount = 0;
+ StartGateFoundCount = 0;
+ startRaceTicker = TFC_Ticker[0]; // keep track of start of race
+ logDataIndex = 0; // reset log data index
+ }
+ }
+
+ // STOP!
+ // if going, stop when button A is pressed
+ if (go) {
+ if(TFC_PUSH_BUTTON_1_PRESSED) {
+ go = false;
+ StartGateFoundCount = 0;
+ }
+ }
+
+ // EMERGENCY STOP!
+ // 'kill switch' to prevent crashes off-track
+ if (killSwitch) {
+ if (UnknownCount > UNKNOWN_COUNT_MAX) { // if track not found after certain time
+ go = false; // kill engine
+ StartGateFoundCount = 0;
+ }
+ }
+
+// ****************
+
+ if (!go) { // stop!
+ TFC_SetMotorPWM(0,0); //Make sure motors are off
+ TFC_HBRIDGE_DISABLE;
+ }
+
+ if (go) { // go!
+ TFC_HBRIDGE_ENABLE;
+ // motor A = right, motor B = left based on way it is mounted
+ TFC_SetMotorPWM(CurrentRightDriveSetting,CurrentLeftDriveSetting);
+ }
+}
+
+
+void adjustLights()
+{
+
+ // LIGHT ADJUST METHOD 1
+ // threshold is 1/5 of light intensity 'range'
+ if (1 == 0) {
+ DerivThreshold = (float) (MaxLightIntensity - MinLightIntensity) / 5;
+ NegDerivThreshold = (float) -1 * (DerivThreshold);
+ PosDerivThreshold = (float) (DerivThreshold);
+ } else {
+ // LIGHT ADJUST METHOD 2 -- SEEMS TO WORK MUCH BETTER
+ // pos edge threshold is half range of max deriv above aver derive
+ // neg edge threshold is half range of min deriv above aver derive
+
+ NegDerivThreshold = (float) (minDerVal - aveDerVal) * DER_RATIO;
+ PosDerivThreshold = (float) (maxDerVal - aveDerVal) * DER_RATIO;
+
+ }
+
+ printAdjustLightsData();
+
+}
+
+void printAdjustLightsData()
+{
+ if (terminalOutput == 3) {
+ TERMINAL_PRINTF("Max Light Intensity: %4d\r\n", MaxLightIntensity);
+ TERMINAL_PRINTF("Min Light Intensity: %4d\r\n", MinLightIntensity);
+ TERMINAL_PRINTF("Deriv Threshold: %9.3f\r\n", DerivThreshold);
+ }
+
+}
+
+void feedbackLights()
+{
+ switch (CurrentTrackStatus)
+ {
+ case LineFound:
+ TFC_BAT_LED0_OFF;
+ TFC_BAT_LED1_ON;
+ TFC_BAT_LED2_ON;
+ TFC_BAT_LED3_OFF;
+ break;
+ case StartGateFound:
+ TFC_BAT_LED0_ON;
+ TFC_BAT_LED1_OFF;
+ TFC_BAT_LED2_OFF;
+ TFC_BAT_LED3_ON;
+ break;
+ default:
+ TFC_BAT_LED0_OFF;
+ TFC_BAT_LED1_OFF;
+ TFC_BAT_LED2_OFF;
+ TFC_BAT_LED3_OFF;
+ }
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Spices/Spices.h Sun Apr 20 03:33:03 2014 +0000 @@ -0,0 +1,133 @@ +#include "mbed.h" + +#ifndef _SPICES_H +#define _SPICES_H + +// ******************************************* +// MASTER CONTROL PROGRAM + +// Loop at Servo cycle (currently 50Hz / 20mS) +// Camera gets updated at its own period +// (currently 50Hz / 20mS) +// +// REQUIREMENTS: +// - Control servos in response to track line position +// - Gather as much camera data as possible between +// servo update cycles to get best read on the line +// (currently 1:1 though @ 50Hz) +// - Ignore erronous signals: +// - "mud" on track +// - low contrast light situations +// - track crossing itself (all black situation) +// - Detects 'starting line' -- ignores first detect (START) +// and stops after second detect (FINISH) +// +// INPUTS: +// - linescancamera data (0-127) +// - maximum speed +// - filter settings: +// A = 15 (number of pixels at each end to ignore) +// B = 20 (estimated width of line)(possible issue if line far away) +// C = 2^12 (max value based on ADC sample rate of 12 bits) +// D = Edge threshold of Derivative (max / 4 = 2^10) +// - control parameters, KP and KD +// +// CONTROL ALGORITHM: +// 1 - Get Line Position +// 2 - Compute Error from Line Position +// 3 - Set Servo position based on Error and Derivative of Error +// 4 - Store error for next cycle to perform derivative +// +// +// GET LINE POSITION +// INPUTS: linescan data +// RETURNS: either position or starting gate flag +// ALGO: +// - Find line edges: +// - filter out first and last A number of pixels from left and right +// - calculate derivative of data +// - search for line edges in derivative data (search for either track line or starting gate) +// - Search for all pixels with Value < -D, indicating negative edge (large negative means going from white to black) +// - Search for all pixels with Value > D, indicating positive edge (large positive means going from black to white) +// - Clean up adjacent line edges-- edges 1 pixel away from each other combined to be considered a single edge, average pixel value +// - if starting gate then send up flag +// - if track, calculate position +// +// +// COMPUTE LINE ERROR +// - take line position and target-- calculate error (negative or positive) +// +// SET SERVO POSITION +// - int servoPosition = KP * Error + KD * (Error - lastError); +// - lastError = Error; +// +// +// TBD: +// - store multiple edge positions in order to average prior to next servo update +// +// ******************************************* +// +void MasterControlProgram(); + + +// prints out line scan data for you +void printLineScanData(uint16_t *LineScanData); + +// calculates derivative of line scan data +void derivativeLineScan(uint16_t* LineScanDataIn, float* DerivLineScanDataOut); + +// prints out derivative of line scan data -- assumes deriv already calculated +void printDerivLineScanData(float* derivLineScanData); + +// serves to grab a frame of camera data +void grabCameraFrame(); + +// find negative and positive edges in image data, store in array, combines edges found in adjacent pixels +void findEdges(float* derivLineScanData); + +// improved algo to find negative and positive edges in image data, store in array, combines edges found in adjacent pixels +void findEdges_v2(float* derivLineScanData); + +// prints out edge data found +void printEdgesFound(); + +// review edge data +// - report line position if there +// - set starting gate flag if there +// - do nothing with position value otherwise +void reviewEdges(); + +// Decide new actions based on track status +void ActOnTrackStatus(); + +// Update Steering settings +void SteeringControl(); + +// Apply steering setting to servo! +void Steer(); + +// Update speed settings +void SpeedControl(); + +// Apply speed settings to motors! +void Drive(); + +// Adjust parameters based on max light measured value +void adjustLights(); + +// print out light adjust data +void printAdjustLightsData(); + +// Give user feedback as to detection state via LEDs +void feedbackLights(); + +// read DIP switches and potentiometers for setting changes +void readSwitches(); + +// capture log data +void captureData(); + +// dump log data to terminal +void dumpData(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common.cpp Sun Apr 20 03:33:03 2014 +0000 @@ -0,0 +1,9 @@ +#include "mbed.h" + +#include "common.h" + +Serial PC(USBTX,USBRX); + +Ticker TFC_TickerObj; + +volatile uint32_t TFC_Ticker[NUM_TFC_TICKERS]; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common.h Sun Apr 20 03:33:03 2014 +0000 @@ -0,0 +1,22 @@ +#include "mbed.h" + +#ifndef _COMMON_H +#define _COMMON_H + +//This macro is to maintain compatibility with Codewarrior version of the sample. This version uses the MBED libraries for serial port access +extern Serial PC; + +#define TERMINAL_PRINTF PC.printf + + + //This ticker code is used to maintain compability with the Codewarrior version of the sample. This code uses an MBED Ticker for background timing. + +#define NUM_TFC_TICKERS 4 + +extern Ticker TFC_TickerObj; + + +extern volatile uint32_t TFC_Ticker[NUM_TFC_TICKERS]; + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Sun Apr 20 03:33:03 2014 +0000
@@ -0,0 +1,200 @@
+#include "mbed.h"
+#include "TFC.h"
+
+#include "common.h"
+#include "Spices.h"
+
+
+void TFC_TickerUpdate()
+{
+ int i;
+
+ for(i=0; i<NUM_TFC_TICKERS; i++)
+ {
+ if(TFC_Ticker[i]<0xFFFFFFFF)
+ {
+ TFC_Ticker[i]++;
+ }
+ }
+}
+
+void DemoProgram()
+{
+ uint32_t i,j,t = 0;
+ float ReadPot0, ReadPot1;
+
+
+ //This Demo program will look at the middle 2 switch to select one of 4 demo modes.
+ //Let's look at the middle 2 switches
+ switch((TFC_GetDIP_Switch()>>1)&0x03)
+ {
+ default:
+ case 0 :
+ //Demo mode 0 just tests the switches and LED's
+ if(TFC_PUSH_BUTTON_0_PRESSED)
+ TFC_BAT_LED0_ON;
+ else
+ TFC_BAT_LED0_OFF;
+
+ if(TFC_PUSH_BUTTON_1_PRESSED)
+ TFC_BAT_LED3_ON;
+ else
+ TFC_BAT_LED3_OFF;
+
+
+/* if(TFC_GetDIP_Switch()&0x01)
+ TFC_BAT_LED1_ON;
+ else
+ TFC_BAT_LED1_OFF;
+
+ if(TFC_GetDIP_Switch()&0x08)
+ TFC_BAT_LED2_ON;
+ else
+ TFC_BAT_LED2_OFF; */
+
+ break;
+
+ case 1:
+ TFC_HBRIDGE_DISABLE;
+
+ // if (TFC_HBRIDGE_ENABLED) {
+
+ // TFC_HBRIDGE_ENABLED = false;
+ // }
+
+ //Demo mode 1 will just move the servos with the on-board potentiometers
+ if(TFC_Ticker[0]>=20) // every 40mS...
+ {
+ TFC_Ticker[0] = 0; //reset the Ticker
+ //update the Servos
+ ReadPot0 = TFC_ReadPot(0);
+ ReadPot1 = TFC_ReadPot(1);
+ TFC_SetServo(0,ReadPot0);
+ TFC_SetServo(1,ReadPot1);
+ TERMINAL_PRINTF("Pot0 = %1.2f\r\n", ReadPot0);
+ // TERMINAL_PRINTF("Pot1 = %1.2f\r\n", ReadPot1);
+ }
+ //Let's put a pattern on the LEDs
+ if(TFC_Ticker[1] >= 125) // every 250mS... cycle through LEDs
+ {
+ TFC_Ticker[1] = 0;
+ t++;
+ if(t>4)
+ {
+ t=0;
+ }
+ TFC_SetBatteryLED_Level(t);
+ }
+
+ TFC_SetMotorPWM(0,0); //Make sure motors are off
+ TFC_HBRIDGE_DISABLE;
+
+ break;
+
+ case 2 :
+ TFC_HBRIDGE_ENABLE;
+
+ ReadPot0 = TFC_ReadPot(0);
+ ReadPot1 = TFC_ReadPot(1);
+ // TERMINAL_PRINTF("Pot0 = %1.2f\n", ReadPot0);
+ // TERMINAL_PRINTF("Pot1 = %1.2f\n", ReadPot1);
+ TFC_SetMotorPWM(ReadPot0,ReadPot1);
+
+ //Let's put a pattern on the LEDs
+ if(TFC_Ticker[1] >= 125)
+ {
+ TFC_Ticker[1] = 0;
+ t++;
+ if(t>4)
+ {
+ t=0;
+ }
+ TFC_SetBatteryLED_Level(t);
+ }
+ break;
+
+ case 3 :
+ //Demo Mode 3 will be in Freescale Garage Mode. It will beam data from the Camera to the
+ //Labview Application
+ //note that there are some issues
+ if(TFC_Ticker[0]>1000 && TFC_LineScanImageReady>0) // every 2s ...
+ {
+ TFC_Ticker[0] = 0;
+ TFC_LineScanImageReady=0; // must reset to 0 after detecting non-zero
+
+ if(t==0)
+ t=4;
+ else
+ t--;
+
+ TFC_SetBatteryLED_Level(t);
+
+ for(i=0;i<8;i++) // print one line worth of data (128) from Camera 0
+ {
+ for(j=0;j<16;j++)
+ {
+
+ TERMINAL_PRINTF("0x%X",TFC_LineScanImage0[(i*16)+j]);
+
+ if((i==7) && (j==15)) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n",TFC_LineScanImage0[(i*16)+j]);
+ else
+ TERMINAL_PRINTF(",",TFC_LineScanImage0[(i*16)+j]);
+
+ }
+ wait_ms(10);
+ }
+
+ /* for(i=0;i<8;i++) // print one line worth of data (128) from Camera 1 ??
+ {
+ for(j=0;j<16;j++)
+ {
+ TERMINAL_PRINTF("0x%X",TFC_LineScanImage1[(i*16)+j]);
+
+ if((i==7) && (j==15)) // when last data reached put in line return
+ TERMINAL_PRINTF("\r\n",TFC_LineScanImage1[(i*16)+j]);
+ else
+ TERMINAL_PRINTF(",",TFC_LineScanImage1[(i*16)+j]);
+ }
+
+ wait_ms(10);
+ } */
+
+ }
+
+
+
+ break;
+ } // end case
+
+}
+
+
+int main()
+{
+ // TERMINAL TYPE
+ PC.baud(115200); // works with Excel and TeraTerm
+ //PC.baud(9600); // works with USB Serial Monitor Lite: https://play.google.com/store/apps/details?id=jp.ksksue.app.terminal; doesn't work > 9600
+ TFC_TickerObj.attach_us(&TFC_TickerUpdate,2000); // update ticker array every 2mS (2000 uS)
+
+ TFC_Init();
+
+ for(;;)
+ {
+ //TFC_Task must be called in your main loop. This keeps certain processing happy (I.E. Serial port queue check)
+ // TFC_Task();
+
+
+ // If DIP switch 1 is high, then run MCP, else Demo program
+ if(TFC_GetDIP_Switch()&0x01)
+ // Run MCP
+ MasterControlProgram();
+ else
+ // Run Demo Program
+ DemoProgram();
+
+ } // end of infinite for loop
+
+
+}
+