c3 and c3x aggressive cooling

This commit is contained in:
whoisdomi
2026-11-02 13:13:27 -06:00
parent ee5170f2f4
commit bcb4f33c27
8 changed files with 59 additions and 15 deletions
+1
View File
@@ -76,6 +76,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ForceStops", {PERSISTENT, BOOL, "0"}},
{"DriveModeProfiles", {PERSISTENT, BOOL, "0"}},
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
{"AggressiveCoolingEnabled", {PERSISTENT, BOOL, "0"}},
{"UsageStatsState", {PERSISTENT | DONT_LOG, JSON}},
{"UsageStatsStatus", {CLEAR_ON_MANAGER_START | DONT_LOG, JSON}},
{"TeslaAOLScreenTap", {PERSISTENT, BOOL, "0"}},
+3 -2
View File
@@ -41,7 +41,7 @@ from openpilot.starpilot.ui.vasm_owner import ANNOTATION as VASM_ANNOTATION, RES
PAGES = frozenset(("hub", "localization", "developer", "vehicle", "slc", "lane", "lane_change", "profiles", "personalities",
"traffic", "aggressive", "standard", "relaxed",
"curve", "torque", "torque/bluepilot", "highway_smoothing", "aol", "wheel", "appearance", "display", "data", "power", "sounds", "pip", "sentry", "vasm",
"curve", "torque", "torque/bluepilot", "highway_smoothing", "aol", "wheel", "appearance", "display", "data", "power", "cooling", "sounds", "pip", "sentry", "vasm",
"conditional", "conditional/cem", "conditional/ccm",
*(f"{name}/{category}" for name in ("traffic", "aggressive", "standard", "relaxed")
for category in ("acceleration", "braking", "following"))))
@@ -58,7 +58,7 @@ CAR_CONTROL_SECTIONS = (
("wheel", "Wheel Controls", ("wheel",)),
("visual", "Visual (Display & UI)", ("appearance", "pip")),
("sounds", "Sounds & Alerts", ("sounds",)),
("device", "Device & Data", ("display", "data")),
("device", "Device & Data", ("display", "data", "cooling")),
("localization", "Localization", ("localization",)),
("developer", "Developer", ("developer",)),
)
@@ -684,6 +684,7 @@ class SettingsGateway:
scope = hashlib.sha256(_session_key(token) + generation + (ctx.cp_raw or b"")).digest()
result = {"page": page, "title": state.title, "subtitle": state.subtitle, "parked": state.parked,
"vehicleIdentified": bool(ctx.cp is not None and getattr(ctx.cp, "carFingerprint", None)),
"coolingAvailable": HARDWARE.get_device_type() in ("tici", "tizi"),
"rows": [{**_projection(row), "revision": hashlib.sha256(scope + repr(row).encode()).hexdigest()}
for row in state.rows], "view": view_id}
if page == "hub" or any(row.page for row in state.rows):
@@ -194,7 +194,7 @@ for (const id of ["wheel", "sounds", "localization"]) {
}
const deviceData = SETTINGS_SECTIONS.find((item) => item.id === "device")
assert.deepEqual(deviceData.pages, ["power", "display", "data"])
assert.deepEqual(deviceData.pages, ["power", "display", "data", "cooling"])
assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceData,
state: { data: { page: "hub" } } }), true)
assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceData,
@@ -203,6 +203,9 @@ assert.equal(SettingsPage.computed.atSectionRoot.call({ activeSection: deviceDat
const dataLinks = SettingsPage.computed.visibleRows.call({ initialPage: "hub", activeSection: deviceData,
state: { query: "", data: { page: "hub", rows: [] } } })
assert.deepEqual(dataLinks.map(({ row }) => row.page), ["power", "display", "data"])
const coolingLinks = SettingsPage.computed.visibleRows.call({ initialPage: "hub", activeSection: deviceData,
state: { query: "", data: { page: "hub", rows: [], coolingAvailable: true } } })
assert.deepEqual(coolingLinks.map(({ row }) => row.page), ["power", "display", "data", "cooling"])
assert.equal(SettingsPage.computed.atSectionRoot.call({initialPage:'hub',state:{data:null,parents:[]}}),true,
'section tabs stay mounted while loading a new section')
@@ -22,7 +22,7 @@ export const SETTINGS_SECTIONS = Object.freeze([
{ id: "wheel", label: "Wheel Controls", icon: "bi-controller", pages: ["wheel"] },
{ id: "visual", label: "Visual (Display & UI)", icon: "bi-eye", pages: ["appearance", "ui_layout", "favorites", "pip"] },
{ id: "sounds", label: "Sounds & Alerts", icon: "bi-volume-up", pages: ["sounds"] },
{ id: "device", label: "Device & Data", icon: "bi-hdd", pages: ["power", "display", "data"] },
{ id: "device", label: "Device & Data", icon: "bi-hdd", pages: ["power", "display", "data", "cooling"] },
{ id: "localization", label: "Localization", icon: "bi-globe", pages: ["localization"] },
{ id: "developer", label: "Developer", icon: "bi-code-slash", pages: ["developer"] },
])
@@ -34,7 +34,8 @@ export const isPipCropRow = (row) => ["Visual camera crop editor", "Camera crop
/^Vehicle (left|right) crop/.test(row.label || "")
const SECTION_LINKS = {
device: [{ label: "Parked Power", page: "power", value: "Adjust shutdown hours and battery-voltage cutoff" }, { label: "Display", page: "display", value: "Adjust screen brightness and display timing" }, { label: "Data Uploads", page: "data", value: "Choose mobile-data uploads and usage-statistics sharing" }],
device: [{ label: "Parked Power", page: "power", value: "Adjust shutdown hours and battery-voltage cutoff" }, { label: "Display", page: "display", value: "Adjust screen brightness and display timing" }, { label: "Data Uploads", page: "data", value: "Choose mobile-data uploads and usage-statistics sharing" },
{ label: "Cooling", page: "cooling", value: "Choose the fan curve for comma 3/3X", requires: "coolingAvailable" }],
visual: [{ label: "Driving Screen Widgets, Colors & Layout", page: "ui_layout" }, { label: "Quick Select", page: "favorites" }, { label: "Blind Spot Camera and Preview", page: "pip" }],
}
@@ -293,6 +294,7 @@ export const SettingsPage = {
const section = this.activeSection
const entries = inHub && SECTION_LINKS[section.id]
? SECTION_LINKS[section.id].map((row, index) => ({ row: { ...row, available: true, action: false, value: row.value || "Customize " + row.label.toLocaleLowerCase() }, index }))
.filter(({ row }) => !row.requires || this.state.data?.[row.requires])
: rows.map((row, index) => ({ row, index })).filter(({ row }) => !inHub || section.pages.includes(row.page))
if (inHub && section.id === "longitudinal") {
entries.sort((a, b) => section.pages.indexOf(a.row.page) - section.pages.indexOf(b.row.page))
@@ -17,6 +17,7 @@ from openpilot.starpilot.lateral.pause import KEYS as LATERAL_PAUSE_KEYS
from openpilot.starpilot.lateral.lane_centering import STRENGTH_BASELINE, STRENGTH_MAX
from opendbc.car.structs import car
from openpilot.common.hardware import HARDWARE
from openpilot.common.params import UnknownKeyName
from openpilot.starpilot.longitudinal.accel_profile import A_CRUISE_MAX_VALS_TRAFFIC_ALL, interpolate_accel_profile
from openpilot.starpilot.longitudinal.profile_runtime import TRAFFIC_CRUISE_BRAKE_MAGNITUDE
@@ -74,7 +75,10 @@ from openpilot.starpilot.curve_speed.actions import ACTION_KEYS as CURVE_ACTION_
from openpilot.starpilot.controllers.toyota_cruise import capability as toyota_cruise_capability
GLOBAL_BOOL_PREFERENCES = frozenset((LEAD_APPROACH_KEY, LEAD_TAKEOFF_KEY, NUDGE_KEY, "DisengageOnAccelerator"))
AGGRESSIVE_COOLING_KEY = "AggressiveCoolingEnabled"
AGGRESSIVE_COOLING_DESCRIPTION = ("Runs the fan sooner and harder to keep the device 10-16 °C cooler on hot days, " +
"at the cost of more fan noise.")
GLOBAL_BOOL_PREFERENCES = frozenset((LEAD_APPROACH_KEY, LEAD_TAKEOFF_KEY, NUDGE_KEY, "DisengageOnAccelerator", AGGRESSIVE_COOLING_KEY))
BOOL_DEFAULTS = {
"GMPedalLongitudinal": False, "ForceStops": False, "AlwaysAllowUploads": False, "TurnAssist": True, "HighwaySmoothing": False,
@@ -94,6 +98,7 @@ BOOL_DEFAULTS = {
LEAD_TAKEOFF_KEY: False,
NUDGE_KEY: True,
"DisengageOnAccelerator": False,
AGGRESSIVE_COOLING_KEY: False,
CURVE_MASTER_KEY: False, CURVE_NO_LEAD_KEY: False,
"ShowCSCStatus": False,
"ForceAutoTuneOff": False, "AlwaysOnLateral": True, "NostalgiaMode": False,
@@ -893,6 +898,11 @@ class FeatureSettingsOwner:
title = "Data Uploads"
rows = [self._bool_row("AlwaysAllowUploads", "Always Allow Uploads", self.authority("preferences"),
"Allow queued full logs and videos on metered networks. Uses mobile data.")]
elif page == "cooling":
title = "Cooling"
if HARDWARE.get_device_type() in ("tici", "tizi"):
rows = [self._bool_row(AGGRESSIVE_COOLING_KEY, "Aggressive Cooling", self.authority("preferences"),
AGGRESSIVE_COOLING_DESCRIPTION)]
elif page == FeaturePage.HUB:
rows = [FeatureRow("", "Curve Speed Controller", "Adjust how speed is reduced for curves ahead", page=FeaturePage.CURVE, available=True),
FeatureRow("", "Speed Limit Controller", "Choose speed-limit sources, offsets, and confirmation behavior", page=FeaturePage.SLC, available=True),
+17 -2
View File
@@ -31,7 +31,7 @@ from openpilot.starpilot.drive_state.owner import DriveStateOwner, Rejected as D
from openpilot.starpilot.drive_state.evidence import PhysicalSource
from openpilot.starpilot.drive_state.control import DriveStateControl, LABELS
from openpilot.starpilot.storage import starpilot_storage_root
from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner
from openpilot.starpilot.ui.feature_settings_owner import AGGRESSIVE_COOLING_DESCRIPTION, AGGRESSIVE_COOLING_KEY, FeatureSettingsOwner
from openpilot.starpilot.ui.conditional_feature import confirmation_question as conditional_question
from openpilot.starpilot.ui.appearance_owner import AppearanceOwner
from openpilot.starpilot.ui.appearance_preferences import CameraViewChoice
@@ -94,7 +94,7 @@ from openpilot.starpilot.ui.software_state import DownloadLabel, SoftwareAction,
from openpilot.starpilot.ui.toggles_state import Personality, ToggleKey, ToggleRequest
from openpilot.system.ui.lib.application import MouseEvent, MousePos, gui_app
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.list_view import button_item, text_item
from openpilot.system.ui.widgets.list_view import button_item, text_item, toggle_item
from openpilot.starpilot.ui.compact_settings_page import StarCompactSettings
NATIVE_SETTINGS_PANELS = (Destination.DEVICE, Destination.SOFTWARE, Destination.BLUETOOTH, Destination.TOGGLES, Destination.DEVELOPER)
@@ -1845,6 +1845,8 @@ class StarMainLayout(MainLayout):
toggle_items = self._large_panels[Destination.TOGGLES]._scroller._items
toggle_items.insert(2, text_item("Safe Mode", "N/A"))
from openpilot.common.hardware import HARDWARE
if HARDWARE.get_device_type() in ("tici", "tizi"):
self._add_aggressive_cooling(self._large_panels[Destination.DEVICE])
from openpilot.starpilot.ui.starpilot_settings_adapter_large import StarPilotSettingsAdapterLarge
for destination, owner in self._large_panels.items():
if destination == Destination.DEVELOPER and HARDWARE.get_device_type() in ('mici', 'tici', 'tizi'):
@@ -1861,6 +1863,19 @@ class StarMainLayout(MainLayout):
self.star._native_favorite_actions = self._native_favorites
self.star._publish_bookmark = self._on_bookmark_clicked
@staticmethod
def _add_aggressive_cooling(device_panel) -> None:
item = toggle_item("Aggressive Cooling", AGGRESSIVE_COOLING_DESCRIPTION,
initial_state=ui_state.params.get_bool(AGGRESSIVE_COOLING_KEY),
callback=lambda state: ui_state.params.put_bool(AGGRESSIVE_COOLING_KEY, state, block=True))
device_panel._scroller.add_widget(item)
native_show = device_panel.show_event
def show_event():
item.action_item.set_state(ui_state.params.get_bool(AGGRESSIVE_COOLING_KEY))
native_show()
device_panel.show_event = show_event
def _exit_spacepilot(self) -> None:
gui_app.pop_widgets_to(self, instant=True)
self._set_mode_for_state()
+17 -6
View File
@@ -4,25 +4,36 @@ import numpy as np
from openpilot.common.pid import PIDController
from openpilot.common.hardware import HARDWARE
IS_MICI = HARDWARE.get_device_type() == "mici"
# raise fan setpoint on tici/tizi to reduce noise
# after raising LMH threshold in AGNOS 18.1 to prevent CPU throttling
OFFSET = 0 if HARDWARE.get_device_type() == "mici" else 5
STOCK_CURVE = dict(offset=0, k_p=0., ff_low=60., ff_high=100.) if IS_MICI else \
dict(offset=5, k_p=0., ff_low=65., ff_high=105.)
# tici/tizi opt-in: runs 10-16 C cooler than stock at the cost of fan noise
AGGRESSIVE_CURVE = dict(offset=-5, k_p=1., ff_low=55., ff_high=80.)
class FanController:
def __init__(self, rate: int) -> None:
self.last_ignition = False
self.controller = PIDController(k_p=0, k_i=4e-3, rate=rate)
self.last_aggressive = False
self.controller = PIDController(k_p=STOCK_CURVE["k_p"], k_i=4e-3, rate=rate)
def update(self, cur_temp: float, ignition: bool, aggressive_cooling: bool = False) -> int:
aggressive = aggressive_cooling and not IS_MICI
curve = AGGRESSIVE_CURVE if aggressive else STOCK_CURVE
def update(self, cur_temp: float, ignition: bool) -> int:
self.controller.pos_limit = 100 if ignition else 30
self.controller.neg_limit = 30 if ignition else 0
self.controller._k_p = ([0], [curve["k_p"]])
if ignition != self.last_ignition:
if ignition != self.last_ignition or aggressive != self.last_aggressive:
self.controller.reset()
self.last_ignition = ignition
self.last_aggressive = aggressive
return int(self.controller.update(
error=(cur_temp - (75 + OFFSET)), # temperature setpoint in C
feedforward=np.interp(cur_temp, [60.0 + OFFSET, 100.0 + OFFSET], [0, 100])
error=(cur_temp - (75 + curve["offset"])), # temperature setpoint in C
feedforward=np.interp(cur_temp, [curve["ff_low"], curve["ff_high"]], [0, 100])
))
+2 -1
View File
@@ -375,7 +375,8 @@ def hardware_thread(end_event, hw_queue) -> None:
all_comp_temp = all_temp_filter.update(max(temp_sources))
msg.deviceState.maxTempC = all_comp_temp
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"],
params.get_bool("AggressiveCoolingEnabled"))
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP: