Update vcs.xml, AndroidManifest.xml, BleScanner.kt, and 4 more files
Sensor Fusion
This commit is contained in:
1 parent
4426388063
commit
4303d34471
7 files changed
+500
-59
No files matched your search
@@ -30,7 +30,7 @@ Minimiere Claude-Tokens:
|
||||
- eigenständig große Implementierungen schreiben
|
||||
- ganze Dateien neu ausgeben (außer Gemini liefert es als Diff)
|
||||
- lange Analysen/Prosatexte erzeugen
|
||||
- “Best guess” ohne Gemini machen
|
||||
- "Best guess" ohne Gemini machen
|
||||
|
||||
4) Kontext-Minimierung:
|
||||
- Öffne nur Dateien, die wirklich benötigt werden.
|
||||
@@ -79,35 +79,37 @@ D) Ausführen:
|
||||
|
||||
## #############################################################################################
|
||||
|
||||
## Project Overview
|
||||
## Projektübersicht
|
||||
|
||||
BLE Indoor Positioning system consisting of two components:
|
||||
1. **Android App** (Kotlin/Compose) — estimates user position via RSSI-based trilateration. Active version in `App v2/`.
|
||||
2. **ESP32 Firmware** (Arduino) — iBeacon sender on Seeed XIAO ESP32-C3. Active version in `ESP32 iBeacon Code/ESP32_iBeacon_v2/`.
|
||||
BLE-basiertes Indoor-Positionierungssystem, bestehend aus zwei Komponenten:
|
||||
1. **Android App** (Kotlin/Compose) — schätzt die Nutzerposition mittels RSSI-basierter Trilateration. Die aktive Version befindet sich in `App v2/`. **Die Version in `App v1_2/` ist eine Legacy-Version und soll NICHT bearbeitet werden.**
|
||||
2. **ESP32 Firmware** (Arduino) — iBeacon-Sender auf einem Seeed XIAO ESP32-C3. Die aktive Version befindet sich in `ESP32 iBeacon Code/ESP32_iBeacon_v2/`.
|
||||
|
||||
## Build & Run
|
||||
|
||||
### Android App (from `App v2/`)
|
||||
### Android App
|
||||
|
||||
Alle Gradle-Kommandos müssen aus dem `App v2/`-Verzeichnis ausgeführt werden (`cd App v2/`).
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug # Debug build
|
||||
./gradlew test # Unit tests
|
||||
./gradlew connectedAndroidTest # Instrumented tests (device/emulator required)
|
||||
./gradlew clean assembleDebug # Clean build
|
||||
./gradlew assembleDebug # Debug-Build
|
||||
./gradlew test # Unit-Tests
|
||||
./gradlew connectedAndroidTest # Instrumentierte Tests (Gerät/Emulator erforderlich)
|
||||
./gradlew clean assembleDebug # Clean-Build
|
||||
|
||||
# Single test class
|
||||
# Einzelne Testklasse
|
||||
./gradlew test --tests "com.erik.bleindoorpositioning.ExampleUnitTest"
|
||||
```
|
||||
|
||||
- Compile/Target SDK: 36, Min SDK: 26
|
||||
- Java 11 compatibility, Kotlin 2.0.21 with Compose compiler plugin
|
||||
- Java 11-kompatibel, Kotlin 2.0.21 mit Compose Compiler-Plugin
|
||||
- AGP 9.0.0, Compose BOM 2024.09.00
|
||||
- Dependencies managed via `gradle/libs.versions.toml`
|
||||
- Additional: kotlinx-serialization (JSON config persistence), material-icons-extended (Sonne/Mond Icons)
|
||||
- Abhängigkeiten werden über `gradle/libs.versions.toml` verwaltet.
|
||||
- Zusätzlich: kotlinx-serialization (Persistenz der JSON-Konfiguration), material-icons-extended (Sonne/Mond-Icons)
|
||||
|
||||
### ESP32 Firmware
|
||||
|
||||
Flash via Arduino IDE (Board: XIAO_ESP32C3). Before flashing each beacon, set `BEACON_MINOR` in `ESP32_iBeacon_v2.ino` to a unique ID (1, 2, 3, ...).
|
||||
Flashen über die Arduino IDE (Board: XIAO_ESP32C3). Vor dem Flashen jedes Beacons muss `BEACON_MINOR` in `ESP32_iBeacon_v2.ino` auf eine eindeutige ID (1, 2, 3, ...) gesetzt werden.
|
||||
|
||||
**ESP32-C3 Besonderheiten:**
|
||||
- Nutzt native USB-CDC → `delay(2000)` am Anfang von `setup()` nötig, damit Serial-Ausgaben nicht verloren gehen
|
||||
@@ -115,56 +117,82 @@ Flash via Arduino IDE (Board: XIAO_ESP32C3). Before flashing each beacon, set `B
|
||||
- Header: `esp_mac.h` statt `esp_system.h` (ESP-IDF v4.0+), `esp_bt_device.h` existiert nicht auf ESP32-C3
|
||||
- Optional: `USE_CUSTOM_MAC` aktivieren falls ein Android-Gerät den Beacon nicht findet (MAC-Blacklisting-Workaround)
|
||||
|
||||
## Architecture
|
||||
## Architektur
|
||||
|
||||
### Android App
|
||||
|
||||
**Data flow:** BLE Hardware → `BleScanner` → `BeaconFilterManager` (RSSI filtering + Ausreißer-Erkennung) → `PositioningCalculator` (Trilateration + Raumbegrenzung) → `PositionSmoother` (2D-EMA Glättung) → UI via `StateFlow`
|
||||
**Datenfluss:**
|
||||
```
|
||||
BLE Hardware → BleScanner → BeaconFilterManager (RSSI-Filterung + Ausreißer-Erkennung)
|
||||
→ PositionCalculator (Trilateration + Raumbegrenzung) → EKF update() ─┐
|
||||
├→ EKF.getPosition() → PositionSmoother (2D-EMA) → UI via StateFlow
|
||||
Step Detector → SensorFusionManager (PDR: Heading + Schrittlänge) → EKF predict() ─┘
|
||||
```
|
||||
|
||||
Key layers (`app/src/main/java/com/erik/bleindoorpositioning/`):
|
||||
Wichtige Layer (`app/src/main/java/com/erik/bleindoorpositioning/`):
|
||||
|
||||
- **`ble/`**: `BleScanner` parses iBeacon packets (Apple 0x4C00 manufacturer ID), validates UUID, extracts Minor/Major/RSSI. Filtert veraltete Beacons (>3s) vor Positionsberechnung aus. Auto-Restart alle 25 Min (Android BLE Scan-Limit). Exponential Backoff bei Scan-Fehlern. `reloadConfig()` lädt Config dynamisch neu. `BeaconData` holds default calibration constants (RSSI_AT_ONE_METER, PATH_LOSS_EXPONENT, BEACON_UUID).
|
||||
- **`positioning/`**: `RssiFilter` interface with `KalmanFilter`/`ExponentialMovingAverageFilter`/`MovingAverageFilter` implementations. `BeaconFilterManager` manages one filter per beacon (keyed by Minor ID) with outlier rejection (Schwellwert: 20 dBm). `PositioningCalculator` runs a 3-stage pipeline: IDW weighted centroid (exponent=4.0) → Gauss-Newton trilateration (weight=1/d³) → adaptive blending based on minimum distance (0.5m=100% IDW, 1.5m=100% Gauss-Newton), mit Raumbegrenzung (Clamping auf dynamische Stockwerk-Grenzen aus Räumen+Beacons). Beacon-spezifische `rssiAtOneMeter` und `pathLossExponent` Werte. `PositionSmoother` applies 2D EMA (alpha=0.15) on the final position to reduce jumping.
|
||||
- **`map/`**: `CoordinateMapper` transforms meters↔pixels with dynamic bounding box (supports negative coordinates). `FloorplanView` draws floor with multiple rooms, beacons, distance circles, and position on a Compose Canvas. Interactive: Pinch-to-Zoom (0.5x–5x), Pan, Double-Tap reset. Zoom-kompensierte Schrift/Borders. Dark Mode kompatible Farben via MaterialTheme.
|
||||
- **`ui/`**: Tab-Navigation mit `MainScreen` (Positioning + Config Tabs). Dark/Light Mode Toggle (Sonne/Mond-Icon, persistent). `PositioningScreen`: Karte mit Vollbild-Modus, einklappbare Beacon-Liste, Scan-Kontrolle. `ConfigScreen`: Scrollbar mit einklappbaren Sektionen (Stockwerk, Räume, Beacons, Anlernen). Auto-Save bei Änderungen. Raum-Verwaltung (Add/Edit/Delete), Beacon-Verwaltung mit Detail-Dialog (Position, RSSI@1m, Path Loss Exponent), Beacon-Anlern-Funktion via BLE-Scan.
|
||||
- **`data/`**: `AppConfig` (floorWidth/Height, rooms, beacons). `ConfigRepository` persistiert Config in SharedPreferences (JSON/kotlinx.serialization). Legacy-Migration für alte Configs.
|
||||
- **`model/`**: `BeaconConfig` (position, rssiAtOneMeter, pathLossExponent), `RoomConfig` (name, position, size), `ScannedBeacon` (live scan data with `lastSeen` timestamp, uuid), `Position`/`PositionResult`.
|
||||
- **`ble/`**: `BleScanner` parst iBeacon-Pakete (Apple 0x4C00 Manufacturer-ID), validiert die UUID und extrahiert Minor/Major/RSSI. Veraltete Beacons (>3s) werden vor der Positionsberechnung ausgefiltert. Auto-Restart alle 25 Min (Android BLE Scan-Limit). Exponentielles Backoff bei Scan-Fehlern. `reloadConfig()` lädt die Konfiguration dynamisch neu. `BeaconData` enthält die `BEACON_UUID` und Standard-Kalibrierungskonstanten.
|
||||
- **`positioning/`**: `RssiFilter`-Interface mit `KalmanFilter`-, `ExponentialMovingAverageFilter`- und `MovingAverageFilter`-Implementierungen. Aktuell wird `MovingAverageFilter(windowSize=20)` verwendet. Der `BeaconFilterManager` verwaltet einen Filter pro Beacon (Schlüssel: Minor ID) mit Ausreißer-Erkennung (Schwellwert: 20 dBm). `PositionCalculator` führt eine 3-stufige Pipeline aus: IDW Weighted Centroid (Exponent=4.0) → Gauss-Newton-Trilateration (Gewicht=1/d³) → adaptives Blending basierend auf der minimalen Distanz (0.5m=100% IDW, 1.5m=100% Gauss-Newton), mit Raumbegrenzung (Clamping auf dynamische Stockwerk-Grenzen). Er verwendet Beacon-spezifische Kalibrierungswerte (siehe Sektion "Kalibrierungsparameter"). `PositionSmoother` wendet einen 2D-EMA (alpha=0.25) auf die finale Position an, um Sprünge zu reduzieren. `PositionKalmanFilter` implementiert einen Extended Kalman Filter (EKF) zur Sensorfusion: `predict()` mit PDR-Schritten, `update()` mit BLE-Positionen (Zustand=[x,y], Prozessrauschen=0.5, Messrauschen=2.0, minimale Kovarianz=0.1 um Einfrieren über Zeit zu verhindern). `SensorFusionManager` ist **optional** und stellt PDR (Pedestrian Dead Reckoning) über Android-Sensoren (`TYPE_STEP_DETECTOR`, `TYPE_ROTATION_VECTOR`, `TYPE_LINEAR_ACCELERATION`) bereit. Er verfügt über Stillstandserkennung (Varianz-Schwellwert=0.05, Fenster=10 Samples) und eine dynamische Schrittlängenschätzung (0.5–1.0m). Sensor-Delays: `SENSOR_DELAY_FASTEST` für Step Detector und Linear Acceleration, `SENSOR_DELAY_GAME` für Rotation Vector. **Graceful Degradation:** Wenn die benötigten Sensoren auf dem Gerät nicht verfügbar sind, läuft die App in einem reinen BLE-Modus ohne Schrittverfolgung. Die Verfügbarkeit der Sensoren wird über die `isAvailable`-Property geprüft.
|
||||
- **`map/`**: `CoordinateMapper` transformiert Meter↔Pixel mit einer dynamischen Bounding Box (unterstützt negative Koordinaten). `FloorplanView` zeichnet den Grundriss mit mehreren Räumen, Beacons, Distanzkreisen und der Position auf einer Compose Canvas. Interaktiv: Pinch-to-Zoom (0.5x–5x), Schwenken, Doppeltipp zum Zurücksetzen. Zoom-kompensierte Schriftgrößen und Strichstärken. Dark-Mode-kompatible Farben über MaterialTheme.
|
||||
- **`ui/`**: Tab-Navigation mit `MainScreen` (Positionierungs- und Konfigurations-Tabs). Dark/Light-Mode-Umschalter (Sonne/Mond-Icon, persistent). `PositioningScreen`: Karte mit Vollbild-Modus, einklappbare Beacon-Liste, Scan-Kontrolle. `ConfigScreen`: Scrollbar mit einklappbaren Sektionen (Stockwerk, Räume, Beacons, Anlernen). Auto-Save bei Änderungen. Raum-Verwaltung (Hinzufügen/Bearbeiten/Löschen), Beacon-Verwaltung mit Detail-Dialog (Position, RSSI @1m, Path Loss Exponent), Beacon-Anlern-Funktion via BLE-Scan.
|
||||
- **`data/`**: `AppConfig` (floorWidth/Height, rooms, beacons). `ConfigRepository` persistiert die Konfiguration in den SharedPreferences (JSON/kotlinx.serialization). Inklusive Legacy-Migration für alte Konfigurationsformate.
|
||||
- **`model/`**: `BeaconConfig` (Position, rssiAtOneMeter, pathLossExponent), `RoomConfig` (Name, Position, Größe), `ScannedBeacon` (Live-Scan-Daten mit `lastSeen`-Zeitstempel, UUID), `Position`/`PositionResult`.
|
||||
|
||||
`MainActivity` creates `BleScanner` + `ConfigRepository` and passes them to `MainScreen`. Lifecycle-Handling: onPause stoppt Scan, onResume startet wieder. No DI framework — direct instantiation. All state is reactive via Kotlin `StateFlow`, observed in Compose with `collectAsState()`. Material 3 Theme mit Schwarz/Lila Farbschema.
|
||||
`MainActivity` erzeugt `BleScanner` und `ConfigRepository` und übergibt sie an `MainScreen`. Lifecycle-Handling: `onPause` stoppt den Scan, `onResume` startet ihn wieder. Kein DI-Framework — direkte Instanziierung. Der gesamte Zustand ist reaktiv über Kotlin `StateFlow` und wird in Compose mit `collectAsState()` beobachtet. Material 3 Theme mit einem Schwarz/Lila-Farbschema.
|
||||
|
||||
### ESP32 Firmware
|
||||
|
||||
Pure iBeacon advertiser (no GATT service, no connections). Key configuration:
|
||||
- Advertising interval: 100ms (fixed)
|
||||
- TX Power: +9 dBm (maximum for ESP32-C3), set via `esp_ble_tx_power_set()` for both ADV and DEFAULT
|
||||
- Signal Power: -65 dBm @ 1m (must match `RSSI_AT_ONE_METER` in Android app)
|
||||
- Advertising Flags: 0x06 (LE General Discoverable + BR/EDR Not Supported)
|
||||
- BLE Sleep enabled between advertising events for battery operation
|
||||
- `BEACON_MINOR` identifies each physical beacon — must match beacon Minor ID in the app's Config tab
|
||||
- Optional MAC-Override (`USE_CUSTOM_MAC`) for Android BLE-Blacklisting workaround
|
||||
Reiner iBeacon-Advertiser (kein GATT-Service, keine Verbindungen). Wichtige Konfiguration:
|
||||
- Advertising-Intervall: 100ms (fest)
|
||||
- TX Power: +9 dBm (Maximum für ESP32-C3), gesetzt über `esp_ble_tx_power_set()` für ADV und DEFAULT
|
||||
- Signal Power: -65 dBm @ 1m (muss mit `rssiAtOneMeter` in der Android-App übereinstimmen)
|
||||
- Advertising-Flags: 0x06 (LE General Discoverable + BR/EDR Not Supported)
|
||||
- BLE Sleep zwischen den Advertising-Events für Batteriebetrieb aktiviert
|
||||
- `BEACON_MINOR` identifiziert jeden physischen Beacon — muss mit der Beacon Minor ID in der App-Konfiguration übereinstimmen
|
||||
- Optionaler MAC-Override (`USE_CUSTOM_MAC`) als Workaround für Android BLE-Blacklisting
|
||||
|
||||
## Calibration Parameters
|
||||
## Kalibrierungsparameter
|
||||
|
||||
### Android App
|
||||
Beacon-Kalibrierung ist jetzt **pro Beacon konfigurierbar** im Config-Tab der App:
|
||||
- `rssiAtOneMeter`: Measured RSSI at 1m distance (default: -65.0 dBm)
|
||||
- `pathLossExponent`: Environment-dependent (default: 5.5; free-space=2.0, indoor=2.5-5.5)
|
||||
- Position (x, y) in meters — supports negative coordinates
|
||||
Die Beacon-Kalibrierung ist **pro Beacon konfigurierbar** im Config-Tab der App:
|
||||
- `rssiAtOneMeter`: Gemessener RSSI bei 1m Abstand (Standard: -65.0 dBm)
|
||||
- `pathLossExponent`: Umgebungsabhängig (Standard: 5.5; Freifeld=2.0, Innenraum=2.5-5.5)
|
||||
- Position (x, y) in Metern — unterstützt negative Koordinaten
|
||||
|
||||
Globale Defaults in `BeaconData.kt`:
|
||||
- `RSSI_AT_ONE_METER`: Default -65.0 dBm
|
||||
- `PATH_LOSS_EXPONENT`: Default 5.5
|
||||
- `BEACON_UUID`: Must match UUID on ESP32 beacons (`87b99b2c-9080-4d04-8f00-a5d9a3f9a0e7`)
|
||||
Globale Standardwerte in `BeaconData.kt`:
|
||||
- `RSSI_AT_ONE_METER`: Standardwert -65.0 dBm
|
||||
- `PATH_LOSS_EXPONENT`: Standardwert 5.5
|
||||
- `BEACON_UUID`: Muss mit der UUID auf den ESP32-Beacons übereinstimmen (`87b99b2c-9080-4d04-8f00-a5d9a3f9a0e7`)
|
||||
|
||||
Stockwerk-Konfiguration (Config-Tab):
|
||||
- `floorWidth` / `floorHeight`: Stockwerk-Dimensionen in Metern
|
||||
- `rooms`: Liste von Räumen (Name, Position, Größe) — beliebig viele, auch an negativen Koordinaten
|
||||
|
||||
### ESP32 (ESP32_iBeacon_v2.ino)
|
||||
- `SIGNAL_POWER_1M`: Must match `RSSI_AT_ONE_METER` in the Android app (currently 0xBF = -65 dBm)
|
||||
- `BEACON_MINOR`: Unique per beacon, must match beacon Minor ID configured in the app's Config tab
|
||||
- `SIGNAL_POWER_1M`: Muss mit `rssiAtOneMeter` in der Android-App übereinstimmen (aktuell 0xBF = -65 dBm)
|
||||
- `BEACON_MINOR`: Eindeutig pro Beacon, muss mit der Beacon Minor ID übereinstimmen, die im Config-Tab der App konfiguriert ist.
|
||||
|
||||
## Known Issues / Troubleshooting
|
||||
## Tuning-Parameter (Positionierungspipeline)
|
||||
|
||||
Die Positionierung durchläuft mehrere Glättungsstufen. Beim Tuning beachten:
|
||||
|
||||
**Pipeline:** RSSI → MovingAverageFilter → PositionCalculator → EKF → PositionSmoother → UI
|
||||
|
||||
| Parameter | Datei | Aktuell | Effekt |
|
||||
|---|---|---|---|
|
||||
| `MovingAverageFilter(windowSize)` | BleScanner.kt | 20 | Mehr = stabilere RSSI, aber trägere Reaktion |
|
||||
| `BeaconFilterManager(outlierThreshold)` | BleScanner.kt | 20.0 dBm | Höher = mehr RSSI-Sprünge erlaubt |
|
||||
| `PositionKalmanFilter(processNoise)` | BleScanner.kt | 0.5 | Höher = EKF folgt BLE schneller, PDR weniger Gewicht |
|
||||
| `positionKalmanFilter.update(measurementNoise)` | BleScanner.kt | 2.0 | Niedriger = mehr Vertrauen in BLE-Position |
|
||||
| `PositionSmoother(alpha)` | BleScanner.kt | 0.25 | Höher = reaktiver, niedriger = glatter |
|
||||
| `BEACON_MAX_AGE_MS` | BleScanner.kt | 3000 | Kürzer = weniger Einfluss veralteter Beacons |
|
||||
| `minCovariance` | PositionKalmanFilter.kt | 0.1 | Verhindert EKF-Einfrieren über Zeit |
|
||||
| `STATIONARY_VARIANCE_THRESHOLD` | SensorFusionManager.kt | 0.05 | Niedriger = weniger Schritte als Stillstand gefiltert |
|
||||
| `ACCELERATION_WINDOW_SIZE` | SensorFusionManager.kt | 10 | Weniger = schnellere Stillstandserkennung |
|
||||
|
||||
**Bekanntes Problem:** Zu viele Glättungsstufen hintereinander können die Position extrem träge machen. Beim Debugging einzelne Stufen testweise deaktivieren (EKF bypass / Smoother bypass), um die Ursache zu isolieren. Der EKF kann über Zeit einfrieren wenn die Kovarianz zu klein wird — daher `minCovariance`-Clamp.
|
||||
|
||||
## Bekannte Probleme / Fehlerbehebung
|
||||
|
||||
### ESP32 Beacon nicht von Android erkannt
|
||||
Wenn ein ESP32-C3 Beacon auf einem bestimmten Android-Gerät nicht erkannt wird (auch nicht in nRF Connect), aber auf anderen Geräten sichtbar ist:
|
||||
@@ -176,6 +204,6 @@ Wenn ein ESP32-C3 Beacon auf einem bestimmten Android-Gerät nicht erkannt wird
|
||||
### ESP32-C3 Serial Monitor leer
|
||||
Der XIAO ESP32-C3 nutzt native USB-CDC. Frühe `Serial.print()`-Aufrufe gehen verloren. Fix: `delay(2000)` nach `Serial.begin()`.
|
||||
|
||||
## Language
|
||||
## Sprache
|
||||
|
||||
The codebase, comments, and documentation are in German. Respond in German when working with this project.
|
||||
Codebasis, Kommentare und Dokumentation sind auf Deutsch. Antworte auf Deutsch, wenn du mit diesem Projekt arbeitest.
|
||||
Reference in new issue
Block a user