Ультразвуковой дальномер

Датчик JSN-SR04T представляет собой водонепроницаемый ультразвуковой дальномер, широко используемый в различных проектах, связанных с измерением расстояний в условиях повышенной влажности или прямого контакта с водой. Это делает его идеальным для таких решений, как уровнемеры для воды, робототехника и автомобильные системы помощи при парковке.

Основные характеристики JSN-SR04T:

  • Диапазон измерения: от 20 см до 600 см, что обеспечивает достаточную гибкость для большинства приложений.

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

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

  • Рабочее напряжение: 5 В, что делает его совместимым с большинством микроконтроллеров, включая Arduino и Raspberry Pi.

  • Простота интеграции: Использует стандартные цифровые сигналы для связи, что упрощает подключение к большинству систем управления.

Принцип работы:

JSN-SR04T работает по принципу эхолокации — излучает ультразвуковой сигнал и измеряет время, за которое эхо сигнала возвращается от препятствия. Это время пропорционально расстоянию до объекта, позволяя точно определять его положение.

Применение:

Датчик JSN-SR04T часто используется в таких проектах, как:

  • Автоматизация и контроль уровня жидкости: Измерение уровня воды в баках, резервуарах и других емкостях.

  • Робототехника: Навигация и избегание препятствий для подводных или влажных сред.

  • Автомобильные приложения: Ультразвуковая парковочная система, которая помогает водителю оценивать расстояние до ближайших объектов при парковке.

Настройка дальномеров в ровере

Дальномер нужно подключить в порты gpio uart в соответствии со схемой в разделе "Одноплатный компьютер". Для настройки дальномеров потребуется программа putty или mobaxterm

  1. Откройте программу для настройки образа и зайдите на ip адрес вашего Orange pi 5

  2. Введите команду sudo vim /boot/orangepiEnv.txt

Строка overlays в вашем файле должна выглядеть так же как и на фотографии. После изменений сохраните и выйдите из файла (Ctrl C + Ctrl X)

  1. Просмотрите доступные вам порты командой ls /dev/ttyS*

Если список показывается подобным образом, значит порты настроены правильно.

Код для проверки дальномеров

from serial import Serial
import time
import threading

ports_in = ["COM18"]
ports = []


IP= "192.168.10.116"
PORT=7851

ifing=False


def connect():
    global ports_in
    for port in ports_in:
        temp_port = Serial(
            port,
            baudrate=9600,
            bytesize=8,
            parity='N',
            stopbits=1,
            timeout=1
        )
        try:
            print('[JSN-SR04T] Connected')
            ports.append({
                'dataChannel': temp_port,
                'message': [],
                'savedData': [],
                'distance': 0
            })
            if len(ports) == len(ports_in):
                start_collect_data()
        except Exception as err:
            print('[JSN-SR04T] Connection ERROR', err)
def set_ports():
    connect()

def question(prompt):
    return input(prompt)

set_ports()


def average(arr):
    return sum(arr) / len(arr)

def get_jsn():
    return [port['distance'] for port in ports]


def collect_data(port):
    try:
        while True:
            data = port['dataChannel'].read(4)  # Чтение данных
            # Если используете bytes, то:
            distance = int.from_bytes(data[1:3], byteorder='big')  # Преобразование в целое число
            # print(f'Distance: {distance/10} cm')  # Вывод расстояния на экране
            port['distance'] =distance/10
            print(f'Distance: {port["distance"]:.2f} cm')  # Вывод расстояния на экране

    except Exception as e:
        print(e)

def start_collect_data():
    for port in ports:
        thread=threading.Thread(target=collect_data,daemon=True,args=(port,))  # Вызов функции в новом потоке
        thread.start()

    global ifing
    while True:
        time.sleep(0.1)
        mass=get_jsn()
        ifing = any(num < 50 for num in mass)
        # print(ifing)

start_collect_data()

Код состоит из следующих основных частей:

  • Импорт библиотек:

    from serial import Serial
    import time
    import threadin

  • Настройка портов:

    ports_in = ["COM18"] # Для Windows
    ports = []
    • ports_in: Список последовательных портов, к которым подключены датчики. Важно: Для Linux используются пути вида "/dev/tty...", для Windows - "COM...". Пример показывает один COM-порт для Windows (COM18).

    • ports: Пустой список, который будет содержать объекты Serial и дополнительную информацию для каждого порт

  • Эти строки закомментированы и, вероятно, предназначались для отправки данных по сети (через сокеты). В текущей версии кода сетевая передача данных не реализована.

  • Глобальная переменная ifing:

    ifing=False
    • Эта глобальная переменная используется для индикации, есть ли датчики, показывающие расстояние меньше порогового значения (50 см).

  • Функция connect():

    def connect():
        global ports_in
        for port in ports_in:
            temp_port = Serial(
                port,
                baudrate=9600,
                bytesize=8,
                parity='N',
                stopbits=1,
                timeout=1
            )
            try:
                print('[JSN-SR04T] Connected')
                ports.append({
                    'dataChannel': temp_port,
                    'message': [],
                    'savedData': [],
                    'distance': 0
                })
                if len(ports) == len(ports_in):
                    start_collect_data()
            except Exception as err:
                print('[JSN-SR04T] Connection ERROR', err)

    • Эта функция устанавливает соединение с каждым последовательным портом, указанным в ports_in.

    • Для каждого порта создается объект Serial с заданными параметрами:

      • port: Имя порта (например, “COM18” или “/dev/ttyUSB0”).

      • baudrate=9600: Скорость передачи данных (бод).

      • bytesize=8: Размер байта данных (8 бит).

      • parity='N': Отсутствие контроля четности.

      • stopbits=1: Один стоп-бит.

      • timeout=1: Максимальное время ожидания ответа (1 секунда).

    • В случае успешного подключения создается словарь (dictionary) для хранения информации о порте и добавляется в список ports. Словарь содержит:

      • 'dataChannel': Объект Serial для связи с портом.

      • 'message': Список для хранения входящих сообщений (в данном коде не используется активно).

      • 'savedData': Список для хранения сохраненных данных (в данном коде не используется).

      • 'distance': Значение расстояния, полученное с датчика.

    • После подключения ко всем портам, вызывает функцию start_collect_data() для запуска сбора данных.

    • В случае ошибки подключения, выводит сообщение об ошибке.

  • Функция set_ports():

    def set_ports():
        connect()

    • Эта функция просто вызывает функцию connect(), т.е. выполняет подключение к портам. В коде присутствует для возможного расширения функциональности в будущем (например, для повторных попыток подключения).

  • Функция question(prompt) (не используется):

    def question(prompt):
        return input(prompt)

    • Эта функция принимает запрос и возвращает ввод пользователя. В данном коде не используется.

  • Функция average(arr) (не используется):

    def average(arr):
        return sum(arr) / len(arr)

    • Эта функция принимает массив чисел и возвращает среднее арифметическое. В данном коде не используется.

  • Функция get_jsn():

    def get_jsn():
        return [port['distance'] for port in ports]

    • Эта функция возвращает список расстояний, полученных с каждого датчика. Она использует списковое включение (list comprehension) для извлечения значения 'distance' из каждого словаря в списке ports.

  • Функция collect_data(port):

    def collect_data(port):
        try:
            while True:
                data = port['dataChannel'].read(4)  # Чтение данных
                # Если используете bytes, то:
                distance = int.from_bytes(data[1:3], byteorder='big')  # Преобразование в целое число
                # print(f'Distance: {distance/10} cm')  # Вывод расстояния на экране
                port['distance'] =distance/10
                print(f'Distance: {port["distance"]:.2f} cm')  # Вывод расстояния на экране
    
        except Exception as e:
            print(e)

    • Эта функция отвечает за чтение данных с конкретного последовательного порта в цикле.

    • data = port['dataChannel'].read(4): Читает 4 байта данных из последовательного порта. Предполагается, что датчик посылает 4 байта данных, представляющих расстояние.

    • distance = int.from_bytes(data[1:3], byteorder='big'): Преобразует байты, полученные из последовательного порта, в целое число, представляющее расстояние. В данном случае, используются байты data[1] и data[2], которые интерпретируются как большое (big-endian) целое число.

    • port['distance'] =distance/10: Преобразует значение расстояния из миллиметров в сантиметры (делит на 10) и сохраняет его в словаре port.

    • print(f'Distance: {port["distance"]:.2f} cm'): Выводит расстояние в сантиметрах на экран с двумя знаками после запятой.

    • В случае ошибки, выводит сообщение об ошибке.

  • Функция start_collect_data():

    def start_collect_data():
        for port in ports:
            thread=threading.Thread(target=collect_data,daemon=True,args=(port,))  # Вызов функции в новом потоке
            thread.start()
    
        global ifing
        while True:
            time.sleep(0.1)
            mass=get_jsn()
            ifing = any(num < 50 for num in mass)
            # print(ifing)

    • Эта функция создает и запускает отдельные потоки для функции collect_data() для каждого порта в списке ports. Это позволяет считывать данные со всех датчиков одновременно.

    • thread=threading.Thread(target=collect_data,daemon=True,args=(port,)): Создает объект Thread.

      • target=collect_data: Указывает функцию, которую нужно запустить в потоке.

      • daemon=True: Устанавливает поток как даймон (daemon thread). Это означает, что поток автоматически завершится, когда завершится основная программа.

      • args=(port,): Передает словарь port в качестве аргумента в функцию collect_data(). Обратите внимание на запятую после port. Это необходимо, чтобы передать аргумент в виде кортежа (tuple).

    • thread.start(): Запускает поток.

    • Затем, в бесконечном цикле, эта функция получает данные с каждого датчика, проверяет, есть ли расстояние менее 50 см, и устанавливает глобальную переменную ifing.

  • Вызов set_ports() и start_collect_data():

    set_ports()
    start_collect_data()

    • Эти строки запускают процесс подключения к портам и сбора данных. (на самом деле start_collect_data уже вызывается внутри connect так что строка start_collect_data() избыточна и никогда не достигнется).

Для работы с кодом требуется установить библиотеку pyserial: pip install pyserial

В порт для проверки требуется подключить один из портов со списка выше (ttyS*)

Файл с кодом требуется закинуть на Orange pi и запустить командой python3 your_filename.py

Если все сделано по инструкции, в консоли побегут данные с дальномера.

Last updated