diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 8c4d7b2a171848..8fa3905d85482d 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -38,6 +38,7 @@ struct FrogPilotPlan @0x80ae746ee2596b11 { slcSpeedLimit @10 :Float64; slcSpeedLimitOffset @11 :Float32; stoppedEquivalenceFactor @12 :Int16; + vtscControllingCurve @13 :Bool; } struct CustomReserved5 @0xa5cd762cd951a455 { diff --git a/common/params.cc b/common/params.cc index 74e013901238a3..caa7967b134f26 100644 --- a/common/params.cc +++ b/common/params.cc @@ -241,6 +241,7 @@ std::unordered_map keys = { {"Compass", PERSISTENT}, {"ConditionalExperimental", PERSISTENT}, {"CurrentRandomEvent", PERSISTENT}, + {"CurveSensitivity", PERSISTENT}, {"CustomColors", PERSISTENT}, {"CustomIcons", PERSISTENT}, {"CustomPersonalities", PERSISTENT}, @@ -363,6 +364,7 @@ std::unordered_map keys = { {"StoppingDistance", PERSISTENT}, {"TetheringEnabled", PERSISTENT}, {"TSS2Tune", PERSISTENT}, + {"TurnAggressiveness", PERSISTENT}, {"TurnDesires", PERSISTENT}, {"UnlimitedLength", PERSISTENT}, {"Updated", PERSISTENT}, @@ -370,6 +372,7 @@ std::unordered_map keys = { {"UpdateTime", PERSISTENT}, {"UseSI", PERSISTENT}, {"UseVienna", PERSISTENT}, + {"VisionTurnControl", PERSISTENT}, {"WheelIcon", PERSISTENT}, }; diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_vtc.png b/selfdrive/frogpilot/assets/toggle_icons/icon_vtc.png new file mode 100644 index 00000000000000..8218b456ccd690 Binary files /dev/null and b/selfdrive/frogpilot/assets/toggle_icons/icon_vtc.png differ diff --git a/selfdrive/frogpilot/functions/frogpilot_planner.py b/selfdrive/frogpilot/functions/frogpilot_planner.py index 4f719f99bba05a..9583d2aae849ba 100644 --- a/selfdrive/frogpilot/functions/frogpilot_planner.py +++ b/selfdrive/frogpilot/functions/frogpilot_planner.py @@ -12,6 +12,10 @@ from openpilot.selfdrive.frogpilot.functions.map_turn_speed_controller import MapTurnSpeedController from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController +# VTSC variables +MIN_TARGET_V = 5 # m/s +TARGET_LAT_A = 1.9 # m/s^2 + class FrogPilotPlanner: def __init__(self, params, params_memory): self.cem = ConditionalExperimentalMode() @@ -23,6 +27,7 @@ def __init__(self, params, params_memory): self.mtsc_target = 0 self.slc_target = 0 self.v_cruise = 0 + self.vtsc_target = 0 self.accel_limits = [A_CRUISE_MIN, get_max_accel(0)] @@ -34,7 +39,7 @@ def update(self, carState, controlsState, modelData, mpc, sm, v_cruise, v_ego): road_curvature = FrogPilotFunctions.road_curvature(modelData, v_ego) # Acceleration profiles - v_cruise_changed = (self.mtsc_target) + 1 < v_cruise # Use stock acceleration profiles to handle MTSC more precisely + v_cruise_changed = (self.mtsc_target or self.vtsc_target) + 1 < v_cruise # Use stock acceleration profiles to handle MTSC/VTSC more precisely if v_cruise_changed: self.accel_limits = [A_CRUISE_MIN, get_max_accel(v_ego)] elif self.acceleration_profile == 1: @@ -48,10 +53,11 @@ def update(self, carState, controlsState, modelData, mpc, sm, v_cruise, v_ego): if self.conditional_experimental_mode and enabled: self.cem.update(carState, sm['frogpilotNavigation'], modelData, mpc, sm['radarState'], road_curvature, carState.standstill, v_ego) - if enabled: + if v_ego > MIN_TARGET_V: self.v_cruise = self.update_v_cruise(carState, controlsState, modelData, enabled, v_cruise, v_ego) else: self.mtsc_target = v_cruise + self.vtsc_target = v_cruise self.v_cruise = v_cruise # Lane detection @@ -101,15 +107,36 @@ def update_v_cruise(self, carState, controlsState, modelData, enabled, v_cruise, self.overriden_speed = 0 self.slc_target = v_cruise + # Pfeiferj's Vision Turn Controller + if self.vision_turn_controller: + # Set the curve sensitivity + orientation_rate = np.array(np.abs(modelData.orientationRate.z)) * self.curve_sensitivity + velocity = np.array(modelData.velocity.x) + + # Get the maximum lat accel from the model + max_pred_lat_acc = np.amax(orientation_rate * velocity) + + # Get the maximum curve based on the current velocity + max_curve = max_pred_lat_acc / (v_ego**2) + + # Set the target lateral acceleration + adjusted_target_lat_a = TARGET_LAT_A * self.turn_aggressiveness + + # Get the target velocity for the maximum curve + self.vtsc_target = (adjusted_target_lat_a / max_curve)**0.5 + self.vtsc_target = np.clip(self.vtsc_target, MIN_TARGET_V, v_cruise) + else: + self.vtsc_target = v_cruise + v_ego_diff = max(carState.vEgoRaw - carState.vEgoCluster, 0) - return min(v_cruise, self.mtsc_target, self.slc_target) - v_ego_diff + return min(v_cruise, self.mtsc_target, self.slc_target, self.vtsc_target) - v_ego_diff def publish(self, sm, pm, mpc): frogpilot_plan_send = messaging.new_message('frogpilotPlan') frogpilot_plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState']) frogpilotPlan = frogpilot_plan_send.frogpilotPlan - frogpilotPlan.adjustedCruise = self.mtsc_target * (CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH) + frogpilotPlan.adjustedCruise = float(min(self.mtsc_target, self.vtsc_target) * (CV.MS_TO_KPH if self.is_metric else CV.MS_TO_MPH)) frogpilotPlan.conditionalExperimental = self.cem.experimental_mode frogpilotPlan.desiredFollowDistance = mpc.safe_obstacle_distance - mpc.stopped_equivalence_factor @@ -127,6 +154,8 @@ def publish(self, sm, pm, mpc): frogpilotPlan.slcSpeedLimit = float(self.slc_target) frogpilotPlan.slcSpeedLimitOffset = SpeedLimitController.offset + frogpilotPlan.vtscControllingCurve = bool(self.mtsc_target > self.vtsc_target) + pm.send('frogpilotPlan', frogpilot_plan_send) def update_frogpilot_params(self, params, params_memory): @@ -165,3 +194,8 @@ def update_frogpilot_params(self, params, params_memory): if self.speed_limit_controller: self.speed_limit_controller_override = params.get_int("SLCOverride") SpeedLimitController.update_frogpilot_params() + + self.vision_turn_controller = params.get_bool("VisionTurnControl") + if self.vision_turn_controller: + self.curve_sensitivity = params.get_int("CurveSensitivity") / 100 + self.turn_aggressiveness = params.get_int("TurnAggressiveness") / 100 diff --git a/selfdrive/frogpilot/ui/control_settings.cc b/selfdrive/frogpilot/ui/control_settings.cc index f2d5af1b8f2a21..d6d17e580c3eb8 100644 --- a/selfdrive/frogpilot/ui/control_settings.cc +++ b/selfdrive/frogpilot/ui/control_settings.cc @@ -62,6 +62,10 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil {"SLCOverride", "Override Method", "Choose your preferred method to override the current speed limit.", ""}, {"TurnDesires", "Use Turn Desires", "Use turn desires for enhanced precision in turns below the minimum lane change speed.", "../assets/navigation/direction_continue_right.png"}, + + {"VisionTurnControl", "Vision Turn Speed Controller", "Slow down for detected road curvature for smoother curve handling.", "../frogpilot/assets/toggle_icons/icon_vtc.png"}, + {"CurveSensitivity", "Curve Detection Sensitivity", "Set curve detection sensitivity. Higher values prompt earlier responses, lower values lead to smoother but later reactions.", ""}, + {"TurnAggressiveness", "Turn Speed Aggressiveness", "Set turn speed aggressiveness. Higher values result in faster turns, lower values yield gentler turns.", ""}, }; for (const auto &[param, title, desc, icon] : controlToggles) { @@ -368,6 +372,18 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil slscPriorityButton->setValue(initialPriorities.join(", ")); addItem(slscPriorityButton); + } else if (param == "VisionTurnControl") { + FrogPilotParamManageControl *visionTurnControlToggle = new FrogPilotParamManageControl(param, title, desc, icon, this); + QObject::connect(visionTurnControlToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() { + parentToggleClicked(); + for (auto &[key, toggle] : toggles) { + toggle->setVisible(visionTurnControlKeys.find(key.c_str()) != visionTurnControlKeys.end()); + } + }); + toggle = visionTurnControlToggle; + } else if (param == "CurveSensitivity" || param == "TurnAggressiveness") { + toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 200, std::map(), this, false, "%"); + } else { toggle = new ParamControl(param, title, desc, icon, this); } @@ -409,7 +425,7 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil mtscKeys = {"MTSCAggressiveness"}; qolKeys = {"DisableOnroadUploads", "HigherBitrate", "NavChill", "PauseLateralOnSignal", "ReverseCruise", "SetSpeedOffset"}; speedLimitControllerKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "SLCFallback", "SLCOverride", "SLCPriority"}; - visionTurnControlKeys = {}; + visionTurnControlKeys = {"CurveSensitivity", "TurnAggressiveness"}; QObject::connect(uiState(), &UIState::offroadTransition, this, [this](bool offroad) { if (!offroad) { diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 01039d6798fcb7..7f6c9184099b22 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -577,7 +577,8 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size); if (is_cruise_set && cruiseAdjustment) { float transition = qBound(0.0f, 4.0f * (cruiseAdjustment / setSpeed), 1.0f); - QColor min = whiteColor(75), max = greenColor(75); + QColor min = whiteColor(75); + QColor max = scene.vtsc_controlling_curve ? redColor(75) : greenColor(75); p.setPen(QPen(QColor::fromRgbF( min.redF() + transition * (max.redF() - min.redF()), diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 77c32e1458f7ae..7c702e12633c8d 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -256,6 +256,7 @@ static void update_state(UIState *s) { scene.speed_limit_overridden_speed = frogpilotPlan.getSlcOverriddenSpeed(); } scene.adjusted_cruise = frogpilotPlan.getAdjustedCruise(); + scene.vtsc_controlling_curve = frogpilotPlan.getVtscControllingCurve(); } if (sm.updated("liveLocationKalman")) { auto liveLocationKalman = sm["liveLocationKalman"].getLiveLocationKalman(); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index f81469bac3166f..7a95e3942e4732 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -215,6 +215,7 @@ typedef struct UIScene { bool unlimited_road_ui_length; bool use_si; bool use_vienna_slc_sign; + bool vtsc_controlling_curve; float adjusted_cruise; float lane_line_width; float lane_width_left;