Отрицательное ускорение или проблема с AccelStepper

Morn91
Offline
Зарегистрирован: 18.10.2014

Как можно реализовать равнозамедленное движение шаговика? То есть мгновенный старт с равномерным замедлением до полной остановки.

 

Сейчас использую для этого AccelStepper, который не понимает отрицательного значения ускорения. Приходится делать через runSpeed() и цикл с setSpeed(). Всё бы хорошо, но из-за цикла runSpeed() вызывается недостаточно часто и скорость получается ниже, чем нужно. Значительно ниже, особенно при большой скорости.

Хотя это странно, т.к. описанная выше проблема наблюдается даже когда время между шагами больше времени, потраченного на цикл. Например цикл проходит за 200 мкс, но при скорости один шаг в 400 мкс мотор крутится примерно в два раза медленнее нужного.

Вот мой текущий код для DRV8834. Но его лучше не смотреть, там черт ногу сломит.

#include <AccelStepper.h>

#define STEP 5
#define DIR 6
#define M0 7
#define M1 8
#define BATTERY 3
#define MOTOR_MAX 1200
#define DRIVER_MAX 10000

AccelStepper stepper(AccelStepper::DRIVER, STEP, DIR);

float path, vRatio, motorSpeed = 0, elapsed = 0;
int accelType = 0, dir = 0, micRes[2] = {0, 1};
unsigned long time = 0, start = 0;

void setup() {
  path = 920 / 2.5 / 16 * 200;
  //path (steps) = path (mm) / timing belt pitch (mm) / teeths on pulley * steps per turnover;
  vRatio = 0.9821;
  Serial.begin(9600);
  stepper.setMaxSpeed(MOTOR_MAX);
  stepper.setSpeed(0);
}

void loop() {
  stepper.runSpeed();
  if (start)
    work();
  else
    tuning();
}

void tuning() {
  if(Serial.available() > 0) {
    char val = Serial.read();
    switch(val) {
    case '*':
      report();
      break;
    case '+':
      delay(100);
      char buffer[20];
      int i = 0;
      while(Serial.available() && i < 19)
        buffer[i++] = Serial.read();
      buffer[i++] = '\0';
      sscanf(buffer, "%d %d %d", &dir, &accelType, &time);
      if((dir == 0 || dir == 1) && ((accelType == 0 && ceil(path / time) <= MOTOR_MAX) || (accelType >= 1 && accelType <= 4 && ceil(path / time * 2) <= MOTOR_MAX))) {
        digitalWrite(13, HIGH);
        work();
      }
      break;
    }
  }
}

void work() {
  if(Serial.available() > 0) {
    char val = Serial.read();
    if(val == '-') {
      stop();
      return;
    }
    if(val == '*')
      report();
  }
  if(start == 0) {
    start = millis();
  }
  elapsed = (millis() - start) / 1000.0;
  if(elapsed < time) {
    switch(accelType) {
    case 0:
      motorSpeed = path / time;
      break;
    case 1:
      motorSpeed = 2.0 * path * elapsed / time / time;
      break;
    case 2:
      motorSpeed = 2.0 * path * (time - elapsed) / time / time;
      break;
    case 3:
      if(elapsed * 2.0 < time)
        motorSpeed = 4.0 * path * elapsed / time / time;
      else
        motorSpeed = 4.0 * path * (time - elapsed) / time / time;
      break;
    case 4:
      if(elapsed * 2.0 < time)
        motorSpeed = 4.0 * path * (time / 2.0 - elapsed) / time / time;
      else
        motorSpeed = 4.0 * path * (elapsed - time / 2.0) / time / time;
      break;
    default:
      stop();
      return;
    }
    while(motorSpeed * micRes[1] * 2 <= DRIVER_MAX && micRes[1] < 32)
      micRes[1] *= 2;
    while(motorSpeed * micRes[1] > DRIVER_MAX && micRes[1] > 1)
      micRes[1] *= 0.5;
    if(micRes[0] != micRes[1])
      microstep();
    if(dir)
      stepper.setSpeed(-motorSpeed * micRes[1]);
    else
      stepper.setSpeed(motorSpeed * micRes[1]);
  }
  else
    stop();
}

void microstep() {
  switch(micRes[1]) {
  case 1:
    pinMode(M0, OUTPUT);
    digitalWrite(M0, LOW);
    digitalWrite(M1, LOW);
    break;
  case 2:
    pinMode(M0, OUTPUT);
    digitalWrite(M0, HIGH);
    digitalWrite(M1, LOW);
    break;
  case 4:
    pinMode(M0, INPUT);
    digitalWrite(M1, LOW);
    break;
  case 8:
    pinMode(M0, OUTPUT);
    digitalWrite(M0, LOW);
    digitalWrite(M1, HIGH);
    break;
  case 16:
    pinMode(M0, OUTPUT);
    digitalWrite(M0, HIGH);
    digitalWrite(M1, HIGH);
    break;
  case 32:
    pinMode(M0, INPUT);
    digitalWrite(M1, HIGH);
    break;
  }
  micRes[0] = micRes[1];
}

void stop() {
  digitalWrite(13, LOW);
  stepper.setSpeed(0);
  start = 0;
  time = 0;
  elapsed = 0;
  while(Serial.available() > 0)
    Serial.read();
}

void report() {
  int i, voltage = 0;
  for(i = 0; i < 10; i++)
    voltage += analogRead(BATTERY);
  Serial.print(dir);
  Serial.print(' ');
  Serial.print(accelType);
  Serial.print(' ');
  Serial.print(time);
  Serial.print(' ');
  Serial.print(time - elapsed, 0);
  Serial.print(' ');
  Serial.println(voltage * vRatio / i, 0);
}

 

vvadim
Offline
Зарегистрирован: 23.05.2012

а как вы собираетесь мгновенно разогнать мотор до больших скоростей

шаговики мгновенно не разгоняются

Morn91
Offline
Зарегистрирован: 18.10.2014

vvadim пишет:

а как вы собираетесь мгновенно разогнать мотор до больших скоростей

шаговики мгновенно не разгоняются

Да, слово «большая» тут неуместно. Я имел ввиду, что погрешность зависит от скорости. Причем от скорости в шагах-в-секунду, а не от скорости в оборотах-в-минуту. А т.к. я использую микрошаг, то даже на низких оборотах сталкиваюсь с этой проблемой.

P.S. Максимальная скорость моего мотора 360 RPM, и он без проблем может разогнаться до неё с места, мгновенно. Но такая скорость использоваться, конечно же, не будет. Речь ведь про всё тот же timelapse-слайдер, с которым я мучаю местных форумчан уже полгода.

Morn91
Offline
Зарегистрирован: 18.10.2014

Частично решил проблему созданием некоего подобия многозадачности библиотекой TimerOne.

maksim
Offline
Зарегистрирован: 12.02.2012

Morn91 пишет:

Сейчас использую для этого AccelStepper, который не понимает отрицательного значения ускорения.

Зачем ей понимать? Ускорение ускорению рознь. Как таковое ускорение не может быть отрицательным, так же как и скорость, но за то имеет напрвление. Но это все теория.

А проблема ваша решается просто, когда стартуете устанавливаете одно ускорение, набрали максимальную скорость (или достигли середины хода) установили другое ускорение. Вот и будет вам быстрое ускорение и медленное замедление.

Morn91
Offline
Зарегистрирован: 18.10.2014

maksim пишет:

Зачем ей понимать? Ускорение ускорению рознь. Как таковое ускорение не может быть отрицательным, так же как и скорость, но за то имеет напрвление. Но это все теория.

А проблема ваша решается просто, когда стартуете устанавливаете одно ускорение, набрали максимальную скорость (или достигли середины хода) установили другое ускорение. Вот и будет вам быстрое ускорение и медленное замедление.

Хмм... Менять на ходу ускорение как-то не додумался. Спасибо, попробую.

vvadim
Offline
Зарегистрирован: 23.05.2012

Morn91 пишет:

P.S. Максимальная скорость моего мотора 360 RPM, и он без проблем может разогнаться до неё с места, мгновенно. 

 

а что это за такой шустрый шаговик?

да ещё на максимум мгновенно выходящий...

Morn91
Offline
Зарегистрирован: 18.10.2014

vvadim пишет:

а что это за такой шустрый шаговик?

да ещё на максимум мгновенно выходящий...

Вот такой. Разве это много? Шесть оборотов в секунду же всего и 1200 PPS. Если верить интернету, то «Немы» до 10000 PPS рабочих бывают.