🏎️ Робот по линии

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

Line Follower — классика соревновательной робототехники

Задача

Робот должен ехать по чёрной линии на белом фоне.

     ┌─────────────────────────────────────┐
     │                                     │
     │    🤖 ═══════════════════►         │
     │         ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀           │
     │              (линия)                │
     │                                     │
     └─────────────────────────────────────┘

Звучит просто? Но дьявол в деталях! 😈

Как робот видит линию?

Инфракрасный датчик линии

         💡 ИК-светодиод
          ▼  (излучает)
     ════════════  ← Поверхность
          │  (отражается)
         👁️ Фотодиод
  • Белое — отражает много света → датчик: 0
  • Чёрное — поглощает свет → датчик: 1

Массив датчиков

Один датчик плохо, много — хорошо!

       Датчики (вид сверху)
    ┌─┬─┬─┬─┬─┬─┬─┬─┐
    │0│0│0│1│1│0│0│0│  ← 8 датчиков
    └─┴─┴─┴─┴─┴─┴─┴─┘
              ▀▀▀
           (линия)

Чем больше датчиков — тем точнее знаем положение линии.

Популярные модули

МодульДатчиковОсобенности
1-канальный1Для начинающих
TCRT5000 x33Минимум для работы
QTR-8A8Аналоговый, точный
QTR-8RC8Цифровой с таймингом

Алгоритмы следования

Алгоритм 1: Бинарный (2 датчика)

Самый простой — всего 2 датчика:

    Левый   Правый    Действие
    ─────   ─────     ────────
      0       0       Вперёд (линия между)
      1       0       Влево (съехали вправо)
      0       1       Вправо (съехали влево)
      1       1       Стоп или назад (потеряли)

Код бинарного алгоритма

const int LEFT_SENSOR = A0;
const int RIGHT_SENSOR = A1;
const int THRESHOLD = 500;  // Порог белое/чёрное

void loop() {
  int left = analogRead(LEFT_SENSOR);
  int right = analogRead(RIGHT_SENSOR);
  
  bool leftOnLine = (left > THRESHOLD);
  bool rightOnLine = (right > THRESHOLD);
  
  if (!leftOnLine && !rightOnLine) {
    forward();           // Линия между датчиками
  } 
  else if (leftOnLine && !rightOnLine) {
    turnLeft();          // Съехали вправо
  }
  else if (!leftOnLine && rightOnLine) {
    turnRight();         // Съехали влево
  }
  else {
    stop();              // Потеряли линию
  }
}

Проблема бинарного алгоритма

Робот дёргается:

   Позиция    Действие
   ────────   ────────
   ..█..      Вперёд
   .█...      ВЛЕВО!
   ..█..      Вперёд
   ...█.      ВПРАВО!
   ..█..      Вперёд
   
   Результат: 🐍 змейка вместо плавного движения

Решение? Пропорциональное управление!

Алгоритм 2: Пропорциональный (P)

Идея: Чем дальше отклонился — тем сильнее поворачиваем.

// С массивом из 8 датчиков
int position = readLinePosition();  // -100 (слева) ... +100 (справа)

int error = position - 0;  // Целевая позиция = 0 (центр)
int correction = Kp * error;

int leftSpeed = BASE_SPEED + correction;
int rightSpeed = BASE_SPEED - correction;

setMotors(leftSpeed, rightSpeed);

Визуализация пропорционального управления

Позиция линии    Ошибка    Коррекция
──────────────   ──────    ─────────
    [████░░░░]     -50       Влево 25%
    [░░██░░░░]     -25       Влево 12%
    [░░░██░░░]       0       Прямо
    [░░░░██░░]     +25       Вправо 12%
    [░░░░░███]     +50       Вправо 25%

Результат: Плавное движение без дёрганий! 🎯

Алгоритм 3: PID (продвинутый)

P + I + D = идеальное следование

// Полный PID-регулятор
float Kp = 0.5, Ki = 0.01, Kd = 0.2;
float lastError = 0;
float integral = 0;

void loop() {
  int position = readLinePosition();
  float error = position;
  
  integral += error;
  float derivative = error - lastError;
  
  float correction = Kp * error + Ki * integral + Kd * derivative;
  
  lastError = error;
  
  setMotors(BASE_SPEED + correction, BASE_SPEED - correction);
}

Подробнее о PID — в уроке 2.3!

Практика: собираем линейщика

Компоненты

КомпонентКоличество
Arduino Nano1
Датчик линии (3+ канала)1
DC моторы с редуктором2
Драйвер TB6612 или L298N1
Аккумулятор Li-Po 7.4V1
Шасси1

Схема подключения

                     Arduino Nano
                    ┌────────────┐
Датчик линии ──────►│ A0-A4      │
                    │            │
                    │ D5,D6 ─────┼──► TB6612 ──► Мотор L
                    │ D9,D10 ────┼──► TB6612 ──► Мотор R
                    │            │
Аккумулятор ───────►│ VIN        │
                    └────────────┘

Код для 3 датчиков

const int SENSORS[] = {A0, A1, A2};  // Левый, Центр, Правый
const int THRESHOLD = 500;
const int BASE_SPEED = 150;
const float Kp = 0.8;

void loop() {
  // Читаем датчики
  int left = analogRead(SENSORS[0]) > THRESHOLD ? 1 : 0;
  int center = analogRead(SENSORS[1]) > THRESHOLD ? 1 : 0;
  int right = analogRead(SENSORS[2]) > THRESHOLD ? 1 : 0;
  
  // Вычисляем позицию: -1 (слева), 0 (центр), +1 (справа)
  int position = -1 * left + 0 * center + 1 * right;
  int count = left + center + right;
  
  if (count > 0) {
    float error = (float)position / count;
    int correction = Kp * error * 100;
    
    setMotors(BASE_SPEED + correction, BASE_SPEED - correction);
  } else {
    // Линия потеряна — ищем!
    searchLine();
  }
}

Оптимизация

Типичные проблемы и решения

ПроблемаПричинаРешение
Съезжает на поворотахСлишком быстроУменьши BASE_SPEED
ДёргаетсяKp слишком большойУменьши Kp
Медленно реагируетKp слишком маленькийУвеличь Kp
Теряет на перекрёсткахНет логикиДобавь обработку

Трюки для соревнований

  1. Калибровка перед стартом

    void calibrate() {
      // Робот проезжает по белому и чёрному
      // Запоминает MIN и MAX для каждого датчика
    }
    
  2. Адаптивная скорость

    // На прямой — быстрее, на повороте — медленнее
    int speed = map(abs(error), 0, 100, MAX_SPEED, MIN_SPEED);
    
  3. Предсказание поворотов

    // Смотрим на боковые датчики заранее
    if (farLeftSensor) prepareForLeftTurn();
    

Wokwi: попробуй сам!

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

Открой симулятор и настрой Kp для плавного движения!

Итоги урока

Алгоритмы следования по линии

АлгоритмСложностьКачество
Бинарный (2 датчика)Дёрганый
Пропорциональный (P)⭐⭐Плавный
PID⭐⭐⭐Идеальный

Ключевые формулы:

error = position - target
correction = Kp × error
leftSpeed = base + correction
rightSpeed = base - correction

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

📚 Датчики линии
📚 TCRT5000
📚 DC моторы
📚 PID-регуляторы

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

🚧 Продвинутый объезд препятствий

Урок 2.2 →

Добавляем несколько датчиков, умную логику и память!