GPS anzeige verbessert und planung für abarbeitung der Route angefangen
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
# Routenplanung To-do
|
||||
|
||||
Stand: 24.05.2026
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
- Karten-Tab in `admin.html` vorhanden
|
||||
- Wegpunkte können gesetzt werden
|
||||
- Wegpunkte können verschoben werden
|
||||
- Wegpunkte können gelöscht werden
|
||||
- Klick auf die Route fügt einen Wegpunkt zwischen zwei vorhandenen Wegpunkten ein
|
||||
- Route kann unter Namen gespeichert werden
|
||||
- Gespeicherte Route kann geladen werden
|
||||
- Gespeicherte Route kann gelöscht werden
|
||||
|
||||
## Offene Produktentscheidung
|
||||
|
||||
Noch nicht final entschieden ist, wie die Fahrart zwischen zwei Wegpunkten festgelegt wird:
|
||||
|
||||
- Vollautomatisch aus der Richtungsänderung ableiten
|
||||
- Pro Wegpunkt manuell festlegen
|
||||
- Automatik als Standard, aber pro Wegpunkt übersteuerbar
|
||||
|
||||
Aktuell bevorzugte Idee:
|
||||
|
||||
- Automatik als Standard
|
||||
- Pro Wegpunkt optionaler Override
|
||||
|
||||
## Geplante Logik für Fahrart-Vorschlag
|
||||
|
||||
Beim Klick auf einen Wegpunkt soll sofort eine Empfehlung berechnet werden:
|
||||
|
||||
- unter `20°`: einfach `Ackermann`
|
||||
- `20° bis 60°`: engerer `Ackermann-Bogen`
|
||||
- über `60°`: erst `Turn on Point`, danach `Ackermann`
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Die Winkeldifferenz soll aus den benachbarten Segmenten berechnet werden
|
||||
- Bei inneren Wegpunkten: Richtung `vorheriger Punkt -> aktueller Punkt` gegen `aktueller Punkt -> nächster Punkt`
|
||||
- Beim ersten Wegpunkt: optional Richtung `Rover -> Wegpunkt` gegen `Wegpunkt -> nächster Punkt`
|
||||
- Beim letzten Wegpunkt: keine automatische Weiterfahr-Empfehlung oder eigener Sonderfall
|
||||
|
||||
## Geplante UI-Erweiterungen
|
||||
|
||||
- Klick auf Wegpunkt zeigt Popup
|
||||
- Im Popup soll zusätzlich zur Löschfunktion die berechnete Fahrart-Empfehlung stehen
|
||||
- Im Popup soll klar stehen:
|
||||
- berechneter Winkel
|
||||
- empfohlene Fahrart
|
||||
- kurze Begründung
|
||||
- Später optional:
|
||||
- manuelle Auswahl `Ackermann`
|
||||
- manuelle Auswahl `Turn on Point`
|
||||
- manuelle Auswahl `Ackermann-Bogen`
|
||||
- manueller Override `Automatik`
|
||||
|
||||
## Wegpunktlogik im Detail
|
||||
|
||||
Für jeden Wegpunkt sind später diese Fragen wichtig:
|
||||
|
||||
- Wie wird der Punkt angefahren?
|
||||
- Muss am Punkt gestoppt werden?
|
||||
- Muss am Punkt neu ausgerichtet werden?
|
||||
- Soll direkt in das nächste Segment übergegangen werden?
|
||||
|
||||
Mögliche Varianten:
|
||||
|
||||
- `nur passieren`
|
||||
- Punkt gilt als erreicht, wenn der Rover den Radius schneidet
|
||||
- `präzise anfahren`
|
||||
- Punkt gilt erst als erreicht, wenn der Rover im Radius steht und genug verlangsamt hat
|
||||
- `stoppen und neu ausrichten`
|
||||
- Punkt wird erreicht, dann `Turn on Point` auf gewünschte Richtung
|
||||
|
||||
## Segmentlogik
|
||||
|
||||
Später soll die Route nicht nur als Punktliste gesehen werden, sondern als Folge von Segmenten:
|
||||
|
||||
- Segment 1: `Start -> WP1`
|
||||
- Segment 2: `WP1 -> WP2`
|
||||
- Segment 3: `WP2 -> WP3`
|
||||
|
||||
Für jedes Segment interessant:
|
||||
|
||||
- Segmentlänge
|
||||
- Zielrichtung
|
||||
- empfohlene Fahrart
|
||||
- erlaubte Geschwindigkeit
|
||||
- Abbruchbedingungen
|
||||
|
||||
## Marsrover-inspirierte Denkweise
|
||||
|
||||
Für den ExoMy sinnvoll:
|
||||
|
||||
- nicht die ganze Route als einen Block behandeln
|
||||
- sondern Abschnitt für Abschnitt
|
||||
- vor jedem neuen Abschnitt bewerten:
|
||||
- Zielrichtung
|
||||
- Platzverhältnisse
|
||||
- gewünschte Genauigkeit
|
||||
- Sicherheitslage
|
||||
|
||||
Das bedeutet:
|
||||
|
||||
- Menschen planen die Route
|
||||
- der Rover arbeitet Segment für Segment ab
|
||||
- am Übergang zum nächsten Segment wird neu bewertet
|
||||
|
||||
## Vorschlag für Standardregeln
|
||||
|
||||
Erste Regelbasis für die spätere Automatik:
|
||||
|
||||
- unter `20°`: `Ackermann`
|
||||
- `20° bis 60°`: `Ackermann-Bogen`
|
||||
- über `60°`: `Turn on Point`, danach `Ackermann`
|
||||
|
||||
Zusätzliche mögliche Regeln:
|
||||
|
||||
- wenn Wegpunkt sehr nah am nächsten liegt:
|
||||
- lieber präzise neu ausrichten
|
||||
- wenn Toleranzradius klein ist:
|
||||
- langsamer anfahren
|
||||
- wenn wenig Platz oder Hindernisse:
|
||||
- eher `Turn on Point`
|
||||
|
||||
## Pro-Wegpunkt-Override
|
||||
|
||||
Wahrscheinlich beste spätere Lösung:
|
||||
|
||||
- Standardmäßig entscheidet die Automatik
|
||||
- pro Wegpunkt kann man die Entscheidung überschreiben
|
||||
|
||||
Mögliche Werte:
|
||||
|
||||
- `automatik`
|
||||
- `ackermann`
|
||||
- `ackermann_bogen`
|
||||
- `turn_on_point`
|
||||
|
||||
## Was noch fehlt, bevor der Rover wirklich fahren kann
|
||||
|
||||
- sauberes Routenformat für den Rover
|
||||
- Status je Wegpunkt:
|
||||
- `offen`
|
||||
- `aktiv`
|
||||
- `erreicht`
|
||||
- `übersprungen`
|
||||
- `fehler`
|
||||
- Status je Route:
|
||||
- `bereit`
|
||||
- `läuft`
|
||||
- `pausiert`
|
||||
- `abgebrochen`
|
||||
- `fertig`
|
||||
- Start/Stop/Pause in der GUI
|
||||
- Anzeige des aktuell aktiven Wegpunkts
|
||||
- Anzeige der aktuell gewählten Fahrart
|
||||
|
||||
## Sensorik-Fragen
|
||||
|
||||
Noch offen für die spätere echte Fahrfunktion:
|
||||
|
||||
- Woher kommt das Heading?
|
||||
- IMU
|
||||
- GPS-Bewegungsrichtung
|
||||
- Fusion aus beidem
|
||||
- Wie genau ist das GPS im Zielbereich?
|
||||
- Brauchen wir zusätzlich Odometrie für den Nahbereich?
|
||||
- Ab welcher Geschwindigkeit ist GPS-Kurs überhaupt brauchbar?
|
||||
|
||||
## Sicherheitsfragen
|
||||
|
||||
Vor echter Abarbeitung der Route klären:
|
||||
|
||||
- Was passiert bei GPS-Verlust?
|
||||
- Was passiert bei ROS-Verlust?
|
||||
- Was passiert bei großem Positionssprung?
|
||||
- Was passiert, wenn ein Wegpunkt nicht erreicht wird?
|
||||
- Wann wird die Route automatisch abgebrochen?
|
||||
- Muss der Rover bei Fehler immer sofort stoppen?
|
||||
|
||||
## Spätere Komfortfunktionen
|
||||
|
||||
- Richtungspfeil oder Segmentrichtung in der Karte anzeigen
|
||||
- Wegpunktnummern und geplante Fahrart direkt in der Karte
|
||||
- Route duplizieren
|
||||
- Wegpunktreihenfolge manuell ändern
|
||||
- Route exportieren/importieren
|
||||
- Routenkommentar oder Missionsname
|
||||
- Toleranzradius pro Wegpunkt einstellen
|
||||
- Geschwindigkeit pro Segment oder Wegpunkt einstellen
|
||||
|
||||
## Späterer Unterbau für echte Rover-Route
|
||||
|
||||
Wenn aus der Planungsroute später eine ausführbare Rover-Route wird, soll das Datenformat erweitert werden:
|
||||
|
||||
- `name`
|
||||
- `waypoints`
|
||||
- pro Wegpunkt:
|
||||
- `id`
|
||||
- `lat`
|
||||
- `lon`
|
||||
- `radius_m`
|
||||
- `mode` oder `mode_override`
|
||||
- optional `heading_deg`
|
||||
- optional `stop_at_waypoint`
|
||||
- optional `arrival_action`
|
||||
- optional `speed_limit`
|
||||
- optional `notes`
|
||||
|
||||
## Technische Folgefragen
|
||||
|
||||
- Woher kommt die zuverlässige aktuelle Fahrtrichtung?
|
||||
- nur GPS ist bei langsamer Fahrt oft ungenau
|
||||
- später besser mit IMU oder gemittelter Bewegungsrichtung
|
||||
- Wann gilt ein Wegpunkt als erreicht?
|
||||
- Toleranzradius in Metern
|
||||
- Soll vor jedem neuen Segment neu entschieden werden?
|
||||
- wahrscheinlich ja
|
||||
|
||||
## Nächster sinnvoller Schritt
|
||||
|
||||
1. Winkelberechnung zwischen Routensegmenten implementieren
|
||||
2. Fahrart-Vorschlag nach obiger Tabelle erzeugen
|
||||
3. Vorschlag im Wegpunkt-Popup anzeigen
|
||||
4. Danach entscheiden, ob wir direkt einen manuellen Override pro Wegpunkt einbauen
|
||||
|
||||
## Nicht vergessen
|
||||
|
||||
- Die Route soll später leicht wiederzufinden und fortsetzbar sein
|
||||
- Entscheidungen nicht still im Code verstecken, sondern in der GUI sichtbar machen
|
||||
- Bei kleinen Rover-Geschwindigkeiten keine Scheingenauigkeit vortäuschen
|
||||
- Lieber klare, nachvollziehbare Regeln als zu frühe komplizierte Autonomie
|
||||
@@ -331,6 +331,9 @@ header {
|
||||
line-height: 1.9;
|
||||
pointer-events: none;
|
||||
}
|
||||
.gps-quality-good { color: #00ff91; }
|
||||
.gps-quality-mid { color: #FFB300; }
|
||||
.gps-quality-bad { color: #FF5252; }
|
||||
.c-tl { top: calc(13px * var(--cam-scale)); left: calc(13px * var(--cam-scale)); }
|
||||
.c-tr { top: calc(13px * var(--cam-scale)); right: calc(13px * var(--cam-scale)); text-align: right; }
|
||||
.c-bl { bottom: calc(13px * var(--cam-scale)); left: calc(13px * var(--cam-scale)); }
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
<div class="c-bl">
|
||||
GPS: <span id="gps-state">Offline</span><br>
|
||||
SAT: <span id="gps-sats">--</span><br>
|
||||
POS: <span id="gps-accuracy">--</span><br>
|
||||
LAT: <span id="gps-lat">--</span><br>
|
||||
LON: <span id="gps-lon">--</span>
|
||||
</div>
|
||||
@@ -603,17 +603,41 @@ function updateAxesDisplay(x, y) {
|
||||
setText("dpy", formatAxis(y));
|
||||
}
|
||||
|
||||
function setGpsAccuracyDisplay(valueMeters) {
|
||||
var node = document.getElementById("gps-accuracy");
|
||||
if (!node) return;
|
||||
|
||||
node.classList.remove("gps-quality-good", "gps-quality-mid", "gps-quality-bad");
|
||||
if (typeof valueMeters !== "number" || isNaN(valueMeters) || valueMeters <= 0) {
|
||||
node.textContent = "--";
|
||||
return;
|
||||
}
|
||||
|
||||
node.textContent = formatNumber(valueMeters, 1) + " m";
|
||||
if (valueMeters <= 1.0) {
|
||||
node.classList.add("gps-quality-good");
|
||||
} else if (valueMeters <= 3.0) {
|
||||
node.classList.add("gps-quality-mid");
|
||||
} else {
|
||||
node.classList.add("gps-quality-bad");
|
||||
}
|
||||
}
|
||||
|
||||
function updateGpsOverlayFromFix(message) {
|
||||
if (!message) return;
|
||||
markGpsFresh();
|
||||
|
||||
var hasFix = message.status && message.status.status >= 0;
|
||||
if (!hasFix) {
|
||||
setGpsAccuracyDisplay(NaN);
|
||||
setText("gps-lat", "--");
|
||||
setText("gps-lon", "--");
|
||||
return;
|
||||
}
|
||||
|
||||
var covariance = Array.isArray(message.position_covariance) ? Number(message.position_covariance[0]) : NaN;
|
||||
var accuracyMeters = covariance > 0 ? Math.sqrt(covariance) : NaN;
|
||||
setGpsAccuracyDisplay(accuracyMeters);
|
||||
setText("gps-lat", formatNumber(Number(message.latitude || 0), 5));
|
||||
setText("gps-lon", formatNumber(Number(message.longitude || 0), 5));
|
||||
}
|
||||
@@ -622,9 +646,6 @@ function updateGpsOverlayFromStatus(text) {
|
||||
var value = String(text || "Offline");
|
||||
markGpsFresh();
|
||||
setText("gps-state", value);
|
||||
|
||||
var satMatch = value.match(/(\d+)\s*Sat/i);
|
||||
setText("gps-sats", satMatch ? satMatch[1] : "--");
|
||||
}
|
||||
|
||||
function updateModeDisplay() {
|
||||
@@ -727,7 +748,7 @@ function clearRosLiveDisplay() {
|
||||
liveIndicator.classList.add("is-offline");
|
||||
}
|
||||
setText("gps-state", "Offline");
|
||||
setText("gps-sats", "--");
|
||||
setGpsAccuracyDisplay(NaN);
|
||||
setText("gps-lat", "--");
|
||||
setText("gps-lon", "--");
|
||||
setText("overlay-speed", "--");
|
||||
|
||||
Reference in New Issue
Block a user