Optimus Prime

This commit is contained in:
firestar5683
2026-05-28 10:21:01 -05:00
parent 9b177b25b5
commit c744f66b2a
9 changed files with 103 additions and 34 deletions
@@ -63,6 +63,15 @@ class RadarInterface(RadarInterfaceBase):
self.ioniq_6_radar_probe_updates = 0
self.rcp = get_radar_can_parser(CP, self.radar_config)
# Precompute (addr, "RADAR_TRACK_xxx") pairs once. _update runs on the
# CAN-driven card loop (core 4, shared with controlsd/selfdrived), so avoid
# rebuilding 32 f-strings per radar frame.
self.track_addrs: list[tuple[int, str]] = []
if self.radar_config is not None:
self.track_addrs = [(addr, f"RADAR_TRACK_{addr:x}")
for addr in range(self.radar_config.start_addr,
self.radar_config.start_addr + self.radar_config.msg_count)]
def update(self, can_strings):
if self.ioniq_6_radar_probe and self.rcp is not None and not self.ioniq_6_radar_probe_logged:
vls = self.rcp.update(can_strings)
@@ -104,10 +113,13 @@ class RadarInterface(RadarInterfaceBase):
if self.radar_config is None:
return ret
for addr in range(self.radar_config.start_addr, self.radar_config.start_addr + self.radar_config.msg_count):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
radar_type = self.radar_config.radar_type
vl = self.rcp.vl
if self.radar_config.radar_type == "mrr30":
for addr, track_name in self.track_addrs:
msg = vl[track_name]
if radar_type == "mrr30":
for i in ("1", "2"):
track_key = addr * 2 + int(i) - 1
if track_key not in self.pts:
@@ -128,24 +140,32 @@ class RadarInterface(RadarInterfaceBase):
del self.pts[track_key]
continue
if radar_type == "mrr35":
# Most of the 32 channels are empty each frame. Only allocate a point
# when the channel is valid; drop it otherwise. Avoids the per-frame
# alloc-then-delete churn on the ~27 idle channels.
if msg["STATE"] in (3, 4):
pt = self.pts.get(addr)
if pt is None:
pt = structs.RadarData.RadarPoint()
pt.trackId = self.track_id
self.track_id += 1
self.pts[addr] = pt
pt.measured = True
pt.dRel = msg["LONG_DIST"]
pt.yRel = msg["LAT_DIST"]
pt.vRel = msg["REL_SPEED"]
pt.aRel = msg["REL_ACCEL"]
pt.yvRel = float("nan")
elif addr in self.pts:
del self.pts[addr]
continue
if addr not in self.pts:
self.pts[addr] = structs.RadarData.RadarPoint()
self.pts[addr].trackId = self.track_id
self.track_id += 1
if self.radar_config.radar_type == "mrr35":
valid = msg["STATE"] in (3, 4)
if valid:
self.pts[addr].measured = True
self.pts[addr].dRel = msg["LONG_DIST"]
self.pts[addr].yRel = msg["LAT_DIST"]
self.pts[addr].vRel = msg["REL_SPEED"]
self.pts[addr].aRel = msg["REL_ACCEL"]
self.pts[addr].yvRel = float("nan")
else:
del self.pts[addr]
continue
valid = msg['STATE'] in (3, 4)
if valid:
azimuth = math.radians(msg['AZIMUTH'])
+6
View File
@@ -957,10 +957,16 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
# ten times the regular interval, or the average interval is more than 10% too high.
EventName.commIssue: {
ET.SOFT_DISABLE: soft_disable_alert("Communication Issue Between Processes"),
ET.PERMANENT: Alert("Communication Issue Between Processes", "",
AlertStatus.normal, AlertSize.small,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 1., creation_delay=30.),
ET.NO_ENTRY: comm_issue_alert,
},
EventName.commIssueAvgFreq: {
ET.SOFT_DISABLE: soft_disable_alert("Low Communication Rate Between Processes"),
ET.PERMANENT: Alert("Low Communication Rate Between Processes", "",
AlertStatus.normal, AlertSize.small,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, 1., creation_delay=30.),
ET.NO_ENTRY: NoEntryAlert("Low Communication Rate Between Processes"),
},
+30 -1
View File
@@ -1,17 +1,46 @@
#include <sys/resource.h>
#include <unistd.h>
#include <QApplication>
#include <QTranslator>
#include "common/swaglog.h"
#include "common/util.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/window.h"
// Qt 5.12.8's qErrnoWarning() emits QtCriticalMsg then calls abort() directly,
// bypassing the fatal-message path. Intercept critical+fatal Wayland messages
// before the unconditional abort() fires and clean-exit so the manager restarts
// us quickly instead of going through the slow abort/crash-handler path.
void waylandAwareMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (type == QtCriticalMsg || type == QtFatalMsg) {
QByteArray bytes = msg.toUtf8();
if (bytes.contains("ayland") || bytes.contains("wl_display")) {
swagLogMessageHandler(type, context, msg);
LOGE("UI WAYLAND EXIT: %s", bytes.constData());
_exit(0); // clean exit; manager restarts us
}
}
swagLogMessageHandler(type, context, msg);
// Non-Wayland fatal: let Qt abort normally; crash_handler will capture it.
}
int main(int argc, char *argv[]) {
setpriority(PRIO_PROCESS, 0, -20);
qInstallMessageHandler(swagLogMessageHandler);
// Pin the UI to the little cores (0-3). The realtime control loop
// (card/controlsd/selfdrived) runs SCHED_FIFO on core 4; without this pin the
// kernel can schedule the UI there, and a UI stall/restart spike preempts
// selfdrived, starving its 100 Hz loop and firing the "System Lagging" alert.
// Set before any threads spawn so children inherit the affinity.
if (!Hardware::PC()) {
util::set_core_affinity({0, 1, 2, 3});
}
qInstallMessageHandler(waylandAwareMessageHandler);
initApp(argc, argv);
QTranslator translator;
+1 -1
View File
@@ -82,7 +82,7 @@ FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent)
// Set up the API request for firehose stats
const QString dongle_id = QString::fromStdString(Params().get("DongleId"));
firehose_stats = new RequestRepeater(this, CommaApi::BASE_URL + "/v1/devices/" + dongle_id + "/firehose_stats",
"ApiCache_FirehoseStats", 30, true);
"ApiCache_FirehoseStats", 30, false);
QObject::connect(firehose_stats, &RequestRepeater::requestDone, [=](const QString &response, bool success) {
if (success) {
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
+1 -1
View File
@@ -16,7 +16,7 @@ PrimeState::PrimeState(QObject* parent) : QObject(parent) {
if (auto dongleId = getDongleId()) {
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 60);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &PrimeState::handleReply);
}
+5
View File
@@ -391,6 +391,11 @@ void UIState::update() {
if (!watchdog_kick(nanos_since_boot())) {
LOGE("UI watchdog kick failed at frame %llu", static_cast<unsigned long long>(sm->frame));
}
// Re-pin to the little cores: power-save can offline our core and the
// kernel may rebalance us onto core 4 (the realtime control loop).
if (!Hardware::PC()) {
util::set_core_affinity({0, 1, 2, 3});
}
}
ui_stall_progress(UIStallPhase::AFTER_WATCHDOG, sm->frame);
emit uiUpdate(*this, *starpilotUIState());
+18 -13
View File
@@ -40,21 +40,26 @@ static void update_state(StarPilotUIState *fs) {
}
capnp::Text::Reader toggles = starpilotPlan.getStarpilotToggles();
QByteArray current_toggles(toggles.cStr(), toggles.size());
static QByteArray previous_toggles;
if (previous_toggles != current_toggles) {
QJsonParseError parse_error;
QJsonDocument toggles_doc = QJsonDocument::fromJson(current_toggles, &parse_error);
if (parse_error.error == QJsonParseError::NoError && toggles_doc.isObject()) {
QJsonObject updated_toggles = starpilot_scene.starpilot_toggles;
const QJsonObject parsed_toggles = toggles_doc.object();
for (auto it = parsed_toggles.begin(); it != parsed_toggles.end(); ++it) {
updated_toggles.insert(it.key(), it.value());
// starpilot_process only broadcasts the full toggles JSON periodically and
// sends an empty string on every other frame. Skip the empty broadcasts so
// we don't parse "" (QJsonParseError "illegal value") every frame.
if (!current_toggles.trimmed().isEmpty()) {
static QByteArray previous_toggles;
if (previous_toggles != current_toggles) {
QJsonParseError parse_error;
QJsonDocument toggles_doc = QJsonDocument::fromJson(current_toggles, &parse_error);
if (parse_error.error == QJsonParseError::NoError && toggles_doc.isObject()) {
QJsonObject updated_toggles = starpilot_scene.starpilot_toggles;
const QJsonObject parsed_toggles = toggles_doc.object();
for (auto it = parsed_toggles.begin(); it != parsed_toggles.end(); ++it) {
updated_toggles.insert(it.key(), it.value());
}
starpilot_scene.starpilot_toggles = updated_toggles;
} else {
qWarning() << "Ignoring invalid StarPilot toggles JSON:" << parse_error.errorString();
}
starpilot_scene.starpilot_toggles = updated_toggles;
} else {
qWarning() << "Ignoring invalid StarPilot toggles JSON:" << parse_error.errorString();
previous_toggles = current_toggles;
}
previous_toggles = current_toggles;
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ from openpilot.system.hardware import HARDWARE, PC, TICI
from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess
WEBCAM = os.getenv("USE_WEBCAM") is not None
UI_WATCHDOG_MAX_DT = int(os.getenv("UI_WATCHDOG_MAX_DT", "5"))
UI_WATCHDOG_MAX_DT = int(os.getenv("UI_WATCHDOG_MAX_DT", "10"))
def driverview(started: bool, params: Params, CP: car.CarParams, starpilot_toggles: SimpleNamespace) -> bool:
return started or params.get_bool("IsDriverViewEnabled")
+5 -1
View File
@@ -122,9 +122,13 @@ def downloader_loop(event):
if alt_path is not None and os.path.exists(alt_path):
shutil.copyfile(alt_path, ASSIST_DATA_FILE)
sm = messaging.SubMaster(['deviceState'])
try:
while not os.path.exists(ASSIST_DATA_FILE) and not event.is_set():
download_assistance()
sm.update(0)
if sm['deviceState'].networkType != log.DeviceState.NetworkType.none:
download_assistance()
event.wait(timeout=10)
except KeyboardInterrupt:
pass