Skip to content

Repository files navigation

Vision Pilot

Open-source SAE Level 2 ADAS for CAN-closed vehicles.

Most retrofit ADAS projects assume you can write to the vehicle's CAN bus. On the vast majority of production cars you cannot — the bus broadcasts, but accepts no control commands. Vision Pilot works anyway: it reads speed from the factory bus and actuates every control at the analog signal level, upstream of the stock ECU. The factory wiring stays intact, and cutting one switch returns the car to stock.

Road-validated on a Honda Brio (2014) and a Toyota Avanza.

Head unit running the full ADAS stack


Features

Feature What it does Status
ACC — Adaptive Cruise Control Holds a set speed, backs off behind a lead vehicle using a Mamdani fuzzy controller over measured gap Road-validated
AEB — Automatic Emergency Braking Always armed; full brake below an 8 m in-path obstacle Road-validated
LKAS — Lane Keeping Assist PID on lateral offset from lane centre, actuated through the EPS torque lines Road-validated
DMS — Driver Monitoring Drowsiness (eye/mouth aspect ratio) and gaze-off-road from 68 facial landmarks Road-validated
Teleoperation Remote/keyboard driving with pedal and steering feedback Bench-validated

Perception

The stack runs YOLOPv2 for joint object detection, drivable-area segmentation and lane-line segmentation, with YOLOv8n as a secondary detector and SORT for tracking. Depth comes from a ZED stereo camera, so obstacle distance is measured rather than inferred from box size.

Detection, drivable area and lane segmentation Urban traffic
Detection (yellow), drivable area (green), lane lines (red) Dense urban traffic
Lane segmentation Night
Lane geometry through a curve Low-light performance

The four frames above are reference output of the YOLOPv2 network the stack uses. Below is a raw capture from the vehicle's own ZED camera, and the head-unit photo at the top of this page shows the complete pipeline running live in the car.

Raw ZED capture from the test vehicle


Architecture

Two processing units. The Jetson Orin Nano runs perception, the feature controllers and the dashboard. The STM32 Nucleo-F446RE owns the actuators and closes the fast inner loops. A USB serial link joins them; the vehicle CAN bus is tapped read-only.

flowchart LR
    subgraph SENSE["Sensors"]
        ZED["ZED stereo camera"]
        DRVCAM["Driver camera"]
        CANBUS["OBD-II CAN tap"]
    end

    subgraph HIGH["High-level — Jetson Orin Nano"]
        PERC["Perception<br/>YOLOPv2 + YOLOv8 + SORT"]
        CTRL["Controller<br/>ACC / AEB / LKAS"]
        DMS["Driver monitoring"]
        GUI["Dashboard"]
    end

    subgraph LOW["Low-level — STM32 F446RE"]
        PID["Cascaded PID"]
    end

    subgraph ACT["Actuators"]
        ETC["Throttle<br/>signal injection"]
        SERVO["Brake<br/>servo on pedal"]
        EPS["Steer<br/>EPS torque lines"]
    end

    ZED --> PERC
    DRVCAM --> DMS
    CANBUS -- "speed 0x1D0" --> CTRL
    PERC -- "distance, lane delta" --> CTRL
    PERC --> GUI
    DMS --> GUI
    GUI -- "mode" --> PERC
    CTRL -- "serial 9600" --> PID
    PID --> ETC
    PID --> SERVO
    PID --> EPS
Loading

How each actuator is driven, without CAN write access:

  • Throttle — the ECU's two accelerator-position signals are synthesised by the STM32 instead of coming from the pedal. Signal 2 is held at half of signal 1; the ECU cross-checks the pair.
  • Brake — a 2.5 N·m servo pulls the pedal through a steel cable. Purely additive, so the driver can always override by pressing harder.
  • Steering — the EPS torque-sensor pair is synthesised anti-phase, so the power steering assists as though the driver were applying torque.

Full detail in docs/architecture.md.


Quick start

git clone https://github.com/adeirman46/Vision_Pilot_CAN_Closed.git
cd Vision_Pilot_CAN_Closed

scripts/setup-env.sh --dms      # venv + deps + .env
source .venv/bin/activate
scripts/fetch-models.sh         # weights (~250 MB, not committed)

scripts/can-up.sh               # bring up slcan0 from the CANable
scripts/run.sh calibrate        # click the road ROI, once per camera mount
scripts/run.sh adas             # launch the full stack

The ZED SDK must be installed separately — pyzed is not on PyPI. See docs/setup.md.

Model weights

Weights are not committed. scripts/fetch-models.sh pulls them automatically; if Drive rate-limits the download, get them from the same folder by hand:

Google Drive — model weights

File Size Used by
yolopv2.pt ~150 MB detection + drivable area + lane segmentation
yolov8n.pt ~6 MB secondary object detector
shape_predictor_68_face_landmarks.dat ~100 MB driver monitoring (optional)

Place all three in models/.

The launcher

Everything runnable goes through one script. Multi-process targets start in dependency order, prefix each process's output, and tear the whole group down if any member dies.

scripts/run.sh adas              # full stack, mode selectable from the dashboard
scripts/run.sh acc               # one feature at a time
scripts/run.sh aeb --no-gui      # headless
scripts/run.sh lkas

scripts/run.sh perception        # single processes, for debugging
scripts/run.sh dashboard
scripts/run.sh dms

scripts/run.sh bench:brake       # open-loop actuator characterisation
scripts/run.sh bench:steer
scripts/run.sh bench:throttle

scripts/run.sh adas --dry-run    # print the launch plan and exit
scripts/run.sh perception -- --conf-thres 0.5 --device 0
scripts/run.sh --help

Flash the matching firmware to the STM32 first — see firmware/README.md.


Validation

Measured on-vehicle. Full tables and plots in docs/test-results.md.

Test Requirement Measured
Brake, command → full travel < 1.17 s ~0.60 s pass
Throttle, rise for +1 km/h < 0.51 s 337 ms ± 46 ms pass
Steering response < 0.30 s 0.12 – 0.20 s pass
AEB stop from ~9 km/h ~2.1 s from 8 m pass
ACC throttle response ≤ 0.5 s 0.14 – 0.59 s pass
LKAS centreline hold converges, tracks curves pass
Driver override reverts to manual immediate pass
System power < 180 W 57.5 W pass
AEB response to a pedestrian LKAS holding centreline
AEB — brake commanded at 8 m, standstill in 2033 ms LKAS — converges from an off-centre start and holds

Repository layout

src/vision_pilot/       Python package — shared by every feature
  config.py             paths, UDP port map, CAN IDs, tuning (single source of truth)
  bus/                  CAN speed reader
  perception/           YOLOPv2 + YOLOv8 + SORT; vendored YOLOPv2 helpers
  control/              adas, acc, aeb, lkas
  driver_monitoring/    drowsiness and gaze detection
  ui/                   PyQt6 dashboard and plan-view widget
firmware/               STM32 sketches — one per run target
tools/bench/            actuator step-response logging
scripts/                run.sh launcher + CAN, model and env setup
config/roi.txt          calibrated perspective ROI
models/                 weights (fetched, not committed)
docs/                   architecture, hardware, CAN, setup, results
tests/                  configuration smoke tests

Perception, dashboard, CAN and plan-view code used to be copy-pasted into every feature folder — read_can.py alone existed in seven identical copies. They now live once in src/vision_pilot/ and every feature imports them.


Configuration

No machine-specific paths or magic numbers in the source. Everything lives in src/vision_pilot/config.py and is overridable per vehicle via .env (copy from .env.example):

VP_CAN_ID_SPEED=0x1D0            # speed frame arbitration ID
VP_SPEED_POLY_A=-0.00000016      # raw counts -> km/h, refit per vehicle
VP_SPEED_POLY_B=0.00650007
VP_SPEED_POLY_C=-1.15230758
VP_MCU_PORT=/dev/ttyACM0
VP_AEB_TRIGGER_DISTANCE_M=8.0

Porting to another vehicle mostly means refitting the speed polynomial — procedure in docs/can-bus.md.


Documentation

Setup Install, ZED SDK, CAN bring-up, calibration, troubleshooting
Architecture System and process topology, per-feature control flow
Hardware Bill of materials, power budget, enclosures, actuator interfaces
CAN bus Wiring, decoded frames, speed conversion, porting
Test results Full validation campaign
Firmware Pin map, host protocol, which sketch to flash

A printable walkthrough covering all of the above is generated by scripts/build-docs-pdf.shdocs/VisionPilot-Tutorial.pdf.


Safety

This drives a real vehicle. Read this before running it on a road.

  • It is Level 2. The driver is responsible at all times and must stay ready to take over.
  • Test on closed ground first. Bench each actuator (scripts/run.sh bench:*) before any closed-loop drive.
  • Verify the override every time. Two panel switches cut power to the processing units; with them off the vehicle is fully manual.
  • The CAN tap is read-only. Vision Pilot never transmits on the vehicle bus. Actuation goes over a separate serial link to a dedicated microcontroller, so a fault in the perception stack cannot inject frames onto the vehicle network.
  • The speed polynomial is vehicle-specific. Running with another car's coefficients gives wrong speed, and every controller downstream depends on it.

Acknowledgements

License

MIT

About

Open source L2 ADAS for CAN-Closed Vehicle, already tested in Honda Brio and Toyota Avanza.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages