To complete the task, I have chosen the same approach as in Q&A video for the project. We control car's speed by increasing\decreasing it no more than 0.224 mph. It allows us not to worry about jerks. I use provided methods for conversion from frenet to cartesian coordinates and vice versa.
To control car's behavior I use final states machine with six possible states:
- CS (constant speed) - initial car's state. It is never actually used.
- KL (keep line) - the car should stay in the same lane. Depending on the situation we can increase car's speed (no cars ahead), adjust speed to the car's in front of us or just drive with maximum speed.
- PLCL (prepare line change left) - the car prepares to change the line. The main idea that we increase our speed disregard whether we have a car in front of us or not.
- PLCR (prepare line change right) - the same as PLCL but for the right turn.
- LCL (line change left) - we try to adjust our speed in a new lane. The idea is the same as for KL. The only difference is that we change
lane
value. - LCR (line change right) - the same as LCL but for the right turn.
Initially, we get coordinates in global map and frenet car's coordinates. To predict and control car's behavior we estimate vehicle's position using global coordinates. Then we get frenet coordinates and use them to make the best decision using cost function.
When we have chosen the best future state, we build new waypoints for our car. To avoid jerks, we use waypoints from our previously formed path. To build the rest of the way we use frenet coordinates and based on them get global coordinates.
To avoid jerks, we have to build our waypoints by dividing our path with the same time frames. For this purpose, we convert our global coordinates to local one and use spline library to build a smooth path. Then we build the path up to desired distance. The last step is to convert local coordinates back to global.
To choose the best state in FSM we use five cost functions:
change_lane_cost
- we use to prefer staying in the lane than switching the lanes.inefficiency_cost
- if we can drive in another lane with better speed, we should change the lanefree_line_cost
- the same purpose as above. We want to force the car to change the lane if we do not see any obstacles in another lane.collision_cost
- if we found a collision in another lane, we must not change the lane (COLLISION
has the largest weight)buffer_cost
- we use it to switch between prepare lane change and actual lane change. If we see some car in front of use, we should change the lane.
Every time frame we update information about cars around us and generate predictions based on their metrics. Then we choose plausible future states for our current state. Next step is to calculate cost for every state and choose the cheapest one. For this purpose, we have to generate car's trajectory based on predictions.
Conversion methods have quite poor accuracy. As a result, I still can sometimes see odd car's behavior and accidents caused by this reason.