8.7 Put it All Together

Introduction

In this section we put all components we have built together. We have to instantiate the pit class, integrate the pit callback and to compute the target point with taking into account the pit offset.

Changes in driver.h

We include pit.h and add a prototype of the pit class.

#include "pit.h"

class Pit;

To remember our pit instance we need a variable. We also compute and remember the current speed square, because we need it in several places.

        Pit *pit;
        float currentspeedsqr;

Integration in driver.cpp

Creation and Destruction of the Pit Object

Add the release of our pit object into the destructor Driver::~Driver().

    delete pit;

Add the creation of the pit object into Driver::newRace(tCarElt* car, tSituation *s).

    /* create the pit object */
    pit = new Pit(s, this);

Changes in Driver::drive(tSituation *s)

Swap the following two statements, because the initialization to zero will overwrite the result of the pit update if it sets the pit race command. Change in Driver::drive(tSituation *s)

    update(s);
    memset(&car->ctrl, 0, sizeof(tCarCtrl));

to

    memset(&car->ctrl, 0, sizeof(tCarCtrl));
    update(s);

Change the brake value computation such that it takes into account the pit brake filter. Change

        car->ctrl.brakeCmd = filterABS(filterBColl(getBrake()));

to

        car->ctrl.brakeCmd = filterABS(filterBColl(filterBPit(getBrake())));

The Pit Callback

Now we integrate the pit getRepair and getFuel methods into the pit callback. We set pitstop to false. Now it should be finally clear how a pit stop works. Our car stops in the pit and if it is slow and near enough to the pit middle and we request a pit stop, TORCS calls this callback function (remember chapter 2 where you registered it). We tell TORCS our wishes and set pitstop to false. TORCS holds our car captured during the repair and refueling time, so we can't drive away. After TORCS releases our car the drive function is called as usual and we leave the pit. Replace

/* Set pitstop commands. */
int Driver::pitCommand(tSituation *s)
{
    return ROB_PIT_IM; /* return immediately */
}

with

/* Set pitstop commands */
int Driver::pitCommand(tSituation *s)
{
    car->_pitRepair = pit->getRepair();
    car->_pitFuel = pit->getFuel();
    pit->setPitstop(false);
    return ROB_PIT_IM; /* return immediately */
}

Change the Target Point Computation

Now we have to modify the getTargetPoint method. That you know where to insert the code the unchanged code is in grey.

/* compute target point for steering */
v2d Driver::getTargetPoint()
{
    tTrackSeg *seg = car->_trkPos.seg;
    float lookahead = LOOKAHEAD_CONST + car->_speed_x*LOOKAHEAD_FACTOR;
    float length = getDistToSegEnd();
    float offset = getOvertakeOffset();

If we are on the pit trajectory we switch to another computation of the lookahead value. If we are in the speed limit range we even take a shorter lookahead value. You could implement this also with one good formula.

    if (pit->getInPit()) {
        if (currentspeedsqr > pit->getSpeedlimitSqr()) {
            lookahead = PIT_LOOKAHEAD + car->_speed_x*LOOKAHEAD_FACTOR;
        } else {
            lookahead = PIT_LOOKAHEAD;
        }
    }

    while (length < lookahead) {
        seg = seg->next;
        length += seg->length;
    }

    length = lookahead - length + seg->length;

We have to pass the distance to the start line of the target point to getPitOffset, so we compute that first. After that we call the computation of the pit offset.

    float fromstart = seg->lgfromstart;
    fromstart += length;
    offset = pit->getPitOffset(offset, fromstart);

    v2d s;
    s.x = (seg->vertex[TR_SL].x + seg->vertex[TR_SR].x)/2.0;
    s.y = (seg->vertex[TR_SL].y + seg->vertex[TR_SR].y)/2.0;

Change the Update

The second last thing left is to update currentspeedsqr and to call the pit update method. Put this code at the end of Driver::update(tSituation *s).

    currentspeedsqr = car->_speed_x*car->_speed_x;
    pit->update();

Change the Track Filter

Finally we need to modify Driver::filterTrk(float accel) that it does allow accelerator commands in the pits. Change

    if (car->_speed_x < MAX_UNSTUCK_SPEED) return accel;

to

    if (car->_speed_x < MAX_UNSTUCK_SPEED ||
        pit->getInPit()) return accel;

Test Drive

Now everything should compile and work fine. To test the pit stops you can insert the following line in the drive function to make the car stop on every lap. If it works you can get now your cold juice out of the fridge... Cheers;-)

    pit->setPitstop(true);

Final Remarks

That finally was it, you got your TORCS driver license, congratulations. I ask myself if anybody will read till here, so in case you did, please let me know and tell me your impressions. The reason for me to write this tutorial was to make it easier for other people to build a robot and to understand the problem better. I tried to separate the different functionality of the robot into filters and classes, the goal was to keep them as independent as possible. In fact the structure is much better than on my berniw robot, but I'm still not really happy with it. Have fun and contribute to TORCS.

Bye, berniw.

Downloads

In case you got lost, you can download my robot for TORCS 1.2.0 or later.

Feedback

Let me know if you read this chapter and your thoughts about it. Please send me also spelling, grammar, math and code corrections. Thank you for the feedback.

Summary

  • You have understood a full featured robot and you are ready to improve it or build your own from scratch.