Files
StarPilot/selfdrive/frogpilot/controls/lib/smart_turn_speed_controller.py
T
2025-01-28 12:53:28 -07:00

89 lines
3.3 KiB
Python

#!/usr/bin/env python3
import json
import numpy as np
from sortedcontainers import SortedDict
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from openpilot.selfdrive.frogpilot.frogpilot_variables import CRUISING_SPEED, PLANNER_TIME, params
CACHE_WINDOW = 1
CURVATURE_THRESHOLD = 1e-10
ROUNDING_PRECISION = 10
class SmartTurnSpeedController:
def __init__(self, FrogPilotVCruise):
self.frogpilot_vcruise = FrogPilotVCruise
self.data = SortedDict({entry["speed"]: [np.array([curve["curvature"], curve["lateral_accel"]]) for curve in entry.get("curvatures", [])] for entry in json.loads(params.get("UserCurvature") or "[]")})
self.last_cached_speed = 0
self.manual_long_timer = 0
self.stsc_target = 0
self.cached_entries = None
self.cached_speeds = None
def update_cache(self, v_ego):
if abs(self.last_cached_speed - v_ego) <= CACHE_WINDOW / 2:
return
speeds_in_range = list(self.data.irange(v_ego - CACHE_WINDOW, v_ego + CACHE_WINDOW))
if speeds_in_range:
self.cached_entries = np.vstack([self.data[speed] for speed in speeds_in_range])
self.cached_speeds = np.array(speeds_in_range)
else:
self.cached_entries = self.cached_speeds = None
self.last_cached_speed = v_ego
def set_stsc_target(self, carControl, v_cruise, v_ego):
if not self.data or v_ego < CRUISING_SPEED or not carControl.longActive:
self.stsc_target = v_cruise
return
self.update_cache(v_ego)
if self.cached_speeds is None:
self.stsc_target = v_cruise
return
road_curvature = round(self.frogpilot_vcruise.frogpilot_planner.road_curvature, ROUNDING_PRECISION)
closest_entry = min(self.cached_entries, key=lambda x: abs(x[0] - road_curvature))
self.stsc_target = clip((closest_entry[1] / closest_entry[0])**0.5, CRUISING_SPEED, v_cruise)
def update_curvature_data(self, v_ego):
road_curvature = round(self.frogpilot_vcruise.frogpilot_planner.road_curvature, ROUNDING_PRECISION)
lateral_accel = round(v_ego**2 * road_curvature, ROUNDING_PRECISION)
if abs(road_curvature) < CURVATURE_THRESHOLD or abs(lateral_accel) < 1:
return
entries = self.data.setdefault(v_ego, [])
for i, entry in enumerate(entries):
if abs(entry[0] - road_curvature) < CURVATURE_THRESHOLD:
entries[i][1] = (entry[1] + lateral_accel) / 2
return
entries.append(np.array([road_curvature, lateral_accel]))
def update(self, carControl, v_cruise, v_ego):
if not carControl.longActive and V_CRUISE_MAX * CV.KPH_TO_MS >= v_ego > CRUISING_SPEED and not self.frogpilot_vcruise.frogpilot_planner.tracking_lead:
if self.manual_long_timer >= PLANNER_TIME:
self.update_curvature_data(v_ego)
self.manual_long_timer += DT_MDL
elif self.manual_long_timer >= PLANNER_TIME:
params.put_nonblocking("UserCurvature", json.dumps([
{"speed": speed, "curvatures": [{"curvature": entry[0], "lateral_accel": entry[1]} for entry in entries]}
for speed, entries in self.data.items()
]))
self.manual_long_timer = 0
else:
self.set_stsc_target(carControl, v_cruise, v_ego)
self.manual_long_timer = 0