- import math
- def dist(point1, point2):
- return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5
- # thanks to https://stackoverflow.com/questions/20924085/python-conversion-between-coordinates
- def rect(r, theta):
- """
- theta in degrees
- returns tuple; (float, float); (x,y)
- """
- x = r * math.cos(math.radians(theta))
- y = r * math.sin(math.radians(theta))
- return x, y
- # thanks to https://stackoverflow.com/questions/20924085/python-conversion-between-coordinates
- def polar(x, y):
- """
- returns r, theta(degrees)
- """
- r = (x ** 2 + y ** 2) ** .5
- theta = math.degrees(math.atan2(y,x))
- return r, theta
- def angle_mod_360(angle):
- """
- Maps an angle to the interval -180, +180.
- Examples:
- angle_mod_360(362) == 2
- angle_mod_360(270) == -90
- :param angle: angle in degree
- :return: angle in degree. Between -180 and +180
- """
- n = math.floor(angle/360.0)
- angle_between_0_and_360 = angle - n*360.0
- if angle_between_0_and_360 <= 180.0:
- return angle_between_0_and_360
- else:
- return angle_between_0_and_360 - 360
- def get_waypoints_ordered_in_driving_direction(params):
- # waypoints are always provided in counter clock wise order
- if params['is_reversed']: # driving clock wise.
- return list(reversed(params['waypoints']))
- else: # driving counter clock wise.
- return params['waypoints']
- def up_sample(waypoints, factor):
- """
- Adds extra waypoints in between provided waypoints
- :param waypoints:
- :param factor: integer. E.g. 3 means that the resulting list has 3 times as many points.
- :return:
- """
- p = waypoints
- n = len(p)
- return [[i / factor * p[(j+1) % n][0] + (1 - i / factor) * p[j][0],
- i / factor * p[(j+1) % n][1] + (1 - i / factor) * p[j][1]] for j in range(n) for i in range(factor)]
- def get_target_point(params):
- waypoints = up_sample(get_waypoints_ordered_in_driving_direction(params), 20)
- car = [params['x'], params['y']]
- distances = [dist(p, car) for p in waypoints]
- min_dist = min(distances)
- i_closest = distances.index(min_dist)
- n = len(waypoints)
- waypoints_starting_with_closest = [waypoints[(i+i_closest) % n] for i in range(n)]
- r = params['track_width'] * 0.9
- is_inside = [dist(p, car) < r for p in waypoints_starting_with_closest]
- i_first_outside = is_inside.index(False)
- if i_first_outside < 0: # this can only happen if we choose r as big as the entire track
- return waypoints[i_closest]
- return waypoints_starting_with_closest[i_first_outside]
- def get_target_steering_degree(params):
- tx, ty = get_target_point(params)
- car_x = params['x']
- car_y = params['y']
- dx = tx-car_x
- dy = ty-car_y
- heading = params['heading']
- _, target_angle = polar(dx, dy)
- steering_angle = target_angle - heading
- return angle_mod_360(steering_angle)
- def score_steer_to_point_ahead(params):
- best_stearing_angle = get_target_steering_degree(params)
- steering_angle = params['steering_angle']
- error = (steering_angle - best_stearing_angle) / 60.0 # 60 degree is already really bad
- score = 1.0 - abs(error)
- return max(score, 0.01) # optimizer is rumored to struggle with negative numbers and numbers too close to zero
- def reward_function(params):
- return float(score_steer_to_point_ahead(params))