Робот-платформа на двух переделанных серво поехала от меня в шкаф на первом же тесте. Я писал её как обычную: «повернись на 45 градусов» превратилось в write(135) и delay — для нормальной серво это рабочая идиома. Непрерывная серво прочитала приказ иначе: 135 — это полная скорость прочь от центра, и платформа честно молотила по комнате, пока не ткнулась в плинтус. Полчаса я правил углы в надежде, что «чуть-чуть» сработает как «чуть-чуть ехать», пока не принял главное: передо мной не серво без упора, а прибор с другим смыслом команды. Угол умер, вместо него скорость. Дальше в этом уроке — перевод с углового языка на скоростной: шкала процентов, мёртвая зона вокруг стопа, торможение командой и разгон, после которого платформа не делает рывок и не выбивает себе питание.
Этот урок — о том, как разговаривать с таким приводом на его языке. Построим функцию-обёртку со шкалой от -100 до 100, найдём мёртвую зону вокруг стопа, научимся тормозить командой и разгонять платформу ramp'ом, чтобы старты не выбивали питание. Симулятор ниже покажет ряд приказов на циферблате: в симе это углы, на живой модифицированной плате — те же числа по шкале скоростей из урока про модификацию SG90, где мы эту серво и сделали.
Шкала скоростей: приказ = стоп + скорость
Модель проста. Пусть STOP — стоп-угол вашей серво, найденный стендом: у моей 88. Тогда write(STOP) — вал стоит, write(STOP+45) — половина скорости вперёд, write(STOP-45) — половина назад, write(0) и write(180) — полные ходы в обе стороны. Скорость пропорциональна удалению приказа от стопа: это та самая линейная лестница, которую мы рисовали рядами в прошлом уроке. Удобно сразу переехать на человеческую шкалу: функция privod(s) принимает процент от -100 до 100 и пересчитывает его в приказ. В коде ниже я беру половину хода как максимум — и это осознанное мнение: полный ход оставляю для разгона по прямой, а вся навигация платформы живёт в середине шкалы, где серво управляется предсказуемее и ест меньше.
Но у шкалы есть дефект, о котором гайды молчат: мёртвая зона. Вокруг стоп-угла лежит полоса в несколько градусов, где вал молчит — у моей это 85–92, то есть приказы privod(3) и даже privod(5) не дают ничего. Причина в механике: на малом рассогласовании мотор не может сдвинуть редуктор. Поэтому «очень медленно» через непрерывную серво не делается вообще — рабочий минимум у моей серво начинается с ±10–15%. Если вал на stop ползёт сам — стоп-угол найден неточно, сдвигайте его по одному градусу, пока не замрёт.
Про шкалу честности ради: линейность у серво не образцовая. На третьем десятке процентов моя серво чуть притормаживает, на краях добавляет хода, и «пятьдесят процентов» — скорее договорённость, чем измерение. Для навигации платформы такой точности хватает, а если хочется цифр — меряйте обороты на каждой отметке стендом из прошлого урока и правьте коэффициент в privod(). Я округляю до десятков процентов: у привода из пластиковых шестерён нет и не может быть круиз-контроля, и притворяться обратным — лишний код.
#include <Servo.h> Servo srv;int STOP = 88; // свой стоп-угол из калибровки стенда void privod(int s) { // s: -100..100, 0 = стоп if (s > 100) s = 100; if (s < -100) s = -100; srv.write(STOP + s * 45 / 100); // половина хода как максимум} void setup() { srv.attach(11); Serial.begin(9600);} void loop() { privod(60); // 60% вперёд Serial.println("skorost=60"); delay(1500); privod(0); // стоп командой, не detach Serial.println("skorost=0"); delay(1000); privod(-40); // 40% назад Serial.println("skorost=-40"); delay(1500);}Разбор по строкам. Ограничение в начале функции — страховка: однажды я забыл clamp, передал 150 и уехал в write(155) вместо задуманной половины — вал рванул с полной скоростью, и платформа снова кандидировала в шкаф. Деление s * 45 / 100 — целочисленное, для процентов этого хватает: шаг получается в один-два градуса приказа. Константа STOP — единственное, что уникально для каждой серво; второй экземпляр моей партии стоял на 92, и скетч без правки заставлял платформу ползти, когда я думал, что она стоит.
Скорость в цифрах при 5 В: privod(60) у моей серво — оборот за 0,9 с, полный ход — 0,62 с. Асимметрия по сторонам доходит до 10%: вперёд 0,62, назад 0,68. Для платформы-игрушки это неважно, а для едущего по меткам робота — уже поправка: либо калибруйте обе стороны отдельно, либо ограничьтесь половиной шкалы, где разброс меньше.
Ещё одна деталь, которую я недооценил на первой платформе: серво на стопе греется. Плата продолжает слать импульсы, мотор стоит, но внутри он не выключен — он упирается в приказ, и на горячую серво мёртвая зона расползается: утром платформа стояла, к вечеру поползла. Поэтому в проектах, где привод стоит больше, чем едет, я даю ему detach() на стоянке и живу с риском свободного вала, а на платформе с уклоном ставлю чёрный резиновый хомут как механический тормоз.
Разгон, торможение и парковка
| Задача | Приём | Число с условием |
|---|---|---|
| Трогаться с места | ramp по 10% каждые 20–30 мс | до полного хода за 0,2–0,3 с, пик тока ниже залпа |
| Стоять | privod(0) — стоп-команда | тормозит за доли секунды, но на уклоне от 15° вал ползёт |
| Ехать медленно | s = ±15–20 | у моей серво зона 85–92: ниже 10–15% вал молчит |
| Повернуть по дуге | разность скоростей колёс | 60/20 закручивает по дуге, 60/60 едет прямо |
| Доехать и осадить | ramp вниз до нуля | со 100% до нуля за 0,2–0,3 с — платформа не кивает |
| Парковка надолго | механический тормоз | непрерывная серво позицию не держит совсем |
Резкий privod(100) на старте — это двойной удар: по питанию и по механике. Серво берёт с места рывком, ток подскакивает к пику, платформа дёргается и отъезжает колесами от зацепа. Лечение — ramp: разгоняю шагами по 10% каждые 20–30 мс, до полного хода доезжаю за 0,2–0,3 с. Ток на таком старте ниже пикового, редуктору легче, а Serial Plotter рисует красивую трапецию разгона вместо кирпича. Плавное движение без delay вообще — отдельный большой приём, он разобран в уроке про интерполяцию серво; там же и причина уйти на millis(), когда платформе надо одновременно ехать и моргать индикацией.
Торможение и парковка — разные вещи. privod(0) тормозит: плата продолжает слать импульсы и мотор сопротивляется вращению, вал гаснет за доли секунды. Но держать позицию непрерывная серво почти не умеет: на уклоне от 15 градусов моя платформа медленно сползала, потому что мотор без датчика положения слаб на удержании. Поэтому для стоянки на склоне я ставлю механический тормоз или возврат к обычной серво с упором — и в этом честное ограничение всей затеи. Куда подать питание, когда приводов становится два и больше, чтобы старты не резали питание платы, — тема урока про внешнее питание серво, а тайминг шагов ramp удобно вести через m[millis() вместо delay.
На двух приводах шкала раскрывается полностью: поворот платформы — это разность скоростей колёс. privod(60, левый) и privod(20, правый) закручивают робота по дуге, радиус которой зависит от разницы; при равных скоростях едет прямо, при противоположных — крутится на месте. Я храню стоп-углы обеих серво отдельными константами: их разброс — не дефект, а повод вынести калибровку в настройки. И последнее про плоттер: если гнать ramp циклично, как в симуляторе выше, ряд skorost рисует непрерывную волну — по ней удобно сверять, что разгон и торможение симметричны, без перекосов в одну сторону.
Для повторяемости записи я печатаю в Serial пары имя=значение: плоттер строит отдельный ряд на каждое имя, и трапеция разгона ложится ровно, не склеиваясь с соседними каналами. И про износ: непрерывная серво — расходник. Пластиковые шестерни SG90 на ежедневных разгонах у меня жили месяца четыре, потом появился люфт и мёртвая зона поползла вправо. Не выбрасывайте уставшую серво сразу: перекалибруйте стоп — часто она едет ещё месяц, а потом всё равно идёт под замену. Дешёвый расходник с честной изнашиваемостью лучше дорогой детали, которую жалко.
- 1. Что означает write(135) для непрерывной серво со стоп-углом 90?
- 2. Вал на write(STOP) медленно ползёт. Что это значит?
- 3. Зачем платформе разгон ramp'ом вместо резкого privod(100)?
- 4. Что сделает detach() с непрерывной серво на ходу?
- 5. Какая часть шкалы скоростей рабочая для точных манёвров?
- write() у непрерывной серво — это скорость: приказ = STOP + скорость, где STOP — свой стоп-угол каждой серво.
- Оборачивайте приказы в функцию privod(s) со шкалой -100..100 и clamp — сырые углы в коде живут недолго.
- Мёртвая зона вокруг стопа съедает первые проценты: рабочий минимум у моей серво — ±10–15%, рабочая маневровая полоса — до ±50%.
- Разгон — ramp по 10% каждые 20–30 мс: меньше пик тока, нет рывка, плоттер рисует трапецию.
- Тормоз — команда стоп; на уклоне от 15 градусов вал ползёт, парковка — только механика.
Частые вопросы по теме урока
- Что означает write(135) для непрерывной серво со стоп-углом 90?
- Угла больше нет: приказ на 45 градусов выше стопа — половина скорости в сторону больших значений. Обычная серво на том же приказе встала бы на угол. поедет вперёд на половину скорости
- Вал на write(STOP) медленно ползёт. Что это значит?
- Ползущий на стопе вал — калибровочная ошибка: настоящая мёртвая зона шире, чем ваша стоп-точка. Правка на единицу-другую градусов обычно успокаивает вал. стоп-угол найден неточно — сдвигать по градусу, пока не замрёт
- Зачем платформе разгон ramp'ом вместо резкого privod(100)?
- Ступенчатый разгон по 10% каждые 20–30 мс растягивает пик тока и бережёт редуктор. Рывок с места — главный источник сбросов питания на двухприводных платформах. меньше скачок тока при старте и нет рывка механики
- Что сделает detach() с непрерывной серво на ходу?
- detach() выключает импульсы, и привод перестаёт сопротивляться: вал свободен. Тормоз — это команда стоп-угла, а парковка на уклоне — механика. отпустит вал — он свободно крутится рукой
- Какая часть шкалы скоростей рабочая для точных манёвров?
- Мёртвая зона съедает первые проценты, а края дают пик тока и максимальный разброс. Половина шкалы — компромисс, которым я пользуюсь в навигации. примерно ±45–50% от полного хода