Главная страница \ Статьи \ Arduino \ Управление шаговым двигателем NEMA 17

Управление шаговым двигателем NEMA 17

Интересно, что по запросу "arduino шаговый двигатель" ищется масса статей, большей частью однотипных. Не спорю, данные статьи помогли и мне, но большей частью они, к сожалению, написаны в стиле "рисование совы" (кто не в курсе: рисуем две окружности, далее рисуем оставшуюся часть совы).

В настоящей статье речь будет идти о работе с шаговым двигателем NEMA 17 с помощью драйвера A4988. Подключение такое же, как и во всех статьях для начинающих.

Какие проблемы я встретил в процессе подключения шагового двигателя?

Их две, практически как базовые статьи о подключении двигателя. Первая - "вибрация" при вращении шагового двигателя, которая решается с помощью библиотеки accelStepper. Второй часто встречающийся пример лишён указанного недостатка, но понимания не добавляет. Ну и вторая - как раскрутить шаговый двигатель до сверхсветовых скоростей собственными руками?

В начале о первой проблеме.

О "вибрации" и accelStepper

Вибрации нет. Как Вы уже должно быть знаете, главное отличие шагового двигателя - это пошаговое вращение. Так вот, вышеописанная "вибрация" есть ни что иное, как обычное нормальное шаговое движение двигателя.

Если теперь вспомнить школьный курс физики, а именно ту его часть, где говорится о разного рода ускорениях, сохранении энергии, моменте инерции и резонансе, в конце концов - становится понятна причина "вибрации". Ротор имеет некоторую массу, которую мы заставляем каждый "шаг" менять положение. Соответственно при изменении положения ротор во-первых получает ускорение, накапливая энергию, а затем пытается сохранить переданный импульс, а мы его тормозим в положении "шага". Если неудачно подобрать время между шагами, вся наша система будет сильно "вибрировать" и даже может войти в какой-либо резонанс.

Отличие между "вибрирующим" и нормальным кодом:

    digitalWrite(stepPin,HIGH);
    delay(10);
    digitalWrite(stepPin,LOW);
    delay(10);
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(500);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(500);

Всё различие в длине управляющих импульсов. "Вибрирующий" код "шагает" каждые 10 миллисекунд (1с = 1000мс), нормальный - каждые 500 микросекунд (1мс = 1000мкс). Т.е. "невибрирующий" код просто шагает чаще и ротор сохраняет свою энергию до следующего шага, а не теряет на остановке.

Собственно вот и всё исправление "вибрации" без применения сторонних библиотек.

Количество микросекунд между импульсами может варьировать от двигателя к двигателя. На имеющихся у меня двигателях NEMA 17 значение меньше 450мкс приводит к нестабильности и пропуску шагов. Но, возможно, просто напросто не хватает мощности блока питания (хотя вряд ли, блока питания с избытком).

Раскручиваем наш вертолёт

В интернете я нашёл как статьи так и видео, свидетельствующие о том, что шаговый двигатель может вращаться достаточно бодро. Тем не менее я столкнулся с ограничением в 500мкс, чаще которого подавать импульсы не получалось. Точнее получалось, но двигатель лишь жужжал и не вращался.

На одном из сайтов я натолкнулся в комментариях на обсуждение ускорения/замедления шагового двигателя (к сожалению, ссылка не сохранилась, т.к. вспомнил об этой информаци сильно позже). Реплика натолкнула на мысли об ускоренном раскручивании шагового двигателя.

Код получился элементарный:

const unsigned int initialDelay = 500;

void loop()
{
  unsigned int delayTime = initialDelay;
  for(int i = 0; i<2000; ++i)
  {
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(delayTime);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(delayTime);
    if(delayTime>250) delayTime--;
  }
  delay(1000);
}

Данный код с каждым шагом уменьшает время между двумя соседними импульсами. После этого двигатель закрутился заметно быстрее. При этом уменьшение порога (250 мкс) во-первых, приводит к уменьшению момента, а во-вторых при некотором минимальном значении двигатель вначале ускоряется, затем останавливается, момента становится не хватать для вращения.

Но появилась другая проблема, это видно из кода, после 2000 шагов двигатель останавливается. Только останавливается он не в той точке, в которой необходимо. Ротор проскакивает несколько шагов. Другими словами, чтобы шаговый двигатель можно было раскрутить и потом остановить в нужном положении, нужно раскручивать и замедлять его с ускорением.

Итог работы

В результате появилась примитивная реализация функции для раскручивания шагового двигателя "на максимум". Начальное и минимальное время между импульсами можно подобрать самостоятельно.

/*  Stepper Motor Control with acceleration Example Code
 *      
 *  by Landgraph, www.Landgraph.ru
 */

const int stepPin = 3; 
const int dirPin = 4; 
// Initial delay time in microseconds
const unsigned int sleepTimeInit = 500;
// Minimal delay time in microseconds
const unsigned int minSleepTime = 150;
 
void setup()
{
  // Sets the two pins as Outputs
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
}

void RotateBySteps(unsigned int steps, bool CCW=false)
{
  unsigned int delayTime = sleepTimeInit;
  unsigned int accelerationDistance = sleepTimeInit - minSleepTime;
  unsigned int accelerationSteps = accelerationDistance,
               decelerationSteps = accelerationDistance,
               workSteps = 0;
  unsigned int i = 0;

  if(steps > (accelerationSteps + decelerationSteps))
  {
    workSteps = steps - accelerationSteps - decelerationSteps;
  }
  else
  {
    accelerationSteps = steps/2;
    decelerationSteps = steps/2;
  }

  digitalWrite(dirPin, CCW? HIGH:LOW);

  for(i = 0; i<accelerationSteps; ++i)
  {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(delayTime); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(delayTime);
    
    delayTime--; 
  }
  
  for(i = 0; i<workSteps; ++i)
  {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(delayTime); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(delayTime);
  }
  
  for(i = 0; i<decelerationSteps; ++i)
  {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(delayTime); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(delayTime);
    
    delayTime++; 
  }
}

void loop()
{
  RotateBySteps(200);
  delay(1000);
  RotateBySteps(400, true);
  delay(1000);
  RotateBySteps(2000);
  delay(1000);
  RotateBySteps(4000, true);
  delay(1000);
}

Вопросы? Предложения?

Powered by Elise