Управление камерой одним пальцем — самая честная причина купить джойстик: наклон ручки превращается в поворот платформы, отпустили — камера стоит. Такой привод я собрал за вечер: серво SG90 на D11, ось X джойстика на A0, скетч из первой ссылки поисковика с одной строкой map(x, 0, 1023, 0, 180). Работало — ровно до первой попытки навести камеру на окно: серво дёргалось у центра, прыгало на 3-4 градуса само, а после отпускания ручки продолжало гудеть и теплеть. При этом Serial показывал честную ось: x= менялся как надо, дрожало не чтение, а результат — первый признак, что чинить надо не датчик, а связку ось-угол.
Три проблемы, и все решаются за один вечер: дрожь в центре лечится мёртвой зоной, рывки при быстром наклоне — сглаживанием, гул и нагрев при простое — командой detach(). Ни одна не видна в учебном скетче из пяти строк, и именно поэтому они заслуживают отдельного разбора: в железе всё будет так же, только заметно тише.
К концу урока у вас скетч, который ведёт себя как приличный привод: держит угол, пока ручка в покое, ходит плавными шагами и отпускает серво, когда тишина длится дольше двух секунд. Джойстик здесь — ручка управления камерой или манипулятором; саму серво и её аппетиты вспомним по вводному уроку про SG90, а платформа из двух серво появится в конце урока.
Ось в угол: map и зона покоя
Арифметика перевода — одна строка: map(x, 0, 1023, 0, 180) сжимает показания АЦП в градусы. До упора влево — 0 градусов, до упора вправо — 180, центр — около 90. Точно около: если центр вашего модуля 505, карта даст 88 градусов, и платформа встанет на два градуса мимо. Лечится калибровкой из первого урока: подставьте свой центр серединой входного диапазона, и покой совпадёт с желаемым углом без подгонки механики. Запомните и обратную сторону map: аргументы можно дать в обратном порядке — map(x, 0, 1023, 180, 0) развернёт ось, и лево станет правом; иногда это нужно, чаще — симптом перепутанного провода.
Ту же арифметику мы делали с потенциометром в уроке про ручное управление серво — разница в характере источника: джойстик возвращается в центр сам, а ползунок остаётся там, где его поставили. Отсюда поведение привода: отпустили ручку — цель вернулась к 90 градусам, серво поедет домой. Для камеры это часто нежелательно: тогда держите цель фиксированной, пока ручка в мёртвой зоне, — и камера стоит там, куда вы её навели, сколько бы ни длился покой.
Мёртвая зона — единственная защита от дрожи. Разброс центра 500-523 плюс шум АЦП в пару единиц превращаются в качание цели на 2-4 градуса, и серво честно отрабатывает каждое: ток, гул, износ редуктора. Коридор 480-540 по оси X: чтение внутри — цель не трогаем, снаружи — пересчитываем. Ширина подбирается под ваш экземпляр, но меньше сорока единиц на дешёвых партиях я бы не ставил.
// Ось X джойстика в угол серво на D11, мёртвая зона держит покой #include <Servo.h> Servo motor; long celta = 90; void setup() { Serial.begin(9600); motor.attach(11); motor.write(celta);} void loop() { int x = analogRead(A0); if (x < 480 || x > 540) { // вне мёртвой зоны цель едет celta = map(x, 0, 1023, 0, 180); motor.write(celta); } // внутри коридора 480-540 цель не трогаем — серво стоит на месте Serial.print("x="); Serial.print(x); Serial.print(" celta="); Serial.println(celta); delay(20);}Плавность: шаги и средние
Плавность — вопрос цикла, а не библиотеки. Резкий наклон ручки даёт скачок цели с 90 сразу на 160, и серво телепортируется за 150 миллисекунд — быстрее, чем глаз успевает заметить. Лечат двумя приёмами. Первый — шаг: за один цикл серво двигается не больше чем на 1-2 градуса; при цикле 20 миллисекунд полный ход 0-180 занимает полторы-три с половиной секунды, и движение читается как управляемое. Второй — среднее: берите среднее из 5-10 чтений оси и отдавайте его в map; ручка начинает ехать с запаздыванием около десятой доли секунды, зато шум оси гасится ещё на входе. Цикл в 20 миллисекунд выбран не с потолка: серво-библиотека шлёт импульсы на частоте 50 герц, то есть раз в те же 20 миллисекунд, — шагать чаще бессмысленно, серво всё равно не услышит.
Приёмы не конкурируют, а складываются: среднее успокаивает вход, шаг — выход. Механику плавного хода мы разбирали в уроке про плавное движение и в интерполяции без delay; для ручки управления хватает простого шага, который не занимает память и не требует таймеров.
| Подход | Что видит глаз | Когда брать |
|---|---|---|
| Сырой map без зоны | дрожь у центра, рывки при наклоне | только проверить, что ось живая |
| Мёртвая зона 480-540 | покой в центре, рывки на ходу остаются | минимум для любого привода |
| Шаг 1-2 градуса за цикл 20 мс | ход 0-180 за 1,8-3,6 с, равномерно | рукоятка камеры, манипулятор |
| Среднее 5-10 чтений плюс шаг | самый мягкий ход, лёгкое запаздывание | финальная версия с вибрациями |
| Шаг 5-6 градусов за цикл | ход 0-180 за 0,7 с, заметно резвее | краны и быстрые приводы |
detach: тишина при простое
Удержание стоит тока: SG90, запертый на месте против лёгкой нагрузки, берёт порядка ста миллиампер, корпус теплеет за десятки минут, редуктор шумит. Ручке камеры это не нужно: навели кадр — пусть серво отдыхает. Логика в скетче простая: следим за millis() с последнего движения; тишина дольше двух секунд — motor.detach(). Пришло новое отклонение цели от текущего угла — attach(11) и продолжаем. Ток покоя и нагрев мы мерили в уроке про ток и нагрев — цифры там получены именно на удержании, а не на движении. Отсчёт тишины ведите от последнего реально выполненного шага, а не от последнего нажатия: ручка, отпущенная в движении, доработает до цели, и только потом начнётся честный отсчёт.
Одна деталь, которую я однажды пропустил: после detach() вал серво поддаётся рукой — платформа с камерой может съехать от ветра или неосторожного касания. Для штатива это плюс, для крана с грузом — минус: там удержание полезно, и detach применяют только на полном простое. Решение принимает задача, а не скетч; в симуляторе состояние видно в кадрах строкой sv=: единица — связано, ноль — отпущено.
Порог тишины я подгоняю под задачу: штативу с камерой хватает двух секунд, а крану, держащему груз, ставлю минуту — там detach оправдан только на обеденном перерыве. Константа одна, а поведение меняется целиком: поставьте TISHINA=5000 в симуляторе и посмотрите в кадрах sv=, как связка рвётся заметно позже.
Есть и третья ступень плавности, о которой редко пишут: скорость самого серво. SG90 без нагрузки проходит 60 градусов примерно за 0,1 секунды — около 600 градусов в секунду. Наш шаг в 2 градуса за 20 миллисекунд даёт 100 градусов в секунду: вшестеро медленнее предела, и это осознанно — ручка камеры не должна пугать кадр резкостью. Если нужен скоростной привод, поднимайте шаг до 5-6 градусов, но следите за током: разгон и торможение у упоров съедают пиковые токи, и слабый USB-кабель роняет питание платы. Мой компромисс для пан-тилта — 3 градуса за цикл: ход 0-180 занимает 1,2 секунды, и камера на кадре не дрожит.
Ось Y — готовый второй канал: та же map, тот же шаг, второй объект Servo на своём пине, и ручка превращается в пульт пан-тилт — горизонталь и вертикаль независимы. В симуляторной макетке одно серво, поэтому второй канал остаётся планом: код расширяется механически, никакой новой логики не появляется. Готовую платформу с раскладкой осей и питанием мы собрали в уроке про пан-тилт для камеры — джойстик туда встаёт вместо двух потенциометров. Только не забудьте про питание: два серво в движении — уже 400-500 миллиампер пиками, и это уже внешний БП с общей землёй, как в уроке про внешнее питание.
Дальше по стеку — кнопка. Одной осью удобно водить, но режимы (ручной ход, авто-обзор, фиксация кадра) переключаются нажатием SW под стержнем: этому посвящён следующий урок кластера со счётом нажатий, меню из трёх режимов и пиком подтверждения.
- 1. Ось показала x=930. Какой угол вернёт map(x, 0, 1023, 0, 180)?
- 2. Ручка в центре, серво дрожит. Что ставить первым делом?
- 3. Какой шаг даёт плавный ход при цикле 20 миллисекунд?
- 4. Зачем вызывать detach() при простое?
- 5. Сколько примерно берёт SG90, удерживая угол против лёгкой нагрузки?
- map(x, 0, 1023, 0, 180) переводит ось в угол; центр модуля 500-523 даёт 88-92 градуса — подставляйте свой центр, чтобы покой совпал с целью.
- Мёртвая зона 480-540 держит цель неподвижной в покое: без неё серво отрабатывает шум оси дрожью и током.
- Плавность складывается из среднего 5-10 чтений на входе и шага 1-2 градуса за цикл 20 мс на выходе.
- detach() при простое дольше 2 с снимает удержание: серво молчит и остывает; новое отклонение возвращает attach(11).
- Ось Y — второй канал пан-тилт: та же арифметика и второй Servo; режимы добавит кнопка SW — это следующий урок.
Частые вопросы по теме урока
- Ось показала x=930. Какой угол вернёт map(x, 0, 1023, 0, 180)?
- 930 умножить на 180 и поделить на 1023 — около 163 градусов. Целочисленный map отсекает дробную часть, поэтому ровно 163. 163
- Ручка в центре, серво дрожит. Что ставить первым делом?
- Дрожь у центра — это шум оси плюс разброс 500-523. Коридор 480-540 объявляет покоем всё внутри; detach лечит гул при простое, но не дрожь цели. мёртвую зону по ширине разброса центра
- Какой шаг даёт плавный ход при цикле 20 миллисекунд?
- Шаг 1-2 градуса за цикл 20 мс растягивает ход 0-180 на 1,8-3,6 секунды — движение читается как управляемое, а не как сбой. 1-2 градуса за цикл
- Зачем вызывать detach() при простое?
- Удержание угла стоит порядка ста миллиампер и греет корпус. detach() снимает импульсы: серво молчит, а угол при этом не сбрасывается. чтобы серво не гудело, не дрожало и не грелось без команды
- Сколько примерно берёт SG90, удерживая угол против лёгкой нагрузки?
- Удержание — самая прожорливая фаза SG90: около ста миллиампер против лёгкой нагрузки и заметный нагрев корпуса за полчаса. порядка 100 мА, корпус теплеет за десятки минут