Skip to content

KOKOSde/ApexVelocity

Repository files navigation

🏎️ ApexVelocity

Physics-Based Road & Track Annotation Engine — QGIS-native geospatial datasets for AVs and research

Turn raw OpenStreetMap road geometry into physics-aware GIS layers — GeoPackage / GeoJSON with per-segment velocity, cornering G, grip usage, safety margin and energy — that drop straight into QGIS as styled, analysis-ready choropleths.

C++20 Python 3.10+ QGIS CRS License: MIT

AboutQGIS & GISGalleryFeaturesQuick StartInstallationUsageArchitecture

Stelvio Pass annotated with lateral G-force, rendered over satellite imagery
Stelvio Pass (SS38), Italy — 48 hairpins annotated with lateral G-force. Exported from ApexVelocity as a GeoPackage and styled with the bundled QGIS color ramp.


About

ApexVelocity turns static road networks into physics-aware datasets you can use to benchmark planners, controllers, and energy models in the real world.

It is built for:

  • Geospatial analysts (QGIS / ArcGIS): Generate richly-attributed road layers — GeoPackage, GeoJSON, with ready-made .qml styles — for cartography, suitability/risk mapping, and HD-map QA without writing a physics stack.
  • AV and robotaxi teams: Stress‑test motion planners across vehicles, weather conditions (dry/wet), and routes using a consistent C++20 physics core.
  • PhD researchers: Generate reproducible benchmarks and rich per‑segment features (lateral/longitudinal G, jerk, friction usage, comfort/safety/difficulty scores, energy).
  • Startups and tooling teams: Stand up a CLI or HTTP service that annotates OSM / HD map data in hours instead of rebuilding a physics stack from scratch.

Under the hood, ApexVelocity combines:

  • A GIS export layer that writes OGC-standard GeoPackage / GeoJSON in EPSG:4326, with QGIS graduated styles embedded directly in the file.
  • A first-class QGIS Processing plugin so analysts can annotate routes from the Processing Toolbox.
  • A high‑performance C++20 core solver (3‑pass velocity profile, friction, rollover, energy).
  • A batteries‑included Python package for routing, feature generation, and visualization.
  • An optional Go HTTP server for remote /v1/analyze integration.

QGIS & GIS Integration

ApexVelocity is GIS-native. Every analysis can be written as an OGC-standard vector dataset that opens, pre-styled, in QGIS, ArcGIS Pro, or any geopandas / web-map pipeline — no manual symbology required.

What you get per export:

Product Description
*.gpkg (GeoPackage) A apex_segments line layer (one feature per OSM edge) with 35+ physics attributes, plus an apex_segments_nodes point layer. EPSG:4326.
Embedded QGIS styles Six graduated .qml styles (speed, friction usage, safety margin, difficulty, lateral-G, energy) saved into the GeoPackage layer_styles table — the layer renders as a choropleth the instant you add it.
*.geojson The same segments as GeoJSON, with a sidecar .qml for one-click styling.
qgis/styles/*.qml Stand-alone, shareable QGIS layer styles.

Open it in QGIS (zero configuration):

# Generate the sample datasets + styles (data/, qgis/styles/)
python python/examples/generate_gis_assets.py

Then in QGIS: Layer → Add Layer → Add Vector Layer → data/dragon.gpkg. The layer arrives already styled by its physics. Switch metrics any time via Layer Properties → Symbology → Load Style → from the GeoPackage database.

These are genuine QGIS print-layout exports of the bundled GeoPackages (rendered by QGIS 4, not a mock-up):

Tail of the Dragon velocity profile, QGIS print layout Stelvio Pass lateral G-force, QGIS print layout

QGIS Processing plugin

Annotate routes from inside QGIS — no scripting. The qgis/apexvelocity_plugin folder is a drop-in QGIS plugin that adds an ApexVelocity provider to the Processing Toolbox:

  1. Copy qgis/apexvelocity_plugin/ into your QGIS profile's python/plugins/ (or zip it and use Plugins → Install from ZIP), then enable it.
  2. Run Processing Toolbox → ApexVelocity → Annotate route with physics on any line layer. Inputs: road condition (dry/wet), posted speed limit, vehicle mass.
  3. Get a styled, physics-annotated segment layer back, ready for further analysis.

The algorithm uses the full ApexVelocity engine when the Python package is on the QGIS Python path (set APEXVELOCITY_PATH), and falls back to a self-contained cornering model otherwise, so it always works as a drop-in.

See docs/QGIS_WORKFLOW.md for the complete analyst workflow (loading, styling, switching metrics, joins, and print layouts).

Gallery

Cartographic renders produced directly from the exported layers (python/examples/generate_gis_assets.py) using the same color ramps as the QGIS styles. Basemaps © CartoDB / Esri, © OpenStreetMap contributors.

Tail of the Dragon velocity profile over satellite imagery
Tail of the Dragon (US-129) — physics velocity profile. Green on the straights, red through the 318 curves.

San Francisco urban route, four-metric dashboard
San Francisco urban route — one GeoPackage, four physics metrics (speed, energy, lateral-G, safety margin).

Tail of the Dragon four-metric dashboard
The same multi-metric annotation on the Dragon: speed, lateral-G, difficulty and safety margin.

Export from Python in three lines

from apexvelocity import RouteRequest
from apexvelocity.gis import route_to_geopackage

route_to_geopackage(
    RouteRequest(start=(35.5466, -83.9352), end=(35.4668, -83.9990)),
    "dragon.gpkg",                 # open in QGIS — already styled by speed
    default_metric="speed_kmh",
)

Or straight from the CLI:

python -m apexvelocity.cli --from-lat 46.6178 --from-lon 10.5894 \
    --to-lat 46.5285 --to-lon 10.4543 --color-by lateral_g \
    --gis-export stelvio.gpkg

Overview

ApexVelocity is a high-performance physics engine that annotates road networks with realistic velocity profiles and energy consumption estimates. It combines:

  • Real Physics: Friction-limited cornering, rollover prevention, power/braking constraints
  • Material Awareness: Different surfaces (asphalt, gravel, cobblestone) affect grip and rolling resistance
  • Energy Modeling: Accurate energy predictions including aerodynamic drag, grade resistance, and regenerative braking potential
  • Configurable Universe: Swap between Earth, Mars, or custom physics constants

Features

QGIS-Ready GIS Export

  • GeoPackage (.gpkg) and GeoJSON output in EPSG:4326 (WGS-84)
  • Line layer (one feature per road segment) + node point layer, 35+ physics attributes
  • Six graduated QGIS styles embedded in the GeoPackage — open and it's a choropleth
  • A drop-in QGIS Processing plugin for in-app annotation

Physics-Based Velocity Profiling

  • 3-pass kinematic solver (static limits → backward braking → forward acceleration)
  • Friction-limited cornering speeds from road curvature
  • Vehicle-specific power and braking constraints

Energy Consumption Modeling

  • Aerodynamic drag: F = ½ρCdAv²
  • Rolling resistance with surface-specific coefficients
  • Grade resistance for hills and elevation changes

Interactive 3D Visualization

  • Dark-themed maps with glowing velocity tubes
  • Height extrusion shows lateral G-force danger zones
  • Rich tooltips with physics data

Plug-and-Play CLI

  • Analyze any city with a single command
  • Automatic OSM data fetching and processing

Quick Start

No API keys required! ApexVelocity works out of the box with free OpenStreetMap tiles.

# Analyze any city (uses free OSM basemap)
python -m apexvelocity.cli --city "Tokyo, Japan"

# Run the San Francisco benchmark
cd python
python examples/demo_san_francisco.py

# Run the Nürburgring wet/dry comparison
python examples/benchmark_nurburgring.py

Optional: Satellite Imagery

For high-quality satellite maps, get a free Mapbox token:

  1. Sign up at mapbox.com
  2. Copy your public token from account.mapbox.com/access-tokens/
  3. Set the environment variable:
    export MAPBOX_API_KEY='pk.your_token_here'
  4. Run with --style satellite:
    python -m apexvelocity.cli --city "Manhattan, NYC" --style satellite

If you do not set MAPBOX_API_KEY:

  • The CLI and visualization APIs will still run and will automatically fall back to free styles (e.g., osm, dark, or light).
  • You will see a warning in the console explaining that satellite imagery is unavailable and that a free Mapbox token can be added later.

Installation

Prerequisites

  • Python 3.10+
  • CMake 3.16+ (for C++ core)
  • C++20 compiler (GCC 10+, Clang 12+)

Python Package (Recommended)

cd python
pip install -e .

Build C++ Core

cd core
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
ctest --output-on-failure

Full Installation

# Clone the repository
git clone https://github.com/KOKOSde/ApexVelocity.git
cd ApexVelocity

# Install Python dependencies
pip install -r requirements.txt

# Build C++ core and Python bindings
cd core && mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

# Install Python package
cd ../../python && pip install -e .

Docker (Production Server)

You can run the ApexVelocity HTTP API as a containerized service.

# From the repo root
docker build -t apexvelocity:local .

# Run the server on http://localhost:8080
docker run --rm -p 8080:8080 apexvelocity:local

# Health check
curl http://localhost:8080/health

For a more complete setup (with restart policy and mounted config), use Docker Compose:

docker-compose up --build

Usage

Command-Line Interface

# Basic usage
python -m apexvelocity.cli --city "San Francisco, CA"

# With options
python -m apexvelocity.cli \
    --city "Manhattan, New York City, USA" \
    --vehicle tesla_model_3 \
    --condition wet \
    --mode energy \
    --output nyc_analysis.html

# Available vehicles: tesla_model_3, compact_car, sports_car, suv
# Visualization modes: speed, energy, gforce
# Conditions: dry, wet

CLI solver path:

  • Uses the high-performance C++ core solver by default (via Python bindings).
  • If the native extension is unavailable, it falls back to a simplified Python solver and emits a warning. Results may differ slightly from the core.

Python API (Stable 1.x Surface)

from apexvelocity import solve_profile, VehicleLoader
from apexvelocity.loader import load_path_from_place
from apexvelocity.viz import visualize_profile

# Load a route
path = load_path_from_place("San Francisco, California")

# Get vehicle parameters
vehicle = VehicleLoader.load_vehicle_params("tesla_model_3")

# Solve velocity profile
geometry, surfaces = path.to_geometry_list()
result = solve_profile(geometry, surfaces, vehicle, condition="dry")

# Visualize
visualize_profile(path.points, mode="speed", output_html="analysis.html")

Data loading:

  • For programmatic ingestion, prefer the stable 1.x APIs:
    • RouteRequest + build_route (canonical start→end routing on top of OSM)
    • solve / solve_profile (C++ core velocity/energy solver)
    • analyze_profile (rich segment‑level AV features)
    • convert_segments_for_viz (dicts for visualization/ML)
  • apexvelocity.osm_fetcher is experimental and primarily used by bundled examples. Its API may change in minor 1.x releases.

Configuration

ApexVelocity uses YAML/JSON configuration files in the config/ directory:

# config/simulation.yaml
gravity: 9.81           # m/s² (use 3.71 for Mars!)
air_density: 1.225      # kg/m³
step_size_meters: 1.0
enable_rollover_checks: true
global_friction_multiplier: 1.0  # Reduce for wet/icy conditions
{
  "asphalt": { "mu_dry": 0.90, "mu_wet": 0.60, "rolling_resistance_coeff": 0.015 },
  "cobblestone": { "mu_dry": 0.55, "mu_wet": 0.40, "rolling_resistance_coeff": 0.025 },
  "gravel": { "mu_dry": 0.60, "mu_wet": 0.45, "rolling_resistance_coeff": 0.020 }
}

HTTP API Authentication

The Go HTTP server exposes the /v1/analyze endpoint for remote analysis. In production you should protect it with API keys and per-key rate limiting.

  • Configure keys in config/auth.yaml:

    api_keys:
      - name: "dev_key"
        hash: "$2a$10$..."   # bcrypt hash of your dev API token
        rate_limit: 100      # requests per minute
    
      - name: "prod_key"
        hash: "$2a$10$..."
        rate_limit: 1000
    
    admin_keys:
      - name: "admin"
        hash: "$2a$10$..."
    
    auth_disabled: false
  • Send requests with a Bearer token:

    curl -X POST "http://localhost:8080/v1/analyze" \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer YOUR_PLAINTEXT_API_KEY" \
      -d '{ "geometry": [...], "surface": [...], "vehicle": "tesla_model_3", "condition": "dry" }'
  • For local development you can disable auth with:

    export AUTH_DISABLED=true

Versioning & Stability

ApexVelocity follows semantic versioning:

  • Stable in 1.x (Python):
    • RouteRequest, build_route
    • solve, solve_profile
    • analyze_profile, ProfileAnalysis, SegmentFeatures, convert_segments_for_viz
    • visualize_profile, visualize_profile_interactive, visualize_comparison
    • export_routes_to_parquet (dataset exporter)
  • Experimental (subject to change in 1.x):
    • apexvelocity.osm_fetcher (Overpass helpers and special‑case tracks)
    • apexvelocity.envs (Gymnasium / RL environments)
    • Example scripts under python/examples/

Python support:

  • The published package targets Python 3.10+ (see pyproject.toml).

Architecture

ApexVelocity/
├── core/                    # C++20 Physics Engine
│   ├── include/
│   │   ├── physics/         # PhysicsMath, VelocityProfileSolver
│   │   ├── utils/           # ConfigManager
│   │   └── capi/            # C API for FFI
│   ├── src/
│   └── tests/               # GoogleTest suite
│
├── python/                  # Python Interface
│   ├── apexvelocity/
│   │   ├── api.py           # High-level Python API
│   │   ├── loader.py        # OSM data loading
│   │   ├── gis.py           # GeoPackage / GeoJSON export (QGIS-ready)
│   │   ├── qgis_style.py    # QGIS .qml graduated style generation
│   │   ├── cartography.py   # Publication-grade static map rendering
│   │   ├── viz.py           # PyDeck visualization
│   │   ├── cli.py           # Command-line interface
│   │   └── envs/            # Gymnasium RL environment
│   └── examples/
│       ├── generate_gis_assets.py   # Build GeoPackages, styles & maps
│       ├── demo_san_francisco.py
│       └── benchmark_nurburgring.py
│
├── qgis/                    # QGIS integration
│   ├── apexvelocity_plugin/ # Processing Toolbox plugin (drop-in)
│   ├── styles/              # Shareable .qml layer styles
│   └── render_layouts.py    # PyQGIS print-layout renderer
│
├── data/                    # Sample QGIS-ready GeoPackages + GeoJSON
├── assets/maps/             # Cartographic map renders
│
├── server/                  # Go REST API (optional)
│   └── cmd/apex-server/     # `go build ./cmd/apex-server` after building core
│
└── config/                  # Configuration files
    ├── simulation.yaml
    ├── materials.json
    ├── tag_mapping.json
    └── vehicle_presets/

Building the Go Server (Optional)

To build the REST API server (requires Go 1.20+ and a C++20 toolchain):

# 1) Build the C++ core (needed for the cgo bridge)
cd core
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j"$(nproc)"

# 2) Build the Go server
cd ../../server
go build ./cmd/apex-server

This produces an apex-server binary that exposes:

  • GET /health
  • POST /v1/config/reload
  • POST /v1/analyze

Benchmarks

San Francisco Hill Climb

Tests the energy model on steep gradients (Lombard Street area):

Vehicle: Tesla Model 3 (1847 kg)

UPHILL:
  Energy consumed: 1.36 kWh
  Hairpin corners: Speed drops to 24 km/h

DOWNHILL:
  Energy consumed: 0.97 kWh
  Regen potential: 0.25 kWh

✓ PASS: Uphill energy > Downhill energy (ratio: 1.4x)

Nürburgring Wet vs Dry

Tests the friction model under varying conditions:

Vehicle: High-Performance Sports Car (350 kW)

DRY:  Lap time 14.2 min, Max speed 214 km/h
WET:  Lap time 15.9 min, Max speed 202 km/h

✓ PASS: Wet 11.9% slower (friction model validated)

nuScenes Validation (Optional)

You can validate ApexVelocity against real-world nuScenes data and generate residuals for uncertainty training:

  • Prerequisites:

    • Install the nuScenes devkit: pip install nuscenes-devkit
    • Download the nuScenes dataset separately and note the dataroot path.
  • Run validation and export residuals:

    cd python
    python -m validation.validate_nuscenes \
        --dataroot /path/to/nuscenes \
        --version v1.0-mini \
        --split train \
        --max-scenes 10 \
        --vehicle tesla_model_3 \
        --condition dry \
        --output validation/nuscenes_residuals.csv

The resulting CSV is directly compatible with validation.uncertainty_training and can be used to train a dataset-specific uncertainty model.

Physics Model

Friction-Limited Cornering Speed

v_max = √(μ · g / κ)

where:
  μ = friction coefficient (surface + condition)
  g = gravitational acceleration
  κ = curvature (1/radius)

Energy Per Segment

E = (F_aero + F_roll + F_grade) · Δs

F_aero = ½ρ · Cd · A · v²
F_roll = Crr · m · g · cos(θ)
F_grade = m · g · sin(θ)

Velocity Profile Solver (3-Pass)

  1. Static Limits: Compute max speed at each point from curvature + friction
  2. Backward Pass: Propagate braking constraints from end to start
  3. Forward Pass: Propagate acceleration constraints from start to end

Advanced Dynamics (Experimental)

For research scenarios you can enable an experimental dynamic vehicle model:

  • The core C++ solver supports a PhysicsModel enum in SolverConfig:

    • KINEMATIC (default): original 3-pass curvature+friction+rollover solver.
    • DYNAMIC: single-track (bicycle) model with:
      • Pacejka-style tire forces (lateral + longitudinal).
      • Simple longitudinal weight transfer using cog_height_m and wheelbase_m.
      • First-order actuator lag on steering and throttle/brake.
  • In C++ you can opt in via:

    apex::physics::SolverConfig cfg;
    cfg.model = apex::physics::PhysicsModel::DYNAMIC;

When DYNAMIC is selected, the solver integrates a DynamicVehicle along the path using the same material μ values from config/materials.json, then computes energy on top of the dynamic velocity profile. This mode is experimental and intended for validation and “what-if” studies rather than strict real-time control.

Uncertainty Quantification (Research)

For research workflows you can attach confidence intervals around the physics-based speed profile:

  • Generate residuals (ground-truth vs ApexVelocity predictions) into a CSV with at least:

    • speed_kmh_true, speed_kmh_pred
    • curvature_1pm, grade_percent, physics_limit_kmh
    • friction_mu_effective, energy_kwh_per_km
  • Train an uncertainty model:

    cd python
    python -m validation.uncertainty_training \
        --csv path/to/residuals.csv \
        --output models/uncertainty_model.joblib \
        --coverage 0.9
  • Use the model at inference time:

    from apexvelocity.analysis import analyze_profile
    from apexvelocity.uncertainty import load_uncertainty_model, predict_speed_intervals
    
    analysis = analyze_profile(path_points)
    # If you saved the model under python/models/:
    import joblib
    bundle = joblib.load("python/models/uncertainty_model.joblib")
    model = bundle["model"]
    meta = bundle["metadata"]
    
    intervals = predict_speed_intervals(
        analysis.segments,
        model=model,
        nominal_coverage=meta["nominal_coverage"],
        coverage_scale=meta["coverage_scale"],
    )
    # intervals[i].speed_kmh_low / speed_kmh_high now approximate a 90% band

The default repo does not ship a nuScenes-trained model; you are expected to run the training script against your own validation dataset and, if desired, check in the resulting uncertainty_model.joblib for reproducible research.

Contributing

Contributions are welcome! Please read our contributing guidelines and submit PRs.

Publishing to Your Own GitHub Repository

To publish ApexVelocity under your own GitHub account:

  1. Create a new empty repository on GitHub (e.g., USERNAME/ApexVelocity).
  2. Add the remote in your local clone:
    git remote add origin git@github.com:USERNAME/ApexVelocity.git
    # or, using HTTPS:
    # git remote add origin https://github.com/USERNAME/ApexVelocity.git
  3. Create a Personal Access Token (PAT) if you use HTTPS:
    • Go to GitHub → Settings → Developer settings → Personal access tokens.
    • Generate a token with repo scope.
    • When pushing over HTTPS, use your GitHub username and the PAT as the password when prompted.
  4. Push the code and tags:
    git push -u origin master        # or main, depending on your branch name
    git push origin v1.0.0           # push the 1.0.0 tag

Tokens and credentials are never stored in this repository; you manage them locally via your own Git configuration or credential helper.

License

MIT License - see LICENSE for details.

Acknowledgments

  • OpenStreetMap contributors for road network data
  • OSMnx for Python OSM interface
  • GeoPandas, Shapely & Fiona for GIS export; QGIS for desktop geospatial analysis
  • contextily, CartoDB & Esri for basemap tiles
  • PyDeck/Mapbox for visualization

Made with ⚡ for realistic road physics simulation

About

Physics-based road & track annotation engine for AV/robotaxi teams and researchers (C++20 core, Python+Go frontends).

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages