7.4 Side Collision Avoidance

Introduction

In this section we add collision avoidance for cars that drive aside of our car. The following implementation will add an additional filter to the steer value. It doesn't check if we leave the track.

Changes in driver.h

We declare the filter method prototype and the side margin. If the opponent is inside this margin we will try to avoid a collision.

        float filterSColl(float steer);

        static const float SIDECOLL_MARGIN;

Implementation in driver.cpp

At the beginning of the file we define the new constant.

const float Driver::SIDECOLL_MARGIN = 2.0;   /* [m] */

In the Driver::drive() method we call the steer filter. Change the line

        car->ctrl.steer = getSteer();

to

        car->ctrl.steer = filterSColl(getSteer());

The filterSColl method searches first for the nearest car aside of ours (approximately). If there is another car it checks if it is inside the SIDECOLL_MARGIN. If yes, it computes a new steer value and returns it.

/* Steer filter for collision avoidance */
float Driver::filterSColl(float steer)
{
    int i;
    float sidedist = 0.0, fsidedist = 0.0, minsidedist = FLT_MAX;
    Opponent *o = NULL;

    /* get the index of the nearest car (o) */
    for (i = 0; i < opponents->getNOpponents(); i++) {
        if (opponent[i].getState() & OPP_SIDE) {

Again we iterate through all cars and check those which have been marked with the OPP_SIDE flag. We init minsidedist with a large value because we search for the minimum.

            sidedist = opponent[i].getSideDist();
            fsidedist = fabs(sidedist);
            if (fsidedist < minsidedist) {
                minsidedist = fsidedist;
                o = &opponent[i];
            }
        }
    }

Here we check if the current opponent is the nearest one found so far. If that is the case we store a pointer to it and update the minimal distance found (minsidedist).

    /* if there is another car handle the situation */
    if (o != NULL) {
        float d = fsidedist - o->getWidth();
        /* near enough */
        if (d < SIDECOLL_MARGIN) {

If we found an opponent we check if it is inside the margin. To keep the method simple I make some assumptions, e. g. our car is parallel to the track (that is obiously not true).

            /* compute angle between cars */
            tCarElt *ocar = o->getCarPtr();
            float diffangle = ocar->_yaw - car->_yaw;
            NORM_PI_PI(diffangle);
            const float c = SIDECOLL_MARGIN/2.0;
            d = d - c;
            if (d < 0.0) d = 0.0;
            float psteer = diffangle/car->_steerLock;
            return steer*(d/c) + 2.0*psteer*(1.0-d/c);
        }
    }
    return steer;
}

Here we compute the angle between the two cars and the weight d. Psteer holds the steer value needed to drive parallel to the opponent (if you draw the scene it is obvious). Finally we weight the original steer and the psteer value and sum them up. This will push away our car from the opponent when we are near. You could improve this function with more accurate distance values and checking if the normal steer value points already more away from the opponent than the new computed value.

Downloads

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

Summary

  • You have implemented side collision avoidance.
  • You know how it works.
  • You know how to improve it.