import math import os from io import BytesIO from time import sleep from PIL import Image import cv2 import numpy as np from position import Position from vision_chunk import VisionChunk from yandex_map import YandexMap import constants import utility class Simulator: def __init__(self, yandex_map: YandexMap = None): self.yandex_map = yandex_map # Используем новый конструктор с yaw, pitch, roll self.pos = Position(x=0, y=0, z=1, yaw=0, pitch=0, roll=0) os.makedirs('./images', exist_ok=True) def set_pitch(self, pitch_deg: float): """Установить тангаж камеры (градусы, -10 до 10)""" clamped_pitch = max(-10, min(10, pitch_deg)) self.pos.pitch = math.radians(clamped_pitch) def set_roll(self, roll_deg: float): """Установить крен камеры (градусы, -10 до 10)""" clamped_roll = max(-10, min(10, roll_deg)) self.pos.roll = math.radians(clamped_roll) def _apply_perspective_transform(self, image: Image.Image) -> Image.Image: """ Применяет перспективную трансформацию к изображению. Возвращает квадратное изображение 700x700. """ img_array = np.array(image) print(img_array.shape) h, w, _ = img_array.shape # Применяем трансформацию pos = self.pos.copy() pos.x = 0 pos.y = 0 K = utility.calc_camera_matrix(w, h) K = constants.K img_array = img_array[:constants.CHUNK_WIDTH, :constants.CHUNK_WIDTH] transformed = cv2.warpPerspective(img_array, pos.get_homography_matrix(K), (constants.CHUNK_WIDTH, constants.CHUNK_WIDTH)) return Image.fromarray(transformed) def update_trajectory(self, dx: float, dy: float): """Обновляет координаты дрона""" self.pos.x += dx * self.pos.z self.pos.y += dy * self.pos.z def handle(self, dangle: float, velocity: float = 50) -> None: """ Управление движением дрона. dangle - изменение угла курса (радианы) velocity - скорость движения """ from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains html = self.yandex_map.driver.find_element(By.TAG_NAME, 'html') action = ActionChains(self.yandex_map.driver) action.move_to_element_with_offset(html, 200, 200) action.click_and_hold() # Обновляем yaw в объекте Position self.pos.yaw += dangle velocity = max(velocity, 10) # Вычисляем смещение на основе текущего yaw dx = math.cos(math.pi / 2 + self.pos.yaw) * velocity / self.pos.z dy = math.sin(math.pi / 2 + self.pos.yaw) * velocity / self.pos.z self.update_trajectory(dx, dy) action.move_by_offset(-dx, dy) action.release() action.perform() def set_zoom(self, zoom_level: float): """Программное изменение масштаба""" self.pos.z = zoom_level def get_chunk(self) -> VisionChunk: """Получить текущий снимок с камеры дрона""" png = self.yandex_map.driver.get_screenshot_as_png() im = Image.open(BytesIO(png)) # Применяем перспективную трансформацию transformed_im = self._apply_perspective_transform(im) return VisionChunk(transformed_im) def get_orientation_info(self) -> dict: """Получить полную информацию об ориентации дрона""" return { 'position': (self.pos.x, self.pos.y, self.pos.z), 'yaw_deg': math.degrees(self.pos.yaw), 'pitch_deg': math.degrees(self.pos.pitch), 'roll_deg': math.degrees(self.pos.roll), 'rotation_matrix': self.pos.get_rotation_matrix() }