AccelStepper.h — Как быстро увеличить скорость двигателя NEMA 17?

Я новичок в Arduino. Я пытаюсь построить самобалансирующегося робота. Я использую драйверы A4988, микроконтроллер ESP32, двигатели NEMA 17 и MPU 6050.

Проблема в том, что переменная скорости меняется в зависимости от положения MPU. Двигатели вращаются с медленной и постоянной скоростью, не меняющейся в зависимости от наклона робота.

Вот код:

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <AccelStepper.h>

#define STEP_IZQ 26
#define DIR_IZQ 27
#define STEP_DER 12
#define DIR_DER 13

AccelStepper stepperizq(AccelStepper::FULL2WIRE,STEP_IZQ,DIR_IZQ);
AccelStepper stepperder(AccelStepper::FULL2WIRE,STEP_DER,DIR_DER);
Adafruit_MPU6050 mpu;

float kp = 20; 
float ki = 0.5;
float kd = 30;

float angulo_grad, error,cumError,rateError,output,lastError;
float pid_error_temp, pid_i_mem, pid_setpoint, gyro_input, pid_output, pid_last_d_error;
int speed,outputAbs;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // приостановит Зеро, Леонардо и т. д., пока не откроется последовательная консоль

  Serial.println("Adafruit MPU6050 test!");

  // Попробуйте инициализировать!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_5_HZ);
 
  pinMode(DIR_IZQ, OUTPUT);
  pinMode(DIR_DER, OUTPUT);

  stepperizq.setMaxSpeed(3000);
  stepperder.setMaxSpeed(3000);
  stepperizq.setAcceleration(1000); //1000
  stepperder.setAcceleration(1000); //1000
}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  angulo_grad = atan((a.acceleration.x)/(a.acceleration.z))*(180/3.14);            //от 0 до 90 градусов
  /////////////////////////////////////////////////// /////////////////////////////////////////////////// //////////////////
  // идентификатор
  /////////////////////////////////////////////////// ////////////////////////////////////////////////

  pid_error_temp = angulo_grad;
  if((pid_error_temp > -2.0 )&&( pid_error_temp < 2.0)) pid_error_temp = 0.0;

  pid_i_mem += ki * pid_error_temp;                                 //Рассчитываем значение I-контроллера и добавляем его в переменную pid_i_mem
  if(pid_i_mem > 800)pid_i_mem = 800;                                       //Ограничиваем I-контроллер максимальным выходом контроллера
  else if(pid_i_mem < -800)pid_i_mem = -800;
  //Рассчитываем выходное значение ПИД-регулятора
  pid_output = kp * pid_error_temp + pid_i_mem + kd * (pid_error_temp - pid_last_d_error);
  if(pid_output > 5000)pid_output = 5000;                                     //Ограничиваем ПИ-регулятор максимальным выходом контроллера
  else if(pid_output < -5000)pid_output = -5000;

  if(output < 50 && output > 50)output = 0;
  pid_last_d_error = pid_error_temp;  
  /////////////////////////////////////////////////// ///////////////////////////////////////////////////
  //двигатели Нема 17
  /////////////////////////////////////////////////// /////////////////////////////////////////////////// //////////
  outputAbs = (int)abs(pid_output);
  speed = map(outputAbs,0,5000,0,3000);
  
  stepperizq.setSpeed(speed);
  stepperder.setSpeed(speed);
  
  if(pid_output<0){
    digitalWrite(DIR_IZQ,LOW);
    digitalWrite(DIR_DER,HIGH);
  }
  else if(pid_output>0){
    digitalWrite(DIR_IZQ,HIGH);
    digitalWrite(DIR_DER,LOW);
  }
  stepperizq.runSpeed();
  stepperder.runSpeed();
}

, 👍-1

Обсуждение

Это всего лишь предположение, но может ли быть так, что ваш loop() работает слишком медленно, чтобы успевать за необходимыми шагами? Если, например, показания датчика занимают слишком много времени (в диапазоне периода между двумя последовательными шагами), то при каждом выполнении runSpeed() будет обнаружено, что всегда пора сделать шаг, выполняя ровно один шаг и затем выходим к оставшейся части loop(). Это ограничит вашу скорость до одного шага на итерацию loop(), которая, вероятно, будет иметь постоянную продолжительность. Вы можете попытаться получать новые измерения только через заданные интервалы времени (например, каждые x мс)., @chrisl

Согласен с @chrisl. Попробуйте обернуть измерения и расчеты в дроссель if (millis() - последний > интервал){...}., @Dave X

Я добавил это: <код> текущийМиллис = миллис(); if(currentMillis - previousMillis >=100){ предыдущийМиллис = текущийМиллис; датчики_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); angulo_grad = atan((a.acceleration.x)/(a.acceleration.z))*(180/3.14); //от 0 до 90 градусов } </код> И это сработало. Спасибо всем за помощь, @camu


1 ответ


0

Чтобы записать свое предположение из комментариев в правильный ответ:

Когда шаговый двигатель, приводимый в действие AccelStepper, показывает постоянную скорость, хотя для скорости установлены разные значения, это предполагает, что временной интервал шагов определяется чем-то другим.

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

Если по какой-либо причине вы вызываете функцию с большими интервалами, функция runSpeed() всегда увидит, что пришло время нового шага, но всегда будет делать только один шаг за каждый исполнение. Таким образом, в общей сложности вы получаете один шаг на выполнение runSpeed(), и это определяется остальной частью вашего кода, а не установленной скоростью.

Остальная часть кода в loop() предназначена для получения измерений MPU и вычисления на их основе результирующих скоростей. Вероятно, это занимает слишком много времени и, таким образом, мешает библиотеке AccelStepper запускать двигатель на указанных скоростях. Поэтому вам необходимо предоставить больший временной интервал для шагового двигателя вашего ESP, сократив частоту дискретизации вашего MPU. Это проще всего сделать с помощью

// Запускаем выборку и расчет MPU только каждые 100 мс
if(millis() - previousMillis >= 100){
  previousMillis += 100;
  // MPU и код расчета здесь
}
// Запуск кода шагового двигателя на каждой итерации цикла
stepperizq.runSpeed();
stepperder.runSpeed();

это стиль неблокирующего кодирования, используемый в примере BlinkWithoutDelay. Если вы не знаете, как использовать millis(), в Интернете есть множество обучающих программ.

,