Skip to content
All projects

Road Semantics

Python pipeline that turns vehicle speed and GPS logs into RDF road-quality data and plain-English reports.

Role
Backend Developer
Timeframe
April 2024
Stack
  • Python
  • Raspberry Pi
  • RDF (rdflib)
  • SPARQL
  • OWL (Owlready2)
  • Flask
  • pandas
  • R-tree
Visit roadsemantics.in (opens in a new tab)

Problem

The project rates road quality from everyday drives. A Raspberry Pi in the vehicle logs speed and GPS position to a CSV file, and that raw log had to become structured data that many drives could add to. GPS readings from different trips never land on exactly the same coordinates, and the Pi has little compute to spare. The final output also had to be readable by drivers, not just by machines.

Architecture

On the vehicle, a watchdog service follows the sensor CSV, classifies each reading with pandas, and writes the road section as an RDF (Turtle) file typed against a shared OWL road-quality ontology. At the aggregation point, a second watcher loads each new Turtle file with the ontology in rdflib and extracts points with SPARQL. An R-tree matches those points to known road locations, and the aggregated RDF is rebuilt once enough drives build up. A summary job queries the aggregated graph, reverse-geocodes the start and end with Geoapify, and uploads a text report to a Flask API on the website’s server.

  1. Raspberry Pi watches the sensor CSV and classifies each reading
  2. Turtle file reaches the aggregation point and loads with the ontology
  3. R-tree matches points to known locations and averages speeds
  4. Report generator runs SPARQL and reverse-geocodes with Geoapify
  5. Text report is uploaded to the Flask API on the web server

What I built

  • Built the on-vehicle service for the Raspberry Pi: a watchdog observer on the live CSV, pandas cleaning (duplicate and invalid GPS fixes dropped), and a sliding-window speed-change classifier with five movement classes.
  • Mapped each movement class to an ontology class (good to go, drive with caution, bump) and generated RDF with rdflib, upserting only the 10 newest readings on each CSV change instead of rebuilding every triple.
  • Replaced a nested-loop proximity check with an R-tree spatial index and Haversine distance, using a 5 m threshold to match incoming points to reference locations.
  • Built the aggregation step: per-location speed history in JSON, averaging and reclassification after six drives, regeneration of the aggregated RDF, and timestamped archiving of the previous version.
  • Wrote the SPARQL report generator: patch-type percentages, bump counts, section length, and average speed, turned into a paragraph with rule-based wording and Geoapify place names.
  • Built the Flask upload endpoint (secure_filename, extension allow-list, JSON errors) behind an ngrok tunnel, plus process pools and logging across all components.

Results

  • The pipeline ran end to end on real drives. Sample reports cover sections from 0.68 km to 16.59 km, including repeated runs over a 16.59 km road, each with a bump count, ride quality, and suggested speed.
  • Readings from separate drives merge into one aggregated graph, so a road’s rating is averaged across trips instead of resting on a single drive.
  • Road data is stored as standard RDF against a shared ontology, so any SPARQL client can query it without a custom parser.
  • Shipped as four deployable parts (vehicle, aggregation point, reports, web API), each with its own config and logging.