Files
dragonpilot/selfdrive/controls/lib/longcontrol.py

131 lines
5.1 KiB
Python
Raw Normal View History

2018-11-17 02:08:34 -08:00
from cereal import log
2016-12-14 21:29:12 -08:00
from common.numpy_fast import clip, interp
2017-09-30 03:07:27 -07:00
from selfdrive.controls.lib.pid import PIController
2019-06-06 04:38:45 +00:00
LongCtrlState = log.ControlsState.LongControlState
2018-11-17 02:08:34 -08:00
2017-09-30 03:07:27 -07:00
STOPPING_EGO_SPEED = 0.5
2018-11-17 02:08:34 -08:00
MIN_CAN_SPEED = 0.3 # TODO: parametrize this in car interface
2017-11-22 04:30:24 -08:00
STOPPING_TARGET_SPEED = MIN_CAN_SPEED + 0.01
2017-09-30 03:07:27 -07:00
STARTING_TARGET_SPEED = 0.5
BRAKE_THRESHOLD_TO_PID = 0.2
2018-11-17 02:08:34 -08:00
STOPPING_BRAKE_RATE = 0.2 # brake_travel/s while trying to stop
STARTING_BRAKE_RATE = 0.8 # brake_travel/s while releasing on restart
BRAKE_STOPPING_TARGET = 0.5 # apply at least this amount of brake to maintain the vehicle stationary
2016-11-29 18:34:21 -08:00
2018-11-17 02:08:34 -08:00
_MAX_SPEED_ERROR_BP = [0., 30.] # speed breakpoints
_MAX_SPEED_ERROR_V = [1.5, .8] # max positive v_pid error VS actual speed; this avoids controls windup due to slow pedal resp
2016-11-29 18:34:21 -08:00
2018-11-17 02:08:34 -08:00
RATE = 100.0
2016-11-29 18:34:21 -08:00
2017-11-22 04:30:24 -08:00
2017-09-30 03:07:27 -07:00
def long_control_state_trans(active, long_control_state, v_ego, v_target, v_pid,
2017-11-22 04:30:24 -08:00
output_gb, brake_pressed, cruise_standstill):
2018-11-17 02:08:34 -08:00
"""Update longitudinal control state machine"""
2017-11-22 04:30:24 -08:00
stopping_condition = (v_ego < 2.0 and cruise_standstill) or \
(v_ego < STOPPING_EGO_SPEED and \
((v_pid < STOPPING_TARGET_SPEED and v_target < STOPPING_TARGET_SPEED) or
brake_pressed))
2016-11-29 18:34:21 -08:00
2017-11-22 04:30:24 -08:00
starting_condition = v_target > STARTING_TARGET_SPEED and not cruise_standstill
2016-11-29 18:34:21 -08:00
2017-09-30 03:07:27 -07:00
if not active:
2016-11-29 18:34:21 -08:00
long_control_state = LongCtrlState.off
2017-09-30 03:07:27 -07:00
2016-11-29 18:34:21 -08:00
else:
if long_control_state == LongCtrlState.off:
2017-09-30 03:07:27 -07:00
if active:
2016-11-29 18:34:21 -08:00
long_control_state = LongCtrlState.pid
2017-09-30 03:07:27 -07:00
2016-11-29 18:34:21 -08:00
elif long_control_state == LongCtrlState.pid:
if stopping_condition:
long_control_state = LongCtrlState.stopping
2017-09-30 03:07:27 -07:00
2016-11-29 18:34:21 -08:00
elif long_control_state == LongCtrlState.stopping:
2017-11-22 04:30:24 -08:00
if starting_condition:
2016-11-29 18:34:21 -08:00
long_control_state = LongCtrlState.starting
2017-09-30 03:07:27 -07:00
2016-11-29 18:34:21 -08:00
elif long_control_state == LongCtrlState.starting:
if stopping_condition:
long_control_state = LongCtrlState.stopping
2017-09-30 03:07:27 -07:00
elif output_gb >= -BRAKE_THRESHOLD_TO_PID:
2016-11-29 18:34:21 -08:00
long_control_state = LongCtrlState.pid
return long_control_state
2016-12-14 21:29:12 -08:00
2019-10-09 18:43:53 +00:00
class LongControl():
2017-11-22 04:30:24 -08:00
def __init__(self, CP, compute_gb):
2017-09-30 03:07:27 -07:00
self.long_control_state = LongCtrlState.off # initialized to off
2019-05-16 13:20:29 -07:00
self.pid = PIController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
2018-11-17 02:08:34 -08:00
rate=RATE,
2017-09-30 03:07:27 -07:00
sat_limit=0.8,
convert=compute_gb)
self.v_pid = 0.0
self.last_output_gb = 0.0
2016-11-29 18:34:21 -08:00
def reset(self, v_pid):
2018-11-17 02:08:34 -08:00
"""Reset PID controller and change setpoint"""
2017-09-30 03:07:27 -07:00
self.pid.reset()
2016-11-29 18:34:21 -08:00
self.v_pid = v_pid
2019-02-20 01:39:02 +00:00
def update(self, active, v_ego, brake_pressed, standstill, cruise_standstill, v_cruise, v_target, v_target_future, a_target, CP):
2018-11-17 02:08:34 -08:00
"""Update longitudinal control. This updates the state machine and runs a PID loop"""
# Actuation limits
2017-09-30 03:07:27 -07:00
gas_max = interp(v_ego, CP.gasMaxBP, CP.gasMaxV)
brake_max = interp(v_ego, CP.brakeMaxBP, CP.brakeMaxV)
2016-11-29 18:34:21 -08:00
2018-11-17 02:08:34 -08:00
# Update state machine
2016-11-29 18:34:21 -08:00
output_gb = self.last_output_gb
2017-11-22 04:30:24 -08:00
self.long_control_state = long_control_state_trans(active, self.long_control_state, v_ego,
v_target_future, self.v_pid, output_gb,
brake_pressed, cruise_standstill)
2016-11-29 18:34:21 -08:00
2017-11-22 04:30:24 -08:00
v_ego_pid = max(v_ego, MIN_CAN_SPEED) # Without this we get jumps, CAN bus reports 0 when speed < 0.3
2017-10-31 02:27:39 -07:00
2016-11-29 18:34:21 -08:00
if self.long_control_state == LongCtrlState.off:
2018-11-17 02:08:34 -08:00
self.v_pid = v_ego_pid
2017-09-30 03:07:27 -07:00
self.pid.reset()
2018-11-17 02:08:34 -08:00
output_gb = 0.
2017-09-30 03:07:27 -07:00
2016-11-29 18:34:21 -08:00
# tracking objects and driving
elif self.long_control_state == LongCtrlState.pid:
2017-12-23 17:15:27 -08:00
self.v_pid = v_target
2017-09-30 03:07:27 -07:00
self.pid.pos_limit = gas_max
self.pid.neg_limit = - brake_max
2018-11-17 02:08:34 -08:00
# Toyota starts braking more when it thinks you want to stop
# Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
prevent_overshoot = not CP.stoppingControl and v_ego < 1.5 and v_target_future < 0.7
2019-05-16 13:20:29 -07:00
deadzone = interp(v_ego_pid, CP.longitudinalTuning.deadzoneBP, CP.longitudinalTuning.deadzoneV)
2018-11-17 02:08:34 -08:00
2017-11-22 04:30:24 -08:00
output_gb = self.pid.update(self.v_pid, v_ego_pid, speed=v_ego_pid, deadzone=deadzone, feedforward=a_target, freeze_integrator=prevent_overshoot)
2018-11-17 02:08:34 -08:00
2017-11-22 04:30:24 -08:00
if prevent_overshoot:
output_gb = min(output_gb, 0.0)
2017-09-30 03:07:27 -07:00
2018-11-17 02:08:34 -08:00
# Intention is to stop, switch to a different brake control until we stop
2016-11-29 18:34:21 -08:00
elif self.long_control_state == LongCtrlState.stopping:
2018-11-17 02:08:34 -08:00
# Keep applying brakes until the car is stopped
if not standstill or output_gb > -BRAKE_STOPPING_TARGET:
output_gb -= STOPPING_BRAKE_RATE / RATE
2016-12-14 21:29:12 -08:00
output_gb = clip(output_gb, -brake_max, gas_max)
2017-09-30 03:07:27 -07:00
2016-12-12 17:47:46 -08:00
self.v_pid = v_ego
2017-09-30 03:07:27 -07:00
self.pid.reset()
2018-11-17 02:08:34 -08:00
# Intention is to move again, release brake fast before handing control to PID
2016-11-29 18:34:21 -08:00
elif self.long_control_state == LongCtrlState.starting:
if output_gb < -0.2:
2018-11-17 02:08:34 -08:00
output_gb += STARTING_BRAKE_RATE / RATE
2016-12-12 17:47:46 -08:00
self.v_pid = v_ego
2017-09-30 03:07:27 -07:00
self.pid.reset()
2016-11-29 18:34:21 -08:00
self.last_output_gb = output_gb
2016-12-14 21:29:12 -08:00
final_gas = clip(output_gb, 0., gas_max)
final_brake = -clip(output_gb, -brake_max, 0.)
2016-12-12 17:47:46 -08:00
2017-09-30 03:07:27 -07:00
return final_gas, final_brake