🚧 Объезд препятствий

Урок 2.2 | Level 2: Оператор

От реактивного поведения к умной навигации

Эволюция робота

Level 1: "Вижу стену → поворачиваю"     ← Реактивно

Level 2: "Вижу стену → смотрю по сторонам →  
          выбираю лучший путь"           ← Умно!

Сегодня апгрейдим нашего разведчика! 🚀

Проблемы простого алгоритма

Проблема 1: Углы

Робот с одним датчиком врезается в угол:

     ┌──────────────┐
     │              │
     │     🤖 →     │  Датчик смотрит вперёд
     │         ┌────┘  Угол не видит!
     │         │
     └─────────┘

Проблема 2: Узкие проходы

Робот застревает в дверях:

     ┌────┐  ┌────┐
     │    │  │    │
     │    │🤖│    │  Проход есть, но робот
     │    │  │    │  не знает ширину!
     └────┘  └────┘

Проблема 3: Тупики

Робот зацикливается:

     ┌───────────────┐
     │ ↓ ← ← ← ←     │
     │               │
     │ → → → → → ↓   │  Робот ходит
     │ ↑           ↓ │  по кругу!
     │ ← ← ← ← ← ←   │
     └───────────────┘

Решение: Больше датчиков + умная логика!

Конфигурация датчиков

Минимум для умного объезда: 3 датчика

           Передний
         ┌────────┐
    Левый│   🤖   │Правый
    ─────►        ◄─────
         └────────┘

Продвинутая конфигурация: 5+ датчиков

       Передний
    ╱     │     ╲
   45°    │    45°
    ╲     │     ╱
     ┌────┴────┐
     │    🤖    │
     │ L     R │  ← Боковые
     └─────────┘

Диагональные датчики видят углы!

Варианты датчиков

ДатчикДальностьУголЦена
HC-SR042-400 см15°$
Sharp GP2Y0A2110-80 смУзкий$$
VL53L0X (ToF)2-120 смТочечный$$$
RPLidar0.2-12 м360°$$$$

Алгоритм с тремя датчиками

Логика принятия решений

int front = getDistance(FRONT);
int left = getDistance(LEFT);
int right = getDistance(RIGHT);

if (front > SAFE_DIST) {
  // Путь свободен — едем!
  goForward();
}
else if (left > right) {
  // Слева свободнее — поворачиваем влево
  turnLeft();
}
else {
  // Справа свободнее — поворачиваем вправо
  turnRight();
}

Визуализация решений

Ситуация 1: Путь свободен
┌─────────────┐
│             │
│     ↑       │   front=100, left=50, right=50
│    🤖       │   → goForward()
└─────────────┘

Ситуация 2: Стена впереди, слева свободнее
┌──────┐      │
│      │      │   front=20, left=80, right=30
│      │  🤖 →│   → turnLeft()
└──────┘      │

Ситуация 3: Стена впереди, справа свободнее
│      ┌──────┐
│      │      │   front=20, left=30, right=80
│←  🤖 │      │   → turnRight()
│      └──────┘

Полный код с 3 датчиками

// Пины ультразвуковых датчиков
const int TRIG_F = 2, ECHO_F = 3;   // Передний
const int TRIG_L = 4, ECHO_L = 5;   // Левый
const int TRIG_R = 6, ECHO_R = 7;   // Правый

const int SAFE_DIST = 30;  // см
const int CRITICAL_DIST = 15;

void loop() {
  int front = measure(TRIG_F, ECHO_F);
  int left = measure(TRIG_L, ECHO_L);
  int right = measure(TRIG_R, ECHO_R);
  
  if (front > SAFE_DIST) {
    // Плавная коррекция на ходу
    int correction = (left - right) / 10;
    setMotors(BASE_SPEED + correction, BASE_SPEED - correction);
  }
  else if (front > CRITICAL_DIST) {
    // Притормаживаем и выбираем направление
    setMotors(SLOW_SPEED, SLOW_SPEED);
    
    if (left > right + 10) prepareLeft();
    else if (right > left + 10) prepareRight();
  }
  else {
    // Экстренный манёвр!
    stop();
    if (left > right) emergencyLeft();
    else emergencyRight();
  }
  
  delay(50);
}

Продвинутые техники

Техника 1: Серво-голова

Один датчик + серво = сканирование 180°:

#include <Servo.h>
Servo headServo;

int scan() {
  int distances[5];
  int angles[] = {0, 45, 90, 135, 180};
  
  for (int i = 0; i < 5; i++) {
    headServo.write(angles[i]);
    delay(200);
    distances[i] = measureDistance();
  }
  
  // Находим направление с max расстоянием
  int maxIndex = 0;
  for (int i = 1; i < 5; i++) {
    if (distances[i] > distances[maxIndex]) {
      maxIndex = i;
    }
  }
  
  return angles[maxIndex];  // Куда ехать
}

Техника 2: Следование вдоль стены

Робот держит постоянное расстояние до стены:

const int TARGET_DIST = 20;  // Желаемое расстояние до стены
const float Kp = 2.0;

void wallFollow() {
  int rightDist = measure(TRIG_R, ECHO_R);
  
  int error = rightDist - TARGET_DIST;
  int correction = Kp * error;
  
  // Если далеко от стены — поворачиваем к ней
  // Если близко — отворачиваемся
  setMotors(BASE_SPEED - correction, BASE_SPEED + correction);
}

Техника 3: Память о прошлом

Запоминаем повороты, чтобы не зацикливаться:

enum Direction { FORWARD, LEFT, RIGHT, BACK };
Direction history[10];
int historyIndex = 0;

void recordTurn(Direction dir) {
  history[historyIndex] = dir;
  historyIndex = (historyIndex + 1) % 10;
}

bool isLooping() {
  // Если последние 4 поворота — LLLL или RRRR
  // Значит, мы в тупике!
  int sameCount = 0;
  for (int i = 0; i < 4; i++) {
    if (history[i] == history[0]) sameCount++;
  }
  return (sameCount >= 4);
}

void loop() {
  // ...
  if (isLooping()) {
    // Разворачиваемся и пробуем другой путь
    turnAround();
    recordTurn(BACK);
  }
}

Техника 4: Конечный автомат (FSM)

Структурированный подход к поведению:

enum State { DRIVING, SCANNING, TURNING, REVERSING };
State currentState = DRIVING;

void loop() {
  switch (currentState) {
    case DRIVING:
      if (obstacleAhead()) {
        currentState = SCANNING;
      } else {
        driveForward();
      }
      break;
      
    case SCANNING:
      int bestAngle = scan();
      if (bestAngle < 60) currentState = REVERSING;
      else currentState = TURNING;
      break;
      
    case TURNING:
      if (turnComplete()) currentState = DRIVING;
      break;
      
    case REVERSING:
      reverse();
      if (clearBehind()) currentState = SCANNING;
      break;
  }
}

Wokwi-проект

Попробуй разные алгоритмы!

🔌 Obstacle Avoider
Открыть симуляцию в Wokwi
Arduino / ESP32 симулятор с возможностью редактирования кода

Задания:

  1. Настрой SAFE_DIST для разных сред
  2. Добавь плавное торможение
  3. Реализуй wall-following

Сравнение алгоритмов

АлгоритмСложностьГде работает
1 датчик, реактивныйОткрытое пространство
3 датчика, выбор пути⭐⭐Комнаты, коридоры
Сканирование серво⭐⭐⭐Сложные лабиринты
Wall-following⭐⭐Лабиринты с выходом
FSM + память⭐⭐⭐⭐Любые условия

Итоги урока

Продвинутый объезд = Больше данных + Умная логика

  1. 3+ датчика — видим ситуацию целиком
  2. Уровни опасности — плавная реакция
  3. Память — не зацикливаемся
  4. FSM — структурированное поведение

Подробнее в справочнике

📚 Ультразвуковые датчики
📚 ToF-датчики VL53L0X
📚 Паттерны проектирования (FSM)
📚 Контуры управления

Следующий урок

⚖️ PID-регулятор

Урок 2.3 →

Математика, которая делает движение идеальным!