4.4 Laserový senzor vzdialenosti
M5Stack ToF HAT
Modul ToF HAT meria vzdialenosť objektu na princípe odrazu pulzov laserového lúča - skratka ToF znamená „Time of Flight“, teda „čas letu“ (pulzu). Z tohoto času dokáže senzor, vďaka znalosti rýchlosti svetla, veľmi presne vypočítať vzdialenosť. Modul je vybavený senzorom VL53L0X, pripojeným ku zbernici I²C na GPIO 26 (SCL) a 0 (SDA), má adresu 0x29.

Senzor vzdialenosti VL53L0X
Senzor VL53L0X je vybavený vysielačom VCSEL (Vertical-Cavity Surface-Emitting Laser) pre infračervené svetlo vlnovej dĺžky 940 nm. V ideálnych podmienkach umožňuje merať vzdialenosť objektu do 200 cm s presnosťou ±3 %, v uhle záberu 25 °. Maximálna vzdialenosť i presnosť je dosiahnuteľná v interiéri pri objekte bielej farby. Vonkajšie svetlo a tmavé farby znižujú dosah - podľa špecifikácii je napríklad v prípade sivého objektu v zamračenom počasí dosah len 50 cm.
Senzor vie merať vo viacerých režimoch, líšia sa dobou merania, dosahovanou vzdialenosťou a presnosťou:
- predvolený režim: 30 ms, 120 cm;
- režim vysokej presnosti: 200 ms, 120 cm, ±3 %;
- režim dlhého dosahu: 33 ms, 200 cm (len v prostredí bez infračerveného svetla);
- režim vysokej rýchlosti: 20 ms, 120 cm, ±5 %.
Ukážkový program
Knižnica (ovládač) pre tento senzor je komplikovanejšia z hľadiska funkčnosti, no pre nás, ako jej používateľov, ide o objekt, ktorého konštruktor vyžaduje inštanciu zbernice. Samotné meranie vykoná objektová funkcia ping(), ktorá je bez parametrov. Vracia vzdialenosť v milimetroch.
Jednoduchý ukážkový program opäť priebežne vypisuje nameranú vzdialenosť, kým nestlačíme tlačidlo:
from machine import I2C, Pin, Signal
i2c = I2C(0, scl = Pin(26), sda = Pin(0))
zariadenia = i2c.scan()
print(i2c, zariadenia)
if 0x29 in zariadenia:
from vl53l0x import VL53L0X
from time import sleep
senzor = VL53L0X(i2c)
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = senzor.ping()
except Exception:
print("Nastala chyba pri meraní! ")
sleep(1)
else:
print("vzdialenosť:", vzdialenost, end = " mm \r")
sleep(0.2)
else:
print("Nie je pripojený správny senzor!")
Všimnite si, čo sa stane, keď je prekážka príliš ďaleko (mimo rozsah).
Vylepšený program s automatickými jednotkami
Program môžeme vylepšiť pre zobrazovanie vhodných jednotiek - malé vzdialenosti budú v milimetroch, väčšie v centimetroch a zistí aj vzdialenosť mimo rozsah.
Senzor tiež prepneme do režimu dlhého dosahu upravením parametrov cez objektovú funkciu set_Vcsel_pulse_period() - podrobnosti sú v ukážkovom programe autora knižnice.
Vylepšený program:
from machine import I2C, Pin, Signal
i2c = I2C(0, scl = Pin(26), sda = Pin(0))
zariadenia = i2c.scan()
print(i2c, zariadenia)
if 0x29 in zariadenia:
from vl53l0x import VL53L0X
from time import sleep
senzor = VL53L0X(i2c = i2c)
# nastavenie vyššieho dosahu
senzor.set_Vcsel_pulse_period(senzor.vcsel_period_type[0], 18)
senzor.set_Vcsel_pulse_period(senzor.vcsel_period_type[1], 14)
tlacidlo = Signal(37, Pin.IN, invert = True)
while not tlacidlo():
try:
vzdialenost = senzor.ping()
except Exception:
print("Nastala chyba pri meraní! ")
sleep(1)
else:
print("vzdialenosť: ", end = "")
if vzdialenost < 100:
print(f"{vzdialenost} mm", end = " \r")
elif vzdialenost <= 2000:
print(f"{vzdialenost/10:.1f} cm", end = " \r")
else:
print("ďaleko", end = " \r")
sleep(0.2)
else:
print("Nie je pripojený správny senzor!")
Všimnite si v programe zopár dôležitých bodov:
- najskôr zisťujeme, či je senzor pripojený, až potom začneme merať;
- meranie je obalené v try-except, aby v prípade poruchy senzoru program neskončil s chybou;
- výpis intenzity svetla je stále v jednom riadku - používa sa znak
"\r", ktorý vráti kurzor na začiatok riadku; - medzery za vzdialenosťou slúžia na prepísanie dlhšieho čísla kratším číslom.
Laserový merač
Pre plnohodnotný laserový merač vzdialenosti potrebujeme mobilné zariadenie, ktoré nie je potrebné pripájať káblom k počítaču. Takže namerané vzdialenosti musíme vypisovať na displej:
from machine import I2C, Pin
from time import sleep, ticks_ms, ticks_diff
from sys import exit
def Tlacidlo(pin):
global tlacidlo_cas, tlacidlo_bolo_stlacene
if ticks_diff(ticks_ms(), tlacidlo_cas) < 100: return
if pin(): return
tlacidlo_cas = ticks_ms()
tlacidlo_bolo_stlacene = True
def PisText(displej, font, text, x = None, y = None, farba = None):
if x == None:
x = (displej.width() - len(text) * font.WIDTH) // 2
if y == None:
y = (displej.height() - font.HEIGHT) // 2
if farba:
for i in (0, 1):
font.PALETTE[i] = farba[i] << 8 & 0xff00 | farba[i] >> 8
for znak in text:
try: displej.bitmap(font, x, y, font.MAP.index(znak))
except: pass
else: x += font.WIDTH
return text
i2c = I2C(0, scl = Pin(26), sda = Pin(0))
zariadenia = i2c.scan()
print(i2c, zariadenia)
if 0x29 not in zariadenia:
print("Nie je pripojený správny senzor!")
exit()
import M5Stick
from st7789 import color565
import inconsolata_SK_32 as font32
farby = (color565(255, 220, 180), color565(20, 0, 0))
displej = M5Stick.lcd
displej.rotation(0)
displej.init()
displej.fill(farby[1])
M5Stick.pmu.lcd_on()
from vl53l0x import VL53L0X
senzor = VL53L0X(i2c = i2c)
DOSAH = const(1200)
# nastavenie vyššieho dosahu
#senzor.set_Vcsel_pulse_period(senzor.vcsel_period_type[0], 18)
#senzor.set_Vcsel_pulse_period(senzor.vcsel_period_type[1], 14)
#DOSAH = const(2000)
tlacidlo_cas = 0
tlacidlo = Pin(37, Pin.IN, pull = Pin.PULL_UP)
tlacidlo.irq(handler = Tlacidlo, trigger = Pin.IRQ_FALLING)
meranie = True
tlacidlo_bolo_stlacene = False
y_text = displej.height() - font32.HEIGHT
indikator_sirka = const(40)
indikator_x = (displej.width() - indikator_sirka) // 2
while True:
if tlacidlo_bolo_stlacene:
tlacidlo_bolo_stlacene = False
meranie = not meranie
if meranie:
displej.sleep_mode(False)
M5Stick.pmu.lcd_on()
else:
displej.sleep_mode(True)
M5Stick.pmu.lcd_off()
if meranie:
try:
vzdialenost = senzor.ping()
except Exception:
print("Nastala chyba pri meraní! ")
displej.fill_rect(0, displej.height() - font32.HEIGHT, displej.width(), font32.HEIGHT, farby[1])
PisText(displej, font32, "CHYBA", None, displej.height() - font32.HEIGHT, farby)
sleep(1)
else:
print("vzdialenosť: ", end = "")
if vzdialenost < 100:
text = f"{vzdialenost} mm"
elif vzdialenost <= DOSAH:
text = f"{vzdialenost/10:.1f} cm"
else:
text = "mimo"
print(f"{text:8}", end = "\r")
okraj = (displej.width() - len(text) * font32.WIDTH + 1) // 2
PisText(displej, font32, text, None, y_text, farby)
displej.fill_rect(0, y_text, okraj, font32.HEIGHT, farby[1])
displej.fill_rect(displej.width() - okraj, y_text, okraj, font32.HEIGHT, farby[1])
y_hranica = int(y_text * min(vzdialenost / DOSAH, 1))
displej.fill_rect(indikator_x, 0, indikator_sirka, y_text - y_hranica, farby[1])
displej.fill_rect(indikator_x, y_text - y_hranica, indikator_sirka, y_hranica, farby[0])
sleep(0.2)
else:
sleep(0.1)
I v tomto programe je zopár zaujímavostí:
- ak nie je senzor pripojený, program skončí cez funkciu sys.exit();
- tlačidlo je riešené cez prerušenie, pričom funkcia odstraňuje zákmity a nezdržuje sa;
- výpis do konzoly je dopĺňaný na 8 znakov medzerami pomocou
f"{text:8}"- tým sa zmaže dlhšie číslo; - výpis textu na displej je automaticky centrovaný;
- vykresľuje sa aj grafický indikátor vzdialenosti, pričom sa nepremazáva celá obrazovka (obraz a text by blikali).