diff --git a/common/params.cc b/common/params.cc index e77e248..2c7920e 100755 --- a/common/params.cc +++ b/common/params.cc @@ -292,6 +292,7 @@ std::unordered_map keys = { {"CEStopLights", PERSISTENT}, {"CEStopLightsLead", PERSISTENT}, {"Compass", PERSISTENT}, + {"ClearpilotShowHealthMetrics", PERSISTENT}, {"ConditionalExperimental", PERSISTENT}, {"CrosstrekTorque", PERSISTENT}, {"CurrentHolidayTheme", PERSISTENT}, diff --git a/selfdrive/clearpilot/dashcamd b/selfdrive/clearpilot/dashcamd index 8b1d7ef..8c325a7 100755 Binary files a/selfdrive/clearpilot/dashcamd and b/selfdrive/clearpilot/dashcamd differ diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index 7a57790..ee973c7 100755 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -338,6 +338,15 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) { }); debug_panel->addItem(telemetry_toggle); + auto *health_metrics_toggle = new ToggleControl("System Health Overlay", + "Show controls lag, model frame drops, temperature, CPU, and memory usage " + "in the lower-right of the onroad UI. For diagnosing slowdown issues.", "", + Params().getBool("ClearpilotShowHealthMetrics"), this); + QObject::connect(health_metrics_toggle, &ToggleControl::toggleFlipped, [](bool on) { + Params().putBool("ClearpilotShowHealthMetrics", on); + }); + debug_panel->addItem(health_metrics_toggle); + auto *vpn_toggle = new ToggleControl("VPN", "Connect to vpn.hanson.xyz for remote SSH access. " "Disabling kills the active tunnel and stops reconnection attempts.", "", diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index c82c99d..7f78d6d 100755 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -468,6 +468,9 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { drawSpeedLimitSign(p); drawCruiseWarningSign(p); + // CLEARPILOT: system health metrics in lower-right (debug overlay) + drawHealthMetrics(p); + // Draw FrogPilot widgets paintFrogPilotWidgets(p); } @@ -727,6 +730,82 @@ void AnnotatedCameraWidget::drawText(QPainter &p, int x, int y, const QString &t p.drawText(real_rect.x(), real_rect.bottom(), text); } +// CLEARPILOT: System health overlay — shows metrics that indicate the system +// is overburdened or behind. Toggled via ClearpilotShowHealthMetrics param. +// Metrics (top→bottom): LAG, DROP, TEMP, CPU, MEM +// LAG (ms): controlsd cumLagMs — most direct "is the loop keeping up" indicator +// DROP (%): modelV2 frameDropPerc — modeld losing frames; controlsd errors >20% +// TEMP (°C): deviceState.maxTempC — thermal throttling starts ~75, serious >88 +// CPU (%): max core from deviceState.cpuUsagePercent +// MEM (%): deviceState.memoryUsagePercent +// Each value color-codes green/yellow/red by severity. +void AnnotatedCameraWidget::drawHealthMetrics(QPainter &p) { + static bool enabled = Params().getBool("ClearpilotShowHealthMetrics"); + static int check_counter = 0; + // re-check the param every ~2s without a toggle signal path + if (++check_counter >= 40) { + check_counter = 0; + enabled = Params().getBool("ClearpilotShowHealthMetrics"); + } + if (!enabled) return; + + SubMaster &sm = *(uiState()->sm); + auto cs = sm["controlsState"].getControlsState(); + auto ds = sm["deviceState"].getDeviceState(); + auto mv = sm["modelV2"].getModelV2(); + + float lag_ms = cs.getCumLagMs(); + float drop_pct = mv.getFrameDropPerc(); + float temp_c = ds.getMaxTempC(); + int mem_pct = ds.getMemoryUsagePercent(); + int cpu_pct = 0; + for (auto v : ds.getCpuUsagePercent()) cpu_pct = std::max(cpu_pct, (int)v); + + auto color_for = [](float v, float warn, float crit) { + if (v >= crit) return QColor(0xff, 0x50, 0x50); // red + if (v >= warn) return QColor(0xff, 0xd0, 0x40); // yellow + return QColor(0xff, 0xff, 0xff); // white (ok) + }; + + struct Row { QString label; QString value; QColor color; }; + Row rows[] = { + {"LAG", QString::number((int)lag_ms), color_for(lag_ms, 50.f, 200.f)}, + {"DROP", QString::number((int)drop_pct),color_for(drop_pct, 5.f, 15.f)}, + {"TEMP", QString::number((int)temp_c), color_for(temp_c, 75.f, 88.f)}, + {"CPU", QString::number(cpu_pct), color_for((float)cpu_pct, 75.f, 90.f)}, + {"MEM", QString::number(mem_pct), color_for((float)mem_pct, 70.f, 85.f)}, + }; + + p.save(); + p.setFont(InterFont(90, QFont::Bold)); + QFontMetrics fm = p.fontMetrics(); + int row_h = fm.height(); // natural line height at 90pt bold + int gap = 40; // requested 40px between values + int margin = 30; // requested 30px margin + int panel_w = 360; // fixed width — fits "TEMP 99" + int n = sizeof(rows) / sizeof(rows[0]); + int panel_h = n * row_h + (n - 1) * gap + 2 * margin; + int x = width() - panel_w - margin; + int y = height() - panel_h - margin; + + // black background + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 200)); + p.drawRoundedRect(QRect(x, y, panel_w, panel_h), 20, 20); + + // rows + int text_y = y + margin + fm.ascent(); + for (int i = 0; i < n; i++) { + p.setPen(rows[i].color); + // label left, value right + p.drawText(x + margin, text_y, rows[i].label); + QRect vrect = fm.boundingRect(rows[i].value); + p.drawText(x + panel_w - margin - vrect.width(), text_y, rows[i].value); + text_y += row_h + gap; + } + p.restore(); +} + void AnnotatedCameraWidget::initializeGL() { CameraWidget::initializeGL(); qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION)); @@ -867,8 +946,9 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) { } if (outlineOnly) { + // CLEARPILOT: center path (tire track) is 2x wider than other lines in nightrider painter.setPen(QPen(QColor(center_lane_color.red(), center_lane_color.green(), - center_lane_color.blue(), 180), outlineWidth)); + center_lane_color.blue(), 180), outlineWidth * 2)); painter.setBrush(Qt::NoBrush); } else { painter.setPen(Qt::NoPen); diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index bdb87e1..9e7170e 100755 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -53,6 +53,7 @@ private: void drawSpeedWidget(QPainter &p, int x, int y, const QString &title, const QString &speedLimitStr, QColor colorSpeed, int width = 176); void drawSpeedLimitSign(QPainter &p); void drawCruiseWarningSign(QPainter &p); + void drawHealthMetrics(QPainter &p); QVBoxLayout *main_layout; QPixmap dm_img; diff --git a/selfdrive/ui/qt/spinner b/selfdrive/ui/qt/spinner index 53b0b3e..2abccd2 100755 Binary files a/selfdrive/ui/qt/spinner and b/selfdrive/ui/qt/spinner differ