open source driving agent
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

133 lines
4.7 KiB

import math
import numpy as np
from common.numpy_fast import clip, interp
from selfdrive.config import Conversions as CV
_K_CURV_V = [1., 0.6]
_K_CURV_BP = [0., 0.002]
def calc_d_lookahead(v_ego, d_poly):
#*** this function computes how far too look for lateral control
# howfar we look ahead is function of speed and how much curvy is the path
offset_lookahead = 1.
k_lookahead = 7.
# integrate abs value of second derivative of poly to get a measure of path curvature
pts_len = 50. # m
if len(d_poly)>0:
pts = np.polyval([6*d_poly[0], 2*d_poly[1]], np.arange(0, pts_len))
else:
pts = 0.
curv = np.sum(np.abs(pts))/pts_len
k_curv = interp(curv, _K_CURV_BP, _K_CURV_V)
# sqrt on speed is needed to keep, for a given curvature, the y_des
# proportional to speed. Indeed, y_des is prop to d_lookahead^2
# 36m at 25m/s
d_lookahead = offset_lookahead + math.sqrt(max(v_ego, 0)) * k_lookahead * k_curv
return d_lookahead
def calc_lookahead_offset(v_ego, angle_steers, d_lookahead, VM, angle_offset):
#*** this function returns the lateral offset given the steering angle, speed and the lookahead distance
sa = (angle_steers - angle_offset) * CV.DEG_TO_RAD
curvature = VM.calc_curvature(sa, v_ego)
# clip is to avoid arcsin NaNs due to too sharp turns
y_actual = d_lookahead * np.tan(np.arcsin(np.clip(d_lookahead * curvature, -0.999, 0.999))/2.)
return y_actual, curvature
def calc_desired_steer_angle(v_ego, y_des, d_lookahead, VM, angle_offset):
# inverse of the above function
curvature = np.sin(np.arctan(y_des / d_lookahead) * 2.) / d_lookahead
steer_des = VM.get_steer_from_curvature(curvature, v_ego) * CV.RAD_TO_DEG + angle_offset
return steer_des, curvature
def pid_lateral_control(v_ego, sa_actual, sa_des, Ui_steer, steer_max,
steer_override, sat_count, enabled, Kp, Ki, rate):
sat_count_rate = 1./rate
sat_count_limit = 0.8 # after 0.8s of continuous saturation, an alert will be sent
error_steer = sa_des - sa_actual
Ui_unwind_speed = 0.3/rate #.3 per second
Up_steer = error_steer*Kp
Ui_steer_new = Ui_steer + error_steer*Ki * 1./rate
output_steer_new = Ui_steer_new + Up_steer
# Anti-wind up for integrator: do not integrate if we are against the steer limits
if (
(error_steer >= 0. and (output_steer_new < steer_max or Ui_steer < 0)) or
(error_steer <= 0. and
(output_steer_new > -steer_max or Ui_steer > 0))) and not steer_override:
#update integrator
Ui_steer = Ui_steer_new
# unwind integrator if driver is maneuvering the steering wheel
elif steer_override:
Ui_steer -= Ui_unwind_speed * np.sign(Ui_steer)
# still, intergral term should not be bigger then limits
Ui_steer = clip(Ui_steer, -steer_max, steer_max)
output_steer = Up_steer + Ui_steer
# don't run steer control if at very low speed
if v_ego < 0.3 or not enabled:
output_steer = 0.
Ui_steer = 0.
# useful to know if control is against the limit
lateral_control_sat = False
if abs(output_steer) > steer_max:
lateral_control_sat = True
output_steer = clip(output_steer, -steer_max, steer_max)
# if lateral control is saturated for a certain period of time, send an alert for taking control of the car
# wind
if lateral_control_sat and not steer_override and v_ego > 10 and abs(error_steer) > 0.1:
sat_count += sat_count_rate
# unwind
else:
sat_count -= sat_count_rate
sat_flag = False
if sat_count >= sat_count_limit:
sat_flag = True
sat_count = clip(sat_count, 0, 1)
return output_steer, Up_steer, Ui_steer, lateral_control_sat, sat_count, sat_flag
class LatControl(object):
def __init__(self):
self.Up_steer = 0.
self.sat_count = 0
self.y_des = 0.0
self.lateral_control_sat = False
self.Ui_steer = 0.
self.reset()
def reset(self):
self.Ui_steer = 0.
def update(self, enabled, v_ego, angle_steers, steer_override, d_poly, angle_offset, VM):
rate = 100
steer_max = 1.0
# how far we look ahead is function of speed and desired path
d_lookahead = calc_d_lookahead(v_ego, d_poly)
# desired lookahead offset
self.y_des = np.polyval(d_poly, d_lookahead)
# calculate actual offset at the lookahead point
self.angle_steers_des, _ = calc_desired_steer_angle(v_ego, self.y_des,
d_lookahead, VM, angle_offset)
output_steer, self.Up_steer, self.Ui_steer, self.lateral_control_sat, self.sat_count, sat_flag = pid_lateral_control(
v_ego, angle_steers, self.angle_steers_des, self.Ui_steer, steer_max,
steer_override, self.sat_count, enabled, VM.CP.steerKp, VM.CP.steerKi, rate)
final_steer = clip(output_steer, -steer_max, steer_max)
return final_steer, sat_flag