Third Place Solution in Fall 2023 Simulation Racing Series
Written by Ryan Chow, a HS sophomore at Sacred Heart Cathedral Preparatory, this paper describes his thought process for his third place solution in the Fall 2023 Simulation Racing Series Competition.
The code for the solution can be found here: https://github.com/Ryan-Chow/ROAR_Competition/tree/main/competition_code
Table of Contents
Introduction
This solution was built off of the default code provided in RoarPy. In this article, I simply go over some strategies and ideas that were implemented in my algorithm. Most of these strategies are not map specific and can be implemented into future solutions.
General Strategy
In my solution, I attempted to maximize speed and optimize the racing line by using a dynamic waypoint viewer. The waypoint viewer would determine which waypoint to look at, depending on the speed of the vehicle. Using this waypoint viewer allowed me to cut some corners and try to optimize my racing line. Here’s a diagram that shows you the general idea of the waypoints:
- # Dynamic waypoint viewer, depends on speed.
- lookahead_distance = np.floor(0.425 * vehicle_velocity_norm)
- waypoint_to_follow = self.maneuverable_waypoints[(self.current_waypoint_idx + int(lookahead_distance)) % len(self.maneuverable_waypoints)]
- # Dynamic waypoint viewer (same as one above), BUT looks further ahead, providing more advanced warning (so the car doesn’t slam into a wall)
- further_lookahead_distance = np.floor(0.90 * vehicle_velocity_norm)
- further_waypoint_to_follow = self.maneuverable_waypoints[(self.current_waypoint_idx + int(further_lookahead_distance)) % len(self.maneuverable_waypoints)
I also had a second dynamic waypoint viewer, which looked even further ahead. While the first dynamic waypoint viewer determined where the car should go, the second dynamic waypoint viewer gave the car data like track curvature:
- turning_radius = 1.0 / np.cross([vector_to_further_waypoint[0], vector_to_further_waypoint[1], 0], [np.cos(vehicle_rotation[2]), np.sin(vehicle_rotation[2]), 0])[2] if np.linalg.norm(vector_to_further_waypoint) > 0 else 0.0
- curvature = 1.0 / turning_radius if turning_radius != 0 else 0.0
Given the curvature (which is a float value), I could now predict when to slow down (and speed up) in anticipation of turns or straightaways.
Track-Specific Strategies and Tuning
Using the dynamic waypoint viewers along with curvature detection, I was able to measure the curvature of every single turn on the track. After observing the vehicle, I tuned the viewer multiplier and bucketted throttle amounts given the road curvature and the waypoint number the car was reaching on the track.
Conclusion
I believe there is still a lot to improve on. There’s a lot to learn from this algorithm and I believe that either myself or others can greatly refine this strategy. Through observation I realized that the racing line could have been better optimized, and perhaps learning more about racing technique could help improve upon this solution. Thank you to the team at ROAR for cooking up such an awesome competition, and I’m excited to see what the future holds for autonomous racing.