from common . numpy_fast import interp
import numpy as np
from cereal import log
CAMERA_OFFSET = 0.06 # m from center car to camera
def compute_path_pinv ( l = 50 ) :
deg = 3
x = np . arange ( l * 1.0 )
X = np . vstack ( tuple ( x * * n for n in range ( deg , - 1 , - 1 ) ) ) . T
pinv = np . linalg . pinv ( X )
return pinv
def model_polyfit ( points , path_pinv ) :
return np . dot ( path_pinv , [ float ( x ) for x in points ] )
def eval_poly ( poly , x ) :
return poly [ 3 ] + poly [ 2 ] * x + poly [ 1 ] * x * * 2 + poly [ 0 ] * x * * 3
def calc_d_poly ( l_poly , r_poly , p_poly , l_prob , r_prob , lane_width , v_ego ) :
# This will improve behaviour when lanes suddenly widen
# these numbers were tested on 2000segments and found to work well
lane_width = min ( 4.0 , lane_width )
width_poly = l_poly - r_poly
prob_mods = [ ]
for t_check in [ 0.0 , 1.5 , 3.0 ] :
width_at_t = eval_poly ( width_poly , t_check * ( v_ego + 7 ) )
prob_mods . append ( interp ( width_at_t , [ 4.0 , 5.0 ] , [ 1.0 , 0.0 ] ) )
mod = min ( prob_mods )
l_prob = mod * l_prob
r_prob = mod * r_prob
path_from_left_lane = l_poly . copy ( )
path_from_left_lane [ 3 ] - = lane_width / 2.0
path_from_right_lane = r_poly . copy ( )
path_from_right_lane [ 3 ] + = lane_width / 2.0
lr_prob = l_prob + r_prob - l_prob * r_prob
d_poly_lane = ( l_prob * path_from_left_lane + r_prob * path_from_right_lane ) / ( l_prob + r_prob + 0.0001 )
return lr_prob * d_poly_lane + ( 1.0 - lr_prob ) * p_poly
class LanePlanner ( ) :
def __init__ ( self ) :
self . l_poly = [ 0. , 0. , 0. , 0. ]
self . r_poly = [ 0. , 0. , 0. , 0. ]
self . p_poly = [ 0. , 0. , 0. , 0. ]
self . d_poly = [ 0. , 0. , 0. , 0. ]
self . lane_width_estimate = 3.7
self . lane_width_certainty = 1.0
self . lane_width = 3.7
self . l_prob = 0.
self . r_prob = 0.
self . l_lane_change_prob = 0.
self . r_lane_change_prob = 0.
self . _path_pinv = compute_path_pinv ( )
self . x_points = np . arange ( 50 )
def parse_model ( self , md ) :
if len ( md . leftLane . poly ) :
self . l_poly = np . array ( md . leftLane . poly )
self . r_poly = np . array ( md . rightLane . poly )
self . p_poly = np . array ( md . path . poly )
else :
self . l_poly = model_polyfit ( md . leftLane . points , self . _path_pinv ) # left line
self . r_poly = model_polyfit ( md . rightLane . points , self . _path_pinv ) # right line
self . p_poly = model_polyfit ( md . path . points , self . _path_pinv ) # predicted path
self . l_prob = md . leftLane . prob # left line prob
self . r_prob = md . rightLane . prob # right line prob
if len ( md . meta . desireState ) :
self . l_lane_change_prob = md . meta . desireState [ log . PathPlan . Desire . laneChangeLeft - 1 ]
self . r_lane_change_prob = md . meta . desireState [ log . PathPlan . Desire . laneChangeRight - 1 ]
def update_d_poly ( self , v_ego ) :
# only offset left and right lane lines; offsetting p_poly does not make sense
self . l_poly [ 3 ] + = CAMERA_OFFSET
self . r_poly [ 3 ] + = CAMERA_OFFSET
# Find current lanewidth
self . lane_width_certainty + = 0.05 * ( self . l_prob * self . r_prob - self . lane_width_certainty )
current_lane_width = abs ( self . l_poly [ 3 ] - self . r_poly [ 3 ] )
self . lane_width_estimate + = 0.005 * ( current_lane_width - self . lane_width_estimate )
speed_lane_width = interp ( v_ego , [ 0. , 31. ] , [ 2.8 , 3.5 ] )
self . lane_width = self . lane_width_certainty * self . lane_width_estimate + \
( 1 - self . lane_width_certainty ) * speed_lane_width
self . d_poly = calc_d_poly ( self . l_poly , self . r_poly , self . p_poly , self . l_prob , self . r_prob , self . lane_width , v_ego )
def update ( self , v_ego , md ) :
self . parse_model ( md )
self . update_d_poly ( v_ego )