Real-time monocular ADAS backend for lane keeping and collision avoidance, using a single camera stream to produce steering and braking commands.
For a detailed explanation of the system architecture, algorithms, and design decisions, see THEORY.md.
The system follows a structured perception → planning → control pipeline.
- The camera frame (any resolution) is letterboxed to 256×256 for model inference.
- Two neural networks run in parallel:
- Road segmentation
- Object detection
- Outputs are mapped back to the original image space.
- The road mask is processed to extract a centerline.
- An MPC planner evaluates candidate trajectories using:
- road mask
- centerline
- GPS bias
- The lowest-cost trajectory determines steering.
- Object detections influence braking decisions.
- Control outputs and system telemetry are streamed to a web dashboard.
flowchart LR
subgraph Geometry
A[Camera Image] --> B[Letterbox 256x256]
end
subgraph Perception
B --> C[Road Segmentation]
B --> D[Object Detection]
end
subgraph Planning
C --> E[Centerline Extraction]
E --> F[MPC Trajectory Scoring]
end
subgraph Control
F --> G[Steering Output]
D --> H[Brake Estimation]
end
subgraph Telemetry
G --> I[Telemetry API]
H --> I
I --> J[Web Dashboard]
end
git clone https://github.com/SrabanMondal/edge-adas.git
cd edge-adasThe project requires ONNX model files for road/lane segmentation and object detection.
Run the download script to automatically fetch the required models.
python download.pyThis downloads:
yolopv2.onnx— road and lane segmentation modelyolov8n.onnx— object detection model (lightweight, good for most systems)
If your GPU supports newer architectures, you can download the newer YOLO26 model instead:
python download.py --newThis downloads:
yolopv2.onnx— road and lane segmentation modelyolov26n.onnx— newer object detection model with improved accuracy
All models are saved to:
src/weights/
| Model | Best For | Notes |
|---|---|---|
yolov8n.onnx |
CPU, older GPUs, Jetson devices | Lightweight and broadly compatible |
yolov26n.onnx |
Modern GPUs (RTX series, newer Intel/AMD iGPUs) | Higher accuracy but slightly heavier |
Both models although produce the different output format (but our code internally handles it), so they can be swapped without changing the rest of the pipeline.
The application uses optimized inference backends and therefore expects models in backend-specific formats.
Convert the ONNX models to one of the following formats:
- TensorRT Engine (.engine) → for NVIDIA GPUs
- OpenVINO IR (.xml + .bin) → for CPUs / Intel GPUs
Note: AMD Radeon GPUs are not directly supported by TensorRT or OpenVINO. To run inference on AMD GPUs you would need a different runtime such as ROCm, ONNX Runtime with ROCm, or a custom inference backend.
TensorRT engines can run in either FP16 or FP32 precision.
| Precision | When to use |
|---|---|
| FP16 | Recommended if your GPU supports it (RTX series, newer Jetson devices) |
| FP32 | Use if FP16 causes issues or is unsupported |
In testing, FP16 engines will not give high performance advantage due to its gpu architecture.
Convert using trtexec
FP16 (recommended if supported)
trtexec --onnx=src/weights/yolopv2.onnx \
--saveEngine=src/weights/yolop/yolop.engine \
--fp16FP32 (fallback)
trtexec --onnx=src/weights/yolopv2.onnx \
--saveEngine=src/weights/yolop/yolop.engine \
--fp32YOLOv8
trtexec --onnx=src/weights/yolov8n.onnx \
--saveEngine=src/weights/yolo/yolo.engine \
--fp16YOLO26
trtexec --onnx=src/weights/yolov26n.onnx \
--saveEngine=src/weights/yolo/yolo.engine \
--fp16For FP32, simply remove the --fp16 flag.
If you have the Ultralytics YOLO CLI installed, you can export the engine directly:
pip install ultralyticsThen run:
yolo export model=src/weights/yolov26n.onnx \
format=engine \
device=0 \
half=TrueMove the generated engine file to:
src/weights/yolo/yolo.engine
src/weights/
├── yolop/
│ └── yolop.engine
└── yolo/
└── yolo.engine
Keep these filenames unchanged — the application expects these exact paths. Unless you want to change filename paths in the code.
OpenVINO uses the OpenVINO Model Converter (ovc) to convert ONNX models to OpenVINO IR format.
If the ovc command is not available, install OpenVINO first:
pip install openvinoAfter installation, the ovc command should be available in your environment.
Convert YOLOPv2
ovc src/weights/yolopv2.onnx \
--output_dir src/weights/yolop/ov \
--compress_to_fp16YOLOv8
ovc src/weights/yolov8n.onnx \
--output_dir src/weights/yolo/ov \
--compress_to_fp16YOLO26
ovc src/weights/yolov26n.onnx \
--output_dir src/weights/yolo/ov \
--compress_to_fp16src/weights/
├── yolop/
│ └── ov/
│ ├── yolopv2.xml
│ └── yolopv2.bin
└── yolo/
└── ov/
├── yolov8n.xml (or yolov26n.xml)
└── yolov8n.bin (or yolov26n.bin)
Note:
If the generated filenames differ from what the code expects, update the model path constants at the top of:
src/camera_api.pysrc/camera_api_cpu.py- other test files
The original PyTorch models support dynamic input sizes and internally resize inputs during inference.
However, the ONNX models provided in this repository use a fixed input resolution of 256×256.
This decision was made for two reasons:
-
TensorRT compatibility
The development environment used TensorRT 8.0.1, which does not reliably support certain dynamic resize operations used by the original models.
Exporting the models with a static input shape avoids these compatibility issues.
-
Deterministic and lower-latency inference
By performing resizing outside the model, the pipeline becomes:
- more deterministic
- easier to optimize
- slightly faster (no internal resize operation)
Even though the model expects 256×256, the system supports any camera resolution.
During preprocessing, frames are:
- Letterboxed (aspect ratio preserved)
- Resized to 256×256
- Passed to the inference engine
This allows the pipeline to work with cameras such as:
- 640×480
- 720p
- 1080p
- RTSP streams
- phone cameras
We evaluated several model input resolutions:
| Input Size | Result |
|---|---|
| 128×128 | Very fast but noticeable accuracy drop |
| 256×256 | Best balance of speed and accuracy |
| 320×320 | Slightly better detection but higher latency |
| 480×480 | Slower with minimal improvement |
| 640×640 | Much slower for real-time use |
Based on testing, 256×256 provided the best latency vs accuracy trade-off for real-time ADAS inference.
The project depends on a small set of core Python libraries.
| Package | Purpose |
|---|---|
numpy |
Array operations and image math |
opencv-python |
Camera capture and image processing |
fastapi |
HTTP/SSE API server |
uvicorn |
ASGI server used to run FastAPI |
If you want to replicate the exact development environment used for this project, use:
- Python 3.12
- uv (modern Python package manager)
Then run:
uv syncThis installs the dependencies defined in pyproject.toml and ensures compatible versions.
If your system:
- uses an older Python version
- runs on older hardware (Jetson Nano, Raspberry Pi, etc.)
- cannot easily install Python 3.12
you can install dependencies manually with pip.
pip install numpy opencv-python fastapi uvicornIn this case, pip will select the latest compatible versions for your Python version and platform.
This project has been tested to run on Python 3.6 and newer.
However:
- Python 3.12 + uv → recommended for reproducing the development setup
- Python 3.6+ + pip → works on older systems and embedded devices
Tip:
If you want to match the exact versions used during development, check the dependency versions listed inpyproject.toml.
If you plan to use the TensorRT pipeline (camera_api.py), you need additional GPU-specific packages.
| Package | Purpose |
|---|---|
pycuda |
Python bindings for CUDA (GPU memory management) |
tensorrt |
NVIDIA inference runtime |
| OpenCV with CUDA | Required for GPU-accelerated image operations |
These packages require careful version matching with your CUDA toolkit and GPU driver. Follow the official guides:
- TensorRT: NVIDIA TensorRT Install Guide
- PyCUDA: PyCUDA Installation
- OpenCV with CUDA: Build OpenCV with CUDA from source
- Install CUDA Toolkit first, then TensorRT, then PyCUDA.
- Make sure
nvcc --versionworks in your terminal before installing PyCUDA. - On Jetson devices, TensorRT and CUDA are typically pre-installed with JetPack.
If using the OpenVINO pipeline (camera_api_cpu.py), install the OpenVINO runtime instead:
pip install openvinoNo CUDA or GPU driver setup is required for the OpenVINO path, though Intel GPU acceleration is available if your system supports it.
Before running the full application, use the test scripts in src/test/ to validate that models load and inference works.
| File | What it tests | Backend | How to run |
|---|---|---|---|
test_model.py |
OpenVINO engine loads and runs inference on dummy input | OpenVINO | python -m src.test.test_model |
test_model_trt.py |
TensorRT engine loads and runs inference on dummy input | TensorRT | python -m src.test.test_model_trt |
debug_engine.py |
Probes TRT engine bindings, shapes, and runs dummy inference | TensorRT | python -m src.test.debug_engine |
benchmark.py |
Benchmarks raw TRT engine latency and throughput | TensorRT | python -m src.test.benchmark |
test_infer.py |
Full OpenVINO pipeline on video (road + objects + steering) | OpenVINO | python -m src.test.test_infer |
test_trt_infer.py |
Full TensorRT pipeline on video (road + objects + steering) | TensorRT | python -m src.test.test_trt_infer |
trt_object_test.py |
TRT object detection on video with visual debug output | TensorRT | python -m src.test.trt_object_test |
trt_yolop_test.py |
TRT road segmentation on video with visual debug output | TensorRT | python -m src.test.trt_yolop_test |
test_gps.py |
GPS checkpoint logic (no models needed) | None | python -m src.test.test_gps |
test_video.py |
Video checker and put the converted video to src/data for testing | None | python -m src.test.test_video |
- Run
test_model.py(OpenVINO) ordebug_engine.py(TensorRT) — confirms model files load correctly. - Run
test_video.pyto create the clean video ready to be used for testing and debugging - Run
test_infer.pyortest_trt_infer.py— confirms the full pipeline works end-to-end.
- Video-based tests (
test_infer.py,test_trt_infer.py, etc.) expect a video file atsrc/data/input.mp4. You need to provide your own test video. - Model paths are hardcoded at the top of each test file. If your converted model filenames differ, edit the path constants before running.
- Use the
--demoflag withtest_infer.py/test_trt_infer.pyto generate a visual debug video:
python -m src.test.test_infer --demoOnce all tests pass, start the camera pipeline.
Use this if your system has an NVIDIA GPU and you built the TensorRT engine.
python -m src.camera_apiUse this if you are running on CPU or Intel GPU with the OpenVINO runtime.
python -m src.camera_api_cpuBoth pipelines start the camera stream and launch the telemetry server.
The MPC controller already supports GPS-based bias correction. If your device has GPS access, you can stream GPS coordinates into the frame processing pipeline.
Steps:
-
Implement GPS reading inside the frame processing logic.
-
Follow the example in
test_gps.py. -
Insert the GPS update checkpoints inside:
camera_api.pycamera_api_cpu.py
-
Pass the returned
gps_biasvalue to the MPC controller.
The test_gps.py script demonstrates how the checkpoint system works and how GPS bias is calculated while moving along a route.
Set the camera source using the CAMERA_IP environment variable.
The application supports:
- Local webcams
- RTSP network cameras
- HTTP video streams (e.g., phone camera apps)
# Use default webcam (index 0)
export CAMERA_IP=0
# Use an IP camera stream
export CAMERA_IP=rtsp://192.168.1.100:554/stream# Use default webcam
$env:CAMERA_IP=0
# Use an IP camera stream
$env:CAMERA_IP="rtsp://192.168.1.100:554/stream"set CAMERA_IP=0set CAMERA_IP=rtsp://192.168.1.100:554/streamIf you don't have an IP camera, you can test using your phone.
- Install an IP Webcam app on your phone.
- Connect your phone and computer to the same Wi-Fi network.
- Start the server inside the app.
The app will provide a stream URL similar to:
http://PHONE_IP:8080/videoExample:
export CAMERA_IP=http://192.168.1.45:8080/videoThis will stream your phone camera feed directly into the application.
Local webcam:
CAMERA_IP=0RTSP camera:
CAMERA_IP=rtsp://192.168.1.100:554/streamPhone camera:
CAMERA_IP=http://192.168.1.45:8080/videoAfter setting the variable, start the application normally.
Once the server starts, open the dashboard in your browser:
http://localhost:8000
This interface provides a live telemetry dashboard, including:
- brake force
- steering angle
- inference latency
- FPS (frames per second)
You can also view the interface from another device (phone, tablet, laptop).
Requirements:
- Both devices must be connected to the same Wi-Fi network.
- Find the local IP address of the machine running this application.
- Replace
localhostwith that IP.
Example:
http://192.168.1.25:8000The IP address is usually shown when the server starts, or you can find it with:
Linux / macOS
ip aWindows
ipconfig| Endpoint | Description |
|---|---|
http://localhost:8000/ |
Web dashboard with live telemetry and camera preview |
http://localhost:8000/api/telemetry |
Latest telemetry snapshot (JSON) |
http://localhost:8000/api/telemetry/stream |
Real-time telemetry stream (Server-Sent Events) |
These endpoints can be used for external dashboards, logging systems, or robotics integrations.
The system was tested on two representative hardware environments.
Hardware:
- Intel i5 CPU
- Intel Iris Xe integrated GPU
- 16 GB RAM
- Windows
Backend:
- OpenVINO
Performance:
- 24–26 FPS
- No frame skipping
- Real-time processing with full pipeline enabled.
Hardware:
- NVIDIA Jetson Nano
- 4 GB RAM
- 128-core Maxwell GPU
Software environment:
- JetPack 4.6.1
- TensorRT 8.0.1
- CUDA 10.2
- cuDNN 8.2.1
Performance:
- 23–25 FPS effective throughput
- Every 3rd frame processed
Frame skipping is used on the Nano to maintain stable real-time performance under limited GPU resources.
These results demonstrate that the pipeline can run in real-time on both embedded edge devices and standard laptops.
MIT License