**Анализ конструкции и проблем:**

Судя по фото и коду, я вижу несколько моментов, которые мешают роботу стабильно ехать медленно и не терять линию:

1.  **Расположение датчиков:** Датчики вынесены далеко вперед относительно ведущих колес, а поворотное колесо (ролик) тоже спереди. Это создает большой «рычаг». Малейший поворот колес вызывает сильное смещение датчиков в сторону. Текущий алгоритм (релейный: "вижу черное слева — резко поворачиваю влево") вызывает раскачку (осцилляцию), из-за которой робот вылетает с линии.
2.  **Проблема минимальной скорости:** Желтые моторы (TT motors) имеют "мертвую зону". Они могут не крутиться при ШИМ < 60, но при 70 уже едут слишком быстро. Пытаться ехать на ШИМ 40-50 бесполезно — момента не хватит.
3.  **Алгоритм:** Ваша текущая логика: «Если оба датчика на белом — едь прямо». Это главная ошибка. На повороте робот сначала смещается одним датчиком на линию, подруливает, но если инерция проносит его чуть дальше, оба датчика оказываются на белом *за пределами* линии. Робот думает "я на прямой" и уезжает в пустоту.

---

**Решение:**

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

**Что изменено:**
1.  **Управление моторами:** Вместо полной остановки одного колеса (что вызывает рывки), мы будем *замедлять* одно и *ускорять* другое.
2.  **Память:** Если оба датчика видят белое, робот не едет прямо, а продолжает поворачивать в ту сторону, где последний раз видел линию.
3.  **ШИМ:** Ограничен до 180, как вы просили. Минимальная скорость выставлена в зону уверенного старта моторов, но регулируется коэффициентами.

**Калибровка:**
Я сохранил чтение из EEPROM. **Но!** Для П-регулятора важно, чтобы значения были нормализованы. Если калибровка была сделана давно или при другом освещении, робот будет ехать криво.
*Если робот ведет себя странно, раскомментируйте `#define CALIBRATION` один раз, прошейте, дайте ему откалиброваться, а затем закомментируйте и прошейте снова.*

Вот оптимизированный код:

```cpp
#include <EEPROM.h>

// --- ПИНЫ ---
#define LEFT_FORWARD 3
#define LEFT_BACKWARD 5
#define RIGHT_FORWARD 6
#define RIGHT_BACKWARD 11
#define LEFT_SENSOR A0
#define RIGHT_SENSOR A7

// --- НАСТРОЙКИ СКОРОСТИ И РЕГУЛЯТОРА ---
// Базовая скорость (когда едем прямо). 
// Для медленной езды ставьте 80-100. Если моторы гудят и не едут, увеличьте.
#define BASE_SPEED 90 

// Максимальный ШИМ (по вашему запросу не более 180)
#define MAX_PWM 180 

// Коэффициент чувствительности поворота (Kp).
// Если робот виляет (осциллирует) - уменьшайте (0.3, 0.2).
// Если робот не вписывается в поворот - увеличивайте (0.6, 0.8).
float Kp = 0.5; 

// --- РЕЖИМ КАЛИБРОВКИ ---
// Раскомментируйте, чтобы запустить калибровку (один раз), затем закомментируйте обратно.
// #define CALIBRATION

struct {
  uint16_t leftMin, leftMax;   // Min = Белый (обычно), Max = Черный
  uint16_t rightMin, rightMax;
} calib;

int lastError = 0; // Для запоминания направления при потере линии

void calibrateSensors();
void setMotorSpeed(int leftSpeed, int rightSpeed);

void setup() {
  Serial.begin(9600);
  
  pinMode(LEFT_FORWARD, OUTPUT);
  pinMode(LEFT_BACKWARD, OUTPUT);
  pinMode(RIGHT_FORWARD, OUTPUT);
  pinMode(RIGHT_BACKWARD, OUTPUT);
  pinMode(LEFT_SENSOR, INPUT);
  pinMode(RIGHT_SENSOR, INPUT);

  setMotorSpeed(0, 0);

  #ifdef CALIBRATION
    calibrateSensors();
    EEPROM.put(0, calib);
    Serial.println("Calibration saved. Upload code without #define CALIBRATION.");
    while(1) { // Остановить выполнение после калибровки
        digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100);
    } 
  #else
    EEPROM.get(0, calib);
    // Проверка на "мусор" в EEPROM
    if (calib.leftMax == 0 || calib.leftMax == 65535) {
      Serial.println("EEPROM empty! Please calibrate first.");
      while(1);
    }
  #endif
}

void loop() {
  // 1. Считываем датчики
  int rawLeft = analogRead(LEFT_SENSOR);
  int rawRight = analogRead(RIGHT_SENSOR);

  // 2. Приводим значения к диапазону 0..1000
  // constrain нужен, чтобы не выйти за пределы, если освещение изменилось
  long mapLeft = map(rawLeft, calib.leftMin, calib.leftMax, 0, 1000);
  long mapRight = map(rawRight, calib.rightMin, calib.rightMax, 0, 1000);
  mapLeft = constrain(mapLeft, 0, 1000);
  mapRight = constrain(mapRight, 0, 1000);

  // 3. Логика определения ошибки
  // Предполагаем, что линия черная (высокое значение), фон белый (низкое).
  // error > 0 -> Линия слева (надо влево)
  // error < 0 -> Линия справа (надо вправо)
  
  int error = mapLeft - mapRight;

  // --- ОБРАБОТКА ПОТЕРИ ЛИНИИ ---
  // Если оба датчика видят белое (значения близки к 0), мы потеряли линию.
  // Порог 200 подбирается экспериментально (это 20% от черного).
  if (mapLeft < 200 && mapRight < 200) {
    // Вспоминаем, какой был знак ошибки в последний раз
    if (lastError > 0) {
       // Линия была слева, крутимся влево на месте (или резкий поворот)
       // Чтобы развернуться на месте, левое колесо назад, правое вперед
       setMotorSpeed(-90, 90); 
    } else {
       // Линия была справа, крутимся вправо
       setMotorSpeed(90, -90);
    }
    return; // Пропускаем остальной код цикла
  }
  
  // Сохраняем текущую ошибку как "последнюю известную"
  lastError = error;

  // 4. Расчет П-регулятора
  int correction = error * Kp;

  // 5. Расчет скоростей моторов
  // При ошибке > 0 (надо влево): correction положителен -> leftSpeed уменьшается, rightSpeed увеличивается
  int leftSpeed = BASE_SPEED - correction;
  int rightSpeed = BASE_SPEED + correction;

  // 6. Ограничение скоростей
  leftSpeed = constrain(leftSpeed, -MAX_PWM, MAX_PWM);
  rightSpeed = constrain(rightSpeed, -MAX_PWM, MAX_PWM);

  // 7. Подача на моторы
  setMotorSpeed(leftSpeed, rightSpeed);
  
  // Небольшая задержка для стабильности АЦП (опционально)
  delay(2);
}

// Удобная функция управления моторами
// speed: от -180 (назад) до 180 (вперед)
void setMotorSpeed(int leftSpeed, int rightSpeed) {
  
  // Левый мотор
  if (leftSpeed > 0) {
    analogWrite(LEFT_FORWARD, leftSpeed);
    analogWrite(LEFT_BACKWARD, 0);
  } else if (leftSpeed < 0) {
    analogWrite(LEFT_FORWARD, 0);
    analogWrite(LEFT_BACKWARD, -leftSpeed); // Модуль числа
  } else {
    analogWrite(LEFT_FORWARD, 0);
    analogWrite(LEFT_BACKWARD, 0);
  }

  // Правый мотор
  if (rightSpeed > 0) {
    analogWrite(RIGHT_FORWARD, rightSpeed);
    analogWrite(RIGHT_BACKWARD, 0);
  } else if (rightSpeed < 0) {
    analogWrite(RIGHT_FORWARD, 0);
    analogWrite(RIGHT_BACKWARD, -rightSpeed);
  } else {
    analogWrite(RIGHT_FORWARD, 0);
    analogWrite(RIGHT_BACKWARD, 0);
  }
}

// Алгоритм калибровки (сохраняем МИН и МАКС значения)
void calibrateSensors() {
  Serial.println("Start Calibration...");
  
  // Инициализируем "наоборот", чтобы найти реальные min/max
  calib.leftMin = 1023; calib.leftMax = 0;
  calib.rightMin = 1023; calib.rightMax = 0;

  // Вращаемся на месте, чтобы пересечь линию несколько раз
  // Включаем вращение
  setMotorSpeed(100, -100); 

  unsigned long startTime = millis();
  while (millis() - startTime < 4000) { // Калибруемся 4 секунды
    int l = analogRead(LEFT_SENSOR);
    int r = analogRead(RIGHT_SENSOR);

    if (l < calib.leftMin) calib.leftMin = l;
    if (l > calib.leftMax) calib.leftMax = l;
    if (r < calib.rightMin) calib.rightMin = r;
    if (r > calib.rightMax) calib.rightMax = r;
    delay(5);
  }
  
  setMotorSpeed(0, 0);
  
  // Добавляем небольшой отступ (шум), чтобы не делить на 0 и иметь четкие границы
  // calib.leftMin += 10; 
  // calib.leftMax -= 10;
  
  Serial.print("L: "); Serial.print(calib.leftMin); Serial.print("-"); Serial.println(calib.leftMax);
  Serial.print("R: "); Serial.print(calib.rightMin); Serial.print("-"); Serial.println(calib.rightMax);
}
```

### Как настроить этот код под вашего робота:

1.  **Калибровка:**
    *   Так как я изменил структуру `calib` (вместо порогов я храню Min и Max для `map`), вам **нужно один раз провести калибровку**.
    *   Раскомментируйте `#define CALIBRATION` в начале.
    *   Залейте код.
    *   Поставьте робота на линию. Робот начнет крутиться вокруг своей оси 4 секунды. Он должен "просканировать" и черное, и белое.
    *   После остановки закомментируйте `#define CALIBRATION` и залейте код снова.

2.  **Настройка `BASE_SPEED` (Базовая скорость):**
    *   Я поставил `90`. Это примерно половина от максимума (180). Если робот едет слишком быстро, попробуйте снизить до `70-80`.
    *   **Важно:** Если поставить слишком мало (например, 40), робот будет гудеть, но не поедет, пока ошибка не станет большой. Это вызовет рывки. Лучше ехать чуть быстрее, но плавно.

3.  **Настройка `Kp` (Коэффициент):**
    *   Если робот "вихляет" на прямой (едет змейкой) -> **уменьшите** `Kp` (например, до 0.3).
    *   Если робот на повороте уезжает прямо и теряет линию -> **увеличьте** `Kp` (например, до 0.7 или 1.0).

Этот алгоритм пытается удержать разницу между датчиками равной нулю (то есть держать линию ровно между ними), что гораздо эффективнее простого переключения состояний.