fix: return to center
This commit is contained in:
28
autopilot.py
28
autopilot.py
@@ -9,7 +9,7 @@ random.seed(1)
|
|||||||
class Pilot:
|
class Pilot:
|
||||||
def __init__(self): pass
|
def __init__(self): pass
|
||||||
def handle(self, image: Image): pass
|
def handle(self, image: Image): pass
|
||||||
def act(self) -> float | None: pass
|
def act(self) -> tuple[float, float] | None: pass
|
||||||
|
|
||||||
class AutoPilot(Pilot):
|
class AutoPilot(Pilot):
|
||||||
prev_image: np.ndarray | None
|
prev_image: np.ndarray | None
|
||||||
@@ -89,11 +89,11 @@ class AutoPilot(Pilot):
|
|||||||
for match in good_matches:
|
for match in good_matches:
|
||||||
# Координаты точки в первом изображении
|
# Координаты точки в первом изображении
|
||||||
pt1 = kp1[match.queryIdx].pt
|
pt1 = kp1[match.queryIdx].pt
|
||||||
src_pts.append([pt1[0] - center_x1, pt1[1] - center_y1])
|
src_pts.append([center_x1 - pt1[0], pt1[1] - center_y1])
|
||||||
|
|
||||||
# Координаты точки во втором изображении
|
# Координаты точки во втором изображении
|
||||||
pt2 = kp2[match.trainIdx].pt
|
pt2 = kp2[match.trainIdx].pt
|
||||||
dst_pts.append([pt2[0] - center_x2, pt2[1] - center_y2])
|
dst_pts.append([center_x2 - pt2[0], pt2[1] - center_y2])
|
||||||
|
|
||||||
# Конвертируем в numpy массивы
|
# Конвертируем в numpy массивы
|
||||||
src_pts = np.float32(src_pts).reshape(-1, 1, 2)
|
src_pts = np.float32(src_pts).reshape(-1, 1, 2)
|
||||||
@@ -125,7 +125,7 @@ class AutoPilot(Pilot):
|
|||||||
tx, ty = H[0, 2], H[1, 2]
|
tx, ty = H[0, 2], H[1, 2]
|
||||||
|
|
||||||
# Вычисляем угол поворота
|
# Вычисляем угол поворота
|
||||||
angle = np.arctan2(a21, a11)
|
angle = -np.arctan2(a21, a11)
|
||||||
|
|
||||||
# Вычисляем масштаб
|
# Вычисляем масштаб
|
||||||
scale_x = np.sqrt(a11**2 + a21**2)
|
scale_x = np.sqrt(a11**2 + a21**2)
|
||||||
@@ -150,8 +150,8 @@ class AutoPilot(Pilot):
|
|||||||
scale = transformation_info['scale']
|
scale = transformation_info['scale']
|
||||||
|
|
||||||
# Координаты уже отцентрированы, поэтому используем их напрямую
|
# Координаты уже отцентрированы, поэтому используем их напрямую
|
||||||
dx_meters = tx
|
dx_meters = ty
|
||||||
dy_meters = ty
|
dy_meters = tx
|
||||||
|
|
||||||
# Применяем поворот к смещению (учитываем текущий угол БПЛА)
|
# Применяем поворот к смещению (учитываем текущий угол БПЛА)
|
||||||
cos_angle = math.cos(self.angle)
|
cos_angle = math.cos(self.angle)
|
||||||
@@ -159,8 +159,8 @@ class AutoPilot(Pilot):
|
|||||||
|
|
||||||
# Поворачиваем смещение в глобальные координаты
|
# Поворачиваем смещение в глобальные координаты
|
||||||
# Обратите внимание: dy_meters инвертирован, так как в изображениях Y направлен вниз
|
# Обратите внимание: dy_meters инвертирован, так как в изображениях Y направлен вниз
|
||||||
dx_global = dx_meters * cos_angle - (-dy_meters) * sin_angle
|
dx_global = dx_meters * cos_angle - dy_meters * sin_angle
|
||||||
dy_global = dx_meters * sin_angle + (-dy_meters) * cos_angle
|
dy_global = dx_meters * sin_angle + dy_meters * cos_angle
|
||||||
|
|
||||||
# Обновляем координаты БПЛА
|
# Обновляем координаты БПЛА
|
||||||
self.x += dx_global
|
self.x += dx_global
|
||||||
@@ -276,7 +276,7 @@ class AutoPilot(Pilot):
|
|||||||
# Обновляем предыдущее изображение
|
# Обновляем предыдущее изображение
|
||||||
self.prev_image = current_image
|
self.prev_image = current_image
|
||||||
|
|
||||||
def act(self) -> float | None:
|
def act(self) -> tuple[float, float] | None:
|
||||||
"""
|
"""
|
||||||
Возвращает угол поворота для управления дроном, чтобы он стремился к точке (0, 0).
|
Возвращает угол поворота для управления дроном, чтобы он стремился к точке (0, 0).
|
||||||
Если дрон находится рядом с началом координат (в радиусе 1 метра), возвращает None.
|
Если дрон находится рядом с началом координат (в радиусе 1 метра), возвращает None.
|
||||||
@@ -284,8 +284,8 @@ class AutoPilot(Pilot):
|
|||||||
# Расстояние до цели (0, 0)
|
# Расстояние до цели (0, 0)
|
||||||
distance_to_target = math.sqrt(self.x**2 + self.y**2)
|
distance_to_target = math.sqrt(self.x**2 + self.y**2)
|
||||||
|
|
||||||
# Если дрон находится рядом с целью (в радиусе 1 метра), останавливаемся
|
# Если дрон находится рядом с целью, останавливаемся
|
||||||
if distance_to_target < 1.0:
|
if distance_to_target < 10.0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Вычисляем угол к цели
|
# Вычисляем угол к цели
|
||||||
@@ -302,7 +302,7 @@ class AutoPilot(Pilot):
|
|||||||
angle_diff -= 2 * math.pi
|
angle_diff -= 2 * math.pi
|
||||||
|
|
||||||
# Возвращаем угол поворота (положительный - поворот влево, отрицательный - вправо)
|
# Возвращаем угол поворота (положительный - поворот влево, отрицательный - вправо)
|
||||||
return max(min(0.05, angle_diff / 2), -0.05)
|
return max(min(0.1, angle_diff), -0.1), min(10., distance_to_target / 2)
|
||||||
|
|
||||||
def reset_position(self, x: float = 0.0, y: float = 0.0, angle: float = 0.0):
|
def reset_position(self, x: float = 0.0, y: float = 0.0, angle: float = 0.0):
|
||||||
"""Сбрасывает позицию и угол БПЛА"""
|
"""Сбрасывает позицию и угол БПЛА"""
|
||||||
@@ -318,12 +318,12 @@ class RandomPilot(Pilot):
|
|||||||
def __init__(self, velocity: float = 1):
|
def __init__(self, velocity: float = 1):
|
||||||
self.counter = 0
|
self.counter = 0
|
||||||
|
|
||||||
def act(self) -> float | None:
|
def act(self) -> tuple[float, float] | None:
|
||||||
self.counter += 1
|
self.counter += 1
|
||||||
if self.counter > 40:
|
if self.counter > 40:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return 1 / (self.counter + 20)
|
return 1 / (self.counter + 20), 10.0
|
||||||
|
|
||||||
# def _test():
|
# def _test():
|
||||||
# randomPilot = RandomPilot()
|
# randomPilot = RandomPilot()
|
||||||
|
|||||||
15
simulator.py
15
simulator.py
@@ -107,26 +107,27 @@ class Simulator:
|
|||||||
|
|
||||||
html = self.driver.find_element(By.TAG_NAME, 'html')
|
html = self.driver.find_element(By.TAG_NAME, 'html')
|
||||||
action = ActionChains(self.driver)
|
action = ActionChains(self.driver)
|
||||||
velocity = 10
|
|
||||||
|
|
||||||
# Добавляем начальную точку в траекторию
|
# Добавляем начальную точку в траекторию
|
||||||
self.update_trajectory(0, 0)
|
self.update_trajectory(0, 0)
|
||||||
self.viz_manager.update_global_map(self.current_x, self.current_y, self.mode)
|
self.viz_manager.update_global_map(self.current_x, self.current_y, self.mode)
|
||||||
|
|
||||||
for i in range(1000):
|
for i in range(1000):
|
||||||
dangle = None
|
signal = None
|
||||||
if self.mode == SimMode.OPERATOR:
|
if self.mode == SimMode.OPERATOR:
|
||||||
dangle = self.operatorPilot.act()
|
signal = self.operatorPilot.act()
|
||||||
if dangle is None:
|
if signal is None:
|
||||||
self.mode = SimMode.AUTONOME
|
self.mode = SimMode.AUTONOME
|
||||||
print("Режим возвращения домой!")
|
print("Режим возвращения домой!")
|
||||||
|
|
||||||
if self.mode == SimMode.AUTONOME:
|
if self.mode == SimMode.AUTONOME:
|
||||||
dangle = self.autonomePilot.act()
|
signal = self.autonomePilot.act()
|
||||||
|
|
||||||
if dangle is None:
|
if signal is None:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
dangle, velocity = signal
|
||||||
|
|
||||||
# Сдвиг камеры
|
# Сдвиг камеры
|
||||||
action = ActionChains(self.driver)
|
action = ActionChains(self.driver)
|
||||||
action.move_to_element_with_offset(html, 200, 200)
|
action.move_to_element_with_offset(html, 200, 200)
|
||||||
@@ -148,7 +149,7 @@ class Simulator:
|
|||||||
im = im.crop([0, 80, im.width-80, im.height-60])
|
im = im.crop([0, 80, im.width-80, im.height-60])
|
||||||
|
|
||||||
# Применяем поворот как будто съемка с дрона
|
# Применяем поворот как будто съемка с дрона
|
||||||
rotated_im = self.rotate_image_like_drone(im, self.angle + math.pi / 2)
|
rotated_im = self.rotate_image_like_drone(im, math.pi / 2 - self.angle)
|
||||||
|
|
||||||
# Передаем изображение в AutoPilot для анализа
|
# Передаем изображение в AutoPilot для анализа
|
||||||
self.autonomePilot.handle(rotated_im)
|
self.autonomePilot.handle(rotated_im)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class VisualizationManager:
|
|||||||
def __init__(self, window_title="Drone Autopilot Visualization"):
|
def __init__(self, window_title="Drone Autopilot Visualization"):
|
||||||
self.window_title = window_title
|
self.window_title = window_title
|
||||||
self.fig = None
|
self.fig = None
|
||||||
|
self.ax_drone_view = None
|
||||||
self.ax_global_map = None
|
self.ax_global_map = None
|
||||||
self.ax_detection = None
|
self.ax_detection = None
|
||||||
self.ax_matches = None
|
self.ax_matches = None
|
||||||
@@ -43,6 +44,9 @@ class VisualizationManager:
|
|||||||
self.keypoints = []
|
self.keypoints = []
|
||||||
self.matches = []
|
self.matches = []
|
||||||
|
|
||||||
|
# Данные для вида БПЛА
|
||||||
|
self.drone_view_image = None
|
||||||
|
|
||||||
self._setup_window()
|
self._setup_window()
|
||||||
|
|
||||||
def _setup_window(self):
|
def _setup_window(self):
|
||||||
@@ -54,11 +58,16 @@ class VisualizationManager:
|
|||||||
# Открываем окно на полный экран
|
# Открываем окно на полный экран
|
||||||
self.fig.canvas.manager.window.state('zoomed')
|
self.fig.canvas.manager.window.state('zoomed')
|
||||||
|
|
||||||
# Создаем сетку 2x2
|
# Создаем сетку 2x2 с разными размерами колонок
|
||||||
gs = self.fig.add_gridspec(2, 2, hspace=0.3, wspace=0.3)
|
gs = self.fig.add_gridspec(2, 2, hspace=0.3, wspace=0.3, width_ratios=[1, 1])
|
||||||
|
|
||||||
# Глобальная карта (левый верхний угол, занимает 2x1)
|
# Вид БПЛА (левый верхний угол)
|
||||||
self.ax_global_map = self.fig.add_subplot(gs[0, :])
|
self.ax_drone_view = self.fig.add_subplot(gs[0, 0])
|
||||||
|
self.ax_drone_view.set_title('Вид БПЛА')
|
||||||
|
self.ax_drone_view.axis('off')
|
||||||
|
|
||||||
|
# Глобальная карта (левый нижний угол)
|
||||||
|
self.ax_global_map = self.fig.add_subplot(gs[1, 0])
|
||||||
self.ax_global_map.set_title('Global Map - Траектория полета беспилотника')
|
self.ax_global_map.set_title('Global Map - Траектория полета беспилотника')
|
||||||
self.ax_global_map.set_xlabel('X координата')
|
self.ax_global_map.set_xlabel('X координата')
|
||||||
self.ax_global_map.set_ylabel('Y координата')
|
self.ax_global_map.set_ylabel('Y координата')
|
||||||
@@ -66,8 +75,8 @@ class VisualizationManager:
|
|||||||
self.ax_global_map.axhline(y=0, color='k', linestyle='-', alpha=0.3)
|
self.ax_global_map.axhline(y=0, color='k', linestyle='-', alpha=0.3)
|
||||||
self.ax_global_map.axvline(x=0, color='k', linestyle='-', alpha=0.3)
|
self.ax_global_map.axvline(x=0, color='k', linestyle='-', alpha=0.3)
|
||||||
|
|
||||||
# Детекция ключевых точек (правый нижний угол)
|
# Детекция ключевых точек (правый верхний угол)
|
||||||
self.ax_detection = self.fig.add_subplot(gs[1, 0])
|
self.ax_detection = self.fig.add_subplot(gs[0, 1])
|
||||||
self.ax_detection.set_title('Keypoint Detection')
|
self.ax_detection.set_title('Keypoint Detection')
|
||||||
self.ax_detection.axis('off')
|
self.ax_detection.axis('off')
|
||||||
|
|
||||||
@@ -100,7 +109,7 @@ class VisualizationManager:
|
|||||||
if len(self.trajectory_x) > 1:
|
if len(self.trajectory_x) > 1:
|
||||||
# Разделяем траекторию по режимам
|
# Разделяем траекторию по режимам
|
||||||
operator_indices = [i for i, m in enumerate(self.trajectory_modes) if m == SimMode.OPERATOR]
|
operator_indices = [i for i, m in enumerate(self.trajectory_modes) if m == SimMode.OPERATOR]
|
||||||
autonome_indices = [i for i, m in enumerate(self.trajectory_modes) if m == SimMode.AUTONOME]
|
autonome_indices = operator_indices[-1:] + [i for i, m in enumerate(self.trajectory_modes) if m == SimMode.AUTONOME]
|
||||||
|
|
||||||
# Рисуем траекторию оператора (синий цвет)
|
# Рисуем траекторию оператора (синий цвет)
|
||||||
if len(operator_indices) > 1:
|
if len(operator_indices) > 1:
|
||||||
@@ -139,6 +148,24 @@ class VisualizationManager:
|
|||||||
self.ax_global_map.set_xlim(x_min - margin, x_max + margin)
|
self.ax_global_map.set_xlim(x_min - margin, x_max + margin)
|
||||||
self.ax_global_map.set_ylim(y_min - margin, y_max + margin)
|
self.ax_global_map.set_ylim(y_min - margin, y_max + margin)
|
||||||
|
|
||||||
|
def update_drone_view(self, image: np.ndarray):
|
||||||
|
"""Обновляет вид БПЛА"""
|
||||||
|
self.drone_view_image = image.copy()
|
||||||
|
|
||||||
|
self.ax_drone_view.clear()
|
||||||
|
self.ax_drone_view.set_title('Вид БПЛА')
|
||||||
|
|
||||||
|
if image is not None:
|
||||||
|
# Конвертируем BGR в RGB для matplotlib
|
||||||
|
if len(image.shape) == 3 and image.shape[2] == 3:
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
else:
|
||||||
|
image_rgb = image
|
||||||
|
|
||||||
|
self.ax_drone_view.imshow(image_rgb)
|
||||||
|
|
||||||
|
self.ax_drone_view.axis('off')
|
||||||
|
|
||||||
def update_detection(self, image: np.ndarray, keypoints):
|
def update_detection(self, image: np.ndarray, keypoints):
|
||||||
"""Обновляет визуализацию детекции ключевых точек"""
|
"""Обновляет визуализацию детекции ключевых точек"""
|
||||||
self.current_frame = image.copy()
|
self.current_frame = image.copy()
|
||||||
|
|||||||
Reference in New Issue
Block a user