Стрелка уровня воды в бочке держалась на серво SG90: прикрутил её к крышке, натянул на качалку спицу и связал с поплавком через леску. Казалось бы, примитив: analogRead, map, write. Но когда я поставил рядом вторую серво с той же программой, стрелки разошлись: одна стояла на «трети бочки», вторая — заметно выше, при одинаковых показаниях датчика. Я перебрал провода, перепрошил обе платы, менял питание с USB на зарядку — расхождение никуда не делось, и вело оно себя странно: у каждой серво стрелка была стабильна в своей точке, не плыла и не дрожала, просто их «90 градусов» означали разные положения вала. Сбой был не в железе, а в моём представлении об угле. На форуме мне ответили одной строкой: «не гоняй градусы, пошли обеим по 1300 микросекунд — и сравнивай». Так я впервые понял, что write() — это не приказ «встань на 33 градуса», а пересчёт в длину импульса, и пересчёт у библиотеки свой, со своей шкалой: то, что для меня было «тем же углом», для плат двух серво означало две разные посылки. Этот урок — о том, что реально происходит на сигнальном проводе: кадры по 20 миллисекунд, импульсы, измеряемые микросекундами, и почему два write(90) на двух серво — не одна и та же команда.
Кадр 20 мс: один импульс и тишина
Начнём с ритма. Каждые 20 миллисекунд библиотека отправляет в провод один импульс: линия подскакивает к 5 В, держится заданное время и падает обратно в ноль, до следующего кадра. Серво засекает длительность высокого уровня — это и есть приказ; всё, что происходит между импульсами, для неё пустой воздух, и линией можно хоть пальцем помахать — вал не шелохнётся, пока не придёт следующий фронт. Частота 50 Гц означает пятьдесят таких кадров в секунду; на живой плате серво терпят период примерно от 16 до 25 мс, то есть 40–60 Гц, а дальше начинают дёргаться или молчать. Это вилка, а не требование: моя партия SG90 спокойно жила на кадрах 22 мс, но у края хода уже ворчала, и я вернулся к честным 20. Библиотека, к слову, не спрашивает вашего мнения о периоде — 50 Гц зашиты в неё, и свои серво она обслужит ровно в этом ритме. Сама длительность у библиотеки Servo.h лежит в вилке 544–2400 мкс, и пересчёт из градусов идёт по формуле: мкс = 544 + угол × (2400 − 544) / 180 — около 10,31 мкс на градус. Отсюда сюрприз, который ловят почти все: write(90) шлёт не «красивые» 1500 мкс, а 1472. Команды при этом идут без вашего участия: библиотека шлёт кадры в фоне, а вот движение между углами — уже ваша забота, про неё был урок про плавное движение серво. Ритм и подключение мы разбирали в уроке про первое подключение SG90 — там же про то, почему сигнальный провод обычно отдают на пин 9 и что из этого следует.
| Угол в write() | Шлёт библиотека | «Идеал» RC 1000–2000 | Разница |
|---|---|---|---|
| 0° | 544 мкс | 1000 мкс | 456 мкс |
| 22,5° | 776 мкс | 1125 мкс | 349 мкс |
| 45° | 1008 мкс | 1250 мкс | 242 мкс |
| 90° | 1472 мкс | 1500 мкс | 28 мкс |
| 135° | 1936 мкс | 1750 мкс | 186 мкс |
| 180° | 2400 мкс | 2000 мкс | 400 мкс |
Что делать с этой разницей? Пока серво одна — ничего страшного: шкала монотонная, и стрелка стабильно стоит на одном и том же месте. Проблемы начинаются, когда серво две и их показания должны совпадать: партии ходят одна от другой, и у моей второй SG90 середина съезжала на два-три градуса при том же write(90). Мёртвая зона у SG90 — порядка 10 мкс: команда на 3–5 мкс в неё тонет, серво честно игнорирует мелочь, и это спасает её от суеты — без мёртвой зоны она дёргалась бы от каждого шума на линии. По той же причине бессмысленно требовать от write() точности в четверть градуса: один градус — это 10,31 мкс, и пары микросекунд погоды не делают. Заодно закроем вечный вопрос: почему нельзя управлять серво через analogWrite(). ШИМ — это скважность, доля высокого уровня за период; серво же меряет длину одного сплошного импульса от фронта до спада. Мы разбирали эту разницу в уроке про ШИМ и яркость: диоду хватает среднего за период, серво — нет, ей нужно конкретное число микросекунд подряд. Если же вам удобно думать градусами, стройте свою шкалу поверх writeMicroseconds(): подобрали ноль и упор — и map() от угла к вашим микросекундам, тогда и две серво встанут вровень. Я делаю так через map(): подобранные ноль и упор — две точки отсчёта, дальше арифметика.
writeMicroseconds(): прямая команда без пересчёта
Мой тупик со стрелкой на бочке тянулся весь вечер: write(90) клонил влево на пару градусов, и я сначала грешил на криво надетую качалку — снимал её, надевал на другой шлиц, и каждый раз «центр» оказывался новым. Вылечилось прямой командой — writeMicroseconds(): она шлёт в провод ровно те микросекунды, что вы назвали, без шкалы 544–2400 и без пересчёта. Я двигал середину шагами по 4 мкс, и на 1508 стрелка встала по уровню. Дальше — рутина: подобрал ноль на слух, замерил упоры, записал три числа в комментарий к скетчу — у моей пары вышло 1012 / 1508 / 2036, и вторая серво после такой же калибровки встала вровень с первой. Три числа вместо одного write() — плата за совпадающие стрелки, я считаю её честной. Кстати, тот вечер я потратил зря ещё и потому, что не слушал серво: гул у середины хода — первый симптом неточной шкалы. Граница у метода честная: для заслонки, кормушки или поворотного столика хватает write() — разница в 28 мкс у середины хода глазу не видна. writeMicroseconds нужна, когда серво две и они обязаны стоять синхронно, когда механика собрана неточно или когда строите прибор со шкалой. И помните: за 0° и 180° стоит пластиковый упор, в который серво бьётся с перегрузом каждые 20 мс — цену упоров мы считали в уроке про серво-переключатель с двумя положениями.
Калибровка на слух и замер чужих импульсов
Пара практических оговорок. writeMicroseconds() тоже не всесильна: библиотека режет значения по краям до тех же 544 и 2400, так что «наказать» серво импульсом в 5000 мкс не выйдет — и хорошо. Калибровку своей серво я делаю на слух: подаю 1000 мкс, и если вал молчит у упора — оставляю; если слышен приглушённый гул — поднимаю по 20 мкс, пока гул не уйдёт. Двадцать микросекунд — это примерно два градуса, процесс занимает минуты, зато потом ноль у вас свой, а не чей-то среднестатистический. Метод грубый, но для стрелки на бочке его хватает с запасом. Захотите померить импульсы чужого скетча — поставьте вторую плату: её пин слушает уровень, и pulseIn() возвращает длину в микросекундах — общий GND между платами обязателен, а замер идёт по одному импульсу за вызов, так что снимайте по десятку и смотрите среднее. Как с такими замерами вообще обращаться, показано в уроке про точные замеры micros(). Увиденный в мониторе 1472 после write(90) убеждает сильнее любой формулы. В симуляторе ниже скетч прогоняет серво через шесть импульсов подряд — от нижней границы библиотеки до верхней; следите за циферблатом и за парами us=/deg= в мониторе. Сверьте каждую строку с таблицей выше: числа там не выдуманы, это та же формула, по которой живёт write(). Если рядом стоит вторая серво — запустите скетч на обеих и сравните deg при одном us: расхождение партий увидите прямо в мониторе.
// Сравниваем два способа на живой плате: смотрите в Serial#include <Servo.h> Servo sv; void setup() { sv.attach(11); Serial.begin(9600);} void loop() { sv.write(90); // библиотека пошлёт 1472 мкс Serial.println("write(90) = 1472 us"); delay(1500); sv.writeMicroseconds(1500); // прямая команда: ровно 1500 мкс Serial.println("writeMicroseconds(1500)"); delay(1500);}- 1. Что реально отправит в провод вызов write(90)?
- 2. Сколько импульсов в секунду шлёт библиотека Servo.h?
- 3. Чем writeMicroseconds() отличается от write()?
- 4. Что произойдёт при вызове writeMicroseconds(3000)?
- 5. Почему серво нельзя управлять через analogWrite()?
- Кадр = 20 мс = один импульс + тишина; длительность импульса и есть команда.
- write() шлёт 544 + угол × 10,31 мкс: write(90) — это 1472 мкс, а не 1500.
- Точный угол или синхронные серво — writeMicroseconds(): моя середина оказалась 1508.
- Библиотека режет импульсы до 544–2400 мкс; за 0° и 180° — пластиковые упоры.
- analogWrite не заменяет серво-сигнал: скважность и длительность импульса — разные вещи.
Частые вопросы по теме урока
- Что реально отправит в провод вызов write(90)?
- Библиотека считает по своей шкале: 544 + 90 × 10,31 ≈ 1472 мкс. Красивые 1500 мкс шлёт только writeMicroseconds(1500). Разница мала для глаза, но реальна для второй серво рядом. импульс 1472 мкс
- Сколько импульсов в секунду шлёт библиотека Servo.h?
- Кадр длится 20 мс, значит 1000 / 20 = 50 кадров в секунду — тот самый стандарт 50 Гц. Пятьдесят раз в секунду серво слушает провод: чаще незачем, реже начинает лениться. 50
- Чем writeMicroseconds() отличается от write()?
- write() пересчитывает градусы по шкале 544–2400 мкс, writeMicroseconds() отправляет названное число микросекунд как есть — ручной доступ к тому, что write() прячет за формулой. шлёт импульс ровно заданной длины, без пересчёта из градусов
- Что произойдёт при вызове writeMicroseconds(3000)?
- Библиотека режет импульсы по краям до 544–2400 мкс, и в упоры вас сознательно не пускает — лишние градусы там стоят шестерёнок. библиотека обрежет значение до 2400 мкс
- Почему серво нельзя управлять через analogWrite()?
- ШИМ задаёт долю высокого уровня за период, серво меряет длину одного импульса от фронта до спада. Это разные языки, и переводчика между ними нет. серво ждёт длительность сплошного импульса, а не скважность