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

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

Потенциометр удобен как первый аналоговый датчик: у него есть питание, 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
Проведите калибровку датчика линии при безопасно остановленных моторах. Проверьте случай, когда диапазон не был показан и минимум совпал с максимумом.
Проверьте себя
- Что физически измеряет АЦП?
- Почему код
analogRead()не равен готовым люксам или сантиметрам? - От чего зависит диапазон возвращаемых кодов?
- Почему
constrain()не защищает вход от перенапряжения? - Для чего нужна калибровка?
- Как усреднение влияет на шум и задержку?
- Чем гистерезис отличается от одного порога?
- Почему моторный шум нужно проверять в реальной сборке?
Сначала ответьте без подсказки. Ответ можно считать полным, если вы:
- формулируете основную мысль своими словами;
- называете важные условия, ограничения или меры безопасности;
- для схемы, кода или расчёта показываете ход решения и ожидаемый результат.
Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.
Словарь статьи
- АЦП (ADC) — аналого-цифровой преобразователь.
- Код АЦП — целочисленный результат преобразования.
- Разрешение — число бит или различимых кодов преобразования.
- Опорное напряжение (
Vref) — уровень, задающий масштаб измерения АЦП. - Калибровка — сопоставление кодов известным реальным условиям.
- Шум — нежелательное изменение измеряемого сигнала.
- Фильтрация — обработка, уменьшающая влияние шума.
- Гистерезис — разные границы включения и выключения состояния.
Связанные темы
- Переменные — хранение измерений и диапазонов.
- Циклы — сбор серии показаний.
- Функции — отдельная функция чтения и фильтрации.
- analogWrite: плавное мигание светодиодом — преобразование измерения в PWM-команду.
Источники
- Arduino: analogRead() — чтение аналогового входа.
- Arduino: analogReadResolution() — настройка разрешения на поддерживаемых платах.
- Arduino: analogReference() — выбор опоры на поддерживаемых архитектурах.
- Arduino Built-in Examples — Analog Read Serial, Calibrate Sensor Input и Smoothing.
- Arduino UNO R3 datasheet — пример характеристик аналоговых входов конкретной платы.
- Microchip ATmega328P datasheet — характеристики АЦП микроконтроллера классической UNO R3.