Last active
July 20, 2022 07:55
-
-
Save tdack/3a43fc42476eed293300 to your computer and use it in GitHub Desktop.
Python object for Sharp 2Y0A21 InfraRed distance sensor (and similar). Includes calibration routine to calculate the coefficient and power for the distance equation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import Adafruit_BBIO.ADC as ADC | |
from time import sleep | |
import sys | |
# | |
# For the BeagleBone Black the input voltage has to be 0-1.8V | |
# The easiest way is with a voltage divider | |
# | |
# Ra Rb | |
# Vin -->--[== 1K ==]--+--[== 470 ==]----+ | |
# | | | |
# V | | |
# | | | |
# Vout === Gnd | |
# - | |
# Rb | |
# Vout = --------- x Vin | |
# Ra + Rb | |
# | |
# scale factor for voltage divider to get original Vin: (Ra+Rb)/Rb | |
class SharpIR(object): | |
def __init__(self, AIN="AIN1", min=10, max=80, scale=2.5,const=1, coeff=28, power=-1): | |
""" | |
min/max: minimum and maximum distances for the sensor | |
scale: scaling factor to apply to measured voltages to convert | |
measured voltage to actual voltage. If sampled voltage is | |
coming from a voltage divider then scale = (Ra+Rb)/Rb | |
coeff: coefficient determined from calibration | |
power: powerterm determined from calibration | |
""" | |
self.AIN = AIN | |
self.min = min | |
self.max = max | |
self.scale = scale | |
self.coeff = coeff | |
self.power = power | |
ADC.setup(AIN) | |
return | |
def calibrate(self, numsteps): | |
""" | |
Calculates the coefficient and power terms for the sensor by recording | |
voltage measurements at known distances and then performing an | |
exponential curve fit on the data. | |
numsteps: the number of measurements to take from min to max distance | |
more steps will give a more accurate curve | |
""" | |
import numpy as np | |
from scipy.optimize import curve_fit | |
distances = range(self.min, self.max+1, (self.max-self.min)/numsteps) | |
voltages = [] | |
def func(x, m, a, b): | |
""" | |
Curve fitting function. Used to fit data points to the curve | |
y = m + a * x ^ b | |
""" | |
return m + a * x ** b | |
print('IR Calibration') | |
print('\nThe calibration process requires you to place an object ' \ | |
'(a flat piece of card is good) at a variety of distances from ' \ | |
'the minumum distance to the maximum distance.\n\n' \ | |
'Voltage readings will be taken at (cm):') | |
print distances | |
for d in distances: | |
print("Measure @ %d cm" % (d)) | |
for x in range(5): | |
sys.stdout.write("%d " % (5-x)) | |
sys.stdout.flush() | |
sleep(1) | |
print("\nReading @ %d cm" % (d)) | |
sleep(2) | |
value = 0.0 | |
total = 0.0 | |
for x in range(5): | |
value = ADC.read(self.AIN) * 1.8 * self.scale | |
total = total + value | |
print("#%d - %2.4f" % (x+1, value)) | |
sleep(1) | |
print("%d cm Average: %2.4f" % (d, total/5)) | |
print"=" * 15 | |
voltages.append(total/5) | |
# fits measured voltages to y = m + a * x ^ B curve | |
popt, pcov = curve_fit(func, np.array(voltages), np.array(distances)) | |
self.const = popt[0] | |
self.coeff = popt[1] | |
self.power = popt[2] | |
print "Calibration Data" | |
print "-" * 15 | |
print "Constant :", self.const | |
print "Coefficient :", self.coeff | |
print "Power :", self.power | |
print "Err 1 std dev:", np.sqrt(np.diag(pcov)) | |
print "\nEquation : distance = %2.2f + %2.2f * scaled_voltage ** %2.2f" % (self.const, self.coeff, self.power) | |
print "-" * 15 | |
print "Example use:" | |
print ' IR = SharpIR("P9_36", scale=1560.0/560.0,const=%2.6f, coeff=%2.6f, power=%2.6f)' % (self.const, self.coeff, self.power) | |
print "-" * 15 | |
def distance(self): | |
""" | |
Returns the distance in cm using the calibration data | |
""" | |
distance = self.const + self.coeff * (ADC.read(self.AIN) * 1.8 * self.scale) ** self.power | |
if distance < self.min: | |
distance = -1 # invalid distance | |
elif distance > self.max: | |
distance = self.max | |
return distance | |
if __name__ == '__main__': | |
IR = SharpIR("P9_36", scale=1470.0/470.0, min=10, max=50, const=1, coeff=26.686363, power=-1.162602) | |
IR.calibrate(10) | |
while 1: | |
print IR.distance() | |
sleep(1) |
Author
tdack
commented
Aug 28, 2014
- Updated the distance equation to be a little more accurate
- fixed the delays and count down display so it is a little nicer and doesn't take quiet as long
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment