Map Turn Speed Control

Added toggle for "Map Turn Speed Control".

Credit goes to Pfeiferj!

https: //github.com/pfeiferj
Co-Authored-By: Jacob Pfeifer <jacob@pfeifer.dev>
This commit is contained in:
FrogAi
2024-04-15 00:17:14 -07:00
parent 62744480ac
commit 4d7674d50c
12 changed files with 229 additions and 8 deletions
+1
View File
@@ -33,6 +33,7 @@ struct FrogPilotNavigation @0xda96579883444c35 {
struct FrogPilotPlan @0x80ae746ee2596b11 {
accelerationJerk @0 :Float32;
accelerationJerkStock @1 :Float32;
adjustedCruise @2 :Float64;
conditionalExperimental @3 :Bool;
desiredFollowDistance @4 :Int16;
egoJerk @5 :Float32;
+6
View File
@@ -259,6 +259,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DeveloperUI", PERSISTENT},
{"DeviceManagement", PERSISTENT},
{"DeviceShutdown", PERSISTENT},
{"DisableMTSCSmoothing", PERSISTENT},
{"DisableOnroadUploads", PERSISTENT},
{"DisableOpenpilotLongitudinal", PERSISTENT},
{"DisengageVolume", PERSISTENT},
@@ -300,7 +301,12 @@ std::unordered_map<std::string, uint32_t> keys = {
{"LoudBlindspotAlert", PERSISTENT},
{"LowVoltageShutdown", PERSISTENT},
{"ManualUpdateInitiated", PERSISTENT},
{"MapTargetLatA", PERSISTENT},
{"MapTargetVelocities", PERSISTENT},
{"ModelUI", PERSISTENT},
{"MTSCAggressiveness", PERSISTENT},
{"MTSCCurvatureCheck", PERSISTENT},
{"MTSCEnabled", PERSISTENT},
{"NoLogging", PERSISTENT},
{"NoUploads", PERSISTENT},
{"OfflineMode", PERSISTENT},
+1
View File
@@ -568,5 +568,6 @@ selfdrive/frogpilot/frogpilot_process.py
selfdrive/frogpilot/controls/frogpilot_planner.py
selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py
selfdrive/frogpilot/controls/lib/frogpilot_functions.py
selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py
selfdrive/frogpilot/controls/lib/theme_manager.py
selfdrive/frogpilot/fleet_manager/fleet_manager.py
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

@@ -2,6 +2,8 @@ import numpy as np
import cereal.messaging as messaging
from cereal import log
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
@@ -17,6 +19,7 @@ from openpilot.system.version import get_short_branch
from openpilot.selfdrive.frogpilot.controls.lib.conditional_experimental_mode import ConditionalExperimentalMode
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import CITY_SPEED_LIMIT, CRUISING_SPEED, calculate_lane_width, calculate_road_curvature
from openpilot.selfdrive.frogpilot.controls.lib.map_turn_speed_controller import MapTurnSpeedController
# Acceleration profiles - Credit goes to the DragonPilot team!
# MPH = [0., 18, 36, 63, 94]
@@ -50,10 +53,12 @@ class FrogPilotPlanner:
self.params_memory = Params("/dev/shm/params")
self.cem = ConditionalExperimentalMode()
self.mtsc = MapTurnSpeedController()
self.staging = get_short_branch() in ["FrogPilot-Development", "FrogPilot-Staging", "FrogPilot-Testing"]
self.jerk = 0
self.mtsc_target = 0
self.t_follow = 0
def update(self, carState, controlsState, frogpilotCarControl, frogpilotNavigation, liveLocationKalman, modelData, radarState):
@@ -71,9 +76,11 @@ class FrogPilotPlanner:
else:
self.max_accel = ACCEL_MAX
if self.deceleration_profile == 1:
v_cruise_changed = self.mtsc_target < v_cruise
if self.deceleration_profile == 1 and not v_cruise_changed:
self.min_accel = get_min_accel_eco(v_ego)
elif self.deceleration_profile == 2:
elif self.deceleration_profile == 2 and not v_cruise_changed:
self.min_accel = get_min_accel_sport(v_ego)
elif not controlsState.experimentalMode:
self.min_accel = A_CRUISE_MIN
@@ -128,7 +135,7 @@ class FrogPilotPlanner:
return jerk, t_follow
def update_v_cruise(self, carState, controlsState, enabled, liveLocationKalman, modelData, road_curvature, v_cruise, v_ego):
gps_check = liveLocationKalman.gpsOK and liveLocationKalman.inputsOK
gps_check = (liveLocationKalman.status == log.LiveLocationKalman.Status.valid) and liveLocationKalman.positionGeodetic.valid and liveLocationKalman.gpsOK
v_cruise_cluster = max(controlsState.vCruiseCluster, controlsState.vCruise) * CV.KPH_TO_MS
v_cruise_diff = v_cruise_cluster - v_cruise
@@ -136,7 +143,19 @@ class FrogPilotPlanner:
v_ego_cluster = max(carState.vEgoCluster, v_ego)
v_ego_diff = v_ego_cluster - v_ego
targets = []
# Pfeiferj's Map Turn Speed Controller
if self.map_turn_speed_controller and v_ego > CRUISING_SPEED and enabled and gps_check:
mtsc_active = self.mtsc_target < v_cruise
self.mtsc_target = np.clip(self.mtsc.target_speed(v_ego, carState.aEgo), CRUISING_SPEED, v_cruise)
if self.mtsc_curvature_check and road_curvature < 1.0 and not mtsc_active:
self.mtsc_target = v_cruise
if self.mtsc_target == CRUISING_SPEED:
self.mtsc_target = v_cruise
else:
self.mtsc_target = v_cruise
targets = [self.mtsc_target]
filtered_targets = [target if target > CRUISING_SPEED else v_cruise for target in targets]
return min(filtered_targets)
@@ -148,6 +167,7 @@ class FrogPilotPlanner:
frogpilotPlan.accelerationJerk = A_CHANGE_COST * (float(self.jerk) if self.lead_one.status else 1)
frogpilotPlan.accelerationJerkStock = A_CHANGE_COST
frogpilotPlan.adjustedCruise = float(self.mtsc_target * (CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH))
frogpilotPlan.conditionalExperimental = self.cem.experimental_mode
frogpilotPlan.desiredFollowDistance = self.safe_obstacle_distance - self.stopped_equivalence_factor
frogpilotPlan.egoJerk = J_EGO_COST * (float(self.jerk) if self.lead_one.status else 1)
@@ -194,3 +214,7 @@ class FrogPilotPlanner:
self.deceleration_profile = self.params.get_int("DecelerationProfile") if longitudinal_tune else 0
self.aggressive_acceleration = longitudinal_tune and self.params.get_bool("AggressiveAcceleration")
self.increased_stopping_distance = self.params.get_int("StoppingDistance") * (1 if self.is_metric else CV.FOOT_TO_METER) if longitudinal_tune else 0
self.map_turn_speed_controller = self.CP.openpilotLongitudinalControl and self.params.get_bool("MTSCEnabled")
self.mtsc_curvature_check = self.map_turn_speed_controller and self.params.get_bool("MTSCCurvatureCheck")
self.params_memory.put_float("MapTargetLatA", 2 * (self.params.get_int("MTSCAggressiveness") / 100))
@@ -0,0 +1,153 @@
# PFEIFER - MTSC
import json
import math
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
params_memory = Params("/dev/shm/params")
R = 6373000.0 # approximate radius of earth in meters
TO_RADIANS = math.pi / 180
TO_DEGREES = 180 / math.pi
TARGET_JERK = -0.6 # m/s^3 should match up with the long planner
TARGET_ACCEL = -1.2 # m/s^2 should match up with the long planner
TARGET_OFFSET = 1.0 # seconds - This controls how soon before the curve you reach the target velocity. It also helps
# reach the target velocity when innacuracies in the distance modeling logic would cause overshoot.
# The value is multiplied against the target velocity to determine the additional distance. This is
# done to keep the distance calculations consistent but results in the offset actually being less
# time than specified depending on how much of a speed diffrential there is between v_ego and the
# target velocity.
def calculate_accel(t, target_jerk, a_ego):
return a_ego + target_jerk * t
def calculate_velocity(t, target_jerk, a_ego, v_ego):
return v_ego + a_ego * t + target_jerk/2 * (t ** 2)
def calculate_distance(t, target_jerk, a_ego, v_ego):
return t * v_ego + a_ego/2 * (t ** 2) + target_jerk/6 * (t ** 3)
# points should be in radians
# output is meters
def distance_to_point(ax, ay, bx, by):
a = math.sin((bx-ax)/2)*math.sin((bx-ax)/2) + math.cos(ax) * math.cos(bx)*math.sin((by-ay)/2)*math.sin((by-ay)/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return R * c # in meters
class MapTurnSpeedController:
def __init__(self):
self.target_lat = 0.0
self.target_lon = 0.0
self.target_v = 0.0
def target_speed(self, v_ego, a_ego) -> float:
lat = 0.0
lon = 0.0
try:
position = json.loads(params_memory.get("LastGPSPosition"))
lat = position["latitude"]
lon = position["longitude"]
except: return 0.0
try:
target_velocities = json.loads(params_memory.get("MapTargetVelocities"))
except: return 0.0
min_dist = 1000
min_idx = 0
distances = []
# find our location in the path
for i in range(len(target_velocities)):
target_velocity = target_velocities[i]
tlat = target_velocity["latitude"]
tlon = target_velocity["longitude"]
d = distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, tlat * TO_RADIANS, tlon * TO_RADIANS)
distances.append(d)
if d < min_dist:
min_dist = d
min_idx = i
# only look at values from our current position forward
forward_points = target_velocities[min_idx:]
forward_distances = distances[min_idx:]
# find velocities that we are within the distance we need to adjust for
valid_velocities = []
for i in range(len(forward_points)):
target_velocity = forward_points[i]
tlat = target_velocity["latitude"]
tlon = target_velocity["longitude"]
tv = target_velocity["velocity"]
if tv > v_ego:
continue
d = forward_distances[i]
a_diff = (a_ego - TARGET_ACCEL)
accel_t = abs(a_diff / TARGET_JERK)
min_accel_v = calculate_velocity(accel_t, TARGET_JERK, a_ego, v_ego)
max_d = 0
if tv > min_accel_v:
# calculate time needed based on target jerk
a = 0.5 * TARGET_JERK
b = a_ego
c = v_ego - tv
t_a = -1 * ((b**2 - 4 * a * c) ** 0.5 + b) / 2 * a
t_b = ((b**2 - 4 * a * c) ** 0.5 - b) / 2 * a
if not isinstance(t_a, complex) and t_a > 0:
t = t_a
else:
t = t_b
if isinstance(t, complex):
continue
max_d = max_d + calculate_distance(t, TARGET_JERK, a_ego, v_ego)
else:
t = accel_t
max_d = calculate_distance(t, TARGET_JERK, a_ego, v_ego)
# calculate additional time needed based on target accel
t = abs((min_accel_v - tv) / TARGET_ACCEL)
max_d += calculate_distance(t, 0, TARGET_ACCEL, min_accel_v)
if d < max_d + tv * TARGET_OFFSET:
valid_velocities.append((float(tv), tlat, tlon))
# Find the smallest velocity we need to adjust for
min_v = 100.0
target_lat = 0.0
target_lon = 0.0
for tv, lat, lon in valid_velocities:
if tv < min_v:
min_v = tv
target_lat = lat
target_lon = lon
if self.target_v < min_v and not (self.target_lat == 0 and self.target_lon == 0):
for i in range(len(forward_points)):
target_velocity = forward_points[i]
tlat = target_velocity["latitude"]
tlon = target_velocity["longitude"]
tv = target_velocity["velocity"]
if tv > v_ego:
continue
if tlat == self.target_lat and tlon == self.target_lon and tv == self.target_v:
return float(self.target_v)
# not found so lets reset
self.target_v = 0.0
self.target_lat = 0.0
self.target_lon = 0.0
self.target_v = min_v
self.target_lat = target_lat
self.target_lon = target_lon
return min_v
@@ -43,6 +43,11 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil
{"AggressiveAcceleration", tr("Increase Acceleration Behind Faster Lead"), tr("Increase aggressiveness when following a faster lead."), ""},
{"StoppingDistance", tr("Increase Stop Distance Behind Lead"), tr("Increase the stopping distance for a more comfortable stop from lead vehicles."), ""},
{"MTSCEnabled", tr("Map Turn Speed Control"), tr("Slow down for anticipated curves detected by the downloaded maps."), "../frogpilot/assets/toggle_icons/icon_speed_map.png"},
{"DisableMTSCSmoothing", tr("Disable MTSC UI Smoothing"), tr("Disables the smoothing for the requested speed in the onroad UI to show exactly what speed MTSC is currently requesting."), ""},
{"MTSCCurvatureCheck", tr("Model Curvature Detection Failsafe"), tr("Only trigger MTSC when the model detects a curve in the road. Purely used as a failsafe to prevent false positives. Leave this off if you never experience false positives."), ""},
{"MTSCAggressiveness", tr("Turn Speed Aggressiveness"), tr("Set turn speed aggressiveness. Higher values result in faster turns, lower values yield gentler turns.\n\nA change of +- 1% results in the speed being raised or lowered by about 1 mph."), ""},
{"QOLControls", tr("Quality of Life"), tr("Miscellaneous quality of life changes to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"},
{"CustomCruise", tr("Cruise Increase Interval"), tr("Set a custom interval to increase the max set speed by."), ""},
{"CustomCruiseLong", tr("Cruise Increase Interval (Long Press)"), tr("Set a custom interval to increase the max set speed by when holding down the cruise increase button."), ""},
@@ -226,6 +231,18 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil
} else if (param == "StoppingDistance") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, std::map<int, QString>(), this, false, tr(" feet"));
} else if (param == "MTSCEnabled") {
FrogPilotParamManageControl *mtscToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(mtscToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
openParentToggle();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(mtscKeys.find(key.c_str()) != mtscKeys.end());
}
});
toggle = mtscToggle;
} else if (param == "MTSCAggressiveness") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 200, std::map<int, QString>(), this, false, "%");
} else if (param == "QOLControls") {
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
@@ -427,7 +444,7 @@ void FrogPilotControlsPanel::hideToggles() {
relaxedProfile->setVisible(false);
std::set<QString> longitudinalKeys = {"ConditionalExperimental", "CustomPersonalities", "ExperimentalModeActivation",
"LongitudinalTune"};
"LongitudinalTune", "MTSCEnabled"};
for (auto &[key, toggle] : toggles) {
toggle->setVisible(false);
@@ -38,7 +38,7 @@ private:
std::set<QString> laneChangeKeys = {};
std::set<QString> lateralTuneKeys = {"ForceAutoTune"};
std::set<QString> longitudinalTuneKeys = {"AccelerationProfile", "AggressiveAcceleration", "DecelerationProfile", "StoppingDistance"};
std::set<QString> mtscKeys = {};
std::set<QString> mtscKeys = {"DisableMTSCSmoothing", "MTSCAggressiveness", "MTSCCurvatureCheck"};
std::set<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "DisableOnroadUploads", "ReverseCruise"};
std::set<QString> speedLimitControllerKeys = {};
std::set<QString> speedLimitControllerControlsKeys = {};
+15 -2
View File
@@ -507,7 +507,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
QString speedLimitStr = (speedLimit > 1) ? QString::number(std::nearbyint(speedLimit)) : "";
QString speedStr = QString::number(std::nearbyint(speed));
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "";
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed - cruiseAdjustment)) : "";
// Draw outer box + border to contain set speed and speed limit
const int sign_margin = 12;
@@ -526,7 +526,17 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
int bottom_radius = has_eu_speed_limit ? 100 : 32;
QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size);
if (scene.reverse_cruise) {
if (is_cruise_set && cruiseAdjustment != 0) {
float transition = qBound(0.0f, 4.0f * (cruiseAdjustment / setSpeed), 1.0f);
QColor min = whiteColor(75);
QColor max = greenColor();
p.setPen(QPen(QColor::fromRgbF(
min.redF() + transition * (max.redF() - min.redF()),
min.greenF() + transition * (max.greenF() - min.greenF()),
min.blueF() + transition * (max.blueF() - min.blueF())
), 10));
} else if (scene.reverse_cruise) {
p.setPen(QPen(blueColor(), 6));
} else {
p.setPen(QPen(whiteColor(75), 6));
@@ -1121,6 +1131,9 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
conditionalStatus = scene.conditional_status;
showConditionalExperimentalStatusBar = scene.show_cem_status_bar;
bool disableSmoothing = scene.disable_smoothing_mtsc;
cruiseAdjustment = disableSmoothing || !is_cruise_set ? fmax(setSpeed - scene.adjusted_cruise, 0) : fmax(0.25 * (setSpeed - scene.adjusted_cruise) + 0.75 * cruiseAdjustment - 1, 0);
customColors = scene.custom_colors;
experimentalMode = scene.experimental_mode;
+2
View File
@@ -135,6 +135,7 @@ private:
bool turnSignalLeft;
bool turnSignalRight;
float cruiseAdjustment;
float distanceConversion;
float laneWidthLeft;
float laneWidthRight;
@@ -162,6 +163,7 @@ private:
QTimer *animationTimer;
inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); }
inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); }
protected:
void paintGL() override;
+2
View File
@@ -247,6 +247,7 @@ static void update_state(UIState *s) {
auto frogpilotPlan = sm["frogpilotPlan"].getFrogpilotPlan();
scene.acceleration_jerk = frogpilotPlan.getAccelerationJerk();
scene.acceleration_jerk_difference = frogpilotPlan.getAccelerationJerkStock() - scene.acceleration_jerk;
scene.adjusted_cruise = frogpilotPlan.getAdjustedCruise();
scene.desired_follow = frogpilotPlan.getDesiredFollowDistance();
scene.ego_jerk = frogpilotPlan.getEgoJerk();
scene.ego_jerk_difference = frogpilotPlan.getEgoJerkStock() - scene.ego_jerk;
@@ -319,6 +320,7 @@ void ui_update_frogpilot_params(UIState *s) {
scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0;
scene.holiday_themes = custom_theme && params.getBool("HolidayThemes");
scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing");
scene.experimental_mode_via_screen = scene.longitudinal_control && params.getBool("ExperimentalModeActivation") && params.getBool("ExperimentalModeViaTap");
scene.model_ui = params.getBool("ModelUI");
+2
View File
@@ -180,6 +180,7 @@ typedef struct UIScene {
bool blind_spot_path;
bool blind_spot_right;
bool conditional_experimental;
bool disable_smoothing_mtsc;
bool driver_camera;
bool dynamic_path_width;
bool enabled;
@@ -212,6 +213,7 @@ typedef struct UIScene {
float acceleration;
float acceleration_jerk;
float acceleration_jerk_difference;
float adjusted_cruise;
float ego_jerk;
float ego_jerk_difference;
float friction;