How to drive a torcs car with a robot
After initializing all data, TORCS calls the drive function of the robot for each driving timestep. To drive a TORCS car, the robot has to set the following values:
- Brake pressure
- Steering angle
These values are part of the race command structure holding the data to be send to TORCS in the Drive-API-Call.
Acceleration, Brake pressure and Clutch are normalized to be values from 0 to 1. The corresponding acceleration is calculated from the engines data, read from a cartype specific setup file. The max brake pressure is initially set in this cartype specific setup file too, but may be redefined in a driver specific setup file merged with the initial settings of the cartype. The steering angle is scaled to be in -1 +1 range. The scaling is done with a cartype specific constant, the steerlock value. The Gear numbers include the reverse gear (0), the neutral gear (1) and than the other gears (2 = first, ...). The total number of gears is also cartype specific.
What to learn from it?
- To be sure, that we take the same values like TORCS, we have to read it from the normal TORCS-setup files (XML-Format). The merging of cartype + driver +... setup files is controlled by min and max values, giving the allowed range. You can not define a driver specific value out of range.
Because all these parameters are known, we can implement the reading in the wrapper using the handles to the ready merged setup files provided to us by TROCS. We can set this values while our DriverBase.Init call in our C#-Objects using our TStaticData structure.
It is practice to have additional parameters in the drivers setup file, only used from the driver. Here we are free to define all we need and to read it with the C#-code!
- To get a replay, we only have to save Acceleration, Brake pressure, Clutch, Gear and Steering angle off all cars in the race.
- These values will come from our code, so we are free, where and when to save it (Our Dispatcher handles all cars). TORCS can reproduce a race exactly, as long as no random input came from the cars (like uninitialized variables used in some robots!)
Basic driving function of Sharpy
The methods used for the first implementation of the SharpyCS driver are very basic, but they are a good base for driving on unknown tracks!
TORCS provides the information about the hole track to drive on while the InitTrack-API-Call. It is separated to segments of unique type (Straight, Turn to left, Turn to right).
All the bots used for the endurance world championship take and evaluate it to get a precalculated racingline. The most robots look at the track beeing of constant width, as the main track TORCS provides is indeed. But there are additional sides, having different chracteristics than the main track. This sides can have different start width and end width, individual friction and so on (based on the segments). The best robots use this additional width under some conditions.
All the functions used there are not of interest, if racing on a (partly) unknow track.
The method used for a basic steering of Sharpy, works with the current segment and the „visible“ part of the following segments. As intentionally used for racing, it drives in the middle of the road (so the lateral offset is zero).
The basic steering angle is calculated from the current postion to a target point, some way in front of the car. It is corrected by the yaw angle of the car. If the car is at a side of the track, it drives back to the middle. If it drives to a turn, the point used as target goes to the turn's inner side. This results in driving to the inner side of the turn. How much depends on the way used to look ahead. This „look ahead distance“ is corrected by the car's current speed. So in short, fast turns, it goes closer to the edges, in long, slower ones it stays more in the middle.
To make a robot drive not in the middle of the road, you have to add a lateral offset to the target point. The same is used, while overtaking and collision avoiding. If an opponent is near in front, the robot has to make a decision: Stay behind or overtake. If possible it will try to overtake by increasing the lateral offset to the better side. Better means here, to be at the inner side of the current or next comming turn.
Beeing on a long straight, the bot has to look a long way in front, but in this case, the next turn will be visible, indeed. In a turn, it uses the current situation (the current segment) to make the decision. So we can assume, that this approach will work, even if the track is unknown and we use only visible parts.
Assuming that we are racing, we allways try to accelerate as much as possible. But how much is possible? Here we start at the current segment too: Driving in a turn, the current segment's radius gives us the limit of the speed we can use (together with segment's and tyre's friction and other parameters of the car). If we are faster, we have to brake! If not or if we are on a straight, we have to look ahead. Again assuming that we are in a race (having no cars comming from the other side), we can calculate the way we would need to stop (speed1 = current speed, speed2 = 0). This gives us the max. distance to look ahead. Now we look from segment to segment in front, till we find a segment, limiting the speed. If there is one, we compare the distance to it with the brakedistance needed to slow down to the limiting segment's possible speed. If the brakedistance becomes greater, we brake. Here we assume, that we can look far enough. If we are in a situation, not seeing far enough, we can modify that method by replacing the brakedistance by the visible distance (or the half of it, if not in a race). Again, it would work (as base priciple).
With this simple methods, we allways use the brakes maximum pressure if braking. The faster bots don't use this binary approach. The used brake pressure is adjusted by the traction circle, the situation (opponents close), the lateral offset (beeing on the outer side) and other parameters. This is done while calculating the brakedistance, so we can combine it later.
For collision avoiding, a filter is used, to correct the basic brakepressure, if an opponent comes to close. the reaction is made, using the nearest opponent's data.
Avoiding cars aside
Our basic Sharpy has the CalcOffset method, to get the lateral offset from the middle of the road. Here we first check, wether there are cars aside. If so, we have to check wether there is more than one car aside.
If it is one car, we move to the other side of the track. If there are two cars, we check wether we are in the middle. If so we steer to the middle between both opponents, if not we go to the outer side of the track.
If there are more than two cars aside, we look only to the two next of us!
Here we mixed two offsets: One is were we are, the other is were we want to steer to! ToDo: To get better results (lower lap times, less damages) we will calculate the change of offset and use methods to control the speed of change. To get a smoother drive, we will replace the "middle of the track" line by the "main line of the track" and use the both distances to left and right instead of the track's width.
What to learn here?
We can see, what our robot needs to get from the "motion planning". The "main line" of the "track" and the distances to the sides, but in 3D! This info we want as long as posible in front of our car.
For an opponent we have to know were it is and to where it moves how fast, yawing or not. At the moment we use a fixed car with and car length (same for opponents and own car), but later we will have to deal with different dimensions.
To be able to calculate the possible speed along our main line, we need assumptions concerning the friction (of track and tyres). Let's assume, our car has no wings!