Skip to main content

Обработка данных и ИИ — Мозг для робота

Робот похож на человека: у него есть чувства (сенсоры), но без мозга (обработки данных) он не сможет ничего понять. Эта часть ПО превращает сырые, шумные данные в осмысленную информацию для принятия решений.

Зачем это нужно?

  1. Датчики врут — акселерометр дрожит, дальномер ошибается, камера видит помехи
  2. Мир сложен — объекты меняются, освещение разное, препятствия появляются неожиданно
  3. Ресурсы ограничены — маленький процессор, мало памяти, надо считать быстро

1. Фильтрация сигналов: Как убрать шум

Представь, что пытаешься слушать музыку в метро. Фильтрация — это наушники с шумоподавлением.

Пример: Дрожащая рука робота

Допустим, ультразвуковой дальномер выдаёт такие значения (расстояние в см):

[10.1, 9.8, 300.0 (выброс!), 10.2, 10.0, 9.9, 10.3]

Настоящее расстояние — 10 см, но датчик шумит и иногда глючит.

Простые методы:

Скользящее среднее (Moving Average)

def moving_average(data, window=3):
    result = []
    for i in range(len(data)):
        window_data = data[max(0, i-window+1):i+1]
        result.append(sum(window_data) / len(window_data))
    return result

# До: [10.1, 9.8, 300.0, 10.2, 10.0, 9.9, 10.3]
# После (window=3): [10.1, 9.95, 106.63, 106.67, 106.73, 10.03, 10.07]
# Видишь? Выброс 300 всё ещё портит всё!

Медианный фильтр (Median Filter)

def median_filter(data, window=3):
    result = []
    for i in range(len(data)):
        window_data = data[max(0, i-window+1):i+1]
        window_data.sort()
        result.append(window_data[len(window_data)//2])  # Берем середину
    
# До: [10.1, 9.8, 300.0, 10.2, 10.0, 9.9, 10.3]
# После: [10.1, 9.8, 10.2, 10.2, 10.0, 10.0, 10.0] 
# Выброс 300 исчез! Медиана игнорирует резкие выбросы.

Более умный метод: Комплементарный фильтр

Проблема: У робота есть два датчика положения:

  • Гироскоп — точный на коротких дистанциях, но “уплывает” со временем
  • Акселерометр — точный в среднем, но шумит при движении

Решение: Объединяем их сильные стороны!

class ComplementaryFilter:
    def __init__(self, alpha=0.98):
        self.alpha = alpha  # Доверие к гироскопу (0-1)
        self.angle = 0
    
    def update(self, gyro_rate, accel_angle, dt):
        # 1. Гироскоп: angle += скорость * время
        gyro_angle = self.angle + gyro_rate * dt
        
        # 2. Смешиваем: 98% гироскопа + 2% акселерометра
        self.angle = self.alpha * gyro_angle + (1 - self.alpha) * accel_angle
        return self.angle

# Использование
filter = ComplementaryFilter(alpha=0.98)
while True:
    # Читаем датчики
    gyro = read_gyro()  # градусы/секунду
    accel = read_accel_angle()  # угол по ускорению
    
    # Обновляем фильтр
    current_angle = filter.update(gyro, accel, 0.01)  # dt = 10 мс
    print(f"Текущий угол: {current_angle:.1f}°")

2. Компьютерное зрение: Как робот видит

Робот не видит “кошку” или “стол”. Он видит матрицу чисел (пикселей). Компьютерное зрение превращает эти числа в понимание.

Простой пример: Поиск красного мяча

import cv2
import numpy as np

def find_red_ball(image):
    # 1. Преобразуем в HSV (проще работать с цветами)
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    
    # 2. Задаем диапазон красного цвета
    lower_red = np.array([0, 100, 100])
    upper_red = np.array([10, 255, 255])
    
    # 3. Ищем красные пиксели
    mask = cv2.inRange(hsv, lower_red, upper_red)
    
    # 4. Находим контуры
    contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    # 5. Ищем самый большой круглый контур (это и есть мяч)
    if contours:
        largest = max(contours, key=cv2.contourArea)
        ((x, y), radius) = cv2.minEnclosingCircle(largest)
        
        if radius > 10:  # Игнорируем мелкие шумы
            return (int(x), int(y), int(radius))
    
    return None

# Использование в роботе
cap = cv2.VideoCapture(0)  # Камера робота

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    ball = find_red_ball(frame)
    
    if ball:
        x, y, radius = ball
        print(f"Мяч найден в ({x}, {y}), радиус {radius}")
        
        # Рисуем круг на экране
        cv2.circle(frame, (x, y), radius, (0, 255, 0), 2)
    
    cv2.imshow('Робот видит', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Что может OpenCV:

  • Распознавание лиц (как в телефоне)
  • Чтение QR-кодов (где прописан адрес доставки)
  • Определение расстояния по двум камерам (стереозрение)
  • Слежение за объектом (держать мяч в центре кадра)

3. Искусственный интеллект: Как робот понимает

ИИ — это не магия, а математика + много данных. Нейросеть учится так же, как ты учишься отличать кошек от собак: показывают много примеров, а потом она сама начинает понимать.

Простая нейросеть для распознавания цифр (MNIST)

import tensorflow as tf
from tensorflow import keras

# 1. Загружаем данные (70,000 рукописных цифр)
mnist = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

# 2. Создаём модель (очень простая)
model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),  # Разворачиваем картинку 28x28
    keras.layers.Dense(128, activation='relu'),  # Скрытый слой (128 нейронов)
    keras.layers.Dense(10, activation='softmax') # Выходной слой (10 цифр: 0-9)
])

# 3. Компилируем
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 4. Обучаем (это долго, можно пропустить на первом знакомстве)
model.fit(train_images, train_labels, epochs=5)

# 5. Тестируем
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f"Точность на тесте: {test_acc*100:.2f}%")  # Около 98%!

# 6. Используем
import numpy as np
from PIL import Image

# Загружаем свою цифру
img = Image.open('моя_цифра.png').convert('L').resize((28, 28))
img_array = np.array(img) / 255.0  # Нормализуем
img_array = np.expand_dims(img_array, 0)  # Добавляем размерность

# Предсказываем
predictions = model.predict(img_array)
predicted_digit = np.argmax(predictions[0])
print(f"Робот думает, что это цифра: {predicted_digit}")

Edge AI: Нейросети прямо на роботе

Проблема: облачные нейросети требуют интернета и работают медленно. Решение: TinyML — крошечные нейросети для микроконтроллеров.

Пример на Arduino с камерой:

// Упрощённый пример для Arduino Nano 33 BLE Sense
#include <TensorFlowLite.h>
#include <tensorflow/lite/micro/all_ops_resolver.h>
#include "model.h"  // Предобученная модель

// Загружаем модель
tflite::MicroInterpreter interpreter(model, op_resolver, tensor_arena, kTensorArenaSize);

void setup() {
    Serial.begin(9600);
    // Инициализация камеры OV7670
    camera.begin();
}

void loop() {
    // 1. Делаем снимок
    uint8_t* image = camera.capture();
    
    // 2. Подготавливаем данные для нейросети
    float input[96*96];  // Модель ждёт картинку 96x96
    for (int i = 0; i < 96*96; i++) {
        input[i] = image[i] / 255.0;
    }
    
    // 3. Запускаем нейросеть
    interpreter.input(0)->data.f = input;
    interpreter.Invoke();
    
    // 4. Получаем результат
    float* output = interpreter.output(0)->data.f;
    // output[0] - вероятность "кошка"
    // output[1] - вероятность "собака"
    // output[2] - вероятность "человек"
    
    int prediction = 0;
    float max_prob = output[0];
    for (int i = 1; i < 3; i++) {
        if (output[i] > max_prob) {
            max_prob = output[i];
            prediction = i;
        }
    }
    
    const char* classes[] = {"кошка", "собака", "человек"};
    Serial.print("Робот видит: ");
    Serial.println(classes[prediction]);
    
    delay(1000);
}

Что умеют современные нейросети в роботах:

ЗадачаПримерТехнология
Распознавание объектов“Это стул, а это стол”YOLO, SSD
Сегментация“Где точно на картинке дорога?”U-Net, Mask R-CNN
Генерация текста“Я вижу красный мяч слева”GPT для роботов
Принятие решений“Объехать слева или справа?”Обучение с подкреплением
Предсказание“Человек сейчас пойдёт налево”LSTM, Transformers

Практическое задание: Робот, который следует за цветом

Создадим простого робота на Raspberry Pi с камерой, который:

  1. Видит красный объект
  2. Едет к нему
  3. Останавливается на расстоянии 30 см

Полный код:

import cv2
import numpy as np
import RPi.GPIO as GPIO
import time

# Настройка моторов Raspberry Pi
GPIO.setmode(GPIO.BCM)
# ... настройка пинов для моторов ...

def find_color_object(frame, color='red'):
    """Находит цветной объект в кадре"""
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    
    if color == 'red':
        lower = np.array([0, 100, 100])
        upper = np.array([10, 255, 255])
    elif color == 'green':
        lower = np.array([40, 100, 100])
        upper = np.array([80, 255, 255])
    else:  # blue
        lower = np.array([100, 100, 100])
        upper = np.array([140, 255, 255])
    
    mask = cv2.inRange(hsv, lower, upper)
    
    # Находим центр объекта
    moments = cv2.moments(mask)
    if moments['m00'] > 0:
        cx = int(moments['m10'] / moments['m00'])
        cy = int(moments['m01'] / moments['m00'])
        return (cx, cy), np.sum(mask)  # Центр и размер объекта
    return None, 0

def control_robot(object_center, object_size, frame_width):
    """Управляет моторами на основе положения объекта"""
    if object_center is None:
        # Объект не найден - медленно вращаемся
        turn_left(speed=30)
        return
    
    cx, cy = object_center
    
    # Определяем расстояние по размеру объекта в кадре
    if object_size > 50000:  # Очень большой - мы близко
        stop()
        print("Достиг цели!")
        return
    
    # Определяем направление
    if cx < frame_width * 0.4:
        # Объект слева
        turn_left(speed=50)
    elif cx > frame_width * 0.6:
        # Объект справа
        turn_right(speed=50)
    else:
        # Объект по центру - едем вперёд
        forward(speed=60)
    
    print(f"Объект в ({cx}, {cy}), размер: {object_size}")

# Главный цикл
cap = cv2.VideoCapture(0)

try:
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        
        # Находим красный объект
        center, size = find_color_object(frame, 'red')
        
        # Управляем роботом
        control_robot(center, size, frame.shape[1])
        
        # Показываем картинку (для отладки)
        if center:
            cv2.circle(frame, center, 10, (0, 255, 0), -1)
        cv2.imshow('Робот', frame)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        
        time.sleep(0.05)  # 20 FPS

finally:
    cap.release()
    cv2.destroyAllWindows()
    GPIO.cleanup()

Чеклист: Что попробовать самому

Для начинающих:

  1. Написать фильтр скользящего среднего для данных с датчика
  2. Сделать программу, которая находит на фото все круги
  3. Обучить нейросеть распознавать свои рукописные цифры
  4. Запустить готовую нейросеть YOLO на Raspberry Pi

Для продвинутых:

  1. Создать комплементарный фильтр для стабилизации дрона
  2. Написать стереозрение для оценки расстояний
  3. Обучить нейросеть отличать полезные объекты от мусора
  4. Реализовать простого чат-бота для робота на GPT

Инструменты для экспериментов:

  • Google Colab — бесплатный GPU для обучения нейросетей
  • Roboflow — готовые наборы данных для обучения
  • Edge Impulse — создание TinyML моделей без кода
  • ROS + OpenCV — готовые пакеты для компьютерного зрения

Важно помнить:

  1. Данные > Алгоритмы — хорошие данные важнее умного алгоритма
  2. Простота — начинай с самого простого, что работает
  3. Итерации — сделал → проверил → улучшил → повторил
  4. Физика важна — никакой ИИ не исправит плохие датчики

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