Кормушка-дозатор проработала месяц: шторка открывалась по расписанию, скетч был скучно правильный — write(180) на выдачу порции. В один день заметил, что корпус SG90 тёплый даже в покое, а шторка ходит с новым люфтом. Снял серво, покрутил рог — зубья выходной шестерни подлизаны по кругу. За месяц не менялось ни одной строки кода, а своё слово сказало одно число: команда на 180°.
Разбор показал классику: паспортный ход SG90 — 0-180, но шкала эта написана для серво с идеальной механикой. У реальных клонов фактический ход гуляет в обе стороны на десяток градусов: один экземпляр упирается на 172, другой проходит 185 и только там упирается в собственный ограничитель. Когда скетч шлёт 180 мотору с упором на 172, серво честно доехала до 172 — а импульс идёт дальше, и мотор продолжает давить в запертые шестерни.
Ток в этой ловушке не холостой и не ходовой, а близкий к пусковому: 500-700 мА, и так каждую секунду удержания. Ниже — что именно ломается под упором, как поставить программную вилку, которая стоит ноль рублей, и почему я режу паспортную шкалу даже у новых экземпляров. А заодно ответ на самый частый вопрос в комментариях к калибровке: серво греется, когда стоит. Стоит — не значит отдыхает: стоит под упором.
Что ломается под упором
Мотор под упором — это замок по механике: ротор стоит, катушки под током, энергия идёт в тепло. При удержании в 0,5-0,7 А корпус SG90 греется до заметного рукой за пару минут; редуктор из хрупкого пластика слизывает зубья за недели постоянного нажима — сначала приходит люфт, потом холостое проворачивание. Как устроен этот редуктор и где стоит ограничитель — видно в уроке про анатомию серво, а нагрузка на рычаг, которая докручивает шестерни, считается в уроке про рычаги и качалки.
Вторая жертва — точность: у серво, пожившей под упором, разрастается мёртвая зона, и после снятия команды она отдаёт позицию с разбросом. Третья — соседняя электроника: если серво питается с шины 5 В, её бросок под упором складывается с чужими бросками — это уже территория первого урока кластера про питание.
Заметьте: снаружи ничего не видно. write() вернулся мгновенно и одинаково что на 170, что на 180; разницы между «доехала» и «давит в стену» в коде не существует. Диагностика ведётся рукой и ухом: гул на 50 Гц и тёплый корпус означают мотор под упором.
Сколько это длится по времени: ток при удержании не импульсный, а постоянный — в отличие от старта, он не гаснет сам. Кормушка в режиме «команда 180° раз в час по тридцать минут» за месяц накрутила пятнадцать часов под упором — редуктор и получил свои подлизанные зубья. Считать эти часы не обязательно, но направление понятно: не режим, а приговор.
Программная вилка: constrain и калибровка
constrain(значение, низ, верх) возвращает само значение, если оно внутри вилки, нижнюю границу — если меньше, верхнюю — если больше. Одна строка заменяет два if, а границы я держу переменными вверху скетча: правка в одном месте вместо вылавливания по всему коду. Для серво это выглядит как constrain(ugol, niz, verh) — и команда 187 молча превращается в 170.
Откуда числа 10-170. Это не магия, а страховка от разброса клонов: вилка в плюс-минус десять градусов покрывает почти любой экземпляр — даже худший доехал до границы и не коснулся упора. Цена — двадцать градусов шкалы, глазу на стрелке они безразличны. Максимум полезного хода даёт калибровка: командами 0-10-20 и 180-170-160 находите реальные границы конкретного мотора — процедура по шагам расписана в уроке про калибровку min и max.
При включённой вилке команда за границу не влечёт ничего драматичного: запрос обрезан, серво доехала и замолчала, ток упал до покоя. Единственное, что я добавляю сверху, — отчёт в Serial при каждой обрезке: частые удары о границу означают, что вилку пора двигать или механизм спроектирован криво. Методика таких отчётов — в уроке про отладку логики.
Границы для типовых механизмов я держу в комментариях скетча: заслонка — 45-135, стрелка — 10-170, шторка кормушки — 30-150. Каждому механизму — своя вилка, потому что полный ход нужен не серво, а задаче: где-то работает центральный сектор, а где-то весь ход без пяти градусов.
#include <Servo.h> Servo srv; int niz = 10; // левая граница — правится после калибровкиint verh = 170; // правая граница void setup() { srv.attach(11); Serial.begin(9600); srv.write((niz + verh) / 2); // старт из середины вилки, не из упора Serial.print("Вилка: "); Serial.print(niz); Serial.print("-"); Serial.println(verh);} void loop() { int zapros = map(analogRead(A0), 0, 1023, 0, 180); int ugol = constrain(zapros, niz, verh); srv.write(ugol); if (zapros != ugol) { // ручку выкрутили за вилку Serial.print("Запрос "); Serial.print(zapros); Serial.print(" обрезан до "); Serial.println(ugol); delay(400); // лог не сыплется } else { delay(100); }}Скетч берёт угол с ручки, пересчитывает map() в 0-180 и зажимает вилкой. Обратите внимание на старт из середины: srv.write((niz + verh) / 2) — после подачи питания рог уходит в безопасную точку, а не в край. И на задержку после обрезки: лог печатается раз в 400 мс, иначе строка в мониторе успевает смениться сто раз за секунду удержания ручки в краю.
Без ручки тот же скетч гоняется по углам из монитора или по расписанию — вилка работает одинаково, потому что constrain стоит на входе у серво, а не привязан к источнику запроса. Это и есть главная выгода: одна вилка защищает от всех команд сразу, и новых источников угла можно добавлять без новых проверок. Вторая выгода вилки, о которой вспоминают позже: constrain защищает не только от вашей ручки, но и от мусорных значений — сбойный analogRead, кривой parseInt, наводка на линии. Серво получает число в вилке, как бы ни бузило наверху.
Диагностика на живой макетке занимает минуту: мультиметр в режим амперметра в разрыв красного провода серво. Покой покажет 6-10 мА, ход — 100-250, а упор — 500-700 и стоит как вкопанный: если треть режима серво сидит на семи сотнях, вилку пора двигать сегодня. В симуляторе ток не снять — зато видно лог обрезок и то, как рог замирает на границе, когда ручка уехала дальше; на реальной плате к этому добавится гул, который слышно раньше мультиметра.
| Граница | Что при команде за границу | Цена |
|---|---|---|
| Паспортные 0-180 по шкале | Мотор давит в упор: гул, ток 500-700 мА, нагрев | Слизанные зубья и люфт за недели |
| Вилка constrain 10-170 | Запрос обрезан, серво доехала и замолчала | Минус 20° хода, глазу не заметно |
| Калиброванные границы экземпляра | Ход максимальный безопасный именно для вашей серво | Полчаса на тесты с Serial |
| Внешние механические упоры | Серво против упора бесконечно, пока идёт импульс | Гарантированный перегрев, спасает только detach() |
Мнение с границей применимости: паспортные 0-180 я режу до 10-170 у всех пластиковых SG90, даже у тех, что проходят полный ход, — запас дешевле двадцати невидимых глазу градусов. Внешние же упоры — винты, скобы, ограничители на корпусе механизма — почти всегда вред: серво против них давит бесконечно, пока идёт импульс, и нагрев тот же, что под внутренним упором. Единственный честный механический ограничитель — тот, что стоит внутри самой серво с завода. Граница у правила тоже есть: если механизм физически не доезжает до 10 или 170 — рычаг упирается в рамку раньше, — вилку сужают под реальный механизм, а не под шкалу.
Если механизм обязан жёстко останавливаться снаружи — рычаг входит в паз, шторка упирается в рамку, — прижим снимается detach(): серво отпускает ротор, ток падает до нуля, механику держит упор, а не мотор. О границах detach — когда он уместен, а когда рог начинает гулять от малейшего толчка — писали в уроке про дрожание и фильтры.
Отдельный случай — серво с непрерывным вращением: упоров у них нет вовсе, write() управляет скоростью, и вилка по углу теряет смысл. Как SG90 переделывается в непрерывный, показано в уроке про модификацию 360 — там же про управление вместо позиции.
- 1. Серва с реальным упором на 172° получила write(180). Что происходит после доезда?
- 2. Что вернёт constrain(190, 10, 170)?
- 3. Почему границы 10-170, а не паспортные 0-180?
- 4. map(analogRead(A0), 0, 1023, 0, 200) при A0 = 1023 вернёт...
- 5. Механизм должен жёстко останавливаться внешним упором. Как снять прижим мотора?
- Реальный ход клонов SG90 гуляет на десяток градусов от паспортных 0-180
- Команда за упор — это удержание с током 500-700 мА: нагрев, гул, слизанные зубья
- constrain(угол, низ, верх) — одна строка вместо двух if, границы держать переменными вверху скетча
- Вилка 10-170 — страховка от разброса; максимум хода даёт калибровка конкретного экземпляра
- Внешние упоры — вред: прижим снимает только detach(), серво отпускает ротор
Частые вопросы по теме урока
- Серва с реальным упором на 172° получила write(180). Что происходит после доезда?
- Импульс идёт, ротор заперт шестернями — мотор потребляет как на пуске и греется. Это режим удержания против стены. Мотор продолжает давить в упор: ток держится на пике, корпус греется
- Что вернёт constrain(190, 10, 170)?
- Значение выше верхней границы — constrain возвращает верхнюю границу. Ниже нижней — нижнюю, внутри — как есть. 170
- Почему границы 10-170, а не паспортные 0-180?
- Разброс механики клонов ±10° — вилка покрывает его с запасом. Полный безопасный ход даёт только калибровка конкретного экземпляра. У клонов реальный ход гуляет на десяток градусов — вилка гарантирует, что даже худший экземпляр не коснётся упора
- map(analogRead(A0), 0, 1023, 0, 200) при A0 = 1023 вернёт...
- map() линейно пересчитывает и за пределами диапазона. Вилку ставит constrain — поэтому они и идут парой, и в скетчах map идёт первым, constrain — вторым. 200 — map не обрезает, обрезает constrain
- Механизм должен жёстко останавливаться внешним упором. Как снять прижим мотора?
- detach() прекращает импульсы — ток падает до нуля, ротор свободен, упор несёт механику. Минус: серво перестаёт сопротивляться внешним силам. detach(): серво отпускает ротор, механику держит упор