Using python for the ultrasonic distance sensor from part one of this post in a project I realized: It is still too slow. In my project I record high queality video using the raspberry pi camera at high resolution. That puts quite some load on the IO. As a result, the timing needed for the distance measurement becomes unreliable. Results vary by more than a meter shot-to shot (without changing the distance to the first obstacle). For higher timing accuracy I decided to do the distance measurement in C or C++. I stole some interrupt based c++ code from here. I modified it to have it measure continuously. The code does one measuerement every 25miliseconds and outputs the result as a binary float to stdout. The result looks like this (download here):
// code based on
// http://stackoverflow.com/questions/22580242/raspberrypi-g-ultrasonic-sensor-not-working
#include<iostream>
#include<wiringPi.h>
#include<errno.h>
#include<string.h>
#include<stdio.h>
#include<stdint.h> //for uint32_t
using namespace std;
uint32_t time1=0,time2=0;
uint32_t time_diff=0;
float Range_cm=0;
volatile int flag=0;
void show_distance(void);
void myInterrupt(void) // Interrupt routine
{ // Called whenever the input pin toggles
uint32_t timeTemp=micros();
if(flag==0) // first toggle? note the time
{
time1=timeTemp;
flag=1;
}
else // second toggle? compute the distance
{
time2=timeTemp;
flag=0;
time_diff=time2-time1;
Range_cm=time_diff/58.;
}
}
void show_distance()// writes the distance as a
{ // binary float to stdout.
fwrite(&Range_cm,sizeof(float),1,stdout);
cout.flush();
}
int main(void)
{
if(wiringPiSetup()<0)
{
cout<<"wiringPiSetup failed !!\n";
}
pinMode(4,OUTPUT);
pinMode(5,INPUT);
pullUpDnControl(5,PUD_DOWN);
if(wiringPiISR(5,INT_EDGE_BOTH,&myInterrupt) < 0)
{
cerr<<"interrupt error ["<<strerror (errno)<< "]:"<<errno<<endl;
return 1;
}
while(1) // this loop starts a new measurement
{ // every 2500 miliseconds
time1=0;
time2=0;
flag=0;
digitalWrite(4,0);
delayMicroseconds(1);
digitalWrite(4,1);
delayMicroseconds(10);
digitalWrite(4,0);
delayMicroseconds(25000);
show_distance();
}
return 0;
}
Now we just need to modify our python class to read the regularly arriving distance from the c-code instead of measuring it itsself. To do so I use the following code (download distanceMeter1.py):
# License GPL
import gps
import threading
import subprocess
import time
import numpy
import os
#from xdg.Menu import tmp
# culd be beefed up with code from here http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/
#GPS
class distanceMeter(threading.Thread):
# the init routine starts the compiled c-code
# at high priority and initializes variables
def __init__(self):
threading.Thread.__init__(self)
self.distanceProcess = subprocess.Popen(['./distance'],stdout=subprocess.PIPE, bufsize=500,preexec_fn=lambda : os.nice(-19))
self.running = True
self.currentdistance=0.0
self.lastFive=numpy.zeros(5)
self.lastFiveValid=numpy.zeros(5)
self.current=0
self.currentValid=0
print "created"
# the run method of the thread is an infinite loop:
# whenever a distance measurement arrives
# the result is checked for validity (changes by more
# than 5 cm in 25msec are rejected as implausible for
# my means). Then mean and standard deviation of last
# five samples is computed.
def run(self):
print "starting loop"
while self.running:
# whenever new binary data comes in
# put it into a numpy float and
# process it.
rawdata=self.distanceProcess.stdout.read(4)
tmp=numpy.fromstring(rawdata,dtype="<f4")[-1]
if (numpy.abs(self.lastFive-tmp)<5).any():
self.currentdistance=tmp
self.lastFiveValid[self.currentValid%5]=tmp
self.currentValid+=1
self.lastFive[self.current%5]=tmp
self.current+=1
def stopController(self):
self.running = False
self.distanceProcess.send_signal(2)
self.distanceProcess.kill()
@property
def distance(self):
return numpy.mean(self.lastFiveValid)
@property
def deviation(self):
return numpy.std(self.lastFiveValid)
# This is just to show how to use the the class above
if __name__ == '__main__':
test=distanceReader()
test.start()
for i in numpy.arange(1000):
try:
print test.distance
time.sleep(.1)
# you can put as much code as you like here,
# test.distance will always contain the current
# distance measurement, updated every 25msec.
except KeyboardInterrupt:
test.stopController()
test.join()
break
With this I get reasonable distance measurements even while recording high resolution high quality video using the camera.