A forward collision and lane departure warning aid built from scratch on a Raspberry Pi Zero 2W with an IMX219 camera and a passive buzzer. It watches the road ahead, and when it thinks you are closing on something too fast, or drifting out of your lane, it beeps. That is all it does: there are no inputs from the car (no speed, no CAN, no turn signal) and no outputs to it (no braking, no steering). It is a hobby project, not a certified safety system, and it must not be relied on — see the disclaimer at the end.
- FCW, two stages — a soft attention chime and a warning beep pattern for the vehicle ahead. The trigger is closing rate (1 / time to contact) from box scale growth fused with ground-plane range, plus a required-deceleration test so that normally stopping behind stopped traffic stays silent. Named suppression rules (out of path, ego stopped, driver braking, cooldown, low quality, ...) are logged with every decision (docs/05).
- Pedestrians and cyclists get their own rule inside the FCW logic, with a time-to-contact gate of their own (docs/05).
- LDW above 50 km/h (
ldw_min_speed_kmh, adjustable) — below that, city markings and normal lane changes make it noise (docs/06). - Lead-moved chime when the car in front of you pulls away and you have not.
- Event clips and traces — every alert saves an mp4 (8 s before, 4 s after), a 12 s per-frame JSONL dump and an event record carrying the settings that were active.
- Phone page over the device's own Wi-Fi: live view with the corridor, lanes and boxes drawn, draggable calibration lines, settings sliders, mute, a large driving mode, the event list and a "Wrong?" button that tags a false alert into the log for later review.
| Part | Note |
|---|---|
| Raspberry Pi Zero 2W | 4 x Cortex-A53 @ 1 GHz, 512 MB. Heatsink recommended |
| Camera | IMX219 (Raspberry Pi Camera v2), 77 degree diagonal lens, CSI ribbon for the Zero |
| Buzzer | passive piezo on GPIO18 (hardware PWM0), no amplifier |
| Power | 5 V from the car, a good cable (a sagging supply shows up as throttled in the log) |
| Storage | 32 GB card is plenty |
Mount it on the windshield like a dashcam, about 1.05 m above the road, camera looking
straight ahead and level. That height is the one value worth getting roughly right; the device
also measures it from the lane width and shows both numbers on the phone page. A mount behind
the mirror (about 1.35 m) works too. If the module ends up upside down, set rotate_180 in the
settings.
- 5 to 6 fps end to end at 320x256 detector input, two passes alternating full frame and centre crop.
- Detector 130-145 ms per pass when cool, about 230 ms hot (65-69 C). The buzzer decision costs nothing next to that.
- City speeds only. Vehicles are tracked to roughly 25 m in the full frame and 45-50 m in the centre crop, so highway-distance warnings are a documented blind spot.
- Night is a degraded mode. The IMX219 has small pixels; unlit roads, heavy rain and low sun all cost detections.
You need a Linux/macOS machine to build from; nothing is compiled on the Pi.
- Flash Raspberry Pi OS trixie (Debian 13) 64-bit with the Imager. Enable SSH and set
the user and your Wi-Fi in the Imager's settings. Hostname
adaspi(soadaspi.localresolves). Attach the camera, wire the buzzer to GPIO18 and ground, boot. - Build for the Pi — an ARM64 Mac or any Docker host:
scripts/fetch_models.sh deploy/build_pi.sh # arm64 Debian toolchain image, static ncnn -> build-pi/ - Install (set
ADAS_PI_HOST=user@hostif you are notpi@adaspi.local):deploy/install_pi.sh - Car mode, once, on the Pi — the buzzer overlay, the always-on hotspot and the boot
service:
Change the hotspot password. The default is
sudo bash ~/adas-zero/setup_car_mode.sh # AP_PASS=yourpassword sudo -E bash ... to set the Wi-Fi password sudo rebootadaszero1and it is printed in this public repository; passAP_PASS=...to the setup script (it warns you if you do not). - Use it: join the Wi-Fi network ADAS-Pi, open http://10.42.0.1:8080, check the horizon and hood lines on the preview, set the camera height, press Drive.
There is no decision logic in the field. The Wi-Fi chip runs one access point and one station at the same time:
- the hotspot ADAS-Pi (10.42.0.1, WPA2) on a virtual interface is always up, about 30 s after power, everywhere;
- your home Wi-Fi is joined on top whenever it is in range, and then the page is also at
http://adaspi.local:8080; - a 60 s watchdog only restores those invariants and writes one line per state change to
/boot/firmware/adas-net.log, which is readable by putting the card in any computer.
Details and recovery procedures: docs/13-pi-deployment.md.
| Sound | Meaning |
|---|---|
| rising chirp | the process started |
| "ready" two-tone | the first camera frame was processed — perception is alive |
| "hotspot" tone | the ADAS-Pi access point is up and the phone can connect |
All three within about 45 s of power. If the chirp comes but "ready" never does, the camera is the problem; if "ready" comes but "hotspot" does not, the network is.
pip install numpy opencv-python-headless # once, for the report script
scripts/pull_and_review.sh # pulls the logs and clips, builds and opens the report
You get data/pi/<timestamp>/report.html: a summary of the drive, then one section per alert
with the frame and the boxes drawn, a plain-language sentence, the last 10 frames as a table,
and Wrong?/OK tagging you can export as JSON. It is a single self-contained file that makes
no network calls. Walkthrough: docs/14-drive-review.md.
The same core library builds on macOS and Linux, where the camera is replaced by a video file.
brew install cmake ninja pkg-config libomp ncnn opencv ffmpeg # macOS; apt equivalents on Linux
scripts/fetch_models.sh
scripts/build.sh # -> build/
build/test_tracker && build/test_policy && build/test_perception && build/test_pipeline
# offline replay: video in, per-frame JSONL out, deterministic
build/replay --config config/default.yaml --video clip.mp4 --out run.jsonl --progress
# the whole device on the laptop, fed by a video in real time, phone page on :8080
scripts/run_device_sim.sh data/incoming/clip.mp4 8080
# replay a pulled drive through the current build and compare against another one
scripts/replay_drive.sh data/pi/<timestamp> --height 1.05
Clips from a wide dashcam have to be cropped to the device's field of view first; the ffmpeg recipe is in docs/08-data-and-testing.md.
core/ C++17 library (detector, tracker, geometry, ego motion, lanes, calibration,
FCW/LDW policy, pipeline, JSONL) + tools: replay, bench, adas_device
webui-phone/ the phone page served by the device (one HTML/CSS/JS file each, no framework)
config/ default.yaml, every threshold
deploy/ Pi cross-build, install, car-mode setup, network units, log pull
scripts/ build, fetch models, device simulator, drive replay, drive review report
docs/ design notes, the written record of how and why this works the way it does
Start with docs/README.md; the code map is docs/09-code-layout.md and the interfaces are docs/11-interfaces.md.
Working device, driven on public roads, tuned over real drives. The honest list of what it does badly — the 5 fps ceiling, the camera-only speed estimate, night, highway distance, faint lane markings — is in docs/12-status-and-handoff.md, together with what to do next if you pick this up.
MIT, see LICENSE. Third-party components and their licences: THIRD_PARTY.md. Contributing: CONTRIBUTING.md.
This is an experimental warning aid, not a driver assistance system. It is not certified, not validated against any standard, and it will both miss real hazards and warn about nothing. It does not see in the dark, at distance, or through rain. Never let it change how you drive, never mount it where it obstructs your view, and keep your eyes on the road: you are the only collision avoidance system in the car.