Представляю вам образовательный проект — “Ультразвуковой парковочный ассистент”, разработанный в рамках учебно-методического комплекса на базе одноплатного компьютера Repka PI 4. #
Этот проект использует ультразвуковой датчик HC-SR04 для измерения расстояния до объекта, что позволяет удобно отслеживать приближение автомобилей или других объектов, например, при парковке. Система визуализирует информацию о расстоянии на 7-сегментном дисплее, а также управляет RGB-светодиодами и пассивным зуммером, которые изменяют свое поведение в зависимости от того, насколько близко находится объект.
Проект «Ультразвуковой парковочный ассистент» является отличным примером применения ультразвуковой технологии в повседневной жизни, позволяя с помощью простых визуальных и звуковых сигналов помогать пользователю в процессе парковки.
Проект будет собираться с использованием “Учебно-методический комплекс REPKA”. Схему сборки можно найти в разделе "Примеры готовых проектов" учебного пособия УМК “REPKA”.
Также все необходимые материалы и схемы подключения доступны в репозитории на платформе Gitflic.
Компоненты проекта
- Зуммер пассивный модуль (Passive buzzer KY-006) — для подачи звуковых сигналов, изменяющихся по тону в зависимости от расстояния, см. рисунок 1.
2. 7-сегментный индикатор 0.36" — для отображения числовых значений расстояния, см рисунок 2.
3. Адресная светодиодная лента (WS2812) — используется для визуальной индикации состояния с помощью цветных светодиодов, см рисунок 3.
4. Ультразвуковой датчик расстояния (HC-SR04) — для измерения расстояния до объекта, см. рисунок 4.
Вы можете приобрести все необходимые компоненты отдельно от "Учебно-методический комплекс REPKA". Ссылки на модули приведены в таблице ниже.
Компонент | Ссылка на приобретение |
---|---|
Монтажная/макетная плата | Ссылка |
Шлейф | Ссылка |
Переходник с шлейфа на макетную плату | Ссылка |
Соединительные провода | |
7-сегментный индикатор 0.36" | Ссылка |
Зуммер пассивный модуль (Passive buzzer KY-006) | Ссылка |
Адресная светодиодная лента (WS2812) | Ссылка |
Ультразвуковой датчик расстояния (HC-SR04) | Ссылка |
Подготовительный этап
1. Подключим дополнительное питание 5V к макетной плате:
2. После чего выведем дополнительное питание на макетную плату:
3. Подключим переходник с шлейфа на макетную плату:
4. Соединим шлейф с переходником для подключения к макетной плате и Repka Pi 4:
5. Итоговый результат должен выглядеть таким образом:
Сборка проекта
Во время сборки проекта будем регулярно обращаться к электрической принципиальной схеме и монтажной схеме, представленными в учебном пособии (см. рисунки 5 и 6). Эти схемы будут служить основным ориентиром на всех этапах подключения компонентов, обеспечивая точность и правильность сборки устройства.
Для разработки кода будет использоваться текстовый редактор Geany, который входит в состав стандартного ПО Репка ОС.
Электрическая принципиальная схема
Монтажная схема
1. Подключение пассивного зуммера.
Обратимся к рисункам 5 и 6. Из них видно, что устройство подключается через GPIO12 и питается от 3.3V.
1.1. Выполним подключение к макетной плате согласно таблице 1.
Макетная плата | Пассивный зуммер |
3.3V | VCC |
GPIO12 | IO |
GND | GND |
Таблица 1. Подключение пассивного зуммера к макетной плате.
1.2. Результат подключения будет выглядеть следующим образом, см. рисунок 7:
2. Для проверки правильности подключения используем python скрипт из репозитория repka-pi_iot-examples.
2.1. Клонируем репозиторий:
git clone git@gitflic.ru:repka_pi/repka-pi_iot-examples.git
2.2. Переходим в репозиторий:
cd repka-pi_iot-examples/
2.3. Выполним установку зависимостей.
2.3.1. Если хотите установить зависимости только для зуммера, выполните:
make setup-KY-006
2.3.2. Если хотите установить зависимости для всех датчиков и проектов, выполните:
make setup-all
2.4. Запускаем скрипт для проверки работоспособности прибора:
make KY-006
2.4.1. Если нет никакой реакции, то проверьте номер GPIO указанный в скрипте по пути devices/executive/KY-006_example/py
BEEP_PIN = 11 # Укажите номер пина, к которому подключен BEEP
2.4.2. Обратимся к пособию УМК “REPKA”, в котором представлена распиновка Repka PI 4 (рисунок 8). Из нее следует, что уникальный идентификатор порта GPIO12 равен 360 и находится на физическом пине 32.
2.4.3. Внесем правки в код и выполним скрипт
BEEP_PIN = 32 # Укажите номер пина, к которому подключен BEEP
2.5. После запуска зуммер должен воспроизвести мелодию.
3. Подключение 7-сегментного индикатор 0.36".
Как видно из схемы устройство подключается через порты GPIO и резисторы 100 Ом.
3.1. Согласно рисункам 6 и 7 подключим резисторы к индикатору:
3.2. Подключаем 7-сегментный индикатор к GPIO: 18, 23, 24, 25, 4, 17, 27, 22 через резисторы для защиты цепей. Важно не перепутать выводы, так как цифра на индикаторе строятся путем подачи низкого или высокого сигнала:
4. Аналогично пункту 2 выполним проверку подключения датчика.
4.1. Если ранее не устанавливали все зависимости командой setup-all, то установим зависимости для FYS-3611, выполнив:
make setup-FYS-3611
4.2. Запустим python скрипт, который находится по пути devices/input-output/FYS-3611_example:
make FYS-3611
4.3. Если все подключено верно, то все сегменты должны загореться, см. рисунок 11.
5. Подключение адресной светодиодной ленты (WS2812).
Как видно из рисунков 5 и 6 устройство подключается к MOSI через резистор 100 Ом и питается от 5V.
5.1. Выполним подключение согласно таблице 2.
Макетная плата | WS2812 |
5V | 5V |
GND | GND |
MOSI | MOSI |
Таблица 2. Подключение WS2812 к макетной плате.
5.2. Результат подключения устройства можно наблюдать на рисунке 12.
6. Аналогично пункту 2 выполним проверку подключения датчика.
6.1. Если ранее не устанавливали все зависимости командой setup-all, то установим зависимости для FYS-3611, выполнив:
make setup-ws2812
6.2. Запустим python скрипт, который находится по пути devices/input-output/WS2812_example:
make ws2812
6.3. При правильном подключении лента будет поочередно освещаться тремя основными цветами: сначала зеленым, затем красным и синим.
7. Подключение ультразвуковой датчик расстояния (HC-SR04).
Наконец, подключим ключевой компонент нашего проекта — ультразвуковой датчик. Как показано на рисунках 5 и 6, датчик работает от питания 5V и подключается к GPIO пинам 5 и 6 для передачи сигнала.
7.1. Выполним подключение согласно таблице 3.
Макетная плата | HC-SR04 |
5V | 5V |
GND | GND |
GPIO 5 | ECHO |
GPIO 6 | TRIG |
Таблица 3. Подключение HC-SR04 к макетной плате.
7.2. Итоговый вид проекта выглядит следующим образом:
8. Аналогично пункту 2 выполним проверку подключения датчика.
8.1. Если ранее не устанавливали все зависимости командой setup-all, то установим зависимости для RGB модуля, выполнив:
make setup-HC-SR04
8.2. В скрипте, который находится по пути devices/sensors/HC-SR04_example, необходимо изменить уникальные идентификаторы пинов GPIO, чтобы они соответствовали вашему подключению.
# Инициализация пинов
TRIGGER_PIN = 31 # GPIO пин для Trigger
ECHO_PIN = 29 # GPIO пин для Echo
8.2. Запустим python скрипт:
make HC-SR04
8.3. При правильном подключении в консоли будет отображаться расстояние до цели:
Запуск проекта
Теперь, когда все компоненты подключены, можно запустить проект "Парковочиный". Для этого в репозитории repka-pi_iot-examples выполняем команду:
make parking-assistant
После запуска в консоли может наблюдать расстояние до цели:
На 7-сегментном дисплее будет отображаться первый разряд числа, которое измеряется с помощью ультразвукового датчика, и отображаться на дисплее, показывая значение до сотни. Зуммер будет изменять свой тон в зависимости от расстояния: чем ближе объект, тем выше тон. RGB-лента также будет изменять цвет в зависимости от расстояния: зеленый — для дальнего расстояния, красный — для ближнего, синий — для средней дистанции.
Вы можете собрать более бюджетную версию данного проекта.
Для более бюджетной реализации проекта можно обойтись без активного охлаждения, используя Repka Pi 4 в стандартной комплектации, а также макетную плату без внешнего источника питания — при этом остальные компоненты остаются неизменными.
Для подключения нам потребуется “Распиновка портов на 40 pin разъёме на Repka Pi 4“, см. рисунок 8.
Поскольку расширительная плата GPIO полностью повторяет конфигурацию распиновки, можно применить те же таблицы и схемы, которые использовались ранее для устройств.
9. Выполним зуммера к макетной плате согласно таблице 1:
Проверка подключения зуммера осуществляется аналогично пункту 2.
10. Выполним подключение 7-сегментного индикатор 0.36" аналогично пункту 3:
11. Подключение адресной светодиодной ленты (WS2812). Выполним подключение согласно таблице 2:
Аналогично пункту 6 выполним проверку подключения ленты.
12. Подключение ультразвуковой датчик расстояния (HC-SR04). Выполним подключение согласно таблице 3:
Для снижения нагрузки на линию 5 В одноплатного компьютера Repka Pi 4, рекомендуется использовать дополнительное питание 5 В:
А. Подключение с использованием лабораторного блока питания:
Б. Подключение с использованием аккумуляторного блока питания:
Разбор кода проекта.
Импортируются необходимые библиотеки: time (работа со временем), periphery.GPIO (работа с GPIO-пинами), spidev (работа с SPI-интерфейсом):
import time
import RepkaPi.GPIO as GPIO
import spidev
Определяются номера пинов для ультразвукового датчика HC-SR04: TRIGGER_PIN — для отправки сигнала, ECHO_PIN — для приёма отражённого сигнала:
for pin in (TRIGGER_PIN, ECHO_PIN):
temp_gpio = GPIO(pin, "out")
temp_gpio.close()
Пины инициализируются как выходы для корректной работы с библиотекой periphery:
segment_pins = {
'A': 18, 'B': 22, 'C': 13, 'D': 11,
'E': 7, 'F': 12, 'G': 16, 'H': 15
}
Настройка SPI-интерфейса для управления RGB светодиодами: создаётся объект, открывается устройство, задаётся скорость передачи:
NUM_LEDS = 50 # Количество RGB светодиодов
spi = spidev.SpiDev() # Создаем объект SPI
spi.open(0, 0) # Открываем SPI-устройство 0, 0
spi.max_speed_hz = 3200000 # Устанавливаем максимальную скорость передачи данных
Определяется пин для зуммера :
BEEP_PIN = 32 # Пин для зуммера
Функция для измерения расстояния с помощью ультразвукового датчика. Отправляет импульс, измеряет время до получения эхо, рассчитывает расстояние:
def measure_distance():
...
Словарь, определяющий, какие сегменты дисплея должны быть включены для отображения каждой цифры от 0 до 9:
segment_map = {
...
}
Функция для отображения одной цифры на 7-сегментном дисплее. Включает/выключает нужные сегменты:
def set_segments(digit):
...
Функция для отображения двухзначного числа на дисплее. Поочерёдно показывает каждую цифру с задержкой:
def display_number(number):
...
Функция преобразует 24-битный цвет в последовательность байтов для передачи на RGB светодиоды по SPI:
def color_to_spi_bytes(color):
...
Функция отправляет массив цветов на все RGB светодиоды через SPI:
def send_colors(colors):
...
Функция генерирует звуковой сигнал на зуммере с заданной частотой и длительностью:
def beep_tone(frequency, duration):
...
Функция формирует тревожный сигнал: чем ближе объект, тем выше частота сигнала:
def alarm_signal(distance):
...
В бесконечном цикле программа:
-
Измеряет расстояние до объекта.
-
Выводит расстояние в консоль.
-
Если расстояние ≤ 100 см — отображает его на дисплее, иначе очищает дисплей.
-
В зависимости от расстояния меняет цвет RGB светодиодов (красный — опасно, жёлтый — средне, зелёный — безопасно).
-
Если расстояние < 100 см — издаёт тревожный сигнал.
-
Делает паузу 1 секунду и повторяет цикл.
-
При нажатии Ctrl+C (KeyboardInterrupt) завершает работу и освобождает ресурсы.
# Основной цикл программы
try:
# --- Инициализация GPIO ---
# Готовим все пины к работе перед запуском основного цикла.
GPIO.setmode(GPIO.BOARD)
GPIO.setup(TRIGGER_PIN, GPIO.OUT)
GPIO.setup(ECHO_PIN, GPIO.IN)
for pin in segment_pins.values():
GPIO.setup(pin, GPIO.OUT)
GPIO.setup(BEEP_PIN, GPIO.OUT)
while True:
distance = measure_distance() # Получаем измерение расстояния
print("_______________________________")
print(f"Расстояние: {distance:.2f} см")
# Если расстояние меньше или равно 100 см, отображаем значение на дисплее
if distance <= 100:
display_number(int(distance // 10)) # Показываем целую часть в метрах
else:
# Очищаем дисплей, если расстояние больше 100 см
# Просто пробегаемся по всем пинам сегментов и выключаем их.
for pin_number in segment_pins.values():
GPIO.output(pin_number, GPIO.LOW)
# Отправляем цвет на RGB LED в зависимости от расстояния
if distance < 20:
send_colors([0x00FF00] * NUM_LEDS) # Красный для опасной дистанции
elif distance < 50:
send_colors([0xFFFF00] * NUM_LEDS) # Желтый для средней дистанции
else:
send_colors([0xFF0000] * NUM_LEDS) # Зеленый для безопасной дистанции
# Если расстояние меньше 100 см, издаем сигнал
if distance < 100:
alarm_signal(distance) # Издаем сигнал с частотой в зависимости от расстояния
time.sleep(1) # Пауза между измерениями
except KeyboardInterrupt:
print("Программа завершена.") # Сообщение при завершении работы программы
finally:
GPIO.cleanup()
spi.close()
Пример кода выше с использованием ООП
ParkingAssistant — основной класс, инкапсулирующий всю логику работы ассистента парковки:
class ParkingAssistant:
# Настройка пинов и параметров
TRIGGER_PIN = 31 # Пин для отправки импульса
ECHO_PIN = 29 # Пин для получения эхо-сигнала
segment_pins = {
'A': 18, # Сегмент A
'B': 22, # Сегмент B
'C': 13, # Сегмент C
'D': 11, # Сегмент D
'E': 7, # Сегмент E
'F': 12, # Сегмент F
'G': 16, # Сегмент G
'H': 15 # Сегмент H
}
NUM_LEDS = 50 # Количество RGB светодиодов
BEEP_PIN = 32 # Пин для зуммера
# Массив для отображения цифр на 7-сегментном дисплее
segment_map = [
(1, 1, 1, 1, 1, 1, 0, 0), # 0
(0, 1, 1, 0, 0, 0, 0, 0), # 1
(1, 1, 0, 1, 1, 0, 1, 0), # 2
(1, 1, 1, 1, 0, 0, 1, 0), # 3
(0, 1, 1, 0, 0, 1, 1, 0), # 4
(1, 0, 1, 1, 0, 1, 1, 0), # 5
(1, 0, 1, 1, 1, 1, 1, 0), # 6
(1, 1, 1, 0, 0, 0, 0, 0), # 7
(1, 1, 1, 1, 1, 1, 1, 0), # 8
(1, 1, 1, 1, 0, 1, 1, 0) # 9
]
_init_ — инициализация всех устройств: пинов, SPI, зуммера:
def __init__(self):
# Инициализация библиотеки RepkaPi.GPIO и настройка всех пинов
GPIO.setmode(GPIO.BOARD)
GPIO.setup(self.TRIGGER_PIN, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(self.ECHO_PIN, GPIO.IN)
for pin in self.segment_pins.values():
GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(self.BEEP_PIN, GPIO.OUT, initial=GPIO.LOW)
# Инициализация SPI для RGB светодиодов
self.spi = spidev.SpiDev()
self.spi.open(0, 0)
self.spi.max_speed_hz = 3200000
measure_distance — измеряет расстояние с помощью ультразвукового датчика.
# Функция измерения расстояния
def measure_distance(self):
# Отправляем короткий импульс на пин TRIGGER
GPIO.output(self.TRIGGER_PIN, GPIO.HIGH)
time.sleep(0.00001)
GPIO.output(self.TRIGGER_PIN, GPIO.LOW)
# Ждем, пока на пине ECHO появится ответный сигнал
start_time = time.time()
stop_time = time.time()
# Замеряем время до начала эхо-сигнала
while GPIO.input(self.ECHO_PIN) == GPIO.LOW:
start_time = time.time()
# Замеряем время до конца эхо-сигнала
while GPIO.input(self.ECHO_PIN) == GPIO.HIGH:
stop_time = time.time()
duration = stop_time - start_time
distance = (duration * 34300) / 2
return distance
set_segments — включает нужные сегменты 7-сегментного дисплея для отображения цифры:
# Функция управления сегментами 7-сегментного дисплея
def set_segments(self, digit):
print(f"Отображаем цифру: {digit}")
if not 0 <= digit <= 9:
print(f"Ошибка: недопустимая цифра {digit}")
return
# Создаем гарантированный порядок
segment_order = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
segment_states = self.segment_map[digit]
for i in range(len(segment_order)):
seg_name = segment_order[i]
pin = self.segment_pins[seg_name]
state = GPIO.HIGH if segment_states[i] else GPIO.LOW
GPIO.output(pin, state)
display_number — отображает число на дисплее:
# Функция для отображения числа на 7-сегментном дисплее
def display_number(number):
"""Отображает ОДНУ цифру (0-9) на дисплее, не полагаясь на порядок словаря."""
print(f"Отображаю цифру: {number}")
# Сначала проверяем, что цифра корректна
if not 0 <= number <= 9:
# Если цифра некорректна (например, -1), гасим все сегменты
for pin in segment_pins.values():
GPIO.output(pin, GPIO.LOW)
return
# Получаем список состояний (включен/выключен) для нашей цифры
# segment_states[0] - состояние для A, segment_states[1] - для B, и т.д.
segment_states = segment_map[number]
segment_order = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
for i in range(len(segment_order)):
# 1. Берем имя сегмента из нашего списка (например, 'A' при i=0)
seg_name = segment_order[i]
# 2. Находим пин, который соответствует этому имени в словаре
pin = segment_pins[seg_name]
# 3. Берем состояние для этого сегмента по тому же индексу 'i'
state = segment_states[i]
# 4. Устанавливаем правильное состояние на правильный пин
GPIO.output(pin, GPIO.HIGH if state == 1 else GPIO.LOW)
color_to_spi_bytes — преобразует цвет в байты для передачи по SPI:
# Функция преобразования цвета для SPI
def color_to_spi_bytes(self, color):
result = []
for i in range(24):
bit = (color >> (23 - i)) & 1
if bit == 1:
result.append(0b110)
else:
result.append(0b100)
bytes_out = []
bits_buffer = 0
bits_count = 0
for val in result:
bits_buffer = (bits_buffer << 3) | val
bits_count += 3
while bits_count >= 8:
bits_count -= 8
byte = (bits_buffer >> bits_count) & 0xFF
bytes_out.append(byte)
if bits_count > 0:
byte = (bits_buffer << (8 - bits_count)) & 0xFF
bytes_out.append(byte)
return bytes_out
send_colors — отправляет массив цветов на RGB светодиоды:
# Функция отправки цветов на RGB светодиоды
def send_colors(self, colors):
data = []
for c in colors:
data.extend(self.color_to_spi_bytes(c))
self.spi.xfer2(data)
time.sleep(0.001)
beep_tone — генерирует звуковой сигнал на зуммере:
# Функция генерации звука на зуммере
def beep_tone(self, frequency, duration):
period = 1 / frequency
half_period = period / 2
end_time = time.time() + duration
while time.time() < end_time:
GPIO.output(self.BEEP_PIN, GPIO.HIGH)
time.sleep(half_period)
GPIO.output(self.BEEP_PIN, GPIO.LOW)
time.sleep(half_period)
alarm_signal — формирует тревожный сигнал в зависимости от расстояния.
# Функция тревожного сигнала
def alarm_signal(self, distance):
frequency = max(1000, 5000 - distance * 100)
self.beep_tone(frequency, 0.1)
time.sleep(0.05)
clear_display — выключает все сегменты дисплея:
# Очистка дисплея
def clear_display(self):
for seg in self.segment_pins:
gpio = GPIO(self.segment_pins[seg], "out")
gpio.write(False)
gpio.close()
if name == "_main_" — запуск программы.
if __name__ == "__main__":
assistant = ParkingAssistant()
assistant.run()
Преимущества ООП-подхода:
1. Модульность и повторное использование кода
В ООП каждая функциональная часть системы (например, работа с датчиком, реле, дисплеем) оформляется в виде отдельного класса.
Это позволяет легко переиспользовать эти классы в других проектах или расширять функциональность без переписывания кода.
Например, если потребуется добавить второй датчик или другой тип дисплея, можно просто создать новый класс или унаследовать существующий.
2. Упрощение поддержки и масштабирования
Код, разделённый на классы с чётко определённой ответственностью, проще читать, тестировать и отлаживать.
Если возникает ошибка или требуется доработка, достаточно изменить только соответствующий класс, не затрагивая остальной код.
Это особенно важно для сложных или развивающихся проектов, где часто появляются новые требования.
3. Инкапсуляция и защита данных
ООП позволяет скрыть внутренние детали реализации (например, работу с I2C или обработку ошибок) внутри класса.
Внешний код работает только с публичными методами, не заботясь о низкоуровневых деталях.
Это снижает вероятность ошибок, связанных с неправильным использованием компонентов, и делает интерфейс системы более понятным и безопасным.
Практическая значимость проекта
Проект "Ультразвуковой парковочный ассистент" представляет собой эффективное применение ультразвуковых технологий для решения повседневных задач, таких как парковка. Этот проект позволяет интегрировать ультразвуковой датчик для измерения расстояния, создавая систему, которая визуально и с использованием звука информирует пользователя о расстоянии до объектов, что особенно полезно при парковке в ограниченных пространствах. Система с 7-сегментным дисплеем, RGB-светодиодами и пассивным зуммером делает использование устройства интуитивно понятным и доступным. Практическая значимость этого проекта заключается в его возможности улучшить безопасность и удобство парковки, предотвращая столкновения и обеспечивая точную информацию о расстоянии до препятствий.
Расширение проекта
Проект "Ультразвуковой парковочный ассистент" можно расширить несколькими способами для улучшения функциональности и повышения удобства использования:
-
Интеграция с мобильными устройствами: можно добавить возможность отправки уведомлений на мобильные устройства пользователя с помощью Bluetooth или Wi-Fi, чтобы отслеживать ситуацию вокруг автомобиля дистанционно.
-
Подключение дополнительных сенсоров: для повышения точности можно интегрировать другие типы датчиков, например, инфракрасные или камеры для создания более сложных систем парковки.
Эти дополнения сделают проект более гибким и функциональным, а также обеспечат его использование в более широком диапазоне приложений.
Видеообзор проекта
Для более детального ознакомления с проектом, вы можете посмотреть видеообзор на платформе Rutube:
Пример использования с Python
Проект полностью реализован на языке Python. Код для работы с парковочным ассистентом можно найти в репозитории на платформе Gitflic.