pimoroni-pico/micropython/examples/breakout_vl53l5cx/vl53l5cx_object_tracking.py

99 lines
3.2 KiB
Python

import pimoroni_i2c
import breakout_vl53l5cx
import time
from ulab import numpy
# This example attempts to track a "bright" object (such as a white business card)
# It uses reflectance to identify the target and compute the X/Y coordinates
# of its "center of mass" in the sensors view.
# Motion indication only works at distances > 400mm so it's not
# really useful as a method to reject data.
# Configure your distance and brightness thresholds to suit your object
DISTANCE_THRESHOLD = 400 # Distance in mm
REFLECTANCE_THRESHOLD = 60 # Estimated reflectance in %
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
# Sensor startup time is proportional to i2c baudrate
# HOWEVER many sensors may not run at > 400KHz (400000)
i2c = pimoroni_i2c.PimoroniI2C(**PINS_BREAKOUT_GARDEN, baudrate=2_000_000)
print("Starting up sensor...")
t_sta = time.ticks_ms()
sensor = breakout_vl53l5cx.VL53L5CX(i2c, firmware=open("vl53l5cx_firmware.bin").read())
t_end = time.ticks_ms()
print("Done in {}ms...".format(t_end - t_sta))
# Make sure to set resolution and other settings *before* you start ranging
sensor.set_resolution(breakout_vl53l5cx.RESOLUTION_8X8)
sensor.set_ranging_frequency_hz(15)
sensor.start_ranging()
while True:
time.sleep(1.0 / 60)
if sensor.data_ready():
# "data" is a namedtuple (attrtuple technically)
# it includes average readings as "distance_avg" and "reflectance_avg"
# plus a full 4x4 or 8x8 set of readings (as a 1d tuple) for both values.
data = sensor.get_data()
reflectance = numpy.array(data.reflectance).reshape((8, 8))
distance = numpy.array(data.distance).reshape((8, 8))
scalar = 0
target_distance = 0
n_distances = 0
# Filter out unwanted reflectance values
for ox in range(8):
for oy in range(8):
d = distance[ox][oy]
r = reflectance[ox][oy]
if d > DISTANCE_THRESHOLD or r < REFLECTANCE_THRESHOLD:
reflectance[ox][oy] = 0
else:
scalar += r
# Get a total from all the distances within our accepted target
for ox in range(8):
for oy in range(8):
d = distance[ox][oy]
r = reflectance[ox][oy]
if r > 0:
target_distance += d
n_distances += 1
# Average the target distance
if n_distances > 0:
target_distance /= n_distances
else:
target_distance = 0
# Flip reflectance now we've applied distance
# both fields are upside-down!
reflectance = numpy.flip(reflectance, axis=0)
# Calculate the center of mass along X and Y
x = 0
y = 0
if scalar > 0:
for ox in range(8):
for oy in range(8):
y += reflectance[ox][oy] * ox
y /= scalar
y /= 3.5
y -= 1.0
for oy in range(8):
for ox in range(8):
x += reflectance[ox][oy] * oy
x /= scalar
x /= 3.5
x -= 1.0
print(round(x, 2), round(y, 2), round(target_distance, 2))