Bike Overtaking Distance Sensor

5,096

45

32

If you ever cycle on main roads, you will be familiar with the occasional car or truck who decides they need to get past you just one foot from your elbow. That probably seems like lots of space to them, but if I had to swerve to miss a pot hole or ironworks in the road, I'd be in trouble. 99.9% of vehicles are in fact very careful, but for those 0.1% of people who drive badly, I decided to build an overtaking sensor for my bike.

The overtaking sensor uses a laser time of flight distance sensor to see how far away the other vehicles are, and a camera to record the vehicle going past. I needed to record (and more importantly retrieve) the data easily, so I decided to use a Raspberry Pi Zero Wireless to manage the recording and to make the results available wirelessly. Lastly, I 3D printed a case to fit it all in.

Components

How it works

The overtaking sensor uses an infra red laser for optical distance measurement. The VL53L1X chip sends an eye safe laser beam, and measures the time it takes to get a reflection, allowing it to measure distances from 40mm to 4m. This LIDAR detection is fast, and can take measurements up to 50 times a second - ideal for recording fast moving vehicles.

The sensor data is captured by a Raspberry Pi, which also uses a camera to record the passing traffic. The code overlays the distance measurement on the video so that the measurements and the activity are always tied together.

The Raspberry Pi uses a web interface to make the files available to download for you to view afterwards. All of the recording and viewing happens through a simple python piece of code.

Step 1: Get Soldering

The Raspberry Pi, the distance sensor and the switches likely come un-soldered, so get your soldering iron out and start building. First of all, solder header pins on to the raspberry pi.

Next get some breadboard wires with female ends, and cut off whatever is on the other end, and solder the wire on to the VL53L1X. I did this so that it was easy to test and prototype, but you can easily just solder wires directly to the raspberry pi if you prefer - just don't do it yet, because some of the raspberry pi pins are dual purpose.

Next solder up the LED. Attach the resistor to the cathode - the side with the shorter leg and with the flat side. Then solder on female breadboard connectors to the anode and to the other end of the resistor. I added some heatshrink to go round the legs of the LED to avoid the bare wires from touching other components.

Lastly, solder up the two switches. One of the switches will be to start and stop recordings, and also to power down the device. The other will be to turn on the device. Choose your switch colours accordingly. For the switch to start and stop, just solder two female breadboard wires to it. For the turn on switch, we need something more complicated. Solder a female breadboard wire to one side. On the other side, solder both a male and a female connector. Again, I used heatshrink round the connections.

Step 2: Set Up the Raspberry Pi

We want the Raspberry Pi to be headless (no screen required), low power, and accessible, so we're going to do a bunch of things;

1) Download the latest raspbian lite from https://www.raspberrypi.org/downloads/raspbian/

2) Install it on your SD card using the installation guide on the download page

3) Create an empty file with the name ssh in the root directory of the SD card

4) Add a file called wpa_supplicant.conf to the root directory of the SD card. Edit this file to add your WiFi settings, e.g.

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev<br>update_config=1
country=«your_ISO-3166-1_two-letter_country_code»
network={
     ssid="«your_SSID»"
     psk="«your_PSK»"
     key_mgmt=WPA-PSK
}

5) Put the card in your Raspberry Pi and plug a power connector in to it.

6) Once it has booted, look on your router for a device named "raspberrypi". Using a tool like putty, ssh to it, with the username pi, and the password raspberry.

7) Update the pi and install some software we'll need

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install -y python3-picamera python3-pip gpac python3-flask python3-rpi.gpio 
sudo pip3 install smbus2 
sudo mkdir -m 1777 /share

8) Update the pi configuration to turn on the camera and the I2C interface

sudo raspi-config

Then go to Interface options and enable the camera (option 1), and the I2C interface (option 5)

9) Download the attached file, overtaking_sensor.zip, and use a tool like FileZilla to sftp it to your raspberry pi. Unzip it so that the contents are in the the /home/pi/overtaking_sensor directory. I'll go in to what the code does in the next step, but if you don't care, just skip that step.

10) Create a file to control your service called /etc/init.d/overtaking.sh

<p>#! /bin/sh</p><p>### BEGIN INIT INFO
# Provides:          overtaking.py
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
### END INIT INFO</p><p># If you want a command to always run, put it here</p><p># Carry out specific functions when asked to by the system
case "$1" in
  start)
    echo "Starting overtaking.py"
    /home/pi/overtaking_sensor/app.py &
    ;;
  stop)
    echo "Stopping overtaking.py"
    pkill -f /home/pi/overtaking_sensor/app.py
    ;;
  *)
    echo "Usage: /etc/init.d/overtaking.sh {start|stop}"
    exit 1
    ;;
esac

exit 0

11) Make that file a service to start the code at boot time

<p>sudo chmod +x /etc/init.d/overtaking.sh</p><p>sudo update-rc.d overtaking.sh defaults</p><p>sudo /etc/init.d/overtaking.sh start</p>

Step 3: Assemble the Electronics

Good news - you've basically done the hard work now, and it's time to wire it all together.

First of all connect the ToF sensor to the pi. The connections are as follows;

Pi Pin 2 (5V, top left) to VL53L1X 3-6V pin

Pi Pin 3 to VL53L1X SDA pin

Pi Pin 5 to VL53L1X SCL pin

Pi Pin 6 to VL53L1X GND pin

Note, the VL53L1X INT pin is not used

Pi Pin 7 to the +ve LED pin (the one without the resistor)

Pi Pin 9 to the LED pin with the resistor.

Pi Pin 37 (second from the right on the bottom row) to one side of the start/stop button

Pi Pin 39 (right hand side, on the bottom row) to the other side of the start/stop button

and now the complicated bit - Plug the single wire side of the boot button in to Pi pin 14. Unplug the VL53L1X SDA pin from Pi pin 3, and plug the female connector from the other side of the boot in to that pin. Plug the VL53L1X SDA pin in to the male connector on the same side of the boot button. What you are doing is connecting the VL53L1X SDA pin still to Pin 3, and also allowing the button to be pressed to short it to Ground.

Step 4: Test

Connect the camera to the Pi too, using its ribbon connector, and power up the Pi. You should see your LED turn on after a minute. If you don't, something's wrong - check your connectors.

Next, the overtaking camera hosts a web page to allow you to view the status of the recording, and to download the videos. To access the web page, go to http://raspberrypi:8080/

You can click Start on the web page to start the video recording, or press the Start/Stop hardware button. The LED should start blinking. Try putting your hand closer and further from the distance sensor as you record, and you should see the distance change on the web page. Click the stop button on the web page to stop, or press the Start/Stop hardware button to stop. The LED will stop blinking, and if you refresh the web page, you will see the video you just recorded (this can take a few seconds, as it needs to convert it from h264 to mp4 format.

Press and hold the Start/Stop button for 5 seconds to shut down the Raspberry Pi. Press the Boot button to boot it up again.

Note, if you press the Boot button while the device is on, you will mess up the I2C messaging, and you will likely need to restart the Raspberry Pi. This was the most frustrating part of the build. I ideally wanted a single button to do everything, but the Raspberry pi only lets you boot by shorting pin 3 to ground, and the I2C messaging only works on pin 3, so the two conflict. I could add a relay to change what the button does depending on whether the device is on or off, but that seemed overkill. If anyone has a software solution, I'd love to know.

Step 5: The Case

You've now got a jumble of electronics that works nicely, but you can't just duct tape that to your bike. The next step is to 3D print a nice box for it. I have a box I have designed for it. You'll find the slt files attached for you to print it. However, you probably don't want to print exactly this box for three reasons;

1) The bottom of the box has a block the width of the gap in my pannier bars. Depending on how you want to fix it on your bike, you'll want to adjust that to your own dimensions.

2) The hole in the side for charging the battery will need to be placed in the right place for whichever battery you buy.

3) I live in the UK where everyone drives on the left, and I want to detect traffic on the right. If you live in a right side driving country, you will want to move the hole for the camera to the left instead of the right. You could probably mirror image it, but I haven't tested.

I have made the original tinkercad design available here, so you can edit and adjust it to your own needs;

https://www.tinkercad.com/things/hsNXzdQCPpA

Once you have printed the box, you can start assembling everything. I put some adhesive foam weather strip on the back of the raspberry pi to press against the camera when it is in the hole - this keeps it nicely in place. Anything soft will do. Get some screws and screw the raspberry pi in to the box. You''ll want to attach the 90 degree USB connector to the power input before you do (adding a regular USB connector is possible, but it adds almost 2cm to the box size).

Next, fasten on the distance sensor to the holes in the side. To get it secure, I found it best to use some 2mm diameter bolts - that was stronger than just screwing in to the plastic like elsewhere, and allowed me to mount it closer to the edge.

Next, put the led in the hole, and add some glue to keep it in place. Screw on the buttons to the holes in the top too.

I chose a battery which has an input on the side, and an output on the top. This allowed me to have a connector inside to the raspberry pi, and a hole in the side of the box to charge it. I just connected the battery and slid it in to the box, which was designed to be snug to the battery.

Screw the lid on and you're done!

Step 6: Mounting to the Bike

You were probably wondering what those hooks were for on the outside of the box. That's how we're going to attach the box to the pannier on your bike. To do that, get a couple of elastic bands and knot them about 1cm along. Then on the other end, put some tape. The tape is going to serve as a handle.

Put the knotted elastic band round the hook on the side where the distance sensor is. Then put the sensor on your pannier, pull on the tape, and hook it round the hook on the other side. As long as you pick the correct length of elastic band, this is really secure. And you're ready to go! Just press the Boot button to start the Raspberry Pi, then the Start/Stop button to begin recording. Press it again when you are done to stop the video recording.

Step 7: The Code in Detail

You can skip this step, but for anyone interested in the internal workings of the code, here is a version with comments throughout.

#!/usr/bin/env python3
<br>from flask import Flask,render_template,request,Response,jsonify,send_from_directory
from picamera import PiCamera
from time import time,sleep, strftime,localtime
import VL53L1X_2 as VL53L1X
import subprocess
import os
from threading import Thread
import math 
from stat import S_ISREG, ST_CTIME, ST_MODE
import RPi.GPIO as GPIO
app = Flask(__name__)

# start the time of flight sensor

tof = VL53L1X.VL53L1X(i2c_bus=1, i2c_address=0x29)
recording=False
converting=False

# start the camera

camera = PiCamera()
camera.rotation=90
camera.framerate=25
camera.annotate_background = True
camera.annotate_text_size=16
camera.annotate_text = strftime('%Y-%m-%d %H:%M:%S %Z', localtime(time()))
videoName=""
distance_in_mm=0
shutdown_pin=26
led_pin=4
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(led_pin,GPIO.OUT)
GPIO.output(led_pin,GPIO.HIGH)
GPIO.setup(shutdown_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# this code watches for button presses. If not recording, it starts recording. If recording,<br># it stops. If you press for more than 5 seconds, it shuts down 

def button_press_event(channel):
    global recording
    start_time = time()
    elapsed_time=0
    while GPIO.input(channel) == 0: # Wait for the button up
      elapsed_time=time() - start_time
      if elapsed_time>5:
        shutdown()
    if elapsed_time<0.1:
      return
    if(recording):
      stop_recording() 
    else:
      start_recording()
      
# this code does the shutdown, turning off the LED and stopping recording

def shutdown():
  if(recording):
    stop_recording()
  GPIO.output(led_pin,GPIO.LOW)
  subprocess.call(['shutdown', '-h', 'now'], shell=False)

# this adds the watch for the button press
GPIO.add_event_detect(shutdown_pin, GPIO.FALLING, callback=button_press_event, bouncetime=500)
# this code starts the distance sensing, an the recording. <br># When the distance sensor gets a valid reading, it adds it to the display
# It also flashes the LED when recording

def start_recording_int():
  global tof,recording,camera,videoName,distance_in_mm,led_pin
  tof.open() # Initialise the i2c bus and configure the sensor
  tof.start_ranging(3)
  startTime=time()
  videoName='/share/video-%s'% strftime("%Y-%m-%d_%H-%M-%S", localtime(time()))
  camera.start_recording("%s.h264" % (videoName))
  recording=True
  i=0
  while(recording):
    i=(i+1)%6
    if(i==0):
      GPIO.output(led_pin,GPIO.HIGH)
    if(i==3):
      GPIO.output(led_pin,GPIO.LOW)
    try:
      fl = tof.get_full_measurement()
      if(fl[1]==0):
        distance_in_mm=fl[0]
        sd=fl[2]
        secondsSinceStart=time()-startTime
        minutes=math.floor(secondsSinceStart/60)
        seconds=secondsSinceStart%60
        camera.annotate_text = '%s %.0fmm +/- %.1fmm'% (strftime("%Y-%m-%d %H:%M:%S", localtime(time())), distance_in_mm,sd)
    except Exception as e:
      print(e)
    sleep(0.1)
# This code is called if recording is started from the web browser

def start_recording():
    print ("Starting recording")
    t = Thread(target=start_recording_int)
    t.start()
    return "Processing"
# This code stops the recording, an the range sensor.
# After recording is complete, it converts the file to an mp4.

def stop_recording():
  global recording, camera,videoName,converting,led_pin
  print ("Stopping recording")
  GPIO.output(led_pin,GPIO.HIGH)
  recording=False
  converting=True
  camera.stop_recording()
  try:
    tof.stop_ranging() # Stop ranging
  except:
    pass
  command = "MP4Box -add %s.h264 %s.mp4"%(videoName,videoName)
  output = subprocess.call(command,  shell=True)
  os.remove("%s.h264"%(videoName))
  converting=False
# This code adds a web service to display the home page, with the file contents 
# from the /share directory

@app.route('/')
def index():
    return render_template('index.html', tree=make_tree("/share"))

# This is the rest service for the start button

@app.route('/camera/start')
def start():
  return Response(start_recording(), mimetype="text/html")
# This is the rest service for the stop button

@app.route('/camera/stop')
def stop():
  stop_recording()
  return "Stopped"


# This is the rest service to get the camera status
@app.route('/camera/status')
def status():
  global recording,converting,distance_in_mm
  return jsonify(
        recording=recording,
        converting=converting,
        distance=distance_in_mm
  )


# This lists the files

@app.route('/files')
def files():
        path = '/share'
        return render_template('files.html', tree=make_tree(path))
 
# This service allows a file from the /share folder to be downloaded

@app.route('/files/
')
def static_proxy(path):
  print(path)
  # send_static_file will guess the correct MIME type
  return send_from_directory('/share',path)
def make_tree(dirpath):
    tree = dict(name=os.path.basename(dirpath), children=[])
    
    entries = (os.path.join(dirpath, fn) for fn in os.listdir(dirpath))
    entries = ((os.stat(path), path) for path in entries)
    entries = ((stat[ST_CTIME], path)
           for stat, path in entries if S_ISREG(stat[ST_MODE]))
    for cdate, path in sorted(entries,reverse=True):
      tree['children'].append(dict(name=os.path.basename(path)))
    return tree
# This starts the web service.

if __name__ == '__main__':
    app.run(debug=False, port=8080,host='0.0.0.0')

In addition to this main code, there is a web template to define the web page layout in the templates folder

The code also uses the pimoroni library for the VL53L1X sensor, with changes to allow detection of sensor accuracy, and we only use the sensor data if it is accurate enough. When the sensor gives an inaccurate reading (e.g. distance too far), we don't update the display, so you will only ever see accurate readings and timestamps on the screen.

https://github.com/pimoroni/vl53l1x-python

Step 8: Improvements

There are a few improvements that you might consider if you are making one too;

1) The box is not waterproof. A clear plastic screen could be added in front of the camera and the distance sensor. The distance sensor will have a reflection from the plastic which will impact the reading, but you can calibrate the sensor to account for that. Search for "VL53L1X API user manual" for instructions for that.

You'd also need a stopper for the USB charging port, and add sealant round the switches.

2) The raspberry pi doesn't have a real time clock. A battery powered RTC can be added so that the time is correct on boot. I use a wifi hotspot on my phone to allow it to connect to get the correct time instead, but an RTC unit would negate the need for that.

3) If you want to make the video file available to windows more easily, you can install a Samba server by running "sudo apt-get install samba samba-common-bin" and then editing the file /etc/samba/smb.conf to add the following content at the end of the file

[share]<br>Comment = Pi shared folder
Path = /share
Browseable = yes
Writeable = Yes
only guest = no
create mask = 0777
directory mask = 0777
Public = yes
Guest ok = yes

Then set the Samba password by typing "sudo smbpasswd -a pi" and then entering a password. Finally restart the Samba server with "sudo /etc/init.d/samba restart". You can then browse to \\raspberrypi\share in windows to see the files.

4) Some people have commented that all this does is records bad driving - it doesn't stop you getting hurt. That is true, and the next steps could be to print a bigger box and to put the words Overtaking Monitor in bright visible letters, perhaps with some flashing lights to attract attention to it.

Share

    Recommendations

    • First Time Author

      First Time Author
    • Toys Contest

      Toys Contest
    • Big and Small Contest

      Big and Small Contest

    32 Discussions

    0
    None
    Yard Sale Dale

    21 days ago

    I would like something on my car which displays the following distance of the car behind, and which lights brightly when they are too close.

    1 reply
    0
    None
    millmoreYard Sale Dale

    Reply 21 days ago

    Yea, that's probably achievable with the same technology. Just point the sensor backwards, and add a large dot matrix display. Humans being what they are, it might encourage people to come closer. It could be difficult to pinpoint the very front of the car though rather than the windsheild.

    1
    None
    PeterH2

    4 weeks ago

    Nice idea but basically flawed. OK - it tells you when cars have passed too close but so what? You can tell that when they do it. By the time they have passed it's too late to do anything about it. I have just come back from a bike ride and was almost knocked off by prattish car behaviour more than once. The frustrating thing is that you cannot communicate your views to the person who has just cut you up. Perhaps an aerosol can of paint pointing sideways and triggered by close passing would be the answer (not serious!)

    3 replies
    0
    None
    cgrilloPeterH2

    Reply 4 weeks ago

    Actually, I beg to differ.
    The police do take bike cam footage and do prosecute drivers who they believe have come too close (5/6 points on your licence for careless driving is a good deterrent to stop them doing it again) - although it doesn't prevent them doing it a first time.
    If you end up reacting to the driver then you end up putting yourself at risk of prosecution - or just being deliberately rammed by the driver ...

    0
    None
    Yard Sale Dalecgrillo

    Reply 21 days ago

    Interesting. In USA, most police agencies don’t take cyclists seriously on complaints of traffic offenses. Even victims of car ramming or preventable collisions often find the police siding with the cager, since bikes rank low in status here.

    0
    None
    millmorePeterH2

    Reply 4 weeks ago

    I know what you mean. I did also consider making it bigger and adding the words "OVERTAKING MONITOR" or something like that to the outside, with some more visible lights so that people were more cautious.

    0
    None
    robindexter

    4 weeks ago

    Interesting approach.

    You may also be interested in the grass roots studies being undertaken by volunteers in Australian cities using a locally developed 'passbox' system that has dual ultrasonic sensor to measure passing distances on both sides, linked up to dual front and rear facing cameras & gps. http://passbox.org/ A couple of weeks of data is logged in the units, then downloaded for research. The project is aimed at obtaining real world information on passing distances on Australian roads - to back up proposed legislative changes. I was a volunteer using the box and recorded 700 passing incidents (less than one metre clearance) in one week of logging my commutes!
    Cheers

    Robin

    1 reply
    0
    None
    millmorerobindexter

    Reply 4 weeks ago

    That's really cool. Thanks for sharing. It's good to see some countries taking this issue seriously.

    BTW, I see their boxes (which detect and film in two directions) cost $500 each! I haven't done a thorough costing, but I think mine cost about $50. That's not to say their solution is bad - it's probably great. But I'm proud of the low cost solution too.

    0
    None
    Vitalij X

    Tip 4 weeks ago on Introduction

    Very interesting idea and i really love it as a full year cyclist. To make this solution better you can make it interactive: place some led screen at your back and print distance updates with some precautions for drivers. To make it even better you can add a speed sensor and add this information to the screen, too. I know that drivers like this stuff). Moreover, i believe that having this information you can predict what can happen afterwards and act accordingly.

    3 replies
    0
    None
    millmoreVitalij X

    Reply 4 weeks ago

    I did wonder about a speed sensor too - e.g. using the camera to look at change in size of the approaching object. You'd need a very accurate sensor for your own speed though to be able to subtract that.

    I like the interactive idea - perhaps an EL wire message on a cycling vest?

    0
    None
    Vitalij Xmillmore

    Reply 4 weeks ago

    I havent done this by myself, but video explanation of speed measuring using Microwave Doppler radar seems promising. There you can find a speed just by frequency reading.

    Moreover, there are solutions that uses distance sensors, too. They are based on time and distance difference(basically applicable to any distance sensor.).
    Speed = (FirstDistance - SecondDistance) / (SecondTime - FirstTime)
    https://forum.arduino.cc/index.php?topic=139820.ms...

    So, I think it's possible to find a cheap and effective way to get, at least, approximate speed of nearby car.

    EL wire is good, but it's a static solution that doesnt offer a much flexibility.
    Actually i was thinking about outdoor led matrix display, something like that: https://www.ebay.com/itm/Red-LED-Display-P10-Dot-M.... It should work well at any light conditions and allows to output dynamic text which perfectly fits here.

    0
    None
    millmoreVitalij X

    Reply 4 weeks ago

    That's cool. They should make an instructable on how to build one!

    1
    None
    attguy

    4 weeks ago

    Good job. Have you thought about adding a speaker or a handlebar mounted monitor? That alerts the rider of vehicles that are in a defined proximity to them.
    Then then rider can be more attentive to them. And not caught off guard.
    This project is very similar to the Garmin Varia taillight.

    1 reply
    0
    None
    millmoreattguy

    Reply 4 weeks ago

    That's an interesting idea. In principle it would be trivial to do a wired connection - a GPIO pin could be used to light up an LED or sound a buzzer. Wireless is also possible, e.g. using the bluetooth connection in the Pi Zero W, and perhaps something like this for a receiver https://www.dfrobot.com/product-1259.html (a small arduino bluetooth device, which can easily switch an LED).

    0
    None

    I know what you mean. I did consider making it bigger and adding the words "OVERTAKING CAMERA" or something like that to scare people off. Prevention is better than cure.

    1
    None

    Can you this footage as a proof for ticketing the violators in your country anyways?
    What advantage does it give compared to a simple camera?

    0
    None
    svkatielee

    Question 4 weeks ago

    Very nice instructable. I ride a small motorcycle in a dangerous area and have been searching for a distance alarm to let me know when something is overtaking me. At highway speeds it needs greater range. Did you evaluate any other sensors?