1.3 Make it Drive

The Track

Before we can start implementing simple steering we need to discuss how the track looks for the robot. The track is partitioned into segments of the following types: left turns, right turns and straight segments. The segments are usually short, so a turn that looks like a big turn or a long straight is most often split into much smaller segments. The segments are organized as linked list in the memory. A straight segment has a width and a length, a turn has a width, a length and a radius, everyting is measured in the middle of the track. All segments connect tangentially to the next and previous segments, so the middle line is smooth. There is much more data available, the structure tTrack is defined in $TORCS_BASE/export/include/track.h.

The Car

You can obtain car data by the tCarElt structure, which contains all you need. It is defined in $TORCS_BASE/export/include/car.h. But now lets implement something.

Implementing Simple Steering

As first challenge we will implement a very simple steering function, that simply tries to follow the middle line on the track. On a high level it could look like that:

  • First steer the front wheels parallel to the track.
  • If we are not in the middle of the track add a correction to the steer value.

You can get the tangent angle of the track with the following function call (look up $TORCS_BASE/export/include/robottools.h):

RtTrackSideTgAngleL(&(car->_trkPos))

The angle of the car is:

car->_yaw

The initial steering angle is then the difference:

angle = RtTrackSideTgAngleL(&(car->_trkPos)) - car->_yaw;
NORM_PI_PI(angle); // put the angle back in the range from -PI to PI

The distance to the middle line is:

car->_trkPos.toMiddle (+ to left, - to right)

The distance to the middle is measured in meters, so the values are too big to add it directly to the steering angle. So we divide it first through the width of the track and multiply it with a "tuning" constant. Now we put all the stuff together:

float angle;
const float SC = 1.0;
angle = RtTrackSideTgAngleL(&(car->_trkPos)) - car->_yaw;
NORM_PI_PI(angle); // put the angle back in the range from -PI to PI
angle -= SC*car->_trkPos.toMiddle/car->_trkPos.seg->width;

We need also to change to the first gear and apply a bit of accelerator pedal. The accelerator and brakes ranges from [0..1], the steer value from [-1.0..1.0]. Now this should raise a question. In the upper code snippet I computed a steering angle, so how do we convert this to [-1.0..1.0]? For that there is a constant in the tCarElt struct:

car->_steerLock

This value defines the angle of 100% steering (1.0), so we need to divide our steering angle by car->_steerLock.

car->ctrl.steer = angle / car->_steerLock;
car->ctrl.gear = 1; // first gear
car->ctrl.accelCmd = 0.3; // 30% accelerator pedal
car->ctrl.brakeCmd = 0.0; // no brakes

You can see that we return all our values in the car->ctrl structure to TORCS. Insert this code in the bt.cpp file in your robots source directory into the drive function. It should look like this after you changed it:

/* Drive during race. */
static void
drive(int index, tCarElt* car, tSituation *s)
{
    memset(&car->ctrl, 0, sizeof(tCarCtrl));

    float angle;
    const float SC = 1.0;

    angle = RtTrackSideTgAngleL(&(car->_trkPos)) - car->_yaw;
    NORM_PI_PI(angle); // put the angle back in the range from -PI to PI
    angle -= SC*car->_trkPos.toMiddle/car->_trkPos.seg->width;

    // set up the values to return
    car->ctrl.steer = angle / car->_steerLock;
    car->ctrl.gear = 1; // first gear
    car->ctrl.accelCmd = 0.3; // 30% accelerator pedal
    car->ctrl.brakeCmd = 0.0; // no brakes
}

Compile and install your robot, and play a bit with it. In case you don't understand the algorithm, take a sheet of paper and a pencil and draw the situation with the track and the car, this will help. You will discover some problems which you have to solve later. Change some of the values, run it on different tracks, add better steering correction, whatever you like. You don't need to restart TORCS every time you did "make install", because if you start a new practice session, your robot module is reloaded. If you practice very often you should have an xosview or something similar running, because TORCS has memory leaks, so you have to restart it from time to time...

Downloads

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

Summary

  • You know the drive function and the file bt.cpp.
  • You know how to access car data.
  • You know where to look up the structure of tCarElt and tTrack.
  • You know about the robottools.
  • You know how to return your data to TORCS.
  • You know about about dynamic loading of your robot.
  • You know about the memory leaks.