Einleitung

Kostengünstige Plattformen wie der Raspberry Pi ermöglichen es, geosensorische Messsysteme einzusetzen und individuell an spezifische Fragestellungen anzupassen. Im Bereich des öffentlichen Personennahverkehrs spielen solche Messungen eine wichtige Rolle, da sie Aufschluss über den Zustand von Fahrzeugen, Gleisanlagen und den Fahrkomfort geben können.

Im Rahmen dieses Projekts wird ein mobiles Messsystem entwickelt, das Vibrationen während der Fahrt mit Berliner S-Bahnen erfasst und räumlich verortet. Der Fokus liegt dabei auf dem Vergleich unterschiedlicher Fahrzeugtypen der Baureihen BR 480, BR 481 (modernisiert) sowie BR 483/484. Durch die Kombination von Sensor- und GPS-Daten sollen Unterschiede im Vibrationsverhalten analysiert, ausgewertet und visuell dargestellt werden.

Motivation und Ziel der Aufgabe

Die Motivation für dieses Projekt liegt in der Anwendung geosensorischer Methoden sowie in der Verknüpfung von Hardware-, Software-, und der Datenverarbeitung. Durch den Einsatz eines Rasperry Pi in Kombination mit einem Beschleunigungssensor (dem U Blox XXXX) und einem GPS-Empfänger wird ein vollständiger Messworkflow nach dem EVAP Modell dokumentiert.

Ziel der Aufgabe ist es, ein funktionsfähiges Messsystem aufzubauen, das während S-Bahn-Fahrten Beschleunigungsdaten aufzeichnet und diese eindeutig mit geografischen Koordinaten und Zeitstempel verknüpft. Die erhobenen Daten sollen anschließend mit Python verarbeitet, analysiert und visualisiert werden. Darüber hinaus soll untersucht werden, inwiefern sich die verschiedenen Fahrzeugbaureihen hinsichtlich ihres Vibrationsverhaltens unterscheiden.

Fragestellung

Ausgehend von der technischen Umsetzung und der Datenerhebung ergibt sich folgende zentrale Fragestellung:

Inwiefern unterscheiden sich die geosensorisch erfassten Vibrationsmuster der Berliner S-Bahn-Baureihen BR 480, BR 481 (modernisiert) sowie BR 483/484, und lassen sich diese Unterschiede räumlich und statistisch nachvollziehbar darstellen?

Aufbau der Messung

Das Messsystem basiert auf:

Zusammenbau

Vor dem Feldeinsatz wurde der Raspberry Pi eingerichtet und über einen externen Bildschirm überprüft. Dabei wurde sichergestellt, dass die Stromversorgung über USB-C stabil ist und eine Internetverbindung besteht. Zusätzlich wurde ein kleiner Bildschirm am Raspberry Pi angeschlossen, um die Daten im Feld live kontrollieren zu können. Auf einem Windows-Rechner wurde die zugehörige u‑blox Software installiert, um einen Überblick über die verfügbaren Sensordaten zu erhalten.

Für die Datenerfassung wurden relevante Codes vom u‑blox System kopiert und in die Python-Umgebung übertragen. Im Feld wurde der Python-Code innerhalb einer Anaconda-Projektumgebung gestartet. Alle 0,2 Sekunden werden dabei die aktuellen GPS-Koordinaten, zwei Zeitstempel (Raspberry Pi und GPS-Modul) sowie die Beschleunigungswerte in drei Achsen erfasst und in einer nach Datum benannten Textdatei gespeichert.

Die gespeicherten Daten werden anschließend am Rechner manuell ausgewertet.

Datenerhebung

Rahmenbedingungen und Vorgehensweise:

Für die Datenerhebung wurden zunächst zwei Verfahren geprüft: Entweder jede Baureihe einmal auf einer festen Strecke mit drei Stationen zu fahren oder jede Baureihe auf einer möglichst geraden Strecke über einen längeren Zeitraum zu messen. Da in Berlin keine Strecke existiert, auf der alle Baureihen verkehren, wurde die zweite Methode gewählt. Jede Baureihe wurde auf einer eigenen, möglichst geraden Strecke für etwa 10 Minuten gefahren, um Unterschiede durch Fahrweise, Gleisbeschaffenheit und andere Streckenparameter auszugleichen.

Mögliche Fehlerquellen

Grobe Fehler:

Verbindungsprobleme zwischen Raspberry Pi und EVK-F9DR (z. B. lose USB-Verbindung)

Ausfall der Stromversorgung während der Messung

Softwareabsturz oder falsches Starten des Python-Codes

Systematische Fehler:

Streckenvariabilität: Unterschiede in Gleisbeschaffenheit, Kurvenradien oder Weichen beeinflussen die Vibrationsdaten konstant auf bestimmten Streckenabschnitten

Fahrweise der Lokführer: konstante Unterschiede im Beschleunigungs- oder Bremsverhalten zwischen einzelnen Baureihen oder Fahrten

GNSS-Signalstörungen: Tunnel oder Unterführungen verursachen systematisch reduzierte Positionsgenauigkeit

Zufällige Fehler:

Umgebungsbedingte Vibrationen, z. B. Passagierbewegungen oder vorbeifahrende Züge

Kurzfristige Störungen in der Datenaufzeichnung (z. B. einzelne fehlerhafte Messpunkte)

Wetterabhängige Einflüsse, die nicht konstant auftreten (z. B. leichte Temperaturschwankungen, Regen)

Datenaufbereitung

Rohdaten

(HIER ROHDATEN BINÄR ZEIGEN)

Datenbereinigung

(Hier SCREENSHOT) 2026-01-02,11:47:37:753,10:47:36,13.435651902,52.509374338,8364.18,-0.400,-0.655,9.854,-0.679,-0.466,0.298 2026-01-02,11:47:37:959,10:47:36,13.435651902,52.509374338,8364.18,-0.382,-0.648,9.848,-0.679,-0.618,0.382 2026-01-02,11:47:38:157,10:47:36,13.435651902,52.509374338,8364.18,-0.378,-0.623,9.871,-0.595,-0.290,0.382 Die erfassten Daten liegen in einer strukturierten Textdatei vor und enthalten pro Messpunkt folgende Informationen:

date: Datum der Messung

timestamp: Zeitstempel des Raspberry Pi

gps_timestamp: Zeitstempel des GPS-Moduls

lon / lat: GPS-Koordinaten (Längengrad / Breitengrad)

accuracy: Genauigkeit der Positionsbestimmung

acc_x / acc_y / acc_z: Beschleunigungswerte in drei Achsen (m/s²)

gyro_x / gyro_y / gyro_z: Gyroskopwerte in drei Achsen (rad/s)

Alle Messwerte werden alle 0,2 Sekunden erfasst, wodurch eine hohe zeitliche Auflösung für die Analyse der Vibrationsmuster der S-Bahn-Fahrzeuge gegeben ist.

Datenanalyse und Visualisierung

Grafische Darstellung der Messdaten

# bibliotheken laden
library(stringr)
library(sf)
## Linking to GEOS 3.13.0, GDAL 3.10.1, PROJ 9.5.1; sf_use_s2() is TRUE
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(zoo)
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(tmap)
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(ggplot2)

# arbeitsverzeichnis festlegen
setwd("C:/Users/sphil/Nextcloud/Geosensorik/Python/")

# csv einlesen
daten <- read.csv("2026-01-21_12-29-26_ubx_F9K_log.csv", stringsAsFactors = FALSE) 

# neue spalte datetime erstellen - für spätere zuordnung der baureihen wichtig (datentyp muss stimmen)
daten <- daten |>
  mutate(
    datetime = ymd(date) +
      hms(str_replace(timestamp, ":(?=[^:]+$)", "."))
  ) 

# nach fahrzeiten zugeordnete baureihe 
daten <- daten |>
  mutate(
    baureihe = case_when( # mehrstufige if-bedingung
      datetime >= ymd_hms("2026-01-21 13:39:00") &
        datetime <= ymd_hms("2026-01-21 14:19:45") ~ "BR481_mod",

      #datetime >= ymd_hms("2026-01-21 15:41:00") &
       # datetime <= ymd_hms("2026-01-21 15:49:30") ~ "BR481_mod",

      datetime >= ymd_hms("2026-01-21 15:08:00") &
        datetime <= ymd_hms("2026-01-21 15:37:00") ~ "BR483_484",

      datetime >= ymd_hms("2026-01-21 16:09:00") &
        datetime <= ymd_hms("2026-01-21 16:41:00") ~ "BR480",

      TRUE ~ NA_character_ # wenn keine zeit zutrifft dann eintrag baureihe auf NA
    )
  ) |>
  filter(!is.na(baureihe)) # alle nicht relevanten zeilen (bauhreihe = NA) werden gelöscht
  # sonst wird auswertung verfälscht - nur aufzeichnungen behalten bei optimalen messbedingungen (umsteigen ausschließen usw.)

# räumliches sf objekt erstellen
daten_sf <- st_as_sf(
  daten,
  coords = c("lon", "lat"),
  crs = 4326,
  remove = FALSE
)

# vibrationsberechnung
# acc_x, acc_y, acc_z sind die Beschleunigungen in den drei Raumrichtungen
# acc_res = sqrt(acc_x^2 + acc_y^2 + acc_z^2))
daten_sf <- daten_sf %>%
  mutate(
    acc_res = sqrt(acc_x^2 + acc_y^2 + acc_z^2)
  ) %>% # die gesamtbewegung berechnen
  group_by(baureihe) %>% # sorteirt nach baureihe 
  mutate(
    acc_dyn = abs(acc_res - mean(acc_res, na.rm = TRUE)), # vibration ohne die normale beschleunigung 
    acc_smooth = rollmean(acc_dyn, k = 20, fill = NA) # geglättete version
  ) %>%
  ungroup() # für weitere berechnungen wieder ungruppieren

# statistischer vergleich tabelle
vibration_stats <- daten_sf %>%
  st_drop_geometry() %>% # wird nicht gebraucht für den vergleich
  group_by(baureihe) %>%
  summarise(
    mean_acc = mean(acc_dyn),
    sd_acc   = sd(acc_dyn),
    rms_acc  = sqrt(mean(acc_dyn^2)),
    q95_acc  = quantile(acc_dyn, 0.95)
  )
print(vibration_stats)
## # A tibble: 3 × 5
##   baureihe  mean_acc sd_acc rms_acc q95_acc
##   <chr>        <dbl>  <dbl>   <dbl>   <dbl>
## 1 BR480        0.246  0.344   0.422   0.913
## 2 BR481_mod    0.164  0.248   0.297   0.539
## 3 BR483_484    0.246  0.474   0.534   1.13
# boxplot mit acc_dyn für normalisierten vergleich
ggplot(daten_sf, aes(x = baureihe, y = acc_dyn, fill = baureihe)) +
  geom_boxplot(outlier.alpha = 0.3) +
  labs(
    title = "Vergleich der Vibrationsintensität nach Baureihe",
    x = "Baureihe",
    y = "Dynamische Beschleunigung"
  ) +
  theme_minimal()

# kartendarstellung
tmap_mode("plot")
## ℹ tmap mode set to "plot".
tm_shape(daten_sf) +
  tm_dots(
    col = "acc_dyn",
    palette = "Reds",
    size = 0.04,
    title = "Vibration"
  ) +
  tm_facets(by = "baureihe") +
  tm_layout(
    title = "Räumliche Verteilung der Vibrationen nach Baureihe",
    legend.outside = TRUE
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_tm_dots()`: migrate the argument(s) related to the scale of the
## visual variable `fill` namely 'palette' (rename to 'values') to fill.scale =
## tm_scale(<HERE>).
## [v3->v4] `tm_dots()`: use 'fill' for the fill color of polygons/symbols
## (instead of 'col'), and 'col' for the outlines (instead of 'border.col').
## [tm_dots()] Argument `title` unknown.
## [v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
## [cols4all] color palettes: use palettes from the R package cols4all. Run
## `cols4all::c4a_gui()` to explore them. The old palette name "Reds" is named
## "brewer.reds"
## Multiple palettes called "reds" found: "brewer.reds", "matplotlib.reds". The first one, "brewer.reds", is returned.
## 
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.

tmap_mode("view")
## ℹ tmap mode set to "view".
tm <- tm_shape(daten_sf) +
  tm_dots(
    col = "acc_dyn",
    palette = "viridis",
    size = 1,
    alpha = 0.7,
    popup.vars = c(
      "Baureihe" = "baureihe",
      "Vibration" = "acc_smooth"
    ),
    title = "Vibrationsstärke"
  )
## 
## ── tmap v3 code detected ───────────────────────────────────────────────────────
## [v3->v4] `tm_tm_dots()`: migrate the argument(s) related to the scale of the
## visual variable `fill` namely 'palette' (rename to 'values') to fill.scale =
## tm_scale(<HERE>).[v3->v4] `tm_dots()`: use `fill_alpha` instead of `alpha`.[tm_dots()] Argument `title` unknown.
tm
## Registered S3 method overwritten by 'jsonify':
##   method     from    
##   print.json jsonlite
# vergleich von acc smooth und zeit 
plot_data <- daten_sf %>%
  filter(!is.na(acc_smooth))

ggplot(plot_data, aes(x = datetime, y = acc_smooth, color = baureihe)) +
  geom_line(size = 1) +                   # Linien für die Vibrationen
  labs(
    title = "Geglättete Vibrationen (acc_smooth) pro Baureihe",
    x = "Zeit",
    y = "Geglättete Beschleunigung",
    color = "Baureihe"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1)
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Interpretation der Ergebnisse

Diskussion

Bewertung der Messergebnisse

Vergleich mit Erwartungen / Theorie

Limitationen des Versuchs

Fazit

Zusammenfassung der Ergebnisse

Beantwortung der Fragestellung

Ausblick und Verbesserungsvorschläge

Anhang

Code (R)

Zusätzliche Grafiken oder Tabellen

Technische Datenblätter