**PART 4**

In this, the fourth in the tutorial series, we will improve the performance of the robot
driver that was presented in the previous tutorial. TutorMan had good
performance on two out of the six tracks that will be used for the April races. On
the other four, he was not able to complete the course. The main thing to do first
is to get him to stay on the track!

If you observe TutorMan try to negotiate various tracks, you will see that he runs off the track consistently if a tight turn follows a large radius turn. He also runs off the track if a tight turn follows a short straightaway where the straightaway is entered at a high speed.

Both of these observations are easy to explain. When the car is in a large radius turn, it naturally is going at a high rate of speed. Our corn_spd() function gives us a target speed proportional to the square root of the radius, like it should. However, if the high speed curve is followed by a sharp corner, the car will enter the sharp corner at a speed appropriate to the high speed curve! There is nothing in TutorMan's code to cause him to slow down for the corner. You can observe this behavior on V03.TRK; Tutorman always goes off the track after the long, gradual right hand curve. On every straightaway, we have code to slow down for the corner. The problem here is that the length of the part of the track where braking takes place is always equal to a fixed fraction of the length of the straightaway. If it is a short straightaway, we will have a short braking section. Since the acceleration section is also short, this would be OK if we entered the straightaway at a low speed. But it often happens that the short straight is entered at a high speed, in which case it will still be going at a high speed when it reaches the braking section. Again, the sharp curve will be entered at too high a speed, resulting in going off the track to the outside. You can observe this behavior on ZANDVORT.TRK; Tutorman always goes off the track in the second turn.

**What we need is a more realistic way of deciding when to brake
for the corner,** and we also need to apply that rule on curves as well as
straightaways. The distance actually required to slow the car down for the curve
depends on three things:

- How fast we are traveling,
- What speed we must have when we arrive at the corner, and
- Our rate of negative acceleration during braking. Notice that the length of the straightaway has no direct influence here; our former rule was totally inadequate! What we need is a function that looks like this:

`// returns the minimum distance in which the speed can be changed`

`// from v0 to v1 with acceleration limited to a_max.`

`double crit_dist(double v0, double v1, double a_max);`

Once we have such a function, then we can compare our current distance from the next curve with this critical distance. As long as we are farther than this distance from the next turn, we do not have to slow down. Otherwise, we must brake to adjust our speed for the next turn.

There is a well known formula for the distance traveled by a body with uniform acceleration during time T. (Due, I believe, to Isaac Newton!) Using the symbols chosen for our crit_dist() function, the formula is:

` dist = v0 * T + .5 * a_max * T * T`

Now by the definition of uniform acceleration, and using dv to mean

the total change in speed during time T:

` dv = a_max *
T (where dv = v1 - v0)`

T can be eliminated from those two equations by simply solving the

second for T, and substituting this for T in the first. The result

is:

` dist = v0 * dv / a_max + .5 * dv * dv / a_max;`

which we can calculate as:

` dist = (v0 + .5 * dv) * dv / a_max;`

That is our desired formula for the required distance.

Our complete function can be written like this:

`// calculates the critical distance necessary to bring a car from speed`

`// v0 to speed v1 when the braking acceleration is a_max, in ft per sec^2`

`// Speeds are in fps. (a_max should be negative and v0 > v1)`

`double crit_dist(double v0, double v1, double a_max)`

`{`

` double dv;`

` dv = v1 - v0;`

` if(dv > 0.0)
// this saves having such a test in the caller`

` return(0.0);`

` return (v0 + .5 * dv) * dv / a_max;`

`}`

Now to apply this to our robot. For the straightaways, instead of comparing s.to_end
with some fraction of s.cur_len, we first compute this crit_dist() function, and compare
s.to_end with that. The first argument is s.v, our current speed. The second
is "speed", already computed, which is the target speed for the curve
ahead. The third argument will be another const parameter, which will replace
ACCEL_FRACTION, which will no longer be used. This is what we wind up with:

`const double BRAKE_ACCEL = -31.0; // acceleration when
braking, ft/sec^2`

` .`

` .`

` .`

` speed = corn_spd(s.nex_rad + DIST_FROM_INSIDE); // target speed for
next turn`

` .`

` .`

` .`

` if(s.cur_rad == 0.0)
{ // If we are on
a straightaway,`

` if(s.to_end > crit_dist(s.v, speed,
BRAKE_ACCEL)) // if far from the end,`

` vc = s.v +
50.0;
// pedal to the metal!`

` else
{
// otherwise, adjust speed for the coming turn:`

.

BRAKE_ACCEL could exceed 32.2 at high speed, due to air drag. At low speed it could
be less than 30.0. This is one more parameter to experiment with. (Yes, it
could be made a function of s.v. You could even calculate the true value by saving
s.v from the previous call and seeing how much it changed.)

*A number of other complexities arise when applying this approach to braking during
curves. * The basic idea is still the same, we compare our distance to the end of
the curve with a critical distance based on our current speed and the speed we must have
at the end of the curve. If the curve is followed by a straightaway, or a
curve of larger radius, we'll assume we don't have to slow down. We are already
using our cornering speed function to calculate a value for "speed",
representing a target value for the curve we are in. It will be necessary to also
calculate the cornering speed for the next segment. We will need another variable
name, say "speed_next". Furthermore, since the next segment may be a
straightaway, we need to consider that, and give speed_next a large value in that case.

The next problem is: what should the braking acceleration be? We are talking about braking during a high speed turn in order to slow down for another, sharper turn. Clearly, we can't usually brake as hard as we would on a straightaway; we need to maintain some steering ability. Our traction force vector is going to be used both for braking and centripetal acceleration. The truth is that an optimal solution would require a much more sophisticated analysis. What we will do is pick another const parameter for the braking acceleration during curves, and also another parameter for the amount of reduction in wheel speed that we use to cause braking.

One last thing to handle is that s.to_end is not a linear distance when the car is in a
curve. It is the angle in radians. It can be converted to a curvilinear
distance by multiplying it by the current radius. We will assume that the current
radius is s.cur_rad + DIST_FROM_INSIDE.

Putting everything together, We wind up with this:

`const double BRK_CRV_ACC = -22.0; // acceleration when
braking in curve`

`const double BRK_CRV_SLIP = 4.0; // tire slip for
braking in curve`

` .`

` .`

` .`

`// Modified cornering speed function - returns 250.0 if called for`

`// a straightaway, assuming that DIST_FROM_INSIDE has been added to radius.`

`// 250.0 is approximately the car's top speed.`

`double corn_spd(double radius) // returns maximum cornering
speed, fps`

`{`

` double
rad;
// absolute value of the radius`

` if(radius == DIST_FROM_INSIDE) //if called for
straight, return top speed`

` return 250.0;`

` `

` rad = radius < 0 ? -radius : radius;
// make radius positive`

` return sqrt(rad * 32.2 *
CORN_MYU); // compute the speed`

`}`

` .`

` .`

` .`

` double to_end; // distance to end of curve,
feet`

` .`

` .`

` .`

` if(s.cur_rad == 0.0) {`

` bias = 0.0;`

` speed = corn_spd(s.nex_rad + DIST_FROM_INSIDE);`

` }`

` else {`

` speed = corn_spd(s.cur_rad + DIST_FROM_INSIDE);`

` speed_next = corn_spd(s.nex_rad + DIST_FROM_INSIDE);`

` .`

` .`

` .`

` if(s.cur_rad != 0.0) { // If
we are in a curve,`

` // calculate vc to maintain speed
in corner`

` vc = .5 * (s.v +
speed)/cos(alpha);`

` // calculate distance to end of
curve:`

` if(s.cur_rad > 0.0)`

` to_end =
s.to_end * (s.cur_rad + DIST_FROM_INSIDE);`

` else`

` to_end =
-s.to_end * (s.cur_rad - DIST_FROM_INSIDE);`

` // compute required braking
distance and compare:`

` if(to_end <= crit_dist(s.v,
speed_next, BRAKE_CURVE))`

` vc -=
BRK_CRV_SLP;`

` }`

With those changes incorporated into our robot it should be able to

get around almost any track. The next tutorial will consist of another

compilable robot program, incorporating all of the above.