VL53L0X и ESP32: собираем лазерный дальномер

          VL53L0X — это компактный лазерный дальномер, разработанный компанией STMicroelectronics. Этот датчик измеряет расстояние до объекта, вычисляя время, которое требуется короткому световому импульсу, чтобы достичь цели и вернуться обратно к сенсору. Благодаря такому принципу он обеспечивает высокую точность и не зависит от цвета и текстуры поверхности объекта, в отличие от ультразвуковых сенсоров. VL53L0X широко применяется в робототехнике для обнаружения препятствий, в системах навигации, бесконтактных выключателях и системах позиционирования.

          В рамках данной статьи мы подробно рассмотрим устройство и принцип работы сенсора, а также соберём в качестве примера несложный дальномер на базе ESP32 с индикацией на TFT-дисплее ST7735S.

Содержание


Общее описание

          Лазерный дальномер VL53L0X — это специализированная микросхема системы на кристалле (SoC), которая измеряет расстояние, вычисляя время пролета короткого импульса света до объекта и обратно. Можно даже сказать, что преобразует длительность времени светового отклика в расстояние. Чип предназначен для определения дистанции до 2 метров и широко применяется в робототехнике для обхода препятствий, в различных «умных» устройствах и бытовой технике, в системах определения присутствия пользователя, а также для мониторинга уровня жидкости или сыпучих веществ в закрытых емкостях.

          Ключевыми параметрами датчика являются: регистрация дальности до 1,5-2 метров, низкое энергопотребление примерно 20 мА при напряжении питания 3,3 Вольта, простой интерфейс подключения I2C, а также высокая точность, которая зависит от алгоритма работы сенсора:

          Ближайшим аналогом является старшая модель VL53L1X, которая отличается большей дальностью (до 4 метров) и достоверностью за счёт более стабильных показаний в сложных условиях (яркий свет, неоднородные объекты). Дополнительно можно отметить, этот сенсор имеет более быструю частоту опроса (10 мс / 100 Гц), что критически важно для динамических систем (робототехника). Также добавлен функционал гибкой настройки ROI (Region of Interest), которая позволяет выделять угол обзора для фильтрации помех. А ещё есть более старшая модель VL53L3CX, которая по всем параметрам немного лучше (в частности, регистрирует длину до 4 метров).

Читайте также:  Датчик температуры и влажности ZS05

          Среди других аналогов можно выделить более дешёвые, но менее точные ультразвуковые модули вроде HC-SR04.

          Есть ещё инфракрасные детекторы типа Sharp GP2Y0A, преимуществом которого являются высокая точность, малый размер и быстродействие, а к недостатком — сравнительно высокая стоимость, чувствительность к прямому солнечному свету и сложности с определением дистанции до прозрачных или сильно поглощающих поверхностей.


Устройство и принцип работы

          Чтобы понять механизм действия, нужно разобраться в устройстве датчика. Внутри микросхемы интегрировано несколько ключевых компонентов:

          — лазерный излучатель (Vertical Cavity Surface Emitting Laser, VCSEL): испускает невидимый глазу инфракрасный импульс;

          — фотодетектор из массива однофотонных лавинных диодов (Single Photon Avalanche Diode, SPAD): улавливает отражённый от объекта световой сигнал;

          — продвинутое вычислительное ядро для определения дистанции (Advanced Ranging Core, ARC) совместно с высокоточным таймером (Time to Digital Converter, TDC): специализированный цифровой сигнальный процессор, который выполняет сложную математическую обработку в реальном времени, в рамках которой производится фиксация времени между излучением светового потока и его приёмом;

          — система управления, которая включает в себя собственный микроконтроллер с I2C-интерфейсом для подключения к внешним устройствам, постоянное запоминающее устройство (Read Only Memory, ROM), оперативная память (Random Access Memory, RAM), а также энергонезависимая память (Non Volatile Memory, NVM) для хранения калибровочных значений.

          Ниже представлена блок-схема чипа из официальной документации:

          В основе работы модуля лежит метод определения длительности времени пролёта светового импульса (Time-of-Flight, ToF). Дальность до объекта вычисляется путем определения времени, в течение которого короткий импульс света пролетает от датчика к объекту и обратно. Поскольку скорость света (3×10^8 м/с) является константой, расстояние (d) рассчитывается по формуле:

d = (c × t) / 2,

где t — время пролёта;

Читайте также:  Датчик температуры К1019ЧТ1

c — скорость света.

          Делитель 2 учитывает, что свет проходит двойной путь: до объекта и обратно.

          Полный цикл определения дальности выполняется следующим образом:

          1. Инициирование. Внешний микроконтроллер (например, ESP32) отправляет команду датчику через I2C-интерфейс. Встроенный в чип контроллер принимает эту команду и управляет всем последующим процессом.

          2. Излучение светового сигнала. По команде микроконтроллера лазерный излучатель (VCSEL) генерирует ультракороткий импульс инфракрасного света с длиной волны 940 нм (класс безопасности Class 1 IEC 60825-1:2013). Эта длина волны выбрана для минимизации влияния солнечного света.

          3. Старт измерения времени отклика. В момент излучения короткой вспышки света запускается высокоточный таймер (TDC). Его задачей является измерить интервал времени с максимально возможной точностью (в пико- или наносекундах).

          4. Приём светового отклика. Свет, отразившийся от цели, попадает на фотодетектор (SPAD), который работает в лавинном режиме, что позволяет ему детектировать отдельные фотоны. Попадание первого же отражённого фотона вызывает лавинообразный токовый сигнал, который легко зафиксировать.

          5. Стоп время отклика. Как только фотодетектор SPAD регистрирует отраженный свет, он немедленно останавливает таймер (TDC), после чего фиксируется временной интервал между излучением и приёмом.

          6. Обработка данных и вычисление расстояния. Полученное с таймера значение времени передается в вычислительное ядро (ARC), которое выполняет критически важные операции:

                    — фильтрация сигнала: отделяет полезный световой отклик от фонового шума и паразитных засветок;

                    — коррекция погрешностей: используя калибровочные коэффициенты, хранящиеся в энергонезависимой памяти (NVM), вносит поправки на системные ошибки. Например, температурный дрейф, внутренние отражения в корпусе чипа и индивидуальные особенности конкретно данного экземпляра микросхемы;

                    — вычисление результата: на основе скорректированного времени по формуле времени пролёта вычисляет окончательное расстояние в миллиметрах. Для промежуточных вычислений ARC использует оперативную память (RAM).

Читайте также:  Подключение TFT-дисплея ST7735 к ESP32: вывод текста, графики и изображений

          Промежуток от модуля до объекта вычисляется по формуле:

Расстояние = (Скорость_света × Время_пролёта) / 2

          7. Вывод результата. Готовое значение длины, а также статус (успех, ошибка, световой отклик слишком слабый/сильный) помещаются в регистры датчика. Встроенный микроконтроллер через I2C-интерфейс сообщает о готовности данных, и внешний микроконтроллер может их считать.


Параметры и характеристики


VL53L0X в составе платы (модуля) GY-530

          Для экспериментов часть применяют готовый отладочный модуль, на котором, помимо самой микросхемы, распаяны все необходимые элементы обвязки, включая линейный стабилизатор с низким уровнем падения напряжения (3,3 В) и подтягивающие резисторы:

          Ниже представлены таблица распиновки и принципиальная схема (один из возможных вариантов) платы модуля GY-530:


Схема подключения VL53L0X к ESP32

          Для построения дальномера воспользуемся отладочной платой NodeMCU-32S (38 pin), построенной на базе модуля ESP-WROOM-32.

          Выводить показания  будем на TFT-дисплей (контроллер ST7735S) с диагональю 0,96 дюйма и разрешением 80×160 пикселей. Ниже представлена таблица подключений сенсора и дисплея к отладочной плате.

          Далее представлена схема подключения лазерного дальномера VL53L0X и дисплея ST7735S к ESP32, а также фотография собранного макета:


Подготовка проекта в Arduino IDE

          Проект создавался в среде программирования Arduino IDE 2. Если нужно настроить среду для работы с ESP32, то подробная инструкция представлена в статье про Интернет-часы на базе ESP32-C3.

          Для взаимодействия с дальномером применим библиотеку VL53L0X от Pololu:

          Для взаимодействия с TFT-дисплеем воспользуемся библиотекой tft_eSPI, как наиболее быстрой и эффективной для данной задачи:

          Необходимо настроить файл конфигурации User_Setup.h библиотеки tft_eSPI, правильно указав пины подключения к плате, а также драйвер и базовые параметры дисплея. После установки библиотеки нужный нам файл по умолчанию лежит примерно по такому пути:

C:\Users\Пользователь\Documents\Arduino\libraries\TFT_eSPI

где Пользователь — имя учетной записи на Вашем компьютере с ОС Windows.

          Откройте файл User_Setup.h в каком-нибудь редакторе (например, Блокнот или VS Code) и скорректируйте следующие блоки:

          — драйвер дисплея

          — разрешение экрана

          — цветовая предустановленная конфигурация

          — настройка пинов подключения к отладочной плате

          — настройка шрифтов (оставляем по умолчанию)

          — настройка частоты SPI-интерфейса

При желании можете ознакомиться с аналогичной инструкцией для среды разработки PlatformIO.


Программный код (скетч)

          Как уже упоминалось выше, модуль может быть настроен по одному из трёх вариантов алгоритма работы:

          1) «Стандартный» с максимальной дальностью 1200 мм и точностью ±3%;

          2) «Дальний» — 2000 мм / ±5%;

          3) «Высокая точность» — 6000 мм / ±1%.

          Ниже представлен подробно прокомментированный код, который реализует все три алгоритма работы, между которыми легко переключать по нажатию кнопки на отладочной плате:

// Подключаем необходимые библиотеки
#include <Arduino.h>      // Основная библиотека Arduino для ESP32
#include <Wire.h>         // Библиотека для работы с I2C интерфейсом
#include <VL53L0X.h>      // Библиотека для работы с лазерным дальномером VL53L0X
#include <TFT_eSPI.h>     // Библиотека для работы с TFT дисплеем ST7735

// Создаем объекты для работы с устройствами
TFT_eSPI tft = TFT_eSPI();  // Создаем объект для управления TFT дисплеем
VL53L0X sensor;             // Создаем объект для работы с лазерным дальномером

// Статические переменные для управления временем и хранения данных
static unsigned long lastUpdate = 0;        // Время последнего обновления дисплея
static unsigned long lastMeasurement = 0;   // Время последнего измерения расстояния
static uint16_t lastDistance = 0;           // Последнее измеренное расстояние в миллиметрах

// Определяем пин для кнопки (встроенная кнопка BOOT на NodeMCU-32S)
#define BUTTON_PIN 0  // Кнопка расположена справа от USB-разъема

// Создаем перечисление для режимов работы датчика
enum SensorMode {
  STANDARD_MODE,      // Стандартный режим: баланс скорости и точности
  LONG_RANGE_MODE,    // Дальний режим: увеличенная дальность, меньшая точность
  HIGH_ACCURACY_MODE, // Режим высокой точности: максимальная точность, малая дальность
  MODE_COUNT          // Автоматический счетчик количества режимов (равен 3)
};

// Объявляем переменную для хранения текущего режима работы
SensorMode currentMode = STANDARD_MODE;  // По умолчанию используем стандартный режим

// Создаем структуру для хранения конфигурации датчика
struct SensorConfig {
  const char* name;           // Название режима (текстовая строка)
  uint32_t timingBudget;      // Время измерения в микросекундах (влияет на точность/скорость)
  float signalRateLimit;      // Порог уровня сигнала (фильтрация шумов)
  uint8_t preRangePulse;      // Длительность предварительных лазерных импульсов
  uint8_t finalRangePulse;    // Длительность финальных лазерных импульсов
  uint16_t maxRange;          // Максимальная измеряемая дистанция в миллиметрах
  uint8_t accuracyPercent;    // Точность измерения в процентах (погрешность)
  uint16_t color;             // Цвет для отображения на дисплее
};

// Создаем массив конфигураций для всех режимов работы
const SensorConfig MODE_CONFIGS[] = {
  // Стандартный режим: 1.2м, 3% точность, быстрые измерения
  {"Standart", 33000, 0.25, 14, 10, 1200, 3, TFT_RED},
  
  // Дальний режим: 2.0м, 5% точность, увеличенная дальность
  {"Long Range", 50000, 0.10, 18, 12, 2000, 5, TFT_GREEN},
  
  // Режим высокой точности: 0.6м, 1% точность, медленные но точные измерения
  {"High Accuracy", 200000, 0.10, 18, 14, 600, 1, TFT_YELLOW}
};

// Класс для фильтра Калмана - сглаживает показания датчика
class KalmanFilter {
private:
  // Параметры фильтра:
  float Q = 0.1;  // Шум процесса (насколько быстро меняется реальное расстояние)
  float R = 0.1;  // Шум измерения (насколько шумят показания датчика)
  float P = 1.0;  // Ковариация (степень уверенности в текущей оценке)
  float X = 0.0;  // Текущая оценка расстояния (отфильтрованное значение)
  
public:
  // Основной метод фильтра - принимает новое измерение, возвращает отфильтрованное значение
  float update(float measurement) {
    // Этап предсказания: увеличиваем неопределенность
    P = P + Q;
    
    // Вычисляем коэффициент Калмана (насколько доверять новому измерению)
    float K = P / (P + R);
    
    // Этап коррекции: обновляем оценку с учетом нового измерения
    X = X + K * (measurement - X);
    
    // Обновляем ковариацию
    P = (1 - K) * P;
    
    // Возвращаем отфильтрованное значение
    return X;
  }
  
  // Метод сброса фильтра (используется при смене режима)
  void reset() {
    P = 1.0;  // Сбрасываем ковариацию
    X = 0.0;  // Сбрасываем текущую оценку
  }
};

// Создаем объект фильтра Калмана
KalmanFilter distanceFilter;

// Функция настройки датчика для выбранного режима
void configureSensor(SensorMode mode) {
  // Получаем конфигурацию для выбранного режима из массива
  SensorConfig config = MODE_CONFIGS[mode];
  
  // Обновляем текущий режим
  currentMode = mode;
  
  // Останавливаем непрерывные измерения (если они были запущены)
  sensor.stopContinuous();
  
  // Даем время на завершение текущих операций
  delay(100);
  
  // Применяем основные настройки датчика:
  
  // Устанавливаем время измерения (в микросекундах)
  // Больше время = выше точность, но медленнее измерения
  sensor.setMeasurementTimingBudget(config.timingBudget);
  
  // Устанавливаем порог уровня сигнала
  // Меньшее значение = более чувствительный режим
  sensor.setSignalRateLimit(config.signalRateLimit);

  // Запускаем непрерывные измерения с периодом 0 (максимальная частота)
  sensor.startContinuous(0);
  
  // Сбрасываем фильтр Калмана при смене режима
  distanceFilter.reset();
  
  // Выводим информацию в Serial-порт
  Serial.print("Режим установлен: ");
  Serial.println(config.name);
}

// Функция отрисовки основного экрана на TFT-дисплее
void drawMainScreen() {
  // Получаем конфигурацию текущего режима
  SensorConfig config = MODE_CONFIGS[currentMode];
  
  // Очищаем экран черным цветом
  tft.fillScreen(TFT_BLACK);

  // Выводим название текущего режима
  tft.setTextColor(config.color, TFT_BLACK);  // Цвет текста соответствует режиму
  tft.setTextSize(1);                         // Размер текста: маленький
  tft.setCursor(5, 5);                        // Позиция курсора: 5px слева, 5px сверху
  tft.print("Mode: ");                        // Выводим текст
  tft.println(config.name);                   // Выводим название режима и переходим на новую строку
  
  // Выводим максимальную дальность режима
  tft.setTextColor(TFT_WHITE, TFT_BLACK);     // Белый текст на черном фоне
  tft.setCursor(5, 15);                       // Позиция: 5px слева, 15px сверху
  tft.print("Range: ");                       // Выводим текст
  tft.print(config.maxRange);                 // Выводим значение максимальной дальности
  tft.println(" mm");                         // Выводим единицы измерения и переходим на новую строку
  
  // Выводим точность режима
  tft.setCursor(5, 25);                       // Позиция: 5px слева, 25px сверху
  tft.print("Accuracy: ");                    // Выводим текст
  tft.print(config.accuracyPercent);          // Выводим значение точности в процентах
  tft.println(" %");                          // Выводим символ процента и переходим на новую строку
}

// Функция setup() выполняется один раз при запуске устройства
void setup() {
  // Инициализируем Serial-порт для отладки (скорость 115200 бод)
  Serial.begin(115200);
  
  // Инициализация TFT-дисплея
  Serial.print("Инициализация TFT-дисплея...");
  tft.init();                    // Инициализируем дисплей
  tft.setRotation(3);            // Устанавливаем ориентацию экрана (альбомная)
  tft.fillScreen(TFT_BLACK);     // Очищаем экран черным цветом
  tft.setTextColor(TFT_WHITE, TFT_BLACK);  // Устанавливаем цвет текста: белый на черном фоне
  Serial.println(" OK!");        // Сообщаем об успешной инициализации

  // Инициализация датчика расстояния VL53L0X
  Serial.print("Инициализация датчика VL53L0X...");
  Wire.begin(); // Инициализируем I2C-интерфейс (по умолчанию: SDA=21, SCL=22 на ESP32)
  
  // Пытаемся инициализировать датчик
  if (!sensor.init()) {
    // Если инициализация не удалась - выводим сообщение об ошибке
    Serial.print(" Ошибка инициализации VL53L0X!");
    
    // Выводим сообщение об ошибке на TFT-дисплей
    tft.fillScreen(TFT_RED);              // Заливаем экран красным цветом
    tft.setTextColor(TFT_WHITE, TFT_RED); // Белый текст на красном фоне
    tft.setTextSize(2);                   // Увеличиваем размер текста
    tft.setCursor(10, 40);                // Позиционируем курсор
    tft.println("ERROR!");                // Выводим сообщение об ошибке

    // Бесконечный цикл - программа останавливается
    while(1);
  } 
  else {
    // Если инициализация успешна - выводим сообщение
    Serial.println(" OK!");
  }
  
  // Устанавливаем таймаут для датчика (500 мс)
  sensor.setTimeout(500);

  // Настраиваем датчик в стандартном режиме
  configureSensor(STANDARD_MODE);
  
  // Отрисовываем основной интерфейс на дисплее
  drawMainScreen();
}

// Функция loop() выполняется бесконечно в цикле
void loop() {
  // Измерение расстояния каждые 100 миллисекунд
  if (millis() - lastMeasurement > 100) {
    // Обновляем время последнего измерения
    lastMeasurement = millis();
    
    // Читаем расстояние с датчика в миллиметрах
    uint16_t distance = sensor.readRangeContinuousMillimeters();
    
    // Проверяем, что измерение успешно и расстояние в допустимом диапазоне
    if (!sensor.timeoutOccurred() && distance < MODE_CONFIGS[currentMode].maxRange * 1.2) {
      // Применяем фильтр Калмана для сглаживания показаний
      float filteredDistance = distanceFilter.update(distance);
      
      // Сохраняем отфильтрованное расстояние (преобразуем к целому числу)
      lastDistance = (uint16_t)filteredDistance;
    }
  }
  
  // Обновление дисплея каждые 200 миллисекунд
  if (millis() - lastUpdate > 200) {
    // Обновляем время последнего обновления
    lastUpdate = millis();
    
    // Обновляем отображение расстояния на дисплее
    updateDisplay(lastDistance);
  }

  // Обработка нажатия кнопки для переключения режимов
  if (digitalRead(BUTTON_PIN) == LOW) {
    // Простая защита от дребезга контактов - ждем 50 мс
    delay(50);
    
    // Проверяем, что кнопка все еще нажата (исключаем ложные срабатывания)
    if (digitalRead(BUTTON_PIN) == LOW) {
      // Переключаем режим по кругу: 0→1→2→0→1...
      currentMode = (SensorMode)((currentMode + 1) % MODE_COUNT);
      
      // Настраиваем датчик для нового режима
      configureSensor(currentMode);
      
      // Перерисовываем основной экран
      drawMainScreen();
      
      // Выводим информацию в Serial-порт
      Serial.print("Режим работы переключен на: ");
      Serial.println(MODE_CONFIGS[currentMode].name);
    }
  }
}

// Функция обновления отображения расстояния на дисплее
void updateDisplay(uint16_t distance) {
  // Получаем конфигурацию текущего режима
  SensorConfig config = MODE_CONFIGS[currentMode];
  
  // Очищаем область отображения расстояния (прямоугольник)
  // Параметры: X=0, Y=45, Width=160, Height=25, Color=черный
  tft.fillRect(0, 45, 160, 25, TFT_BLACK);
  
  // Устанавливаем цвет текста в соответствии с режимом
  tft.setTextColor(config.color, TFT_BLACK);
  
  // Устанавливаем большой размер текста для лучшей читаемости
  tft.setTextSize(3);

  // Устанавливаем позицию для вывода расстояния
  tft.setCursor(30, 45);
  
  // Выводим значение расстояния
  tft.print(distance);
  
  // Выводим единицы измерения и переходим на новую строку
  tft.println(" mm");
}

          Для простоты взаимодействия с чипом в коде уже заранее настроены все параметры для каждого алгоритма:

// Конфигурации для разных режимов
const SensorConfig MODE_CONFIGS[] = {
  {"Standart", 33000, 0.25, 14, 10, 1200, 3, TFT_RED},
  {"Long Range", 50000, 0.10, 18, 12, 2000, 5, TFT_GREEN},
  {"High Accuracy", 200000, 0.10, 18, 14, 600, 1, TFT_YELLOW}
};

          Разберём поподробнее каждый метод, чтобы Вы могли более гибко производить настройку модуля под свои требования.

          Конфигурация режима работы представляет собой массив параметров (не считая последний — цвет текста):

// Структура для конфигурации датчика
struct SensorConfig {
  const char* name;           // Название режима (текстовая строка)
  uint32_t timingBudget;      // Время измерения в микросекундах
  float signalRateLimit;      // Порог уровня сигнала (фильтрация шумов)
  uint8_t preRangePulse;      // Длительность предварительных лазерных импульсов
  uint8_t finalRangePulse;    // Длительность финальных лазерных импульсов
  uint16_t maxRange;          // Максимальная измеряемая дистанция в миллиметрах
  uint8_t accuracyPercent;    // Точность измерения в процентах (погрешность)
  uint16_t color;             // Цвет для отображения на дисплее
};

          Если Вы хотите поэкспериментировать с алгоритмами, то давайте рассмотрим подробнее каждый параметр, за что каждый из них отвечает, и какие возможные значения они могут принимать.

          1) timingBudget — Время измерения

            Определяет количество времени, в течение которого датчик выполняет одно измерение:

            Чем больше время, тем выше достоверность, но ниже скорость.

            2) signalRateLimit — Порог уровня сигнала

            От этого значения зависит тот минимальный уровень полезного светового отклика, который будет взят в обработку для корректного вычисления:

            Чем выше значение, тем меньше дальность, но меньше шумов, а значит — более стабильные показания.

            3) preRangePulse — Предварительные импульсы

            Величина определяет длительность лазерных сигналов для грубого определения длины просвета между сенсором и препятствием:

            Чем больше значение, тем больше энергии лазера потребляется для надёжного определения дальних дистанций.

            4) finalRangePulse — Финальные импульсы

            Длительность лазерных импульсов для достоверного определения длины:

            Чем больше величина, тем точнее, но более длительное время на расчёты, а значит — ниже скорость обработки.

            5) maxRange — Максимальная дальность

            Ожидаемая максимально возможная регистрируемая длина промежутка до препятствия:

            6) accuracyPercent — Точность измерений

            Предполагаемая погрешность измерений в процентах от максимальной дальности для конкретного режима:

            Также важно отметить, что в программе используется упрощённая версия фильтра Калмана для сглаживания показаний. Этот фильтр работает как «умный» усреднитель, который учитывает как предыдущие измерения, так и новое показание датчика. Он нивелирует влияние шумов датчика или мелких помех в измеряемой среде.

            Фильтр Калмана здесь работает по принципу «предсказание-коррекция». Так как каждое новое измерение содержит случайные погрешности, то фильтр не просто усредняет значения, а сочетает предыдущее «отфильтрованное» состояние с новым измерением, учитывая степень приближения каждого из них к достоверному значению (исходя из статистики).

            Для вычислений в фильтре используются следующие параметры:

Q (шум процесса) = 0,1 показывает, насколько быстро может меняться реальное расстояние. Малые значения означают, что мы ожидаем плавное изменение расстояния.

R (шум измерения) = 0,1 характеризует погрешность самого датчика.

P (ковариация) это «степень неуверенности» фильтра в своей текущей оценке.

K (коэффициент Калмана) вычисляемый вес, который определяет, насколько сильно мы доверяем новому измерению.

            Процесс фильтрации разделён на несколько шагов:

            1) Предсказание: фильтр увеличивает свою «неуверенность» (P = P + Q) и предполагает, что реальное расстояние могло измениться;

            2) Коррекция: вычисляет коэффициент Калмана K = P/(P + R). Чем больше шум измерения R, тем меньше мы доверяем новым данным;

            3) Обновление: вычисляется по формуле

новая оценка = старая оценка + K × (новое измерение - старая оценка)

            Если датчик внезапно показывает резкий скачок, фильтр не сразу принимает это значение, а плавно «подстраивается» к нему, отсекая случайные выбросы, но при этом успевая отслеживать реальные изменения расстояния.

            Для калибровки VL53L0X можно создать процедуру компенсации систематической погрешности. Простейший способ — это расположить датчик на точно известном расстоянии от объекта (например, 100 мм), сделать серию измерений, вычислить среднее отклонение от реального значения и сохранить этот поправочный коэффициент в памяти ESP32.

            Также полезно калибровать датчик под конкретные условия. Например, для разных материалов поверхностей (белая бумага, чёрный пластик) можно установить разные поправочные коэффициенты.


Эксперимент

            Попробуем проверить, насколько достоверно и адекватно будет измерять наш импровизированный дальномер.

            В предлагаемом выше коде добавлена функция: оперативное переключение (по кругу) между режимами посредством нажатия на кнопку BOOT, которая справа от USB-разъёма.

            При инициализации микроконтроллера по умолчанию активирована универсальная конфигурация «Стандартный». На дисплее отображается базовая информация:

            Для начала попробуем измерить на разных режимах длину 100 мм. В качестве тела, до которого измерялась дистанция, был использован лист белой бумаги и бумажная салфетка:

— Standart

— Long Range

— High Accuracy

            Как видим, фиксируемые значения более-менее корректные. Разумеется, установка датчика по линейке выполнялась «на глаз», поэтому не будем обращать внимание на отклонения в пару миллиметров. Главное, что мы удостоверились — вычисления адекватные.

            Теперь давайте убедимся, что сенсор действительно НЕ способен выполнять оценку дистанций менее 30 мм при максимальной точности:

— при фактических 20 модуль показывает 31

— при фактических 30 видим ожидаемые 30

— при фактических 40 дисплей показывает адекватные 41

            Теперь посмотрим, как преобразователь справится с дистанцией 600:

— Standart

— Long Range

— High Accuracy

            Результаты вполне себе адекватные и соответствуют заявленной точности (кроме режима High Accuracy, но нужно помнить, что расстояние линейкой выставлено «на глазок»).

            Теперь проверим дистанцию 1000:

— Standart

— Long Range

            Результаты также были довольно близки к ожидаемым значениям.

            Попытка произвести замер максимально возможной дальности 2000 миллиметров в режиме Long Range окончилась неудачей: датчик просто показывал случайные значения:


Заключение

            В данной статье мы подробно рассмотрели лазерный дальномер VL53L0X: его устройство, принцип работы и ключевые особенности. Этот модуль, благодаря своей точности, компактности и простоте использования, открывает широкие возможности для создания интеллектуальных систем: от колёсных мини-роботов и автоматических дверей до систем контроля уровня заполнения емкостей.

            В качестве наглядного примера мы на базе отладочной платы ESP32 собрали функциональный измеритель расстояния. Эксперименты показали, что дальномер способен вполне адекватно производить оценку расстояний от 30 до 1000 мм с приемлемой погрешностью. Важно понимать, что рассмотренный сенсор не годится для прецизионных измерений, зато является идеальным вариантом для экспериментальных проектов за счёт оптимального соотношения цены, качества и функциональности.