Visuals - Custom Themes - Random Events

Enjoy a bit of unpredictability with random events that can occur during certain driving conditions. This is purely cosmetic and has no impact on driving controls!
This commit is contained in:
FrogAi
2024-07-07 05:26:08 -07:00
parent a66ba24fed
commit 322eab5173
23 changed files with 252 additions and 11 deletions
+82 -4
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import os
import math
import random
import time
import threading
from typing import SupportsFloat
@@ -21,7 +22,7 @@ from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.car.car_helpers import get_car_interface, get_startup_event
from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offroad_alert
from openpilot.selfdrive.controls.lib.drive_helpers import VCruiseHelper, clip_curvature
from openpilot.selfdrive.controls.lib.drive_helpers import IMPERIAL_INCREMENT, VCruiseHelper, clip_curvature
from openpilot.selfdrive.controls.lib.events import Events, ET
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
@@ -194,10 +195,14 @@ class Controls:
self.always_on_lateral_active = False
self.drive_added = False
self.fcw_random_event_triggered = False
self.holiday_theme_alerted = False
self.no_entry_alert_played = False
self.no_entry_alert_triggered = False
self.onroad_distance_pressed = False
self.openpilot_crashed_triggered = False
self.previous_traffic_mode = False
self.random_event_triggered = False
self.resume_pressed = False
self.resume_previously_pressed = False
self.speed_check = False
@@ -208,7 +213,10 @@ class Controls:
self.display_timer = 0
self.drive_distance = 0
self.drive_time = 0
self.max_acceleration = 0
self.previous_speed_limit = 0
self.previous_v_cruise = 0
self.random_event_timer = 0
self.speed_limit_timer = 0
def set_initial_state(self):
@@ -411,6 +419,11 @@ class Controls:
planner_fcw = self.sm['longitudinalPlan'].fcw and self.enabled
if (planner_fcw or model_fcw) and not (self.CP.notCar and self.joystick_mode):
self.events.add(EventName.fcw)
self.fcw_random_event_triggered = not self.random_event_triggered
elif self.fcw_random_event_triggered and self.frogpilot_toggles.random_events:
self.events.add(EventName.yourFrogTriedToKillMe)
self.fcw_random_event_triggered = False
self.random_event_triggered = True
for m in messaging.drain_sock(self.log_sock, wait_for_one=False):
try:
@@ -553,6 +566,7 @@ class Controls:
if self.events.contains(ET.ENABLE):
if self.events.contains(ET.NO_ENTRY):
self.current_alert_types.append(ET.NO_ENTRY)
self.no_entry_alert_triggered = True
else:
if self.events.contains(ET.PRE_ENABLE):
@@ -665,8 +679,18 @@ class Controls:
turning = abs(lac_log.desiredLateralAccel) > 1.0
good_speed = CS.vEgo > 5
max_torque = abs(self.sm['carOutput'].actuatorsOutput.steer) > 0.99
if undershooting and turning and good_speed and max_torque:
lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream else EventName.steerSaturated)
if undershooting and turning and good_speed and max_torque and not self.random_event_triggered:
event_choices = [1, 2]
if self.sm.frame % (10000 // len(event_choices)) == 0 and self.frogpilot_toggles.random_events:
event_choice = random.choice(event_choices)
if event_choice == 1:
lac_log.active and self.events.add(EventName.firefoxSteerSaturated)
self.params_memory.put_int("CurrentRandomEvent", 1)
elif event_choice == 2:
lac_log.active and self.events.add(EventName.goatSteerSaturated)
self.random_event_triggered = True
else:
lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream else EventName.steerSaturated)
elif lac_log.saturated:
# TODO probably should not use dpath_points but curvature
dpath_points = model_v2.position.y
@@ -933,9 +957,56 @@ class Controls:
self.events.add(EventName.leadDeparting)
if not self.openpilot_crashed_triggered and os.path.isfile(os.path.join(sentry.CRASHES_DIR, 'error.txt')):
self.events.add(EventName.openpilotCrashed)
if self.frogpilot_toggles.random_events:
self.events.add(EventName.openpilotCrashedRandomEvent)
else:
self.events.add(EventName.openpilotCrashed)
self.openpilot_crashed_triggered = True
if self.frogpilot_toggles.random_events and not self.random_event_triggered:
acceleration = CS.aEgo
if not CS.gasPressed:
self.max_acceleration = max(acceleration, self.max_acceleration)
else:
self.max_acceleration = 0
if 3.5 > self.max_acceleration >= 3.0 and acceleration < 1.5:
self.events.add(EventName.accel30)
self.params_memory.put_int("CurrentRandomEvent", 2)
self.random_event_triggered = True
self.max_acceleration = 0
elif 4.0 > self.max_acceleration >= 3.5 and acceleration < 1.5:
self.events.add(EventName.accel35)
self.params_memory.put_int("CurrentRandomEvent", 3)
self.random_event_triggered = True
self.max_acceleration = 0
elif self.max_acceleration >= 4.0 and acceleration < 1.5:
self.events.add(EventName.accel40)
self.params_memory.put_int("CurrentRandomEvent", 4)
self.random_event_triggered = True
self.max_acceleration = 0
if frogpilotPlan.takingCurveQuickly:
self.events.add(EventName.dejaVuCurve)
self.params_memory.put_int("CurrentRandomEvent", 5)
self.random_event_triggered = True
if self.no_entry_alert_triggered and not self.no_entry_alert_played:
self.events.add(EventName.hal9000)
self.no_entry_alert_played = True
self.random_event_triggered = True
conversion = 1 if self.is_metric else IMPERIAL_INCREMENT
v_cruise = max(self.v_cruise_helper.v_cruise_kph, self.v_cruise_helper.v_cruise_cluster_kph) * conversion
if 70 > v_cruise >= 69 and v_cruise != self.previous_v_cruise:
self.events.add(EventName.vCruise69)
self.random_event_triggered = True
self.previous_v_cruise = v_cruise
if self.frogpilot_toggles.speed_limit_alert and self.speed_limit_changed:
self.events.add(EventName.speedLimitChanged)
@@ -994,6 +1065,13 @@ class Controls:
self.experimental_mode = not self.experimental_mode
self.params.put_bool_nonblocking("ExperimentalMode", self.experimental_mode)
if self.random_event_triggered:
self.random_event_timer += DT_CTRL
if self.random_event_timer >= 4:
self.random_event_triggered = False
self.random_event_timer = 0
self.params_memory.remove("CurrentRandomEvent")
if self.sm.frame % 10 == 0 or self.resume_pressed:
self.resume_previously_pressed = self.resume_pressed
+1 -1
View File
@@ -14,7 +14,7 @@ V_CRUISE_MAX = 145
V_CRUISE_UNSET = 255
V_CRUISE_INITIAL = 40
V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 105
IMPERIAL_INCREMENT = 1.6 # should be CV.MPH_TO_KPH, but this causes rounding errors
IMPERIAL_INCREMENT = round(CV.MPH_TO_KPH, 1) # round here to avoid rounding errors incrementing set speed
MIN_SPEED = 1.0
CONTROL_N = 17
+73
View File
@@ -1117,6 +1117,79 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
AlertStatus.normal, AlertSize.small,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1, alert_rate=0.75),
},
# Random Events
EventName.accel30: {
ET.WARNING: Alert(
"UwU u went a bit fast there!",
"( ⁄•⁄ω⁄•⁄ )",
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.uwu, 4.),
},
EventName.accel35: {
ET.WARNING: Alert(
"I ain't giving you no tree-fiddy",
"You damn Loch Ness Monsta!",
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.nessie, 4.),
},
EventName.accel40: {
ET.WARNING: Alert(
"Great Scott!",
"🚗💨",
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.doc, 4.),
},
EventName.dejaVuCurve: {
ET.WARNING: Alert(
"♬♪ Deja vu! ᕕ(⌐■_■)ᕗ ♪♬",
"🏎️",
AlertStatus.frogpilot, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.dejaVu, 4.),
},
EventName.firefoxSteerSaturated: {
ET.WARNING: Alert(
"Turn Exceeds Steering Limit",
"IE Has Stopped Responding...",
AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.firefox, 4.),
},
EventName.hal9000: {
ET.WARNING: Alert(
"I'm sorry Dave",
"I'm afraid I can't do that...",
AlertStatus.normal, AlertSize.mid,
Priority.HIGH, VisualAlert.none, AudibleAlert.hal9000, 4.),
},
EventName.openpilotCrashedRandomEvent: {
ET.PERMANENT: Alert(
"openpilot crashed 💩",
"Please post the 'Error Log' in the FrogPilot Discord!",
AlertStatus.normal, AlertSize.mid,
Priority.HIGHEST, VisualAlert.none, AudibleAlert.fart, 10.),
},
EventName.vCruise69: {
ET.PERMANENT: Alert(
"Lol 69",
"",
AlertStatus.frogpilot, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.noice, 2.),
},
EventName.yourFrogTriedToKillMe: {
ET.PERMANENT: Alert(
"Your Frog tried to kill me...",
"👺",
AlertStatus.frogpilot, AlertSize.mid,
Priority.MID, VisualAlert.none, AudibleAlert.angry, 5.),
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 940 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

@@ -56,6 +56,7 @@ class FrogPilotPlanner:
self.override_force_stop = False
self.override_slc = False
self.slower_lead = False
self.taking_curve_quickly = False
self.tracking_lead = False
self.acceleration_jerk = 0
@@ -124,6 +125,9 @@ class FrogPilotPlanner:
self.override_force_stop |= frogpilotCarControl.resumePressed
self.road_curvature = calculate_road_curvature(modelData, v_ego) if not carState.standstill and driving_gear else 1
if frogpilot_toggles.random_events and v_ego > CRUISING_SPEED and driving_gear:
self.taking_curve_quickly = v_ego > (1 / self.road_curvature)**0.5 * 2 > CRUISING_SPEED * 2 and abs(carState.steeringAngleDeg) > 30
self.set_acceleration(controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_toggles)
self.set_follow_values(controlsState, frogpilotCarState, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles)
self.set_lead_status(lead_distance, stopping_distance, v_ego)
@@ -347,6 +351,8 @@ class FrogPilotPlanner:
frogpilotPlan.slcSpeedLimitOffset = SpeedLimitController.offset
frogpilotPlan.unconfirmedSlcSpeedLimit = SpeedLimitController.desired_speed_limit
frogpilotPlan.takingCurveQuickly = self.taking_curve_quickly
frogpilotPlan.vCruise = self.v_cruise
pm.send('frogpilotPlan', frogpilot_plan_send)
+44 -2
View File
@@ -19,6 +19,18 @@ void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrus
p.restore();
}
void drawIconGif(QPainter &p, const QPoint &center, const QMovie &img, const QBrush &bg, float opacity) {
p.setRenderHint(QPainter::Antialiasing);
p.setOpacity(1.0); // bg dictates opacity of ellipse
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center.x() - btn_size / 2, center.y() - btn_size / 2, btn_size, btn_size);
p.setOpacity(opacity);
QPixmap currentFrame = img.currentPixmap();
p.drawPixmap(center - QPoint(currentFrame.width() / 2, currentFrame.height() / 2), currentFrame);
p.setOpacity(1.0);
}
// ExperimentalButton
ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) {
setFixedSize(btn_size, btn_size);
@@ -36,6 +48,13 @@ ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(fals
{5, loadPixmap("../frogpilot/assets/wheel_images/hyundai.png", {img_size, img_size})},
{6, loadPixmap("../frogpilot/assets/wheel_images/stalin.png", {img_size, img_size})},
};
wheelImagesGif = {
{0, new QMovie("../frogpilot/assets/random_events/images/firefox.gif", QByteArray(), this)},
{1, new QMovie("../frogpilot/assets/random_events/images/weeb_wheel.gif", QByteArray(), this)},
{2, new QMovie("../frogpilot/assets/random_events/images/tree_fiddy.gif", QByteArray(), this)},
{3, new QMovie("../frogpilot/assets/random_events/images/great_scott.gif", QByteArray(), this)},
};
}
void ExperimentalButton::changeMode() {
@@ -67,13 +86,31 @@ void ExperimentalButton::updateState(const UIState &s) {
conditionalExperimental = scene.conditional_experimental;
conditionalStatus = scene.conditional_status;
navigateOnOpenpilot = scene.navigate_on_openpilot;
randomEvent = scene.current_random_event;
rotatingWheel = scene.rotating_wheel;
trafficModeActive = scene.traffic_mode_active;
wheelIcon = scene.wheel_icon;
wheelIconGif = 0;
if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
if (randomEvent == 0 && gifLabel) {
delete gifLabel;
gifLabel = nullptr;
} else if (randomEvent == 1 || randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
gifLabel = new QLabel(this);
QMovie *movie = wheelImagesGif[randomEvent - 1];
if (movie) {
movie->setScaledSize(QSize(img_size, img_size));
gifLabel->setMovie(movie);
gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2);
gifLabel->movie()->start();
}
wheelIconGif = randomEvent - 1;
update();
} else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
steeringAngleDeg = scene.steering_angle_deg;
update();
} else if (!rotatingWheel) {
steeringAngleDeg = 0;
}
@@ -87,6 +124,7 @@ void ExperimentalButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
engage_img = wheelImages[wheelIcon];
QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
QMovie *gif = wheelImagesGif[wheelIconGif];
QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
(alwaysOnLateralActive ? bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE] :
@@ -96,7 +134,11 @@ void ExperimentalButton::paintEvent(QPaintEvent *event) {
(navigateOnOpenpilot ? bg_colors[STATUS_NAVIGATION_ACTIVE] : QColor(0, 0, 0, 166)))))) :
QColor(0, 0, 0, 166);
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, QColor(0, 0, 0, 166), (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
if (wheelIconGif != 0) {
drawIconGif(p, QPoint(btn_size / 2, btn_size / 2), *gif, background_color, 1.0);
} else {
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
}
}
// MapSettingsButton
+7
View File
@@ -1,5 +1,7 @@
#pragma once
#include <QLabel>
#include <QMovie>
#include <QPushButton>
#include "selfdrive/ui/ui.h"
@@ -32,10 +34,15 @@ private:
bool trafficModeActive;
int conditionalStatus;
int randomEvent;
int steeringAngleDeg;
int wheelIcon;
int wheelIconGif;
QLabel *gifLabel;
QMap<int, QPixmap> wheelImages;
QMap<int, QMovie*> wheelImagesGif;
Params paramsMemory{"/dev/shm/params"};
};
+35 -4
View File
@@ -41,6 +41,17 @@ sound_list: dict[int, tuple[str, int | None, float]] = {
AudibleAlert.warningSoft: ("warning_soft.wav", None, MAX_VOLUME),
AudibleAlert.warningImmediate: ("warning_immediate.wav", None, MAX_VOLUME),
# Random Events
AudibleAlert.angry: ("angry.wav", 1, MAX_VOLUME),
AudibleAlert.dejaVu: ("dejaVu.wav", 1, MAX_VOLUME),
AudibleAlert.doc: ("doc.wav", 1, MAX_VOLUME),
AudibleAlert.fart: ("fart.wav", 1, MAX_VOLUME),
AudibleAlert.firefox: ("firefox.wav", 1, MAX_VOLUME),
AudibleAlert.hal9000: ("hal9000.wav", 1, MAX_VOLUME),
AudibleAlert.nessie: ("nessie.wav", 1, MAX_VOLUME),
AudibleAlert.noice: ("noice.wav", 1, MAX_VOLUME),
AudibleAlert.uwu: ("uwu.wav", 1, MAX_VOLUME),
# Other
AudibleAlert.goat: ("goat.wav", None, MAX_VOLUME),
}
@@ -69,6 +80,20 @@ class Soundd:
self.frogpilot_toggles = FrogPilotVariables.toggles
self.previous_sound_directory = None
self.random_events_directory = BASEDIR + "/selfdrive/frogpilot/assets/random_events/sounds/"
self.random_events_map = {
AudibleAlert.angry: MAX_VOLUME,
AudibleAlert.dejaVu: MAX_VOLUME,
AudibleAlert.doc: MAX_VOLUME,
AudibleAlert.fart: MAX_VOLUME,
AudibleAlert.firefox: MAX_VOLUME,
AudibleAlert.goat: MAX_VOLUME,
AudibleAlert.hal9000: MAX_VOLUME,
AudibleAlert.nessie: MAX_VOLUME,
AudibleAlert.noice: MAX_VOLUME,
AudibleAlert.uwu: MAX_VOLUME,
}
self.update_toggles = False
@@ -81,12 +106,15 @@ class Soundd:
for sound in sound_list:
filename, play_count, volume = sound_list[sound]
try:
if sound in self.random_events_map:
wavefile = wave.open(self.random_events_directory + filename, 'r')
else:
if sound == AudibleAlert.goat and not self.frogpilot_toggles.goat_scream:
continue
wavefile = wave.open(self.sound_directory + filename, 'r')
except FileNotFoundError:
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
try:
wavefile = wave.open(self.sound_directory + filename, 'r')
except FileNotFoundError:
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
assert wavefile.getnchannels() == 1
assert wavefile.getsampwidth() == 2
@@ -172,6 +200,9 @@ class Soundd:
elif self.frogpilot_toggles.alert_volume_control and self.current_alert in self.volume_map:
self.current_volume = self.volume_map[self.current_alert] / 100.0
elif self.current_alert in self.random_events_map:
self.current_volume = self.random_events_map[self.current_alert]
self.get_audible_alert(sm)
rk.keep_time()
+2
View File
@@ -336,6 +336,7 @@ void ui_update_frogpilot_params(UIState *s, Params &params) {
scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0;
scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0;
scene.holiday_themes = custom_theme && params.getBool("HolidayThemes");
scene.random_events = custom_theme && params.getBool("RandomEvents");
scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing");
scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing");
@@ -461,6 +462,7 @@ void UIState::update() {
// FrogPilot variables that need to be constantly updated
scene.conditional_status = scene.conditional_experimental && scene.enabled ? paramsMemory.getInt("CEStatus") : 0;
scene.current_holiday_theme = scene.holiday_themes ? paramsMemory.getInt("CurrentHolidayTheme") : 0;
scene.current_random_event = scene.random_events ? paramsMemory.getInt("CurrentRandomEvent") : 0;
scene.started_timer = scene.started || started_prev ? scene.started_timer + 1 : 0;
}
+2
View File
@@ -146,6 +146,7 @@ typedef struct UIScene {
bool onroad_distance_button;
bool parked;
bool pedals_on_ui;
bool random_events;
bool red_light;
bool reverse;
bool reverse_cruise;
@@ -189,6 +190,7 @@ typedef struct UIScene {
int conditional_speed_lead;
int conditional_status;
int current_holiday_theme;
int current_random_event;
int custom_colors;
int custom_icons;
int custom_signals;