Skip to main content

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.

hat-tof.webp

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).