Справка по библиотеке AccelStepper - Одновременное управление двигателем

Я работаю над 4-колесным роботом, приводимым в движение шаговыми двигателями. У меня есть Raspberry Pi, который посылает случайный (основанный на входном изображении) отсчет импульсов на плату Arduino последовательно, а затем шагает эту сумму. Я использую плату Arduino CNC для управления всеми 4 шаговыми двигателями, просто к вашему сведению. Моя проблема в том, что в настоящее время мои степперы не будут одновременно двигаться. Я довольно хорошо прочитал документацию по библиотеке AccelStepper и попробовал некоторые примеры кода, но безуспешно. У меня есть много кода функции/ синтаксического анализа до фактического шагового кода. Это почти в самом конце, функция под названием " yMovement’. В настоящее время значение int получено, затем yStepper перемещается, затем yStepper1 перемещается после.

Мой обновленный код теперь заставляет их двигаться одновременно, но команда done ведет себя не так, как раньше.

#include <AccelStepper.h>

const int stepperCount = 4;
AccelStepper FLStepper(AccelStepper::FULL2WIRE, 2, 5);
AccelStepper FRStepper(AccelStepper::FULL2WIRE, 3, 6);
AccelStepper BRStepper(AccelStepper::FULL2WIRE, 4, 7);
AccelStepper BLStepper(AccelStepper::FULL2WIRE, 12, 13);

bool movementComplete = false;

// defines pins numbers

//Front left wheel
const int stepX = 2;
const int dirX  = 5;

//Front right wheel
const int stepY = 3;
const int dirY  = 6;

//Back left wheel
const int stepZ = 4;
const int dirZ  = 7;

//Back right wheel
const int stepA = 12;
const int dirA  = 13;
const int enPin = 8;


char split = ':';         //this is the character that would be used for seperating the different parts of your commands
                          //the syntax for commands would be:   command:value1:value2

int listSize = 5;                                     //the amount of commands in the list
String commands[] = {"hello", "add", "sub", "YMOV", "XMOV"};     //the list of every command name


void setup()
{
  Serial.begin(115200);     //sets the data transfer rate for the serial interface
                          //9600 is good for basic testing, but should be as high
                          //as possible for both devices.
  FRStepper.setMaxSpeed(500);
  FRStepper.setAcceleration(400);
  BRStepper.setMaxSpeed(500);
  BRStepper.setAcceleration(400);
 
  FLStepper.setMaxSpeed(500);
  FLStepper.setAcceleration(400);
  BLStepper.setMaxSpeed(500);
  BLStepper.setAcceleration(400);

  pinMode(stepX, OUTPUT);
  pinMode(dirX, OUTPUT);
 
  pinMode(stepY, OUTPUT);
  pinMode(dirY, OUTPUT);
 
  pinMode(stepZ, OUTPUT);
  pinMode(dirZ, OUTPUT);

  pinMode(enPin, OUTPUT);

  digitalWrite(enPin, LOW);
  digitalWrite(dirX, HIGH);
  digitalWrite(dirY, HIGH);
  digitalWrite(dirZ, HIGH);
  digitalWrite(dirA, HIGH);


}

void loop()
{
  CommCheck(); //checks serial buffer for data commands
  runMotors();
}

void runMotors()
{

  if ((FLStepper.distanceToGo() != 0) || (FRStepper.distanceToGo() != 0) || (BLStepper.distanceToGo() != 0) || (BRStepper.distanceToGo() != 0))
  {
   
    FLStepper.run();
    //BLStepper.run();
    FRStepper.run();
    //BRStepper.run();
    movementComplete = true;
  }
  if (movementComplete == true)
  {
    CommConfirm();
    movementComplete = false;
  }
  //if ((FLStepper.distanceToGo() == 0) && (FRStepper.distanceToGo() == 0) && (BLStepper.distanceToGo() == 0) && (BRStepper.distanceToGo() == 0))
  //if ((FLStepper.distanceToGo() == 0) || (FRStepper.distanceToGo() == 0) || (BLStepper.distanceToGo() == 0) || (BRStepper.distanceToGo() == 0))
  //{
    //movementComplete = true;
  //}
}
void CommCheck()
{
  if(Serial.available())                    //checks to see if there is serial data has been received
  {
    //int len = Serial.available();           //stores the character lengh of the command that was sent
                                             //this is used for command parsing later on
                                           
    String command = Serial.readString();   //stores the command as a text string
    int len = command.length();
    //Serial.println(command);
    Serial.flush();
    //command.remove(len-2,1);                //removes characters added by the pi's serial data protocol
    //command.remove(0,2);
    //len -= 3;                               //updates the string length value for parsing routine

    int points[2] = {0, 0};                 //offset points for where we need to split the command into its individual parts
   
    for(int x = 0; x < len; x++)            //this loop will go through the entire command to find the split points based on
    {                                       //what the split variable declared at the top of the script is set to.
      //Serial.print("Char ");
      //Serial.print(x);
      //Serial.print("- ");
      //Serial.println(command[x]);
      if(command[x] == split)               //this goes through every character in the string and compares it to the split character
      {
        if(points[0] == 0)                  //if the first split point hasn't been found, set it to the current spot
        {
          points[0] = x;
        }
        else                                //if the first spot was already found, then set the second split point
        {                                   //this routine is currently only set up for a command syntax that is as follows
          points[1] = x;                    //command:datavalue1:datavalue2
        }
      }
    }
    CommParse(command, len, points[0], points[1]);      //now that we know the command, command length, and split points,
  }                                                     //we can then send that information out to a routine to split the data
}                                                       //into individual values.

void CommParse(String command, int len, int point1, int point2)
{
  //Serial.print("Command Length: ");
  //Serial.println(len);
  //Serial.print("Split 1: ");
  //Serial.println(point1);
  //Serial.print("Split 2: ");
  //Serial.println(point2);

 
  String com = command;                 //copy the full command into all 3 parts
  String val1 = command;                //this is needed for the string manipulation
  String val2 = command;                //that follow

  com.remove(point1, len - point1);     //each of these use the string remove to delete
  val1.remove(point2, len - point2);    //the parts of the command that aren't needed
  val1.remove(0, point1 + 1);           //basically splitting the command up into its
  val2.remove(0, point2 + 1);           //individual pieces
  val2.remove(val2.length()-1,1);

  CommLookup(com, val1, val2);    //these pieces are then sent to a lookup routine for processing
}


void CommLookup(String com, String val1, String val2)
{
 
  int offset = 255;                   //create a variable for our lookup table's offest value
                                      //we set this to 255 because there won't be 255 total commands
                                      //and a valid command can be offset 0, so it's just to avoid
                                      //any possible coding conflicts if the command sent doesn't
                                      //match anything.
                                     
  for(int x = 0; x < listSize; x++)   //this goes through the list of commands and compares
  {                                   //them against the command received
    if(commands[x] == com)
    {
      offset = x;                     //if the command matches one in the table, store that command's offset
    }
  }
 
  switch(offset)                //this code compares the offset value and triggers the appropriate command
  {
    case 0:                                 //essentially a hello world.                       |  Syntax: hello:null:null
      CommHello();                          //this activates the hello world subroutine        |  returns Hello!
      break;
    case 1:                                 //adds both values together and return the sum.    |  Syntax: add:value1:value2
      CommAdd(val1.toInt(), val2.toInt());  //this activates the addition subroutine           |  returns value1 + value2
      break;
    case 2:                                 //subtracts both values and return the difference  |  Syntax: subtract:value1:value2
      CommSub(val1.toInt(), val2.toInt());  //this activates the subtraction subroutine        |  returns value1 - value2
      break;
    case 3:
      yMovement(val1.toInt(), val2.toInt());
      break;
    case 4:
      xMovement(val1.toInt(), val2.toInt());
    default:                                        //this is the default case for the command lookup and will only
      Serial.println("Command not recognized");     //trigger if the command sent was not known by the arduino
      break;
  }
}


void CommHello()                               //each of these routines are what will be triggered when they are successfully processed
{
  Serial.println("Hello!");
  CommConfirm();
}

void CommAdd(int val1, int val2)
{
  Serial.println(val1 + val2);
  CommConfirm();
}

void CommSub(int val1, int val2)
{
  Serial.println(val1 - val2);
  CommConfirm();
}

void yMovement(int val1, int val2)
{
  if (val1 < 0) {
    int yMoveNew = (val1 * (-4));
    //Serial.println(val1 * (-1));
    //delay(500);
   
    FRStepper.moveTo(-yMoveNew);
   
    BRStepper.moveTo(-yMoveNew);
   
    FLStepper.moveTo(-yMoveNew);
   
    BLStepper.moveTo(-yMoveNew);

    //delayMicroseconds(500);
  }

  else {

    int yMoveNew = (val1 * 4);
    //Serial.println(val1);
    //delay(500);
    FRStepper.moveTo(yMoveNew);
   
    BRStepper.moveTo(yMoveNew);
   
    FLStepper.moveTo(yMoveNew);
   
    BLStepper.moveTo(yMoveNew);

    //delayMicroseconds(500);
   
  }
}

void xMovement(int val1, int val2)
{
  if (val1 < 0) {
    int xMoveNew = (val1 * (-4));
    //Serial.println(val1 * (-1));
    //delay(1000);
    FLStepper.moveTo(-xMoveNew);
   
    BLStepper.moveTo(-xMoveNew);

    FRStepper.moveTo(-xMoveNew);

    BRStepper.moveTo(-xMoveNew);

    //delayMicroseconds(500);
  }

  else {

    int xMoveNew = (val1 * 4);
    //Serial.println(val1);
    //delay(1000);
    FLStepper.moveTo(xMoveNew);

    BLStepper.moveTo(xMoveNew);

    FRStepper.moveTo(xMoveNew);

    BRStepper.moveTo(xMoveNew);

    //delayMicroseconds(500);
   
  }
}
void CommConfirm()                                  
{                                                    
  Serial.println("Done");
 
}


, 👍1


1 ответ


Лучший ответ:

1

С помощью функции AccelStepper.move() вы устанавливаете целевое положение двигателя. Затем вы запускаете двигатель с помощью функции AccelStepper.runToPosition(). Но это блокирующая функция. Он не выйдет, пока двигатель не достигнет своего целевого положения. Таким образом, вы просто выбрали неправильную функцию из библиотеки AccelStepper.


Небольшая примыкание о одновременном действии на микроконтроллеры: Так как большинство микроконтроллеров, как и в Arduino, имеют только одно ядро, они могут делать только одну вещь одновременно. Но вы все равно можете выполнять вещи одну за другой, но так быстро, что человек сказал бы, что они происходят одновременно. Для этого нужно разделить действия на небольшие куски, которые выполняются очень быстро. В вашем случае у нас есть большие действия по запуску нескольких двигателей к их целевым позициям. Каждое из этих больших действий занимает много времени, но мы можем разделить их. И с шаговыми двигателями это действительно легко, потому что один шаг будет одним маленьким кусочком этого действия.

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


Библиотека AccelStepper поможет вам сделать это, проверив, пришло ли время сделать шаг. Вот что происходит в функции AccelStepper.run (). Вам просто нужно вызывать его регулярно, достаточно быстро, чтобы не упустить время для шага.

Таким образом, решение состоит в том, чтобы удалить все вызовы функции AccelStepper.runToPosition() и вместо этого вызвать функцию AccelStepper.run() для всех ваших шаговых двигателей непосредственно в функции loop ().

,

Спасибо за помощь! Проблема сейчас в том, что моя функция подтверждения() не отправляет сообщение в нужное время. Я попытался добавить его в цикл с помощью функции run (), но теперь понимаю, почему это так не работает, потому что технически функция run() выполняется на каждом шаге. Поэтому команда "готово" отправляется сразу же после запуска двигателей., @PlasticBlaze

У вас есть "if (movementComplete == true)" для выполнения commConfirm() с переменной, установленной в инструкции if выше. Сделайте это предложением else if в приведенном выше операторе if (но проверяйте на ложь вместо истины) и установите " movementComplete = true; в этом предложении if else. В противном случае эта часть будет выполняться только тогда, когда расстояния равны нулю, и выполняется только один раз. Поэтому что-то вроде " еще, если(movementComplete == false){ commConfirm(); movementComplete = true;}. Также не забудьте сбросить значение "movementComplete" на значение false, когда вы начинаете другое движение по последовательному каналу., @chrisl

Спасибо за помощь! Вскоре после того, как я это понял!, @PlasticBlaze