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

Массив инфракрасных датчиков для робота, следующего по линии

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

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

Как датчик отражения воспринимает линию

Каждый датчик объединяет инфракрасный светодиод (IR LED) с фототранзистором. Светодиод направляет свет вниз, а фототранзистор измеряет отражение. Матовая чёрная линия отражает мало инфракрасного света (низкое показание), а более светлый фон отражает больше (высокое показание). При перемещении массива по трассе из чёрного на белом каждый датчик переключается между значением «на линии» и значением «вне линии».

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

Аналоговые и цифровые датчики

  • Аналоговые датчики дают непрерывное значение (например, 0–1023 на ADC Arduino). По ним можно определить, на каком расстоянии от каждого датчика находится линия, вычислить плавную позицию и выполнять пропорциональное управление.
  • Цифровые датчики сообщают только о состоянии вкл./выкл. после сравнения с внутренним порогом. Их проще подключить, но промежуточная информация теряется, поэтому управление становится менее точным.

Для пропорционального следования по линии — того варианта, который настраивают с помощью ПИД-регулятора (PID), — используйте аналоговые показания. Именно этот подход моделирует симулятор робота, следующего по линии.

Сначала выполните калибровку

Перед запуском проведите каждый датчик по линии и фону, записывая минимальное и максимальное значения для каждого датчика. Затем нормируйте каждое текущее показание в диапазон 0–1000 относительно собственного диапазона:

// Per-sensor calibration captured during a sweep.
int minv[N], maxv[N];

int normalized(int raw, int i) {
  long span = maxv[i] - minv[i];
  if (span <= 0) return 0;                       // sensor never saw contrast
  long v = (long)(raw - minv[i]) * 1000 / span;  // 0 (background) … 1000 (line)
  return constrain(v, 0, 1000);
}

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

От показаний к единой позиции линии

Теперь преобразуйте массив в единую позицию с помощью средневзвешенного по отражению значения. Задайте каждому датчику фиксированную позицию (…−2, −1, 0, +1, +2… по длине планки) и назначьте вес, равный нормированному показанию. Результат показывает положение линии относительно центра планки: положительное с одной стороны, отрицательное с другой и нулевое, когда линия находится точно по центру.

Столбчатая диаграмма пяти датчиков отражения на планке робота. Линия находится справа от центра, поэтому показания правых датчиков максимальны. Пунктирная линия отмечает центр планки, соответствующий нулевой ошибке, а сплошная линия — взвешенную по отражению позицию со значением плюс 0.69, то есть вычисленную ошибку управления.
Средневзвешенное значение показаний датчиков задаёт плавную позицию линии: в этом примере линия находится справа от центра, поэтому ошибка положительна и регулятор возвращает робот к нулю. Скачать SVG
// Sensor positions across the bar, e.g. { -2000,-1000,0,1000,2000 }.
long readLinePosition() {
  long weighted = 0, total = 0;
  for (int i = 0; i < N; i++) {
    int v = normalized(analogRead(pins[i]), i);
    weighted += (long)v * position[i];
    total    += v;
  }
  if (total == 0) return lastPosition;  // line lost — reuse last direction
  lastPosition = weighted / total;
  return lastPosition;                  // 0 = centered, sign = which side
}

Две детали обеспечивают надёжность решения:

  • Делите на сумму показаний, а не на число датчиков, чтобы слабая и широкая линия отображались в один и тот же диапазон позиции.
  • Если все датчики потеряли линию (total == 0), используйте последнюю известную позицию вместо нуля. Робот продолжит поворачивать туда, где находилась линия, а не сойдёт с трассы на резком повороте.

Сколько нужно датчиков?

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

Процедура калибровки целиком

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

const int N = 8;
const int PIN[N] = {A0, A1, A2, A3, A4, A5, A6, A7};
int minv[N], maxv[N];

void calibrate(unsigned long durationMs) {
  for (int i = 0; i < N; i++) { minv[i] = 1023; maxv[i] = 0; }

  const unsigned long start = millis();
  while (millis() - start < durationMs) {
    for (int i = 0; i < N; i++) {
      const int v = analogRead(PIN[i]);
      if (v < minv[i]) minv[i] = v;
      if (v > maxv[i]) maxv[i] = v;
    }
  }

  // A channel that never saw contrast would turn noise into huge normalised
  // values and drag the whole estimate toward itself.
  for (int i = 0; i < N; i++) {
    if (maxv[i] - minv[i] < 50) {
      Serial.print(F("channel dead or never crossed the line: "));
      Serial.println(i);
    }
  }
}

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

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

Устранение влияния внешнего освещения

Солнечный свет содержит много инфракрасного излучения с длиной волны 940 nm, и фототранзистор не может отличить его от отражения собственного излучателя. Именно это наблюдается, когда робот работает в помещении, но отказывается работать у окна.

Если излучатели массива можно переключать — у большинства есть вывод LEDON или CTRL, — решение полностью устраняет помеху и требует всего одного дополнительного чтения на канал:

int ambientSubtracted(int i) {
  digitalWrite(LEDON, LOW);
  delayMicroseconds(200);              // let the phototransistor settle
  const int dark = analogRead(PIN[i]); // ambient only

  digitalWrite(LEDON, HIGH);
  delayMicroseconds(200);
  const int lit = analogRead(PIN[i]);  // ambient + our emitter

  return lit - dark;                   // just our emitter
}

Любая постоянная составляющая окружающей среды компенсируется точно. Цена — удвоение времени измерения, что для быстрого робота, следующего по линии, существенно, — однако такой подход превращает ненадёжный датчик в надёжный, и именно его применяют в коммерческих массивах.

Есть и второе преимущество: между измерениями излучатели выключены, поэтому их средний ток резко снижается. Излучатели 8-канального массива могут постоянно потреблять 100–200 mA, что для небольшого аккумулятора составляет заметную часть бюджета питания.

Потеря линии и повторный поиск

Случай total == 0, рассмотренный выше, заслуживает больше внимания, чем обычно, поскольку именно здесь чаще всего происходит заметный отказ робота, следующего по линии.

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

Возврат последней позиции заставляет робот продолжать поворачивать в том же направлении, куда исчезла линия. Это единственное изменение отличает робота, способного восстановиться на резком повороте, от робота, который съезжает с трассы.

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

long lastPosition = 0;
unsigned long lostSince = 0;

long readLinePosition() {
  long weighted = 0, total = 0;
  for (int i = 0; i < N; i++) {
    const int v = normalized(analogRead(PIN[i]), i);
    if (v > 100) {                    // ignore channels clearly on the floor
      weighted += (long)v * POSITION[i];
      total    += v;
    }
  }

  if (total == 0) {
    if (lostSince == 0) lostSince = millis();
    if (millis() - lostSince > 1500) stopMotors();   // genuinely lost
    return lastPosition;              // keep turning the way we were
  }

  lostSince = 0;
  lastPosition = weighted / total;
  return lastPosition;
}

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

Высота над полом определяет контраст

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

Высота над полом Что видно в диапазоне калибровки
1–3 mm Наибольший диапазон, но массив задевает любую неровность пола
3–8 mm Рабочий диапазон. Большинство массивов рассчитано примерно на 3 mm
8–15 mm Диапазон заметно сужается; нормирование начинает усиливать шум
> 15 mm На большинстве поверхностей датчик фактически слеп

Отсюда следуют два вывода. Установите массив жёстко — при гибком кронштейне высота меняется при ускорении робота, показания меняются, а контур регулирования воспринимает это как перемещение линии. Кроме того, установите массив горизонтально, поскольку при наклоне эффективная высота каждого канала, а значит и его калибровка, различается; робот хорошо идёт в одном направлении и плохо в противоположном.

Сколько датчиков: количественная оценка

Каналы Разрешение позиции на линии шириной 19 mm Подходит для
3 Фактически слева / в центре / справа Понимания принципа
5 ~4 дискретные области с плавным переходом между ними Первого робота, следующего по линии, на пологой трассе
8 ~7 областей Быстрые трассы, резкие повороты, обнаружение разветвлений
12–16 Высокое Соревновательные роботы на высокой скорости

Важнее числа датчиков — ширина массива относительно ширины линии. Для обычного следования по линии массив должен быть достаточно широким, чтобы на каждом краю линии находился хотя бы один датчик, то есть его ширина должна примерно вдвое превышать ширину ленты.

Для обнаружения разветвлений в лабиринте из линий требование строже: робот распознаёт ветвь, когда крайние датчики темнеют, а центральные всё ещё находятся на линии, поэтому ширина массива должна быть не менее чем втрое больше ширины ленты. 8-канальный массив с шагом 8 mm имеет ширину около 60 mm, что подходит для ленты 19 mm.

Диагностика

Симптом Вероятная причина Исправление
Работает на одной поверхности, отказывает на другой Устаревшая или отсутствующая калибровка Повторите калибровку в начале каждого запуска
Один канал смещает оценку Неисправный излучатель — диапазон слишком мал Проверьте maxv − minv каждого канала и примените проверку
При потере линии робот движется прямо При потере линии возвращается 0 Возвращайте последнюю позицию
Хорошо идёт по широкой ленте, сходит с потёртой Фоновые показания датчиков попадают в средневзвешенную оценку Добавьте порог v > 100
Отказывает у окна Инфракрасное излучение окружающей среды Вычитайте фон при выключенном излучателе или защитите массив
Позиция скачет при пересечении линии датчиками Нет нормировки по каналам Откалибруйте каждый канал по его собственным минимуму и максимуму
Показания дрейфуют при долгой работе Просадка аккумулятора приводит к уменьшению яркости излучателей Проверьте напряжение шины под нагрузкой
Все каналы показывают одинаковые значения Массив расположен слишком высоко Установите 3–8 mm и повторите калибровку
В одном направлении идёт хорошо, в другом плохо Массив не горизонтален Эффективная высота каждого канала различается

Что делать дальше

Эта позиция является сигналом ошибки. Подайте его в контур регулирования, который увеличивает скорость одного колеса и снижает скорость другого, — и получите робота, следующего по линии. Изучите этот контур в руководстве Настройка ПИД-регулятора (PID), а затем попробуйте всю систему от начала до конца в симуляторе робота, следующего по линии.

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

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

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

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

Источники