Перейти к основному содержимому

analogRead: обработка датчика

Трёхпроводной датчик передаёт сигнал на аналоговый вход

Аналоговый датчик выдаёт не только два состояния, а меняющийся электрический уровень. Функция analogRead() запускает измерение через аналого-цифровой преобразователь и возвращает целое число. Чтобы робот принял осмысленное решение, сырой код нужно связать с реальными условиями, шумом и особенностями платы.

Коротко

analogRead(pin) возвращает код АЦП. Диапазон кодов, разрешение, опорное напряжение, допустимый входной уровень и скорость измерения зависят от платы и настроек. Сначала наблюдают реальные данные, затем калибруют и выбирают алгоритм реакции.

Зачем это нужно

Аналоговые значения встречаются в робототехнике постоянно:

  • отражательная способность поверхности для датчика линии;
  • положение потенциометра;
  • освещённость через фотодатчик и делитель;
  • сигнал аналогового дальномера;
  • ток через измерительный усилитель;
  • температура через совместимый преобразователь.

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

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

Потенциометр как аналоговый датчик на Arduino UNO в Tinkercad

Потенциометр удобен как первый аналоговый датчик: у него есть питание, GND и средний вывод с меняющимся напряжением. Средний вывод подключается к аналоговому входу, например A0.

УзелКуда подключитьЧто получает программа
один край потенциометрак 5Vверхняя граница делителя
второй край потенциометрак GNDнижняя граница делителя
средний выводк A0значение для analogRead(A0)

Главная идея

АЦП сравнивает входной уровень с опорным диапазоном и помещает результат в один из дискретных кодов. При идеальном N-битном преобразовании число возможных кодов равно 2^N, но реальная точность дополнительно ограничена шумом, допусками и нелинейностью.

физическая величина → датчик → напряжение → АЦП → код → обработка → действие

Потенциометр, коды АЦП, пороги и команда исполнительному узлу

Слева показан потенциометрический датчик: крайние выводы образуют делитель, движок передаёт сигнал на ADC-вход. Видимый последовательный резистор в цепи питания — намеренный пример дополнительного ограничения/защиты; он не обязателен для каждого потенциометра и вместе с нагрузкой может уменьшить доступный диапазон АЦП. Цветные точки, пороговые метки и фиолетовая команда справа — концептуальная обработка, а не продолжение электрических проводов.

Правильное подключение

Типичный трёхпроводной модуль имеет питание, GND и аналоговый выход. Подключение проверяют по документации модуля, а не только по цвету провода. Сигнал должен оставаться в допустимом диапазоне входа платы при всех режимах, включая запуск и отдельное питание датчика.

ЛинияНазначениеОшибка, которой нужно избежать
питаниеэнергия датчикаподать несовместимое напряжение
GNDобщий опорный потенциалзабыть общую землю между узлами
analog outизмеряемый уровеньподать уровень выше допустимого входа

Если датчик работает от более высокого напряжения, его выход не становится автоматически безопасным для 3,3-вольтового ADC. Может понадобиться делитель, буфер, ограничитель или совместимый преобразователь, рассчитанный по документации.

Первое чтение

const int sensorPin = A0;

void setup() {
Serial.begin(9600);
}

void loop() {
const int rawValue = analogRead(sensorPin);
Serial.println(rawValue);
delay(50);
}

Этот скетч нужен для наблюдения. Медленная печать делает поток читаемым, но не показывает максимальную скорость АЦП и может скрывать быстрые изменения. Перед выбором порога запишите значения в нескольких известных условиях: минимум, максимум, типичное рабочее положение и помехи от включённых моторов.

На классической Arduino UNO R3 с настройками по умолчанию часто приводят диапазон 0…1023 для 10-битного АЦП. Это не универсальное свойство analogRead(): другие платы поддерживают иное аппаратное или настроенное разрешение, например через analogReadResolution().

Разрешение и опорное напряжение

Упрощённая оценка размера младшего разряда:

шаг ≈ Vref / 2^N

Для реальной платы нужно учитывать, как именно кодируются границы, качество Vref, характеристики АЦП и источник сигнала. Формула описывает идеальный масштаб, а не гарантированную точность.

ПараметрЧто меняетГде проверять
разрешение Nколичество кодовсправочник платы и analogReadResolution()
Vrefвходной диапазон преобразованиясхема платы и analogReference()
допустимый уровень GPIO/ADCбезопасность входаdatasheet платы и микроконтроллера
источник сигналавремя установления и шумdatasheet датчика и АЦП

Нельзя подавать на вход уровень выше разрешённого только потому, что программа «всё равно ограничит число». Программный constrain() работает после физического измерения и не защищает контакт.

От сырого числа к относительному значению

Предположим, в рабочей установке датчик стабильно даёт от 210 до 790. Можно ограничить измерение этим диапазоном и перевести в проценты:

const int sensorPin = A0;
const int sensorMin = 210;
const int sensorMax = 790;

void setup() {
Serial.begin(9600);
}

void loop() {
int raw = analogRead(sensorPin);
raw = constrain(raw, sensorMin, sensorMax);

const int percent = map(raw, sensorMin, sensorMax, 0, 100);
Serial.println(percent);
delay(50);
}

Числа — пример результатов калибровки, не значения конкретной модели датчика. map() выполняет целочисленное линейное преобразование и не ограничивает вход самостоятельно. Кроме того, некоторые датчики нелинейны: для них линейный процент не равен физической величине. Тогда используют формулу или таблицу из datasheet и проверяют её эталонными измерениями.

Калибровка в начале работы

Робот линии может несколько секунд наблюдать светлую и тёмную поверхность, сохраняя минимум и максимум:

const int sensorPin = A0;

int sensorMin = 0;
int sensorMax = 0;

void calibrateFor(unsigned long durationMs) {
const unsigned long startedAt = millis();
const int firstValue = analogRead(sensorPin);
sensorMin = firstValue;
sensorMax = firstValue;

while (millis() - startedAt < durationMs) {
const int value = analogRead(sensorPin);

if (value < sensorMin) {
sensorMin = value;
}
if (value > sensorMax) {
sensorMax = value;
}
}
}

void setup() {
Serial.begin(9600);
calibrateFor(2000);
}

void loop() {
const int raw = analogRead(sensorPin);
Serial.println(raw);
delay(50);
}

Во время этих двух секунд программа блокирующе занята калибровкой. Это допустимо только в контролируемой стартовой фазе, когда приводы безопасно остановлены. Пользователь должен действительно показать датчику весь рабочий диапазон. После калибровки необходимо проверить, что sensorMax > sensorMin; иначе преобразование диапазона недопустимо.

Шум и усреднение

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

int readAverage(int pin, int sampleCount) {
if (sampleCount <= 0) {
return 0;
}

long sum = 0;
for (int i = 0; i < sampleCount; i++) {
sum += analogRead(pin);
}

return sum / sampleCount;
}

Больше выборок обычно уменьшает быстрый случайный разброс, но повышает задержку и не исправляет систематическую ошибку. Тип суммы выбирают с запасом под максимальный код и число измерений.

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

Фильтр выбирают по характеру сигнала, а не по принципу «чем сильнее, тем лучше». Слишком медленный фильтр заставит робота поздно заметить край линии.

Порог и гистерезис

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

const int sensorPin = A0;
const int ledPin = 13;

const int turnOnThreshold = 650;
const int turnOffThreshold = 600;

bool alert = false;

void setup() {
pinMode(ledPin, OUTPUT);
}

void loop() {
const int value = analogRead(sensorPin);

if (!alert && value >= turnOnThreshold) {
alert = true;
} else if (alert && value <= turnOffThreshold) {
alert = false;
}

digitalWrite(ledPin, alert ? HIGH : LOW);
}

Между 600 и 650 состояние сохраняется. Границы приведены для структуры алгоритма; их нужно выбирать по измеренному распределению значений и допустимой задержке.

Датчик управляет роботом

Для следования по линии одного порога часто недостаточно. Два датчика дают ошибку положения:

const int leftSensorPin = A0;
const int rightSensorPin = A1;

int calculateLineError() {
const int left = analogRead(leftSensorPin);
const int right = analogRead(rightSensorPin);
return left - right;
}

void loop() {
const int error = calculateLineError();
// Следующий этап преобразует error в команды драйверу моторов.
}

void setup() {
}

Знак ошибки показывает сторону отклонения только после проверки ориентации и реакции конкретных датчиков. Команды моторам должны идти через драйвер и быть ограничены допустимым диапазоном.

Диагностика

СимптомВозможная причинаПроверка
всегда минимальный кодсигнал у GND или обрыв питания датчикаизмерить питание и сигнал мультиметром
всегда максимальный кодвход насыщен или превышен диапазонотключить, проверить уровни и Vref
сильный шум при старте моторапомехи питания/земли или проводовпроверить разводку, развязку и питание
диапазон слишком узкийсхема датчика или калибровкаснять минимум/максимум в реальных условиях
порог дрожитшум около одной границыдобавить фильтр или гистерезис

При измерении осциллографом или внешним прибором соединяй земли осознанно и учитывай заземление самого прибора.

Что запомнить об аналоговом входе

  • analogRead() возвращает код АЦП, а не готовую физическую величину.
  • Диапазон, разрешение и опора зависят от платы и настроек.
  • Входной уровень обязан быть электрически допустим до выполнения программы.
  • Калибровка связывает коды с реальными условиями.
  • Фильтр уменьшает шум, но добавляет задержку.
  • Гистерезис предотвращает частые переключения около порога.
  • Универсальные числа порогов без измерений ненадёжны.

Практика

Задание 1

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

Задание 2

Реализуйте усреднение для нескольких размеров выборки. Измерьте разброс и задержку, затем выберите размер для своей задачи.

Задание 3

Создайте пороговую индикацию с двумя границами. Подберите гистерезис по наблюдаемому шуму, не копируя числа из урока.

Задание 4

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

Проверьте себя

  1. Что физически измеряет АЦП?
  2. Почему код analogRead() не равен готовым люксам или сантиметрам?
  3. От чего зависит диапазон возвращаемых кодов?
  4. Почему constrain() не защищает вход от перенапряжения?
  5. Для чего нужна калибровка?
  6. Как усреднение влияет на шум и задержку?
  7. Чем гистерезис отличается от одного порога?
  8. Почему моторный шум нужно проверять в реальной сборке?
Ориентиры для самопроверки

Сначала ответьте без подсказки. Ответ можно считать полным, если вы:

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

Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.

Словарь статьи

  • АЦП (ADC) — аналого-цифровой преобразователь.
  • Код АЦП — целочисленный результат преобразования.
  • Разрешение — число бит или различимых кодов преобразования.
  • Опорное напряжение (Vref) — уровень, задающий масштаб измерения АЦП.
  • Калибровка — сопоставление кодов известным реальным условиям.
  • Шум — нежелательное изменение измеряемого сигнала.
  • Фильтрация — обработка, уменьшающая влияние шума.
  • Гистерезис — разные границы включения и выключения состояния.

Связанные темы

Источники