Skip to content

Задача одновременной локализации и построения карты. EKF SLAM.

EKF SLAM (Extended Kalman Filter Simultaneous Localization and Mapping) — это метод, который используется в робототехнике для одновременного построения карты неизвестного пространства и определения местоположения робота в этой карте. Чтобы понять, как работает EKF SLAM, давай разобьем его на более простые части.

SLAM (Simultaneous Localization and Mapping)

SLAM — это задача, где робот: 1. Создает карту окружающей среды. 2. Определяет свое местоположение в этой карте.

Kalman Filter (Фильтр Калмана)

Фильтр Калмана — это математический инструмент, который используется для оценивания состояния системы, когда есть шумные измерения. В контексте роботов это может быть: - Состояние системы: Положение и ориентация робота. - Измерения: Данные с сенсоров, такие как данные с камер, лидаров или других датчиков.

Фильтр Калмана помогает предсказывать будущее состояние системы, а затем обновлять это предсказание на основе новых данных.

Extended Kalman Filter (Расширенный фильтр Калмана)

Расширенный фильтр Калмана (EKF) — это версия фильтра Калмана, которая работает с нелинейными системами. В реальной жизни большинство систем, включая роботов, нелинейны, и EKF позволяет обрабатывать такие системы, используя приближенные (линейные) модели.

Как работает EKF SLAM

  1. Предсказание (Prediction):
  2. Робот использует свою модель движения (например, скорость и направление), чтобы предсказать, где он окажется через небольшой промежуток времени.
  3. Используя эту информацию, фильтр обновляет предполагаемое положение робота и карту.

  4. Обновление (Update):

  5. Робот собирает данные с сенсоров (например, видит препятствия или ориентиры).
  6. Эти данные используются для корректировки предположений о положении робота и обновления карты.

Пример

Представь, что у тебя есть робот, который едет по комнате с несколькими ориентирующими точками (например, стульями или столами): 1. Робот начинает с некой начальной позиции и знает, что он может двигаться вперед или поворачивать. 2. По мере движения он предсказывает, где окажется через секунду, используя свои датчики движения. 3. Затем он сканирует окружающую среду и замечает стул в определенном месте. 4. Робот использует данные о стуле, чтобы откорректировать свое предположение о собственном положении и обновить карту комнаты.

Эти шаги повторяются снова и снова, пока робот исследует новое пространство. EKF помогает роботу эффективно справляться с неопределенностями и шумами в измерениях и предсказаниях, обеспечивая точное построение карты и определение местоположения.

Python

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

class EKF_SLAM:
    def __init__(self, num_landmarks, initial_pose, motion_noise, measurement_noise):
        self.num_landmarks = num_landmarks
        self.pose = initial_pose
        self.motion_noise = motion_noise
        self.measurement_noise = measurement_noise
        self.landmarks = self.generate_landmarks()
        self.covariance = np.zeros((3 + 2 * num_landmarks, 3 + 2 * num_landmarks))

    def generate_landmarks(self):
        return np.random.uniform(-10, 10, size=(self.num_landmarks, 2))

    def motion_model(self, u):
        # Простая модель движения
        v = np.random.normal(u, self.motion_noise)
        return v

    def measurement_model(self):
        # Модель измерения
        measured_landmarks = []
        for landmark in self.landmarks:
            dx = landmark[0] - self.pose[0]
            dy = landmark[1] - self.pose[1]
            d = np.sqrt(dx ** 2 + dy ** 2)
            bearing = np.arctan2(dy, dx) - self.pose[2]
            measured_landmarks.append([d, bearing])
        return np.array(measured_landmarks)

    def predict(self, u):
        # Предсказание
        v = self.motion_model(u)
        self.pose[0] += v * np.cos(self.pose[2])
        self.pose[1] += v * np.sin(self.pose[2])
        self.pose[2] += np.random.normal(0, self.motion_noise)

        G = np.eye(3 + 2 * self.num_landmarks)
        G[0, 2] = -v * np.sin(self.pose[2])
        G[1, 2] = v * np.cos(self.pose[2])

        self.covariance = G @ self.covariance @ G.T

    def update(self, z):
        # Обновление
        H = np.zeros((2, 3 + 2 * self.num_landmarks))
        for i, landmark in enumerate(self.landmarks):
            dx = landmark[0] - self.pose[0]
            dy = landmark[1] - self.pose[1]
            q = dx ** 2 + dy ** 2
            sqrt_q = np.sqrt(q)

            H[0, 0] = -sqrt_q * dx
            H[0, 1] = -sqrt_q * dy
            H[0, 2] = 0
            H[0, 3 + 2 * i] = sqrt_q * dx
            H[0, 3 + 2 * i + 1] = sqrt_q * dy

            H[1, 0] = dy
            H[1, 1] = -dx
            H[1, 2] = -q
            H[1, 3 + 2 * i] = -dy
            H[1, 3 + 2 * i + 1] = dx

            z_hat = np.array([
                sqrt_q,
                np.arctan2(dy, dx) - self.pose[2]
            ])

            Y = z - z_hat
            Y[1] = np.arctan2(np.sin(Y[1]), np.cos(Y[1]))  # Нормализация угла

            H = H @ self.covariance @ H.T + np.eye(2) * self.measurement_noise
            K = self.covariance @ H.T @ np.linalg.inv(H)

            self.pose += K @ Y
            self.covariance = (np.eye(3 + 2 * self.num_landmarks) - K @ H) @ self.covariance

    def plot(self):
        sns.set()
        plt.scatter(self.pose[0], self.pose[1], color='r', marker='o', label='Robot Pose')
        plt.scatter(self.landmarks[:, 0], self.landmarks[:, 1], color='b', marker='*', label='Landmarks')
        plt.legend()
        plt.xlabel('X')
        plt.ylabel('Y')
        plt.title('EKF SLAM')
        plt.grid(True)
        plt.show()

if __name__ == "__main__":
    # Пример использования
    num_landmarks = 5
    initial_pose = np.array([0, 0, 0])
    motion_noise = 0.1
    measurement_noise = 0.1

    ekf_slam = EKF_SLAM(num_landmarks, initial_pose, motion_noise, measurement_noise)

    for _ in range(10):
        u = np.random.uniform(0, 1)
        ekf_slam.predict(u)
        z = ekf_slam.measurement_model()
        ekf_slam.update(z)

    ekf_slam.plot()