Самодельный световой барьер

Всем дорого времени. Основа очередного самодельного проекта – Raspberry Pi Pico на два ядра, где оба используются для светового барьера с кодируемыми импульсными последовательностями и автокорреляцией. Как известно, собрать простейший микроконтроллерный световой барьер можно из светодиода, датчика освещенности (фотоэлемента) и простого кода:

while True:
measure()
if not autocorrelation():
beep(800)

Где Measure() измеряет полученный свет, излучаемый светодиодом, autocorrelation() вычисляет поиск сигнала от LED в измеренных данных, а beep() подает звуковой сигнал. Вроде бы просто, но на самом деле то, что стоит за этими функциями, несколько сложнее. Проблема: свет. Подключаем датчик освещенности и светодиод к Grove Shield:



Самодельный световой барьер

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

Самодельный световой барьер



Но такая схема будет работать только в темных помещениях. Как только свет от лампы падает на датчик, тот постоянно станет измерять высокое значение, которое больше не становится низким, когда световой барьер блокируется:

Самодельный световой барьер

Это затрудняет поиск правильного порога для измеряемой интенсивности света. Кроме того, большинство источников света излучают не постоянный свет, а импульсы:

Самодельный световой барьер

Простой пороговый метод здесь не сработает. Решение: импульсный светодиод.

Конечно вместо белого светодиода можно использовать инфракрасный и соответствующий ИК-датчик. Это сведет к минимуму проблему с окружающим светом. Но давайте найдем решение для светового барьера с видимым светом.

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

Самодельный световой барьер

Это уже лучше. Но все же окружающий свет вызывает проблемы, потому что форма измеряемого сигнала меняется:

Самодельный световой барьер

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

Далее автокорреляция. Автокорреляция — это математическое представление степени сходства между данным временным рядом и его запаздывающей версией на последовательных интервалах времени.

Итак, как это работает? Предположим, светодиод излучает короткий одиночный световой импульс. Этот световой импульс измеряется датчиком освещенности. Конечно, измеряемый датчиком сигнал зашумлен и показывает смещение:

Самодельный световой барьер

Продолжительность сигнала светодиода короче чем время, в течение которого данные измеряются датчиком освещенности. Это позволяет сдвигать сигнал светодиода во времени относительно измеренного сигнала. И во время каждого шага сдвига умножаем значения сигнала светодиода на значения в соответствующих позициях измеряемого сигнала. Сумма этих умножений является значением корреляции для шага сдвига. Если в данных автокорреляции обнаружен максимум, это означает что в измеренных данных был обнаружен сигнал светодиода:

Полезное на сайте:
Правильная схема и плата для стабилизаторов на микросхемах LM317, LM337, LM350

Самодельный световой барьер

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

Самодельный световой барьер

Что нам нужно, так это интеллектуальная схема импульсов для светодиода, которая дает четкий результат автокорреляции. Это коды Баркера, доступны разной длины:

Самодельный световой барьер

Если позволить светодиоду мигать такими последовательностями импульсов, можно добиться наиболее оптимального результата обнаружения с автокорреляцией:

Самодельный световой барьер

Пришло время для кодирования. Raspberry Pi Pico имеет два ядра, которые работают независимо. Это полезно, потому что тогда можем использовать одновременно одно ядро для управления светодиодом с последовательностями кода Баркера, а второе — для измерения данных от датчика освещенности.

Самодельный световой барьер

Для управления светодиодом в отдельном ядре, а следовательно, и в отдельном потоке, для этого должна быть определена собственная функция:

def led_task():
led.value(False)
led_start_ticks_us = utime.ticks_us()
# wait two pulse lenghts before start sequence
wait_until_ticks(led_start_ticks_us, 2*pulse_length*dt_samples)
for n, led_on_off in enumerate(Barker_Code):
if led_on_off > 0:
led.value(True)
else:
led.value(False)
wait_until_ticks(led_start_ticks_us, (n+3)*pulse_length*dt_samples)
led.value(False)

Затем простым вызовом эту функцию можно запустить как отдельный поток: _thread.start_new_thread(led_task, ())

Поскольку функция выполняется на втором ядре, она выполняется параллельно функциям, выполняемым на первом. Как раз то что нужно для измерения данных датчика. При этом функция Measure () выглядит следующим образом:

def measure():
# start the led pulse generation on the second core
_thread.start_new_thread(led_task, ())
start_ticks_us = utime.ticks_us()
for n in range(n_samples):
data[n] = light_sensor.read_u16() / 0xFFFF
wait_until_ticks(start_ticks_us, (n+1)*dt_samples)

Функция сначала запускает светодиодный поток, а затем сохраняет измеренные данные в массиве данных [] для последующего выполнения автокорреляции с сигналом светодиода. Последовательность сигналов светодиода предопределена в массиве Barker_Code_samples[]. Функция autocorrelation() выполняет вычисления и сохраняет значения корреляции в массиве autocorr[]:

def autocorrelation():
for shift in range(n_autocorr):
autocorr[shift] = 0
for pos in range(n_Barker_Code_samples):
autocorr[shift] += Barker_Code_samples[pos] * data[pos+shift]

В итоге получаем такие значения, когда свет светодиода хорошо измерить:

Полезное на сайте:
Знаменитый усилитель мощности класса A First Watt Нельсон Пасс

Самодельный световой барьер

Но как обнаружить максимум в данных? Это задача для конечного автомата:

Самодельный световой барьер

В состоянии 1 конечный автомат ожидает увеличения значений и ждет, пока значение не превысит пороговое значение, а затем ждет, пока значение снова не уменьшится. Затем переходит в состояние 2, где ждет, пока значение не упадет ниже порогового. Только если это задано, для переменной signal_detected устанавливается значение True. На следующем изображении показан процесс с использованием реальных данных:

Самодельный световой барьер

При использовании этого конечного автомата функция autocorrelation() в конечном итоге будет выглядеть так:

def autocorrelation():
autocorr_threshold = 0.75
autocorr_max = -1000
autocorr_max_pos = 0
detection_state = 1
signal_detected = False
for shift in range(n_autocorr):
autocorr[shift] = 0
for pos in range(n_Barker_Code_samples):
autocorr[shift] += Barker_Code_samples[pos] * data[pos+shift]
if detection_state == 1:
if autocorr[shift] > autocorr_max:
autocorr_max = autocorr[shift]
autocorr_max_pos = shift
if (autocorr[shift] < autocorr_max) and ((autocorr_max – autocorr[0]) > autocorr_threshold) and (autocorr_max_pos >= autocorr_valid_window[0]):
if autocorr_max_pos > autocorr_valid_window[1]:
detection_state = 3
else:
detection_state = 2
if detection_state == 2:
if (autocorr[shift] < (autocorr_max – autocorr_threshold)):
signal_detected = True
detection_state = 3
return (signal_detected)

Функция дополнительно проверяет находится ли максимум в средней области диапазона переключения. Он не должен лежать на краях. Если допустимый максимум был найден, то возвращается True. В противном случае False. Вот ещё пример, когда сигнал был или не был обнаружен:

Самодельный световой барьер

Вот и все: световой барьер готов!

Советы и рекомендации по сборке

Светодиодная плата (Grove – White LED) имеет потенциометр, с помощью которого можно регулировать яркость светодиода. С более ярким светодиодом световой барьер работает стабильнее, но смотрите чтоб светодиод не перегорел.

Самодельный световой барьер

Окружающий свет плохо влияет на световой барьер, потому что датчик освещенности (Grove — Light Sensor v1.2) быстро насыщается. Это означает, что датчик выдает только максимальные значения. Тут помогает защита в виде небольшого картонного рулона, который надевается на датчик.

Самодельный световой барьер

Реализован проект с помощью Grove Starter Kit для Raspberry Pi Pico от Seeed Studio.

Если хотите сохранить программу на Raspberry Pi Pico, чтобы она выполнялась автоматически, как только на модуль подается напряжение, то можно сохранить код под именем boot.py.

В Github.com/electricidea/pico_light_barrier находится вторая версия кода, которая содержит вывод командной строки для просмотра значений и функцию экспорта для сохранения данных в файлы CSV для последующего анализа.

Ханс-Гюнтер