Files
autopilot/simulator.py

113 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import math
from io import BytesIO
from time import sleep
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from autopilot import Pilot
from geolocation import Geolocation
from visualization import VisualizationManager, SimMode
from yandex_map import YandexMap
from PIL import Image
import os
class Simulator:
geo: Geolocation
yandexMap: YandexMap
# Менеджер визуализации
viz_manager: VisualizationManager
def __init__(self, yandexMap: YandexMap = None):
self.yandexMap = yandexMap
# Инициализация переменных для отслеживания траектории
self.geo = Geolocation(0, 0, 1, 0)
# Создаем папку для изображений, если её нет
os.makedirs('./images', exist_ok=True)
def rotate_image_like_drone(self, image: Image.Image, angle: float) -> Image.Image:
"""
Поворачивает картинку как будто съемка ведется с летящего дрона.
Выделяет концентрический квадрат, поворачивает его и извлекает результат.
"""
# Получаем размеры изображения
width, height = image.size
square_size = min(width, height)
off_x = (width - square_size) / 2
off_y = (height - square_size) / 2
cropped_image = image.crop((off_x, off_y, off_x + square_size, off_y + square_size))
cropped_image = cropped_image.rotate(angle / math.pi * 180, expand=True)
# Определяем размер концентрического квадрата (80% от минимальной стороны)
local_square_size = int(square_size / 2 ** 0.5)
# Вычисляем координаты для центрирования квадрата
left = (cropped_image.width - local_square_size) // 2
top = (cropped_image.height - local_square_size) // 2
right = left + local_square_size
bottom = top + local_square_size
# Вырезаем концентрический квадрат
final_image = cropped_image.crop((left, top, right, bottom))
return final_image
def update_trajectory(self, dx: float, dy: float):
"""
Обновляет траекторию полета беспилотника
"""
# Обновляем текущие координаты
self.geo.x += dx * self.geo.z
self.geo.y += dy * self.geo.z
def update_map(self):
"""
Обновляет карту траектории полета
"""
self.viz_manager.update_global_map(self.geo.x, self.geo.y, self.mode)
def handle(self, dangle: float, velocity: float = 50) -> None:
""" Сдвиг камеры """
html = self.yandexMap.driver.find_element(By.TAG_NAME, 'html')
action = ActionChains(self.yandexMap.driver)
action.move_to_element_with_offset(html, 200, 200)
action.click_and_hold()
self.geo.angle += dangle
# print(f" [Simulator] angle: {self.angle / math.pi * 180:.1f}°")
velocity = max(velocity, 10)
dx = math.cos(math.pi / 2 + self.geo.angle) * velocity / self.geo.z
dy = math.sin(math.pi / 2 + self.geo.angle) * velocity / self.geo.z
# print(" [Simulator] dx, dy:", [dx, dy])
self.update_trajectory(dx, dy)
action.move_by_offset(-dx, dy)
action.release()
action.perform()
# print(f" [Simulator] Position: {self.current_x}, {self.current_y}")
def change_zoom(self, direction: str = 'down'):
""" Изменение масштаба """
self.yandexMap.scroll(0.5, 0.5, 2, direction == 'down')
sleep(0.4)
if direction == 'down':
self.geo.z /= 2
else:
self.geo.z *= 2
def get_photo(self) -> Image.Image:
png = self.yandexMap.driver.get_screenshot_as_png()
im = Image.open(BytesIO(png))
# Применяем поворот как будто съемка с дрона
rotated_im = self.rotate_image_like_drone(im, -self.geo.angle)
return rotated_im