Руководство · Начальный · 20 min read

Робот, следующий по линии: от датчиков к ПИД-управлению

Как работает робот, следующий по линии: датчики отражения, взвешенная ошибка по положению и контур ПИД для плавного управления дифференциальным приводом.

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

Обнаружение линии

Датчик отражения направляет инфракрасный (IR) свет вниз и измеряет, сколько света отражается обратно. Тёмная линия отражает меньше, чем светлая поверхность вокруг неё, поэтому каждый датчик сообщает, находится ли он над линией. Полоса из нескольких датчиков поперёк передней части робота сообщает не только находится ли робот на линии, но и где находится линия относительно центра.

Пять датчиков — хорошая отправная точка. Три подходят для низкой скорости; соревновательные роботы часто используют восемь для более высокого разрешения.

Пять датчиков линии в ряд, каждый помечен весом от минус двух слева через ноль в центре до плюс двух справа, и под каждым показан отсчёт один или ноль. Два датчика с весами ноль и плюс один показывают один и выделены; остальные показывают ноль. Формула взвешенного среднего вычисляет сумму весов, умноженную на показания, делённую на сумму показаний, получая ноль плюс один, делённое на два, то есть ноль целых пять десятых. Предупреждение сообщает, что все нули означают потерю линии, что является другим случаем, а не ошибкой нуля.
Пять включений и выключений превращаются в одно знаковое число, на которое может реагировать контур руления. Его знак задаёт направление, а величина — насколько далеко вы отклонились. Скачать SVG

Превращение показаний в одно число ошибки

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

// positions: {-2, -1, 0, 1, 2}; readings: reflectance per sensor
float weightedPosition = sum(position[i] * reading[i]) / sum(reading[i]);

Результат — ваша ошибка: ноль означает центр, отрицательное значение — линия слева, положительное — справа. Плавная непрерывная ошибка — именно то, что нужно регулятору.

Руление с помощью ПИД (PID)

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

float correction = kp * error + ki * integral + kd * derivative;
setMotorSpeeds(baseSpeed - correction, baseSpeed + correction);

Пропорциональный коэффициент обеспечивает большую часть руления; производный коэффициент гасит раскачку на поворотах; небольшая интегральная составляющая устраняет установившийся дрейф. Если вы только начинаете выбирать коэффициенты, сначала разберитесь, как настроить ПИД-регулятор.

Исправление типичных сбоев

  • Колебания вокруг линии: пропорциональный коэффициент слишком велик или производный слишком мал. Сначала снизьте скорость, затем настройте заново. На реальном оборудовании длинный delay() в цикле добавляет мёртвое время, которое мешает производной составляющей.
  • Потеря линии на резких поворотах: между обновлениями датчиков робот проходит слишком большой участок. Замедлитесь, расширьте полосу датчиков или используйте последнее известное направление линии, чтобы робот повернул обратно, а не ушёл прямо с траектории.
  • Смещение в одну сторону: откалибруйте датчики. Проведите ими по линии и фону, запишите минимум и максимум для каждого и нормализуйте текущие показания по этому диапазону.

Весь цикл в одном месте

Фрагменты выше образуют рабочего робота только тогда, когда совместно обрабатываются временные интервалы, калибровка и случай потери линии:

const int N = 5;
const int PIN[N] = {A0, A1, A2, A3, A4};
const int POS[N] = {-2000, -1000, 0, 1000, 2000};   // weights, in the error's units

int minv[N], maxv[N];
long lastError = 0;
float integral = 0;
unsigned long lastMicros = 0;

const float KP = 0.10f, KI = 0.0f, KD = 1.6f;
const int   BASE = 140;                              // out of 255

void loop() {
  // 1. Real elapsed time — never assume the loop period.
  const unsigned long now = micros();
  const float dt = (now - lastMicros) * 1e-6f;
  if (dt < 0.002f) return;                           // cap the rate at 500 Hz
  lastMicros = now;

  // 2. Position from the weighted average.
  long weighted = 0, total = 0;
  for (int i = 0; i < N; i++) {
    const int v = normalized(analogRead(PIN[i]), i);
    if (v > 100) { weighted += (long)v * POS[i]; total += v; }
  }

  // 3. Line lost: hold the last error, do NOT zero it.
  const long error = (total == 0) ? lastError : (weighted / total);

  // 4. PID, with derivative on measurement and conditional integration.
  const float derivative = -(float)(error - lastError) / dt;
  const float candidate = integral + error * dt;
  const float raw = KP * error + KI * candidate + KD * derivative;
  const float correction = constrain(raw, -BASE, BASE);
  if (raw == correction) integral = candidate;

  lastError = error;

  // 5. Differential steering.
  setMotors(BASE - correction, BASE + correction);
}

Пять вещей здесь отличают робота, который действительно следует по линии, от робота, который почти делает это.

Настоящий dt. И I и D делят на прошедшее время. Цикл, длительность которого меняется по мере добавления функций — а она обязательно будет меняться, — вычисляет обе величины по ложному предположению, если считать период постоянным.

Порог v > 100. Без него пять каналов, каждый из которых показывает небольшое фоновое значение, тянут взвешенное среднее к центру независимо от положения линии. Робот хорошо отслеживает контрастную линию, но плохо справляется с выцветшей.

Сохранение последней ошибки при потере линии. Ноль означает «центр», поэтому робот, который обнуляет ошибку, едет прямо на полной скорости — прочь от линии, которая почти наверняка изгибалась. Сохранение последней ошибки заставляет робот поворачивать в том же направлении, в котором он уже поворачивал.

Производная по измерению. Дифференцирование ошибки означает резкий выброс при изменении уставки. Здесь уставка всегда равна нулю, поэтому разница невелика, но полезно сформировать эту привычку.

Условное интегрирование. После насыщения коррекции дальнейшее накопление интеграла создаёт перерегулирование, которое затем придётся отработать.

Рабочий порядок настройки

Не настраивайте все три параметра одновременно. Установите KI = 0 и KD = 0, затем:

Шаг Изменение На что смотреть Когда остановиться
1 Увеличить KP от небольшого значения Робот начинает реагировать на линию Он проходит плавный поворот, слегка покачиваясь
2 Продолжать увеличивать KP Покачивание перерастает в колебания Колебания устойчивы — запомните это значение как Ku
3 Установить KP примерно вдвое меньше Ku Отклик быстрый, есть перерегулирование —
4 Увеличить KD Покачивание гаснет Прямые чистые; дальнейшее увеличение KD вызывает рывки
5 Только если он постоянно отслеживает со смещением, добавить немного KI Смещение уменьшается Оно устраняется — KI редко нужен роботу, следующему по линии
6 Увеличить скорость BASE Всё ухудшается одновременно Настройте KP и KD заново на новой скорости

Шаг 6 часто забывают: коэффициенты зависят от скорости. Настройка, идеальная при рабочем значении 140, будет колебаться при 220, потому что та же коррекция руления теперь даёт большее изменение курса за единицу времени. Ожидайте повторной настройки при каждом существенном изменении скорости.

Интегральная составляющая здесь действительно редко полезна. Уставка робота, следующего по линии, равна нулю, а возмущения кратковременны, поэтому обычно нет постоянного смещения, которое мог бы устранить KI. Если постоянное смещение сохраняется, сначала заподозрите неравномерную настройку двигателей или неоткалиброванный датчик, и только затем увеличивайте интегральный коэффициент.

Частота цикла незаметно ограничивает всё

Частота цикла Интервал Расстояние при 0.4 m/s
20 Hz 50 ms 20 mm между решениями
50 Hz 20 ms 8 mm
200 Hz 5 ms 2 mm
500 Hz 2 ms 0.8 mm

Робот, принимающий решение каждые 20 mm, не может точно отслеживать поворот, как бы ни были настроены коэффициенты, и никакая производная составляющая не восстановит информацию, которую никогда не измерили.

Обычных виновников легко убрать: delay() в любом месте цикла, Serial.print более нескольких значений на 9600 baud и чтение неиспользуемых датчиков. Их удаление обычно поднимает первую конструкцию с 50 Hz до нескольких сотен, и отслеживание улучшается без изменения коэффициентов. Измеряйте частоту до настройки, а не после.

Диагностика

Симптом Вероятная причина Исправление
Покачивается на прямых KP слишком велик или KD слишком мал Уменьшить KP вдвое, затем увеличить KD
Колебания ускоряются KP выше предельного коэффициента Снижать до стабилизации
Срезает углы Слишком быстро для расстояния до датчиков Замедлиться или переместить массив вперёд
Уезжает при потере линии Ошибка обнуляется при потере линии Сохранять последнюю ошибку
В одном направлении идёт хорошо, в другом плохо Массив не выровнен или двигатели отрегулированы неодинаково Выровнять массив; отрегулировать каждый двигатель
Смещается в одну сторону Неоткалиброванные датчики или разные двигатели Откалибровать каждый канал, затем отрегулировать
На стенде хорошо, на полу бродит Изменилась высота посадки 3–8 mm, жёсткое крепление, повторная калибровка
Вчера работал, сегодня вялый Напряжение аккумулятора Коэффициенты — свойства объекта; настройте заново или замкните контур скорости
Дрожит только на низкой скорости Ниже мёртвой зоны двигателей Измерить минимальное рабочее значение ШИМ для запуска каждого двигателя и скомпенсировать
KD ни на что не влияет delay() в цикле Мёртвое время полностью подавляет действие производной

Попробуйте сами

В симуляторе робота, следующего по линии можно нарисовать трассу, изменить число датчиков и скорость и настроить коэффициенты, наблюдая ошибку и время прохождения. Всё, что вы узнаете о взвешенной ошибке и контуре руления, напрямую переносится на Arduino-робота.

Исследуйте граф

Входит в эти сборки

Проекты и учебные пути, в которые входит это руководство.

Дополнительные материалы

Источники