Простая система управления сервоприводом уровнем освещенности | Arduino
В этом уроке вы разберёте проект для Arduino UNO: фоторезистор (LDR) управляет углом сервопривода. Это статья для начинающего: мы подробно объясним не только переменные (raw, flt, alpha), но и методы/функции, которые часто выглядят “магией”: analogRead(), map(), constrain(), Servo.attach(), Servo.write(), Serial.print(), abs(), delay(). В конце — практика с решениями и чек‑лист самопроверки.
Главная мысль: Arduino измеряет не “свет”, а напряжение на A0, превращая его в число АЦП 0…1023. Мы переводим это число в угол 0…180 через map() и отправляем на сервопривод через Servo.write(angle). Для устойчивости добавляем фильтр и порог обновления угла.
Содержание
- 1. Цели урока
- 2. Контекст: как работает “свет → угол”
- 3. Переменные проекта: что означает raw, flt и другие
- 4. analogRead(): как Arduino читает LDR (raw)
- 5. map() и constrain(): как получить угол (angle)
- 6. Servo + стабильность: attach(), write(), фильтр, abs(), delay()
- 7. Практика: задачи (с решениями)
- 8. Чек‑лист самопроверки
1. Цели урока
- Понять, что такое
rawи почему он в диапазоне0…1023. - Разобраться, как работает
analogRead()(АЦП) и от чего зависит результат. - Понять, как работает
map()и почему после него часто нуженconstrain(). - Научиться управлять сервоприводом через
Servo.attach()иServo.write(). - Сделать управление спокойным: фильтр
flt, коэффициентalpha, порог по углу. - Уметь читать отладочный вывод в Serial.
raw → flt → angle → servo и понимаете назначение каждой функции.2. Контекст: как работает “свет → угол”
LDR — это резистор, который меняет сопротивление от освещённости. Arduino не умеет измерять сопротивление напрямую, поэтому LDR подключают через делитель напряжения: два резистора последовательно между +5V и GND, а середина — на вход A0.
Дальше программа делает простую цепочку:
Частая путаница
raw — это не “люксы” и не “проценты света”. Это просто число АЦП, которое зависит от вашей схемы делителя, питания и конкретного LDR.3. Переменные проекта: что означает raw, flt и другие
Словарь переменных
| Имя | Тип | Что это | Диапазон |
|---|---|---|---|
PIN_LDR |
const byte |
Пин, куда подключена середина делителя с LDR | A0 |
PIN_SERVO |
const byte |
Пин управляющего сигнала сервопривода | например 9 |
raw |
int |
Raw (сырое) значение с АЦП прямо сейчас | 0…1023 |
filtered |
float |
Состояние фильтра (плавно “догоняет” raw) | примерно 0…1023 |
flt |
int |
Filtered (сглаженное) значение, округлённое до целого | 0…1023 |
alpha |
const float |
Коэффициент фильтра: скорость реакции | обычно 0.05…0.2 |
angle |
int |
Целевой угол сервопривода | 0…180 |
lastAngle |
int |
Последний угол, который реально отправили на серву | 0…180 (старт: “маркер”) |
raw — “как датчик измерился сейчас”, flt — “то же, но сглаженное”, angle — “команда серве”.Как читать Serial‑лог
Мы печатаем три ключевых числа в одной строке:
rawможет прыгать быстрее (шум, мгновенное изменение освещения).fltобычно меняется плавнее (это цель фильтра).angle— уже готовая команда сервоприводу.
Типичные ошибки
Ошибка: ожидать “проценты” вместо 0…1023
raw — это число АЦП, а не процент.
Если хотите проценты, сделайте отдельную переменную: percent = map(raw, 0, 1023, 0, 100).
Ошибка: считать, что flt всегда должен отличаться от raw
Если освещённость стабильная, фильтр “догонит” raw, и числа станут почти одинаковыми.
4. analogRead(): как Arduino читает LDR (raw)
Что возвращает analogRead()
analogRead(A0) измеряет напряжение на входе A0 и возвращает целое число: 0 примерно соответствует 0V, а 1023 — примерно опорному напряжению (обычно около 5V для UNO).
raw может уменьшаться. Это не ошибка — это просто направление зависимости.Мини‑пример чтения A0
Типичные ошибки
Ошибка: raw всегда 0 или всегда 1023
Обычно это означает, что A0 фактически сидит на GND (0) или на +5V (1023), а не в середине делителя.
Ошибка: печатают в Serial, но забыли Serial.begin()
Serial нужно включить в setup(): Serial.begin(9600).
5. map() и constrain(): как получить угол (angle)
Как работает map() (с формулой)
Функция map(x, inMin, inMax, outMin, outMax) делает линейное преобразование диапазона. То есть она “растягивает/сжимает” число x из одного диапазона в другой.
Идея такая: если x находится посередине входного диапазона, результат будет посередине выходного диапазона.
y = (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMinВ Arduino
map() работает с целыми числами, поэтому результат округляется вниз (из‑за целочисленного деления).Зачем нужен constrain()
Важный момент: map() не “зажимает” (не ограничивает) значения. Если x случайно станет меньше inMin или больше inMax, результат может выйти за пределы outMin…outMax.
Поэтому после map() часто добавляют constrain(value, min, max) — это “зажим” значения в диапазон.
Как сделать инверсию зависимости
Если вам нужно “чем светлее, тем меньше угол” (или наоборот), проще всего инвертировать значение АЦП:
Типичные ошибки
Ошибка: перепутать диапазоны в map()
Нужно: вход 0..1023, выход 0..180.
Ошибка: не использовать constrain()
Иногда из‑за шумов/неожиданных значений угол может стать меньше 0 или больше 180 — constrain это предотвращает.
6. Servo + стабильность: attach(), write(), фильтр, abs(), delay()
Servo.attach() и Servo.write()
Библиотека Servo.h управляет сервой через объект Servo:
sv.attach(pin)— подключить серву к конкретному пину (настроить управление).sv.write(angle)— отправить команду угла (обычно 0…180).
Что такое flt (фильтр) и alpha
Даже если освещённость меняется идеально, raw может немного “шуметь”. Чтобы серва не получала множество мелких команд, мы используем сглаживание (экспоненциальный фильтр).
alpha— “скорость реакции”. Пример:0.12означает, что за один шаг фильтр делает примерно 12% “приближения к требуемому значению”.filtered— float, чтобы фильтр был плавным.flt— целое число (округление), с ним удобно делатьmap()и печатать в Serial.
Зачем нужны abs() и delay()
abs(x) возвращает модуль числа (абсолютное значение). Мы используем это для “мертвой зоны”: не обновлять серву, если угол изменился слишком мало.
delay(ms) останавливает программу на указанное число миллисекунд. В этом проекте мы используем delay(100), чтобы печатать в Serial не слишком часто (так лог проще читать).
delay() “замораживает” выполнение. Для учебного проекта это нормально. В более сложных проектах часто переходят на таймер через millis(), чтобы не блокировать цикл.Типичные ошибки
Ошибка: отправлять на серву raw вместо angle
raw = 0..1023, а серва ждёт угол 0..180.
Ошибка: порог обновления есть, но lastAngle не обновляется
Нужно обязательно: lastAngle = angle; после sv.write(angle).
7. Практика: задачи (с решениями)
Блок 1: raw и Serial
Задача 1: вывести raw в Serial
Считать A0 и печатать raw раз в 200 мс.
Блок 2: map → angle
Задача 2: вывести angle, рассчитанный через map()
Посчитать угол из raw и печатать обе величины.
Задача 3: сделать инверсию зависимости
Добавить raw = 1023 - raw и посмотреть, как меняется angle.
Блок 3: фильтр и “мертвая зона”
Задача 4: собрать полный скетч (raw → flt → angle → servo)
Реализуйте сглаживание, порог обновления угла и Serial‑лог. Обратите внимание на комментарии — это и есть “документация коду”.
8. Чек‑лист самопроверки
Отметьте пункты, которые вы действительно понимаете и можете объяснить без подсказок.
| ✓ | Навык | Проверка |
|---|---|---|
| Понимаю raw | Могу объяснить, что raw — это результат analogRead() (0…1023) |
|
| Понимаю analogRead() | Могу объяснить, что функция измеряет напряжение на A0 и возвращает число | |
| Понимаю map() | Могу объяснить идею и формулу линейного преобразования диапазонов | |
| Понимаю constrain() | Могу объяснить, что функция ограничивает значение заданным диапазоном | |
| Понимаю Servo.attach()/write() | Могу подключить серву к пину и отправить угол 0…180 | |
| Понимаю filtered/flt и alpha | Могу объяснить, что такое сглаживание и как alpha влияет на реакцию |
|
| Понимаю abs() | Могу объяснить, как модуль помогает сделать “мертвую зону” по углу | |
| Понимаю delay() | Могу объяснить, что delay “останавливает” выполнение на N миллисекунд | |
| Понимаю Serial.begin/print/println | Могу включить Serial и вывести лог в удобном формате raw | flt | angle |
|
| Могу собрать полный скетч | Могу написать код: raw → flt → angle → servo и объяснить каждую строку |
