feat: onroad health overlay + 2x tire path in nightrider
System health overlay: - Lower-right 5-metric panel: LAG (controlsState.cumLagMs), DROP (modelV2.frameDropPerc), TEMP (deviceState.maxTempC), CPU (max core of deviceState.cpuUsagePercent), MEM (deviceState.memoryUsagePercent) - Color-coded white→yellow→red by severity (LAG: 50/200ms, DROP: 5/15%, TEMP: 75/88°C, CPU: 75/90%, MEM: 70/85%) - Toggle in ClearPilot → Debug → "System Health Overlay" - New param ClearpilotShowHealthMetrics, PERSISTENT (disk, survives reboots), default false — re-polled every ~2s so toggle takes effect without process restart - InterFont(90, Bold) to match speed limit numeric styling, 30px margin, 40px between rows, black rounded background Nightrider center lane path (the "tire track" polygon from scene.track_vertices) is now drawn at 2x the width of other lines — highlights the planned path distinctly against the otherwise stark outline-only rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -292,6 +292,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
|||||||
{"CEStopLights", PERSISTENT},
|
{"CEStopLights", PERSISTENT},
|
||||||
{"CEStopLightsLead", PERSISTENT},
|
{"CEStopLightsLead", PERSISTENT},
|
||||||
{"Compass", PERSISTENT},
|
{"Compass", PERSISTENT},
|
||||||
|
{"ClearpilotShowHealthMetrics", PERSISTENT},
|
||||||
{"ConditionalExperimental", PERSISTENT},
|
{"ConditionalExperimental", PERSISTENT},
|
||||||
{"CrosstrekTorque", PERSISTENT},
|
{"CrosstrekTorque", PERSISTENT},
|
||||||
{"CurrentHolidayTheme", PERSISTENT},
|
{"CurrentHolidayTheme", PERSISTENT},
|
||||||
|
|||||||
Binary file not shown.
@@ -338,6 +338,15 @@ ClearPilotPanel::ClearPilotPanel(QWidget* parent) : QFrame(parent) {
|
|||||||
});
|
});
|
||||||
debug_panel->addItem(telemetry_toggle);
|
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",
|
auto *vpn_toggle = new ToggleControl("VPN",
|
||||||
"Connect to vpn.hanson.xyz for remote SSH access. "
|
"Connect to vpn.hanson.xyz for remote SSH access. "
|
||||||
"Disabling kills the active tunnel and stops reconnection attempts.", "",
|
"Disabling kills the active tunnel and stops reconnection attempts.", "",
|
||||||
|
|||||||
@@ -468,6 +468,9 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
|||||||
drawSpeedLimitSign(p);
|
drawSpeedLimitSign(p);
|
||||||
drawCruiseWarningSign(p);
|
drawCruiseWarningSign(p);
|
||||||
|
|
||||||
|
// CLEARPILOT: system health metrics in lower-right (debug overlay)
|
||||||
|
drawHealthMetrics(p);
|
||||||
|
|
||||||
// Draw FrogPilot widgets
|
// Draw FrogPilot widgets
|
||||||
paintFrogPilotWidgets(p);
|
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);
|
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() {
|
void AnnotatedCameraWidget::initializeGL() {
|
||||||
CameraWidget::initializeGL();
|
CameraWidget::initializeGL();
|
||||||
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
|
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
|
||||||
@@ -867,8 +946,9 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (outlineOnly) {
|
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(),
|
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);
|
painter.setBrush(Qt::NoBrush);
|
||||||
} else {
|
} else {
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
|
|||||||
@@ -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 drawSpeedWidget(QPainter &p, int x, int y, const QString &title, const QString &speedLimitStr, QColor colorSpeed, int width = 176);
|
||||||
void drawSpeedLimitSign(QPainter &p);
|
void drawSpeedLimitSign(QPainter &p);
|
||||||
void drawCruiseWarningSign(QPainter &p);
|
void drawCruiseWarningSign(QPainter &p);
|
||||||
|
void drawHealthMetrics(QPainter &p);
|
||||||
|
|
||||||
QVBoxLayout *main_layout;
|
QVBoxLayout *main_layout;
|
||||||
QPixmap dm_img;
|
QPixmap dm_img;
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user