Каретка плоттера на NEMA17 с места ревела и стояла: скетч подавал шаги с интервалом 800 микросекунд — 1250 шагов в секунду — и ротор просто не успевал за полем. Срыв шагов на старте выглядит всегда одинаково: мотор гудит, вал дрожит или стоит, счётчик шагов отсчитывает несуществующее движение. Снизишь скорость — на старте едет, но весь маршрут ползёт как больной, а головка на 333 шагах в секунду чертит квадрат полчаса. Классический тупик новичка: либо медленно всегда, либо быстро никогда.
Выход — трапеция: стартуем с интервала, где момент в запасе, и укорачиваем его по ходу разгона; перед целью зеркально растягиваем, чтобы ротор успел затормозить. Библиотека AccelStepper умеет это из коробки — там про неё отдельный урок — но механизм стоит увидеть руками: он помещается в двадцать строк и живёт на micros(), не блокируя ничего. Ниже — скетч для пары STEP/DIR драйвера A4988: разгон, полёт, торможение, и мотор, который наконец едет быстро.
Интервал шага: переменная вместо константы
Скорость шаговика без библиотек — это интервал между фронтами на пине STEP: 3000 микросекунд — 333 шага в секунду, 800 — уже 1250. Разгон — не «включить скорость», а честно меняемая переменная: каждый шаг interval уменьшается на 25 мкс, и через 88 шагов мотор выходит на максимум. Почему старт с 3000: на низкой частоте ротор успевает следовать за полем даже с грузом, момент в запасе; к 800 мкс инерция уже раскручена, и она несёт вал через зону, где момент уже слаб. Сорвать шаги можно и на разгоне — если укорачивать интервал быстрее, чем ротор набирает обороты: 25 мкс за шаг — эмпирика, проверенная на каретке в триста грамм, для тяжёлого стола режьте до 10-15.
Торможение — та же арифметика, запущенная в обратную сторону, и стартует оно не «на глаз», а по остатку пути: сколько шагов ушло на разгон, столько и нужно на остановку. Если тормозить позже — проскочим цель; раньше — потеряем скорость зря. Никаких delay() и никаких циклов ожидания: loop() опрашивает micros() и между шагами успевает делать всё остальное — тот же принцип, что в уроке про millis-планировщик шагов.
const int PIN_STEP = 4;const int PIN_DIR = 5; long target = 2000; // цель, шагов от нуляlong pos = 0; // текущая позицияint dirSign = 1; // 1 вперёд, -1 назад long interval = 3000; // мкс между шагами: старт медленныйconst long INTERVAL_MIN = 800; // потолок скорости: 1250 шагов/сconst long INTERVAL_START = 3000;const long ACCEL = 25; // мкс за шаг: крутить под вес каретки unsigned long lastStep = 0; void setup() { pinMode(PIN_STEP, OUTPUT); pinMode(PIN_DIR, OUTPUT); digitalWrite(PIN_DIR, HIGH); Serial.begin(9600); Serial.println("трапеция: 2000 шагов с разгоном");} void loop() { if (pos == target) return; if (micros() - lastStep < (unsigned long)interval) return; lastStep = micros(); long left = target - pos; // сколько до цели if (left <= (INTERVAL_START - interval) / ACCEL + 1) { if (interval < INTERVAL_START) interval += ACCEL; // тормозим } else { if (interval > INTERVAL_MIN) interval -= ACCEL; // разгоняемся } digitalWrite(PIN_STEP, HIGH); digitalWrite(PIN_STEP, LOW); pos += dirSign; if (pos % 200 == 0) { Serial.print("pos="); Serial.print(pos); Serial.print(" interval="); Serial.println(interval); }}Вся физика — в двух строках с interval. Строка тормоза сравнивает остаток пути с числом шагов, уже потраченных на разгон: (3000 − 800) / 25 + 1 = 89 шагов. Пока до цели больше 89 — разгоняемся, дальше — тянем интервал обратно к 3000, и на последнем шаге мотор приходит в цель уже практически стоя. Отсюда же берётся и честная остановка: pos == target при растянутом интервале — ротор никуда не проскочил.
Два digitalWrite подряд дают фронт на STEP длиной в несколько микросекунд — A4988 требуется от одной, запас есть даже на 16 МГц. Направление задаётся один раз уровнем на DIR, поэтому мотор обязан останавливаться для разворота: реверс с полного хода без торможения — верный срыв шагов, про цену резких разворотов у нас есть урок про рывок. Если захотите готовое с косинусными профилями и плавными стопами — AccelStepper ждёт, но менять ACCEL под вес железа вы теперь умеете и без неё.
| Интервал | Скорость | Что по моменту |
|---|---|---|
| 3000 мкс | 333 шага/с | максимальный запас: старт с грузом и трогание с места |
| 1500 мкс | 667 шагов/с | рабочий разгон: момент ещё уверенный на каретке до 500 г |
| 800 мкс | 1250 шагов/с | потолок: годится только с разгона, с места сорвёт шаги |
| 500 мкс | 2000 шагов/с | за пределами честного: индуктивность обмоток режет ток |
- 1. Мотор гудит и не крутится с места. Что крутить первым?
- 2. Сколько шагов займёт разгон от 3000 до 800 мкс при ACCEL = 25?
- Скорость без библиотек — интервал между фронтами STEP: 3000 мкс = 333 шага/с, 800 мкс = 1250
- Разгон — переменная interval, укорачиваемая на ACCEL за шаг; торможение зеркально по остатку пути
- Число шагов разгона = (старт − минимум) / ACCEL: столько же идёт тормоз, цель достигается на стоячем моторе
- Всё на micros() без delay(): между шагами loop() остаётся живым для концевиков, Serial и второй оси
Частые вопросы по теме урока
- Мотор гудит и не крутится с места. Что крутить первым?
- Стартовые 800 мкс — это 1250 шагов/с: момент на такой частоте без инерции не вытянет. Стартуйте с 3000-5000 мкс и разгоняйте интервал по шагам. Слишком малый интервал старта: ротор не успевает за полем
- Сколько шагов займёт разгон от 3000 до 800 мкс при ACCEL = 25?
- (3000 − 800) / 25 = 88 плюс один шаг на границе — 89 шагов. Столько же нужно и на торможение, поэтому строка тормоза сравнивает остаток пути именно с этим числом. 89