ФАЙЛ «CONTACT_CONTROLLER.PY»
ИНИЦИАЛИЗАЦИЯ БИБЛИОТЕК
asyncio - библиотека в Python для написания асинхронного кода.
json - библиотека для работы с данными в формате JSON.
logging - библиотека для ведения логов в Python.
socket - библиотека для работы с сокетами в Python.
matplotlib.pyplot - библиотека для построения графиков в Python.
numpy - библиотека для работы с массивами и матрицами в Python.
math - модуль в Python, предоставляющий математические функции.
aiofiles - библиотека для работы с файлами в асинхронном режиме.
gpsd - библиотека для работы с GPS данными в Python.
geopy.distance - библиотека для вычисления расстояний между двумя точками на Земле.
plotly.graph_objs - библиотека для создания графиков и диаграмм с использованием Plotly.
КЛАСС «ROBOT_CONTROLLER»
Данный класс используется для управления движением и поворотами ровера. Содержит в себе следующие методы:
perform_movement: перемещение на определенное расстояние в сантиметрах.
perform_spin_angle: поворот на определенный градус.
navigate_to_marker: навигация к маркеру или цели.
Данный класс используется для определения поведения и стратегии движения ровера в различных сценариях и задачах.
КЛАСС «SIMULATION CONTROLLER»
Данный класс является симулятором движений ровера. Все действия, прописанные в этом классе, выполняются в двумерном графике в виде отдельного окна Python.
КОНСТРУКТОР КЛАССА
Данная функция является конструктором для класса SimulationController. В данном классе конструктор инициализирует настройки системы ровера, использующего маркеры ArUco для навигации.
Описание функции init Параметры: aruco_map (dict): Словарь, содержащий координаты маркеров ArUco. Эти маркеры используются для определения местоположения и ориентации ровера в пространстве. robot_state (ContactState): Объект, представляющий состояние робота, включая его координаты (x, y) и ориентацию. Это позволяет отслеживать текущее местоположение и направление движения ровера. ax (matplotlib.axes.Axes): Объект из библиотеки Matplotlib, используемый для отображения графики. Он определяет область для рисования на графике. HOST_SOCKET (str): Адрес хоста для подключения по сокету (по умолчанию 'localhost'). PORT_SOCKET (int): Порт для подключения по сокету (по умолчанию 5530).
Функциональность: Создание сокета: Конструктор создает TCP-сокет для связи с сервером, что позволяет роботу обмениваться данными или получать команды. Инициализация атрибутов: Сохраняются параметры aruco_map, robot_state, и ax, чтобы их можно было использовать в других методах класса. Отображение робота на графике: Добавляется маркер для позиции робота на графике с заданным размером, цветом и формой (треугольник). Инициализация дополнительных параметров: Создаются переменные для индикатора света и линии пути, что позволяет отслеживать состояние освещения и движение робота соответственно. Построение графа: Вызывается метод для построения графа на основе карты маркеров, что позволяет анализировать связи между маркерами. Поиск ближайших маркеров: Метод используется для нахождения ближайших к роботу маркеров, что может быть полезно для навигации и принятия решений. Отображение карты: Вызывается функция для отображения карты маркеров на графике, что позволяет визуализировать положение робота относительно этих маркеров.
ФУНКЦИЯ UPDATE_SITE
Функция update_site отвечает за отображение графика на сайте, что позволяет визуализировать данные и состояние ровера для пользователей. Она отправляет график в формате JSON через сокет на веб-сервер.
ПРИНЦИП РАБОТЫ:
Генерация JSON-строки:
Функция начинает с преобразования графика (представленного атрибутом self.ax) в формат JSON. Для этого используется json.dumps, что позволяет превратить объект графика в строку, которую можно отправить по сети.
Используется класс PlotlyJSONEncoder, чтобы гарантировать корректное кодирование объектов, специфичных для библиотеки Plotly.
Отправка данных:
Полученная JSON-строка сохраняется в переменной fig_json.
Затем функция использует метод sendall сокета (self.client_socket) для отправки этой строки на сервер. Строка предварительно кодируется в байты с помощью .encode('utf-8'), что необходимо для передачи данных по сети.
Таким образом, update_site играет ключевую роль в поддержании связи между ровером и пользователем через визуализацию данных на веб-сайте.
ФУНКЦИЯ PLOT_MAP
Функция plot_map предназначена для визуализации карты маркеров ArUco. Она рисует маркеры и их идентификаторы на графике, а также настраивает оси, добавляет сетку и легенду, чтобы пользователи могли легко интерпретировать данные.
ПРИНЦИП РАБОТЫ:
Итерация по маркерам:
Функция начинает с цикла по всем маркерам в self.aruco_map. Каждый маркер представлен в виде словаря, где ключом является его уникальный идентификатор (marker_id), а значением — информация о маркере (marker_info).
Добавление маркеров на график:
Для каждого маркера создается новый след (trace) на графике с использованием метода add_trace из библиотеки Plotly.
В координатах x и y указываются значения из словаря marker_info, которые обозначают положение конкретного маркера
Добавление аннотации:
После добавления маркера, функция создает аннотацию для каждого маркера, используя метод add_annotation.
Аннотация содержит текст с идентификатором маркера (f'{marker_id}') и размещается непосредственно над точкой маркера. Параметр yshift=-10 используется для смещения текста вниз, чтобы избежать наложения на сам маркер.
Настройка графика (не показано в коде):
Хотя в приведенном коде не указаны настройки осей, сетки или легенды, обычно в подобных функциях эти элементы тоже настраиваются для лучшего восприятия данных. Это может включать установку имен осей, границ, добавление сетки и легенд для пояснения к графику.
ФУНКЦИЯ UPDATE_ROBOT_POSITION
Функция update_robot_position предназначена для обновления текущего положения и ориентации ровера в двумерном пространстве. Она также перерисовывает маркер, представляющий ровера, и (возможно) добавляет визуальный индикатор света на графике.
ПРИНЦИП РАБОТЫ:
Обновление состояния робота:
Сначала функция обновляет координаты ровера, вызывая метод update_position класса, к которому относится объект self.robot_state. Это позволяет хранить актуальные данные о текущем положении ровера.
Обновление ориентации:
Затем обновляется ориентация ровера с помощью метода update_orientation, передавая новый угол как параметр.
Перерисовка маркера робота:
Следующий шаг заключается в обновлении данных о маркере, который представляет ровера на графике. Координаты x и y маркера заменяются на новые значения.
Управление индикатором света:
Если свет включен (self.lights_on), функция проверяет, существует ли уже индикатор света (self.light_indicator). Если он есть, он удаляется из списка аннотаций графика, чтобы избежать наложения.
Затем добавляется новая аннотация, указывающая, что свет включен ("Lights On"), с различными параметрами оформления стрелки:
Обновление других элементов графика:
Метод self.update_site() может обновлять другие элементы графика, такие как линии пути или другие визуальные представления, связанные с движением ровера.
Пауза для обновления графика:
Наконец, вызов plt.pause(0.5) ставит график на паузу на полсекунды, позволяя пользователю увидеть изменения, прежде чем они будут обновлены снова.
ФУНКЦИЯ PLOT_ORIENTATION_LINE
Функция plot_orientation_line предназначена для визуализации направления, в котором ориентирован ровер. Она рисует линию, исходя из текущей позиции и угла ориентации робота.
ПРИНЦИП РАБОТЫ:
Определение длины линии:
Переменная length задает длину линии ориентации, которая будет отрисована. В данном случае она равна 0.1, что означает, что линия будет длиной 10% от единичного расстояния.
Вычисление конечных координат линии:
Конечные координаты end_x и end_y вычисляются с использованием тригонометрических функций. Угол ориентации преобразуется из градусов в радианы с помощью функции np.radians(), так как тригонометрические функции в NumPy работают с радианами.
Отрисовка линии ориентации:
Метод self.ax.plot() используется для рисования линии на графике. Линия рисуется от начальной точки (x, y) до конечной точки (end_x, end_y).
Параметр 'r-' указывает, что линия будет красного цвета ('r') и сплошной ('-').
Аргумент label='Orientation' позволяет задать метку для этой линии, что может быть полезно для создания легенды графика.
ФУНКЦИЯ PERFORM_MOVEMENT
Функция perform_movement управляет передвижением ровера по заданному расстоянию в сантиметрах. Она обновляет состояние ровера, вычисляет новые координаты, а также отображает изменения на графике. Время движения имитируется с помощью asyncio.sleep.
ПРИНЦИП РАБОТЫ:
Преобразование расстояния в метры:
Переменная distance_m хранит преобразованное значение расстояния в метрах. Это необходимо, поскольку дальнейшие вычисления основываются на метрах.
Вычисление новых координат:
Угол ориентации ровера преобразуется из градусов в радианы с помощью np.radians().
Новые координаты new_x и new_y рассчитываются с учетом текущих координат (self.robot_state.x и self.robot_state.y) и направления движения, используя тригонометрические функции np.cos() и np.sin().
Обновление состояния робота:
Создаются списки x_values и y_values, содержащие старые и новые координаты ровера для дальнейшей отрисовки.
Обновляются значения координат ровера в состоянии self.robot_state.
Отрисовка линии движения:
На графике добавляется линия, показывающая путь, который прошел ровера. Используется библиотека Plotly для создания линейной графики (модуль go).
Обновление маркера робота:
Маркер робота на графике обновляется до новых координат.
Индикация освещения:
Если освещение включено (self.lights_on), проверяется, есть ли предыдущая аннотация индикатора света. Если да, она удаляется.
Затем добавляется новая аннотация на график, указывающая, что огни включены, с соответствующими параметрами визуализации.
Логирование действия:
Записывается лог (logging.info) о том, что ровер перемещается на указанное расстояние.
Обновление сайта и ожидание:
Вызывается метод self.update_site() для обновления состояния или интерфейса.
С помощью await asyncio.sleep(0.5) эмулируется время, необходимое для выполнения движения. Это позволяет функции работать асинхронно, не блокируя другие операции.
ФУНКЦИЯ PERFORM_SPIN_ANGLE
Функция perform_spin_angle предназначена для того, чтобы управлять поворотом ровера на заданный угол, обновляя его ориентацию и визуальное представление.
ПРИНЦИП РАБОТЫ:
Вычисления текущей и новой ориентации
Текущая ориентация:
Получается текущее значение ориентации ровера из состояния (self.robot_state.orientation) и используется оператор остатка % для ограничения значения в пределах от 0 до 359 градусов.
Новая ориентация:
Новая ориентация (new_orientation) вычисляется путём сложения текущей ориентации и угла поворота, также с применением оператора остатка для приведения к диапазону от 0 до 359 градусов.
Вычисление разницы углов
Разница углов:
Сначала вычисляется разница (angle_difference) между новой и текущей ориентацией.
Этот расчёт также включает добавление 360 для корректной обработки отрицательной разницы.
Минимизация угла поворота:
Если разница превышает 180 градусов, то вычитается 360. Это помогает определить наименьший угол поворота, что может быть полезно для управления движением ровера.
Обновление состояния робота
Обновляется ориентация робота в состоянии (robot_state), присваивая новое значение, рассчитанное ранее.
Обновление визуального представления
Метод update_robot_position вызывается для обновления положения ровера на графике. Используются текущие координаты x и y, а также новая ориентация.
Логирование информации о повороте
Записывается информация о выполненном повороте в лог (logging.info). Указываются величина поворота и направление (по часовой стрелке или против).
Имитация времени поворота
С помощью await asyncio.sleep(0.5) происходит приостановка выполнения функции на 0.5 секунды, имитируя время, необходимое для выполнения физического поворота ровера. Это позволяет остальным асинхронным задачам продолжать выполняться без блокировки.
ФУНКЦИЯ CONTROL_LIGHTS
Эта асинхронная функция отвечает за управление светом на ровере. Она принимает один параметр и выполняет несколько действий для обновления состояния света и его визуализации.
Установка состояния света
Здесь мы обновляем состояние света ровера (lights_on), присваивая ему значение on. Если on равно True, значит свет включен; если False, свет выключен.
Включение света
Проверяем, нужно ли включить свет (if on:).
Если свет уже включен (то есть существует индикатор света), мы убираем последний элемент из списка аннотаций (это необходимо для корректного обновления визуализации).
Затем добавляем новую аннотацию на график, чтобы визуально показать, что свет включен.
Указываем координаты x и y, где будет располагаться аннотация, а также задаем текст "Lights On".
Параметры showarrow, arrowhead, arrowsize, arrowwidth, и arrowcolor позволяют настроить внешний вид стрелки, указывающей на позицию света.
Выключение света
Если свет выключается (ветвь else), мы проверяем, существует ли индикатор.
Если он существует, удаляем последнюю аннотацию с графика, тем самым визуально указывая, что свет теперь выключен.
Формирование сообщения
Здесь мы формируем строку для логирования (action): если свет включен, то сообщение будет "Свет включены.", иначе — "Свет выключены."
Обновление состояния сайта
self.update_site()
Эта строка вызывает метод для обновления сайта или интерфейса пользователя. Это важно, чтобы изменения стали видимыми для пользователя.
Логирование информации
В конце функции записываем сформированное сообщение в лог (logging.info). Это полезно для отслеживания состояния системы и ее работы.
ФУНКЦИЯ NAVIGATE_TO_MARKER
Эта асинхронная функция управляет движением ровера к целевому маркеру, используя заданный идентификатор (goal_id).
ПРИНЦИП РАБОТЫ:
Нахождение пути с использованием алгоритма A*
Здесь вызывается метод astar_algorithm, который реализует алгоритм A* для нахождения пути.
Метод принимает два параметра: идентификатор ближайшего маркера, получаемый через self.find_closest_marker_id(), и идентификатор целевого маркера (goal_id).
Использование await позволяет дождаться завершения работы алгоритма перед продолжением выполнения функции.
Проверка наличия пути
После выполнения алгоритма мы проверяем, был ли найден путь. Если переменная path пустая или равна None, это означает, что путь не найден.
В таком случае записываем сообщение в лог (logging.info) и завершаем выполнение функции с помощью оператора return.
Логирование найденного пути
Если путь был найден, записываем его в лог (logging.info) для дальнейшего анализа или отладки. Это может помочь разработчикам понять, какой маршрут выбрал ровер.
Перемещение по каждому маркеру на пути
Используя цикл for, функция проходит по каждому идентификатору маркера в списке path.
Для каждого маркера вызывается метод move_to_marker(marker_id), который отвечает за перемещение ровера к данному маркеру. Опять же, используется await для ожидания завершения этого процесса.
После перемещения обновляется интерфейс или сайт, чтобы отображались изменения, вызванные перемещением ровера.
ФУНКЦИЯ MOVE_TO_MARKER
Эта асинхронная функция управляет движением ровера к целевому маркеру, используя его идентификатор (marker_id).
ПРИНЦИП РАБОТЫ:
Получение координат маркера
Здесь мы извлекаем информацию о целевом маркере из словаря aruco_map по его идентификатору marker_id. Объект target теперь содержит координаты этого маркера (например, x и y).
Расчет изменений координат
Переменные dx и dy представляют собой изменения в координатах между текущим состоянием робота и координатами целевого маркера:
dx — изменение по оси X.
dy — изменение по оси Y.
Расчет угла поворота
Здесь мы используем функцию atan2 для вычисления угла к маркеру, исходя из координат dx и dy.
Результат переводится из радиан в градусы с помощью math.degrees, чтобы получить нужный угол для поворота.
Выполнение поворота
Перед тем как двигаться к маркеру, ровер должен повернуться в нужном направлении.
Метод perform_spin_angle вызывается с аргументом, представляющим разницу между целевым углом (target_angle) и текущей ориентацией ровера (self.robot_state.orientation). Это позволяет роверу правильно настроить своё направление перед перемещением.
Расчет расстояния до маркера
После поворота рассчитываем расстояние до маркера (distance) с использованием теоремы Пифагора.
Значение умножается на 100, вероятно, для перевода в соответствующую единицу измерения (например, сантиметры или миллиметры).
Выполнение движения к маркеру
Теперь, когда робот развернулся в нужную сторону, он двигается к маркеру, вызывая метод perform_movement с расстоянием (distance), рассчитанным ранее.
ФУНКЦИЯ MOVE_TO_POSITION
Эта функция управляет движением ровера к указанной цели, определяемой координатами x и y.
ПРИНЦИП РАБОТЫ:
Расчет изменений координат
Здесь мы вычисляем изменения координат:
dx — разница между целевой координатой X (x) и текущей координатой X ровера (self.robot_state.x).
dy — разница между целевой координатой Y (y) и текущей координатой Y ровера (self.robot_state.y)..
Расчет угла поворота
Угол к целевой позиции (target_heading) вычисляется с использованием функции atan2, которая возвращает угол в радианах.
Этот угол затем переводится в градусы с помощью math.degrees.
Расчет расстояния до позиции
Мы используем теорему Пифагора для вычисления расстояния до целевой позиции (distance):
distance рассчитывается как квадратный корень из суммы квадратов dx и dy.
Результат умножается на 100 для перевода в нужные единицы измерения (например, сантиметры или миллиметры).
Выполнение поворота
Теперь необходимо повернуть ровера в сторону целевой позиции.
Вычисляется угол поворота как разница между целевым углом (target_heading) и текущим углом ориентации ровера (self.robot_state.orientation).
Используется операция % 360, чтобы гарантировать, что угол входит в диапазон от 0 до 360 градусов.
Затем вычитается 180, чтобы правильно настроить угол поворота.
Метод perform_spin_angle вызывается с указанием направления поворота (clockwise=True), чтобы робот вращался по часовой стрелке.
Выполнение движения к позиции
После того как ровер повернулся, он готов двигаться к целевой позиции.
Метод perform_movement вызывается с аргументом distance, который представляет собой расстояние до цели.
ФУНКЦИЯ SHOW
Отображение графического интерфейса
Здесь вызывается метод show из Matplotlib (plt — это общепринятое имя для модуля matplotlib.pyplot).
Этот метод запускает цикл событий и отображает все текущие графики или фигуры, которые были подготовлены к показу.
Если графиков нет, окно будет пустым, но оно все равно будет открыто.
КЛАСС ROVER_CONTROLLER
Класс для управления ровером через сервер с использованием Socket.IO
ПРИМЕЧАНИЕ: В данном блоке могут отсутствовать некоторые объяснения ввиду схожести функций.
Большая часть функционала дублируется из симулятора на реальном ровере
ФУНКЦИЯ INIZIALIZE_SOCKETIO
Эта функция выполняет следующие задачи:
Подключает клиент Socket.IO к указанному серверу.
Обрабатывает успешное подключение и ошибки при подключении.
Подключение к серверу
Здесь вызывается метод connect у объекта self.sio (предположительно, это экземпляр клиента Socket.IO).
Используются три аргумента:
self.server_url: URL сервера для подключения.
auth={"token": self.token}: передача токена авторизации для аутентификации на сервере.
namespaces=['/vehicles']: указание namespace, с которым будет работать клиент.
await позволяет дождаться завершения операции подключения перед продолжением выполнения кода.
Логирование успешного подключения
Если подключение прошло успешно, выводится информационное сообщение в лог (LOGGING.INFO) о том, что клиент успешно подключился к серверу.
ФУНКЦИЯ WRITE_REPORT
Асинхронная функция для записи сообщений в файл отчета. Если точнее, создает файл отчета и записывает в него сообщения, используемые для ведения журнала действий робота.
ФУНКЦИЯ ON_MOVE
Функция записывает результаты выполнения команд движения в отчет.
Процесс подготовки кода:
Создайте файл Contact_controller.py:
Поэтапно копируйте код в файл, в итоге окно программирования должно выглядеть так
Установите необходимые библиотеки: в данном случае у нас не установлены следующие библиотеки: matplotlib.pyplot, numpy, aiofiles, plotly. Если они у вас не установлены, то вот как их установить:
Откройте терминал в Visual Studio Code (Terminal - New Terminal или комбинацией Ctrl + Shift + `)
Устанавливаем библиотеки: поочередно в консоль прописываем следующие библиотеки, запускать каждую команду по отдельности: pip install matplotlib; pip install numpy; pip install aiofiles; pip install plotly
Так же у нас имеются две библиотеки, которые установить не получится. Это всё из-за того, что мы переносим в этот файл данные из других файлов, в данном случае это Math и Frenet_frame. Они у вас заработают как только в иерархию добавятся коды с такими названиями!
При запуске кода в консоль выводится следующее: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение. Связано это с тем, что команды на движение передаются в файл Main.py, оттуда всё будет работать как надо.
Last updated