Code for autonomous ground vehicle, Data Bus, 3rd place winner in 2012 Sparkfun AVC.

Dependencies:   Watchdog mbed Schedule SimpleFilter LSM303DLM PinDetect DebounceIn Servo

Committer:
shimniok
Date:
Wed Jun 20 14:57:48 2012 +0000
Revision:
0:826c6171fc1b
Updated documentation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
shimniok 0:826c6171fc1b 1 #include "Steering.h"
shimniok 0:826c6171fc1b 2 #include "math.h"
shimniok 0:826c6171fc1b 3
shimniok 0:826c6171fc1b 4 /** create a new steering calculator for a particular vehicle
shimniok 0:826c6171fc1b 5 *
shimniok 0:826c6171fc1b 6 */
shimniok 0:826c6171fc1b 7 Steering::Steering(float wheelbase, float track)
shimniok 0:826c6171fc1b 8 : _wheelbase(wheelbase)
shimniok 0:826c6171fc1b 9 , _track(track)
shimniok 0:826c6171fc1b 10 , _intercept(2.0)
shimniok 0:826c6171fc1b 11 {
shimniok 0:826c6171fc1b 12 }
shimniok 0:826c6171fc1b 13
shimniok 0:826c6171fc1b 14 void Steering::setIntercept(float intercept)
shimniok 0:826c6171fc1b 15 {
shimniok 0:826c6171fc1b 16 _intercept = intercept;
shimniok 0:826c6171fc1b 17 }
shimniok 0:826c6171fc1b 18
shimniok 0:826c6171fc1b 19 /** Calculate a steering angle based on relative bearing
shimniok 0:826c6171fc1b 20 *
shimniok 0:826c6171fc1b 21 */
shimniok 0:826c6171fc1b 22 float Steering::calcSA(float theta) {
shimniok 0:826c6171fc1b 23 return calcSA(theta, -1.0); // call with no limit
shimniok 0:826c6171fc1b 24 }
shimniok 0:826c6171fc1b 25
shimniok 0:826c6171fc1b 26 /** calcSA
shimniok 0:826c6171fc1b 27 * minRadius -- radius limit (minRadius < 0 disables limiting)
shimniok 0:826c6171fc1b 28 */
shimniok 0:826c6171fc1b 29 float Steering::calcSA(float theta, float minRadius) {
shimniok 0:826c6171fc1b 30 float radius;
shimniok 0:826c6171fc1b 31 float SA;
shimniok 0:826c6171fc1b 32 bool neg = (theta < 0);
shimniok 0:826c6171fc1b 33
shimniok 0:826c6171fc1b 34 // I haven't had time to work out why the equation is slightly offset such
shimniok 0:826c6171fc1b 35 // that negative angle produces slightly less steering angle
shimniok 0:826c6171fc1b 36 //
shimniok 0:826c6171fc1b 37 if (neg) theta = -theta;
shimniok 0:826c6171fc1b 38
shimniok 0:826c6171fc1b 39 // The equation peaks out at 90* so clamp theta artifically to 90, so that
shimniok 0:826c6171fc1b 40 // if theta is actually > 90, we select max steering
shimniok 0:826c6171fc1b 41 if (theta > 90.0) theta = 90.0;
shimniok 0:826c6171fc1b 42
shimniok 0:826c6171fc1b 43 // Compute |radius| based on intercept distance and specified angle with extra gain to
shimniok 0:826c6171fc1b 44 // overcome steering slop, misalignment, sidehills, etc.
shimniok 0:826c6171fc1b 45 radius = _intercept / ( 2 * sin(angle_radians(theta)) );
shimniok 0:826c6171fc1b 46
shimniok 0:826c6171fc1b 47 if (minRadius > 0) {
shimniok 0:826c6171fc1b 48 if (radius < minRadius) radius = minRadius;
shimniok 0:826c6171fc1b 49 }
shimniok 0:826c6171fc1b 50
shimniok 0:826c6171fc1b 51 // Now calculate steering angle based on wheelbase and track width
shimniok 0:826c6171fc1b 52 SA = angle_degrees(asin(_wheelbase / (radius - _track/2)));
shimniok 0:826c6171fc1b 53 // The above ignores the effect of speed on required steering angle.
shimniok 0:826c6171fc1b 54 // Even when under the limits of traction, understeer means more angle
shimniok 0:826c6171fc1b 55 // is required to achieve a turn at higher speeds than lower speeds.
shimniok 0:826c6171fc1b 56 // To consider this, we'd need to measure the understeer gradient of
shimniok 0:826c6171fc1b 57 // the vehicle (thanks to Project240 for this insight) and include
shimniok 0:826c6171fc1b 58 // that in the calculation.
shimniok 0:826c6171fc1b 59
shimniok 0:826c6171fc1b 60 if (neg) SA = -SA;
shimniok 0:826c6171fc1b 61
shimniok 0:826c6171fc1b 62 return SA;
shimniok 0:826c6171fc1b 63 }
shimniok 0:826c6171fc1b 64
shimniok 0:826c6171fc1b 65 /**
shimniok 0:826c6171fc1b 66 * Bxy - robot coordinates
shimniok 0:826c6171fc1b 67 * Axy - previous waypoint coords
shimniok 0:826c6171fc1b 68 * Cxy - next waypoint coords
shimniok 0:826c6171fc1b 69 */
shimniok 0:826c6171fc1b 70 float Steering::crossTrack(float Bx, float By, float Ax, float Ay, float Cx, float Cy)
shimniok 0:826c6171fc1b 71 {
shimniok 0:826c6171fc1b 72 // Compute rise for prev wpt to bot; or compute vector offset by A(x,y)
shimniok 0:826c6171fc1b 73 float Rx = (Bx - Ax);
shimniok 0:826c6171fc1b 74 // compute run for prev wpt to bot; or compute vector offset by A(x,y)
shimniok 0:826c6171fc1b 75 float Ry = (By - Ay);
shimniok 0:826c6171fc1b 76 // dx is the run for the path
shimniok 0:826c6171fc1b 77 float dx = Cx - Ax;
shimniok 0:826c6171fc1b 78 // dy is the rise for the path
shimniok 0:826c6171fc1b 79 float dy = Cy - Ay;
shimniok 0:826c6171fc1b 80 // this is hypoteneuse length squared
shimniok 0:826c6171fc1b 81 float ACd2 = dx*dx+dy*dy;
shimniok 0:826c6171fc1b 82 // length of hyptoenuse
shimniok 0:826c6171fc1b 83 float ACd = sqrtf( ACd2 );
shimniok 0:826c6171fc1b 84
shimniok 0:826c6171fc1b 85 float Rd = Rx*dx + Ry*dy;
shimniok 0:826c6171fc1b 86 float t = Rd / ACd2;
shimniok 0:826c6171fc1b 87 // nearest point on current segment
shimniok 0:826c6171fc1b 88 float Nx = Ax + dx*t;
shimniok 0:826c6171fc1b 89 float Ny = Ay + dy*t;
shimniok 0:826c6171fc1b 90 // Cross track error
shimniok 0:826c6171fc1b 91 float NBx = Nx-Bx;
shimniok 0:826c6171fc1b 92 float NBy = Ny-By;
shimniok 0:826c6171fc1b 93 float cte = sqrtf(NBx*NBx + NBy*NBy);
shimniok 0:826c6171fc1b 94
shimniok 0:826c6171fc1b 95 return cte;
shimniok 0:826c6171fc1b 96 }
shimniok 0:826c6171fc1b 97
shimniok 0:826c6171fc1b 98
shimniok 0:826c6171fc1b 99
shimniok 0:826c6171fc1b 100 float Steering::purePursuitSA(float hdg, float Bx, float By, float Ax, float Ay, float Cx, float Cy)
shimniok 0:826c6171fc1b 101 {
shimniok 0:826c6171fc1b 102 float SA;
shimniok 0:826c6171fc1b 103
shimniok 0:826c6171fc1b 104 // Compute rise for prev wpt to bot; or compute vector offset by A(x,y)
shimniok 0:826c6171fc1b 105 float Rx = (Bx - Ax);
shimniok 0:826c6171fc1b 106 // compute run for prev wpt to bot; or compute vector offset by A(x,y)
shimniok 0:826c6171fc1b 107 float Ry = (By - Ay);
shimniok 0:826c6171fc1b 108 // dx is the run for the path
shimniok 0:826c6171fc1b 109 float dx = Cx - Ax;
shimniok 0:826c6171fc1b 110 // dy is the rise for the path
shimniok 0:826c6171fc1b 111 float dy = Cy - Ay;
shimniok 0:826c6171fc1b 112 // this is hypoteneuse length squared
shimniok 0:826c6171fc1b 113 float ACd2 = dx*dx+dy*dy;
shimniok 0:826c6171fc1b 114 // length of hyptoenuse
shimniok 0:826c6171fc1b 115 float ACd = sqrtf( ACd2 );
shimniok 0:826c6171fc1b 116
shimniok 0:826c6171fc1b 117 float Rd = Rx*dx + Ry*dy;
shimniok 0:826c6171fc1b 118 float t = Rd / ACd2;
shimniok 0:826c6171fc1b 119 // nearest point on current segment
shimniok 0:826c6171fc1b 120 float Nx = Ax + dx*t;
shimniok 0:826c6171fc1b 121 float Ny = Ay + dy*t;
shimniok 0:826c6171fc1b 122 // Cross track error
shimniok 0:826c6171fc1b 123 float NBx = Nx-Bx;
shimniok 0:826c6171fc1b 124 float NBy = Ny-By;
shimniok 0:826c6171fc1b 125 float cte = sqrtf(NBx*NBx + NBy*NBy);
shimniok 0:826c6171fc1b 126 float NGd;
shimniok 0:826c6171fc1b 127
shimniok 0:826c6171fc1b 128 float myLookAhead;
shimniok 0:826c6171fc1b 129
shimniok 0:826c6171fc1b 130 if (cte <= _intercept) {
shimniok 0:826c6171fc1b 131 myLookAhead = _intercept;
shimniok 0:826c6171fc1b 132 } else {
shimniok 0:826c6171fc1b 133 myLookAhead = _intercept + cte;
shimniok 0:826c6171fc1b 134 }
shimniok 0:826c6171fc1b 135
shimniok 0:826c6171fc1b 136 NGd = sqrt( myLookAhead*myLookAhead - cte*cte );
shimniok 0:826c6171fc1b 137 float Gx = NGd * dx/ACd + Nx;
shimniok 0:826c6171fc1b 138 float Gy = NGd * dy/ACd + Ny;
shimniok 0:826c6171fc1b 139
shimniok 0:826c6171fc1b 140 float hdgr = hdg*PI/180;
shimniok 0:826c6171fc1b 141
shimniok 0:826c6171fc1b 142 float BGx = (Gx-Bx)*cos(hdgr) - (Gy-By)*sin(hdgr);
shimniok 0:826c6171fc1b 143 float c = (2 * BGx) / (myLookAhead*myLookAhead);
shimniok 0:826c6171fc1b 144
shimniok 0:826c6171fc1b 145 float radius;
shimniok 0:826c6171fc1b 146
shimniok 0:826c6171fc1b 147 if (c != 0) {
shimniok 0:826c6171fc1b 148 radius = 1/c;
shimniok 0:826c6171fc1b 149 } else {
shimniok 0:826c6171fc1b 150 radius = 999999.0;
shimniok 0:826c6171fc1b 151 }
shimniok 0:826c6171fc1b 152
shimniok 0:826c6171fc1b 153 // Now calculate steering angle based on wheelbase and track width
shimniok 0:826c6171fc1b 154 SA = angle_degrees(asin(_wheelbase / (radius - _track/2)));
shimniok 0:826c6171fc1b 155
shimniok 0:826c6171fc1b 156 return SA;
shimniok 0:826c6171fc1b 157 }