← До всіх проєктів
Автоматизація Середній

NEMA17 + A4988 і AccelStepper: розгін, гальмування і чому крокові двигуни зриваються

Кроковий двигун не може одразу стартувати на повній швидкості: ротор не встигає за полем і пропускає кроки. Бібліотека AccelStepper будує трапецієподібний профіль швидкості. Налаштовуємо A4988, мікрокрок і прискорення, керуємо рухом командами з порту.

Складність
Середній
Час
1 h
Категорія
Автоматизація
Оновлено

Про проєкт

NEMA17 — стандартний кроковий двигун 3D-принтерів і ЧПК: 200 кроків на оберт (1.8°), крутний момент 0.3–0.5 Н·м. Керує ним драйвер A4988: один імпульс на STEP — один крок, рівень на DIR — напрямок. Але якщо просто почати слати імпульси з частотою 1000 Гц, двигун загуде й не зрушить: ротор з інерцією не встигає за полем, що обертається. Потрібен розгін, і бібліотека AccelStepper робить його сама.

Що знадобиться

  • ESP32 DevKit
  • Двигун NEMA17 (наприклад 17HS4401) + драйвер A4988 (або DRV8825)
  • Живлення 12 В ≥ 2 А і конденсатор 100 мкФ на VMOT
  • Бібліотека AccelStepper

Схема

A4988 Підключення
VMOT / GND +12 В / GND (конденсатор 100 мкФ біля виводів!)
VDD / GND 3V3 / GND ESP32
STEP GPIO26
DIR GPIO27
EN GPIO25 (активний LOW)
RESET ↔ SLEEP з'єднати перемичкою
1A 1B 2A 2B обмотки двигуна
MS1–MS3 мікрокрок (див. нижче)

Усі землі спільні. Ніколи не від'єднуйте двигун від увімкненого драйвера: викид напруги вбиває A4988.

Струм виставляється підстроювальним резистором ще до першого запуску: Vref = I × 8 × Rs. Для типового клону з R100 (0.1 Ом) і двигуна на 1.2 А це ≈ 0.8 × 1.2 × 0.85 ≈ 0.8 В. Беремо 85 % від номіналу, щоб драйвер не перегрівався.

Мікрокрок

Перемички MS1–MS3 ділять повний крок: 1/2, 1/4, 1/8, 1/16. На 1/16 двигун робить 3200 мікрокроків на оберт і крутиться тихіше й плавніше, але для тієї ж швидкості потрібно в 16 разів більше імпульсів.

Чому потрібен розгін

Двигун може «підхопити» лише певну частоту старту (pull-in rate, кілька сотень кроків за секунду з місця). Щоб вийти на 1000–2000 кроків/с, швидкість треба нарощувати поступово. Якщо поспішати, ротор відстане на крок, і двигун зірветься: загуде, завібрує, зупиниться, а позиція в програмі розійдеться з реальною.

AccelStepper рахує інтервал до кожного наступного кроку так, щоб швидкість росла лінійно (трапецієподібний профіль): розгін → рух на maxSpeed → гальмування до точки зупинки.

Код

// NEMA17 + A4988 з AccelStepper: розгін/гальмування, їзда туди-назад і команди з порту
#include <AccelStepper.h>

const int STEP_PIN = 26, DIR_PIN = 27, EN_PIN = 25;
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
long target = 800;       // 4 оберти при повному кроці (200 кроків на оберт)
bool shuttle = true;     // їздити туди-назад, поки не надійде команда

void setup() {
  Serial.begin(115200);
  stepper.setEnablePin(EN_PIN);
  stepper.setPinsInverted(false, false, true);   // EN у A4988 активний LOW
  stepper.enableOutputs();
  stepper.setMaxSpeed(800);        // кроків/с
  stepper.setAcceleration(1600);   // кроків/с²
  stepper.moveTo(target);
  Serial.println("Команди: go <кроки>, speed <кроки/с>, accel <кроки/с2>, stop");
}

void loop() {
  if (shuttle && stepper.distanceToGo() == 0) {   // доїхали — розвертаємося
    target = -target;
    stepper.moveTo(target);
  }
  stepper.run();   // викликати якомога частіше: крок робиться лише коли настав час

  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim();
    if (cmd.startsWith("go ")) { shuttle = false; stepper.moveTo(cmd.substring(3).toInt()); }
    else if (cmd.startsWith("speed ")) stepper.setMaxSpeed(cmd.substring(6).toFloat());
    else if (cmd.startsWith("accel ")) stepper.setAcceleration(cmd.substring(6).toFloat());
    else if (cmd == "stop") { shuttle = false; stepper.stop(); }   // плавне гальмування
    Serial.printf("позиція %ld, ціль %ld\n", stepper.currentPosition(), stepper.targetPosition());
  }
}

Правила AccelStepper

  • run() має викликатися постійно. Жодних delay() у loop(): за 100 мс затримки двигун пропустить сотні кроків. Довгі дії розбивайте на частини через millis().
  • moveTo() — абсолютна позиція, move() — відносна. stop() гальмує з заданим прискоренням, а не зупиняє миттєво.
  • runSpeed() + setSpeed() — рух зі сталою швидкістю без розгону, наприклад для конвеєра. Стартувати так можна лише на невеликій швидкості.
  • runToPosition() блокує програму до прибуття. Зручно для простих сценаріїв.
  • Той самий AccelStepper крутить і 28BYJ-48 з ULN2003: AccelStepper stepper(AccelStepper::HALF4WIRE, IN1, IN3, IN2, IN4);. Зверніть увагу на порядок виводів.

Експерименти в симуляторі

  • Надішліть accel 100000: розгін стане майже миттєвим, і симулятор покаже, що двигун зривається й губить кроки.
  • Поставте у властивостях двигуна «важке навантаження»: максимальні швидкість і прискорення зменшаться.
  • Спробуйте мікрокрок 1/16 і speed 12800, щоб побачити ту саму швидкість обертання при в 16 разів більшій кількості імпульсів.