Can you use acceleration with joystick and AccelStepper?

Hi all:

Background:
I have cobbled together a pan/tilt device for cameras using stepper motors and a joystick. It all works relatively well. Aside from pan and tilt, I have three buttons for Set Home, Go Home and Recalibrate, the latter using code from another user which averages the centering error of an analog joystick as well as de-energizes the steppers. I built a dual joystick box for the pan/tilt steppers and zoom servo, using shielded Cat6 cables to go from the joystick to the Arduino in a box next to the pan/tilt device. The joystick generates -500 to +500 (-ish) and that gets put into a motSpeed that gets multiplied by a few factors to reset the orientation of the joystick and compensate for desired speeds.

Problem:
The numbers from the position of the joystick go into the setSpeed() command, then the steppers get the runSpeed() command. Those commands do not take advantage of the acceleration and deceleration programming that smoothes out the movement. (I am taking advantage of the acceleration programming for the Go Home routine.)

In AccelStepper, the acceleration does not work with the setSpeed() and runSpeed() commands. It seems to only work with the run() command, but then what is the speed set at? (side question: Does the run() command use the setAcceleration() and setMaxSpeed() commands to accelerate to the max speed only if there is time between the acceleration and deceleration times?)

Question:
Can you use acceleration from AccelStepper while getting speed settings from a joystick?

Here is a snippet of the joystick>stepper code for the X axis (full code below):

  if (abs(Analog_X - Analog_X_AVG) > valAway)
  {
    speedMult = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG);  //yields +1 or -1 factor
    valAwayFix = valAway * speedMult;
    motSpeed = (((Analog_X - Analog_X_AVG) - valAwayFix) * xMotorState) / xSpeedFactor;  //Set left/right
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
    //  digitalWrite(enPin, LOW); // enable motor
    stepper.setSpeed(motSpeed);
    stepper.runSpeed();

    /*
      if (abs(Analog_X - Analog_X_AVG) > speedShift)
      {
        j=++j;
        if (j > speedCount);
        {
          motSpeed = (maxSpeed_val / 2) * (motSpeed / (abs(motSpeed)));
          stepper.setSpeed(motSpeed);
          stepper.runSpeed();
        }
      }
      else

      {
        j = 1;
      }
    */

    //----------------X Motor Print-----------------------
    currentMillis = millis();  //
    if (currentMillis - refMillis >= period)  //test whether the period has elapsed
    {
      Serial.print ("Analog_X= ");
      Serial.print (Analog_X);
      Serial.print (" Current Position X =");
      Serial.print (stepper.currentPosition());
      Serial.print (" motSpeed X = ");
      Serial.println (motSpeed);

      refMillis = currentMillis;  //IMPORTANT to save the start time of the speed printout.

    }

I'm placing my lengthy, probably very overweight code below.


#include <AccelStepper.h> //accelstepper library
AccelStepper stepper(1, 8, 9); //  pulses Digital 8 (STEP), direction Digital 9 (CCW)
AccelStepper stepperY(1, 10, 11);  //  pulses Digital 10 (STEP), direction Digital 11 (CCW)

// include the Servo library
#include <Servo.h>
Servo myServo;  // create a servo object

/*Author's notes
   This version is for Camera 3 with Servo

   This version (111) will use
   max motor speed * .5
   high speed at end of joystick
   High Speed disabled 2/13/22  Too bouncy

   Using Pololu A4988 at 1/16th step
   MS1  MS2  MS3
     L   L    L   Full Step
     H   L    L   Half Step
     L   H    L   Quarter Step
     H   H    L   Eighth Step
     H   H    H   Sixteenth Step
*/


//Pins
const byte Analog_X_pin = A0; //x-axis readings
const byte Analog_Y_pin = A1; //y-axis readings
const byte homeSetpin = 2; //set home to 0
const byte homeGopin = 3; //go home switch
int redPin = 4; //Red LED pin
int const potPin = A2; // analog pin used to connect the potentiometer for the servo
int servoPin = 12; // Output servoAngle to servo
const byte initializeButton = 7; //Button 3


//Motor data
int enPin = 5;
int enPinY = 6;
int stepPin = 8; // Pan Direction
int dirPin = 9; // Step
int stepPinY = 10;  //Tilt direction
int dirPinY = 11;  //Tilt step
int accelRate = 2000;  //mostly for Go Home routine
int motSpeed = 0;
int motSpeedY = 0;
int speedMult = 1;
int speedMultY;
int valAway = 100;  //  Value away from center before Joystick works
int valAwayServo = 15; // Value away from center before servo moves
int servoRange = 40; // Max deviation of servo from 90
int valAwayFix;
int valAwayFixY;
int speedShift = 450;
int maxSpeed_val = 1000;
int maxSpeed_valY = 1000;
int startPos = 0;
int stepsPerRev = 3200;
int newPos = 20;
int yButtonOld = 1;
int yMotorState = 1;
int xMotorState = -1;
int hiSpeed = 950;
int xSpeedFactor = 2;
int ySpeedFactor = 4;

//  Boundary Limits
long highMax = 1200;
long lowMax = -1200;
long highMaxY = 700;
long lowMaxY = -700;

int safePos = 0;


//Variables
int Analog_X = 0; //x-axis value
int Analog_Y = 0; //y-axis value
int homeSet_Val; //switch value
int homeGo_Val ; //switch value
int initializeButton_Val = 1; //joystick button switch
int Analog_X_AVG = 0; //x-axis value average
int Analog_Y_AVG = 0; //y-axis value average
int Analog_S_AVG = 512; //servo pot value average
int potVal;  // variable to read the value from the analog pin
int servoAngle;   // variable to hold the servoAngle for the servo motor
int servoCenter = 90; //Servo at rest

//int homeGo_Val_Value = 0; //

//Time variables
unsigned long refMillis;
unsigned long currentMillis;
unsigned long stoppedMillis;
unsigned long speedCount = 200000; //j counter for high speed X
const unsigned long period = 1000; //the value is a number of milliseconds, ie 1 second
int j;  //counter one
int j2; //counter two
long j3; //counter three
long j3Max = 10003; //counter three reset
int speedDiff;
int speedDiffY;
int printCount = 1;
int glacierT = 100;
int dt = 1000;
int ydt = 100; //ybutton delay
unsigned long pauseT;
unsigned long pauseTY;

unsigned long pauseMax = 350;
unsigned long pauseMaxY = 350;



void setup()
{
  //SERIAL
  Serial.begin(9600);
  Serial.println ("Begin Sketch");
  //----------------------------------------------------------------------------
  //PINS
  pinMode(Analog_X_pin, INPUT);
  pinMode(Analog_Y_pin, INPUT);
  pinMode(initializeButton, INPUT);
  pinMode(homeSetpin, INPUT);
  pinMode(homeGopin, INPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPinY, OUTPUT);
  pinMode(stepPinY, OUTPUT);
  pinMode(enPin, OUTPUT);
  pinMode(enPinY, OUTPUT);
  pinMode(redPin, OUTPUT);
  pinMode(potPin, INPUT);

  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor

  Serial.println("disabled before setup");

  digitalWrite (homeSetpin, HIGH);
  digitalWrite (homeGopin, HIGH);
  digitalWrite (initializeButton, HIGH);
  digitalWrite(redPin, LOW);  // turn off redLED
  myServo.attach(12); // attaches the servo on pin 12 to the servo object


  //----------------------------------------------------------------------------
  InitialValues(); //averaging the values of the 3 analog pins (values from potmeters)
  //----------------------------------------------------------------------------
  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setCurrentPosition(0);
  stepper.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepper.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(0);

  // stepperY
  stepperY.setCurrentPosition(0);
  stepperY.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepperY.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepperY.setSpeed(0);



  //----------------------------------------------------------------------------
  refMillis = millis();  //initial start time

  Serial.println ("Let's GO!");
}

//-----------------------------------------
//------------       Void Loop        -----
//-----------------------------------------

void loop() {
  // put your main code here, to run repeatedly:
  Analog_X = analogRead(Analog_X_pin);
  Analog_Y = analogRead(Analog_Y_pin);
  homeSet();
  homeGo();
  checkBoundary();
  servoCheck();

  initializeButton_Val = digitalRead(initializeButton); //recalibrates joystick
  if (initializeButton_Val == 0)
  {
    InitialValues();
  }

  //if the value is valAway "value away" from the average (midpoint), we allow the update of the speed
  //This is a sort of a filter for the inaccuracy of the reading

  //-----------------------------------------
  //------------       X Motor Speed    -----
  //-----------------------------------------
  if (abs(Analog_X - Analog_X_AVG) > valAway)
  {
    speedMult = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG);  //yields +1 or -1 factor
    valAwayFix = valAway * speedMult;
    motSpeed = (((Analog_X - Analog_X_AVG) - valAwayFix) * xMotorState) / xSpeedFactor;  //Set left/right
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
    //  digitalWrite(enPin, LOW); // enable motor
    stepper.setSpeed(motSpeed);
    stepper.runSpeed();

    /*
      if (abs(Analog_X - Analog_X_AVG) > speedShift)
      {
        j=++j;
        if (j > speedCount);
        {
          motSpeed = (maxSpeed_val / 2) * (motSpeed / (abs(motSpeed)));
          stepper.setSpeed(motSpeed);
          stepper.runSpeed();
        }
      }
      else

      {
        j = 1;
      }
    */

    //----------------X Motor Print-----------------------
    currentMillis = millis();  //
    if (currentMillis - refMillis >= period)  //test whether the period has elapsed
    {
      Serial.print ("Analog_X= ");
      Serial.print (Analog_X);
      Serial.print (" Current Position X =");
      Serial.print (stepper.currentPosition());
      Serial.print (" motSpeed X = ");
      Serial.println (motSpeed);

      refMillis = currentMillis;  //IMPORTANT to save the start time of the speed printout.

    }
    else
    {
      motSpeed = 0;
      printCount = 1; //reset for homeGo routine
    }
  }  //---------------------------end Analog_X val check

  //-----------------------------------------
  //------------       Y Motor Speed    -----
  //-----------------------------------------
  if (abs(Analog_Y - Analog_Y_AVG) > valAway)
  {
    speedMultY = abs(Analog_Y - Analog_Y_AVG) / (Analog_Y - Analog_Y_AVG);
    valAwayFixY = valAway * speedMultY;
    motSpeedY = ((Analog_Y - Analog_Y_AVG - valAwayFixY) * yMotorState) / ySpeedFactor; //determines flightStick or gamerStick
    stepperY.runSpeed();
    stepperY.setSpeed(motSpeedY);
    stepperY.runSpeed();

    /*
       if (abs(Analog_Y - Analog_Y_AVG) > speedShift) // Full speed at end of joystick
       {
         motSpeedY = (maxSpeed_valY/2) * (motSpeedY / (abs(motSpeedY))); //get sign of motor speed
         stepperY.setSpeed(motSpeedY);
         //     stepperY.runSpeed();
         stepperY.run();


       }
    */

    //----------Y motor print  -------------
    currentMillis = millis();  //
    if (currentMillis - refMillis >= period)  //test whether the period has elapsed
    {
      Serial.print ("Analog_ Y= ");
      Serial.print (Analog_Y);
      Serial.print (" Current Position Y =");
      Serial.print (stepperY.currentPosition());
      Serial.print (" motSpeed Y = ");
      Serial.println (motSpeedY);


      refMillis = currentMillis;  //IMPORTANT to save the start time of the speed printout.
    }

  }

  else
  {
    motSpeedY = 0;
  }


  j3 = ++j3;
  if (j3 >= j3Max) //test whether the period has elapsed
  {
    Serial.print (j3);
    Serial.print (" Waiting on Joystick X & Y= ");
    Serial.print (Analog_X);
    Serial.print (" ");
    Serial.print (Analog_Y);
    Serial.print (" motSpeed X&Y = ");
    Serial.print (motSpeed);
    Serial.print (" ");
    Serial.println (motSpeedY);
    Serial.print("potVal: ");
    Serial.print(potVal);
    Serial.print(", servoAngle: ");
    Serial.println(servoAngle);

    j3 = 1; //IMPORTANT to save the start time of the speed printout.
  }

}  // end void loop

//----------------------------------------\
//-----------     ROUTINES    ------------->
//----------------------------------------/


//-------------------------------------------
//-----------     Initial Values()    -----
//------------------------------------------
void InitialValues()
{

  blinkRed();
  stepper.setSpeed(0);
  stepperY.setSpeed(0);

  motSpeed = 0;
  motSpeedY = 0;

  //  digitalWrite(enPin, HIGH); // disable motor
  //Set the values to zero before averaging
  float tempX = 0;
  float tempY = 0;
  float tempS = 0;

  //----------------------------------------------------------------------------
  //read the analog 50x, then calculate an average.
  //they will be the reference values
  for (int i = 0; i < 50; i++)
  {
    tempX += analogRead(Analog_X_pin);
    delay(10); //allowing a little time between two readings
    tempY += analogRead(Analog_Y_pin);
    delay(10);
    tempS += analogRead(potPin);
    delay(10);

  }
  //----------------------------------------------------------------------------
  Analog_X_AVG = tempX / 50;
  Analog_Y_AVG = tempY / 50;
  Analog_S_AVG = tempS / 50;

  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor

  //----------------------------------------------------------------------------
  Serial.print("AVG_X: ");
  Serial.println(Analog_X_AVG);
  Serial.print("AVG_Y: ");
  Serial.println(Analog_Y_AVG);
  Serial.print("AVG_S: ");
  Serial.println(Analog_S_AVG);
  Serial.println("Calibration finished, still disabled");
  // two blinks
  blinkRed();
  delay(ydt * 4);
  blinkRed();
}


//-------------------------------------------
//-----------     blinkRed()    -----
//------------------------------------------
void blinkRed() //Red LED Blink
{
  digitalWrite(redPin, HIGH);
  delay(ydt);
  digitalWrite(redPin, LOW);

}


//-------------------------------------------
//-----------     homeSet()    ------------
//------------------------------------------
void homeSet()
{
  homeSet_Val = digitalRead(homeSetpin);
  if (homeSet_Val == 0) //set home location
  {
    blinkRed();
    stepper.setCurrentPosition(0);  // Set the current position as zero for now
    Serial.print("Home - X");
    stepper.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
    stepper.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
    stepper.setSpeed(motSpeed);
    stepper.runSpeed();
    stepperY.setCurrentPosition(0);  // Set the current position as zero for now
    Serial.print(" Home - Y");
    digitalWrite(enPin, LOW); // enable X motor
    digitalWrite(enPinY, LOW); // enable Y motor
    Serial.println(" Motors Enabled by Home Switch");
    stepperY.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
    stepperY.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
    stepperY.setSpeed(motSpeed);
    stepperY.runSpeed();
    //   myServo.write(servoCenter);

  }
}

//-------------------------------------------
//-----------     homeGo()    ------------
//------------------------------------------
void homeGo()
{
  homeGo_Val = digitalRead (homeGopin);

  if (homeGo_Val == 0) // Go Home
  {
    blinkRed();
    speedMult = abs (stepper.currentPosition()) / stepper.currentPosition();
    speedMultY = abs (stepperY.currentPosition()) / stepperY.currentPosition();


    Serial.print (stepper.currentPosition());
    Serial.print (" X < Going Home > Y ");
    Serial.println (stepperY.currentPosition());
    stepper.moveTo (0);
    motSpeed = (maxSpeed_val * .5);
    motSpeedY = (maxSpeed_val * .5);
    stepper.setSpeed(motSpeed);
    stepperY.moveTo (0);
    stepperY.setSpeed(motSpeedY);

    while (stepper.currentPosition() != stepper.targetPosition())
    {
      //     stepper.runSpeedToPosition();
      stepper.runToPosition();

    }

    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      //     stepperY.runSpeedToPosition();
      stepperY.runToPosition();

    }


  }

  // If move is completed display message on Serial Monitor
  if ((stepper.distanceToGo() == 0 && printCount < 2))
  {
    Serial.println("Now Home!");
    motSpeed = 0;
    motSpeedY = 0;

    printCount = printCount + 1;
  }

}



//----------------------------------------------------------------------------
//                 checkBoundary()
//----------------------------------------------------------------------------
void checkBoundary() //Protection from over rotation
{
  //---------------- X Boundary
  if (stepper.currentPosition() <= lowMax || stepper.currentPosition() >= highMax)
  {
    stepper.setSpeed(0);
    Serial.print ("Over Rotated ");
    Serial.println (stepper.currentPosition());
    delay (dt);
  }
  if (stepper.currentPosition() <= lowMax) // Negative Overrotation
  {
    safePos = (lowMax + 100);
    stepper.moveTo (safePos);
    stepper.setSpeed(maxSpeed_val / 4);

    while (stepper.currentPosition() != stepper.targetPosition())
    {
      stepper.runSpeedToPosition();
    }
  }
  if (stepper.currentPosition() >= highMax) // Positive Overrotation
  {
    safePos = (highMax - 100);
    stepper.moveTo (safePos);
    stepper.setSpeed(maxSpeed_val / 4);
    while (stepper.currentPosition() != stepper.targetPosition())
    {
      stepper.runSpeedToPosition();

    }
  }

  //-----------  Y Boundary
  if (stepperY.currentPosition() <= lowMaxY || stepperY.currentPosition() >= highMaxY)
  {
    stepperY.setSpeed(0);
    Serial.print ("Over Rotated ");
    Serial.println (stepperY.currentPosition());
    delay (dt);
  }
  if (stepperY.currentPosition() <= lowMaxY) // Negative Overrotation
  {

    safePos = (lowMaxY + 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);

    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      stepperY.runSpeedToPosition();
    }
  }
  if (stepperY.currentPosition() >= highMaxY) // Positive Overrotation
  {
    safePos = (highMaxY - 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);
    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      stepperY.runSpeedToPosition();

    }
  }

}

//----------------------------------------------------------------------------
//                 servoCheck()
//----------------------------------------------------------------------------
void servoCheck()
{
  potVal = analogRead(potPin); // read the value of the potentiometer

  // scale the numbers from the pot
  servoAngle = map(potVal, 0, 1023, (90 + servoRange), (90 - servoRange));


  if (abs(potVal - Analog_S_AVG) > valAwayServo)
  { // set the servo position
    myServo.write(servoAngle);

  }
  else {
    //  servoAngle = 90;
    myServo.write(servoCenter);

  }
  /* while (abs(potVal - Analog_S_AVG) <= valAwayServo)
    {
     myServo.write(servoCenter);
    }
  */
}

The runSpeed function does use acceleration. Don't use setSpeed with runSpeed, use setMaxSpeed (with any of the functions that use acceleration). The setSpeed function is for the constant speed functions (that are generally blocking).

Get the runSpeed function out of other functions. The runSpeed or the run functions needs to be called as often as possible. That means in a fast loop() function and there cannot be blocking code that slows the loop() down if you want speed from the steppers. The runToPosition function is a blocking function.

Hi groundFungus:
Thank you for your quick reply. If I don't use setSpeed, do I simply use the joystick derived value in the setMaxSpeed function? That could be a game-changer.

Hi groundFungus:
I tried what I understood that you said, and got no movement. I just tried it in the X direction, and it did not budge. I had to reinstate the setSpeed command to get any movement in the X direction.

Here is the pertinent snippet of code:


    speedMult = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG);  //yields +1 or -1 factor
    valAwayFix = valAway * speedMult;
    motSpeed = (((Analog_X - Analog_X_AVG) - valAwayFix) * xMotorState) / xSpeedFactor;  //Set left/right
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
    //  digitalWrite(enPin, LOW); // enable motor
    stepper.setMaxSpeed(motSpeed);
    //    stepper.setSpeed(motSpeed);
    stepper.runSpeed();

The runSpeed() and setSpeed() methods are used to rotate the stepper at the specified speed. There is no acceleration and deceleration and no stop at a specified target position.
In contrast, run() moves the stepper to a specified target position with acceleration and deceleration. When the target position is reached, there is no more movement ( no matter how often you still call run() :wink: ).

If you want to have acceleration and deceleration with Accelstepper, you have to do that:

  • Set a target position
  • use setMaxSpeed(), setAcceleration() and run() to move the stepper
  • do not use setSpeed() and runSpeed(), as these functions are sub-functions of run(). You must not call them yourself when using run().

run() ( and runSpeed() ) must be called as frequently as possible. At least more often than the highest steprate. Therefore you need a very short loop() turnaround time. Don't use delay() or other blocking code in loop().

Hi MicroBahner:
Thank you for the clear explanation. In my somewhat blind experimentation, I was starting to fear that conclusion, but I was hoping someone could explain it to me.

It appears then that using a joystick to set a speed in AccelStepper precludes acceleration. I’m thinking that an alternative could be sending the run() command after a moving target that is always a joystick fraction or multiple of steps ahead, then when you let off the joystick, the target suddenly becomes zero and the motor stops. Probably not practical to program, and it might cause erratic movement, but it would be an interesting concept to pursue.

Again, thank you for making this clear. For now, the acceleration will continue to be physical finesse on the joystick.

rcarbaugh

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.