Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Stepper Control based on 2 changable values.

  1. #1
    Junior Member
    Join Date
    Oct 2016
    Location
    Essex, England
    Posts
    4

    Stepper Control based on 2 changable values.

    Hello to Everybody and Best Wishes for the New Year.

    I would like a bit (or a lot) of help please. I am trying to operate a stepper motor, via an Easydriver board, based on 2 analog inputs. These inputs are the 'Command' or 'Set Point' and the other is a 'Feedback' signal that changes in response to the stepper motor moving a sensor over a variable number of rotations, basically a closed loop system. The stepper actually rotates a threaded rod that moves a nut up and down then in turn rotates the sensor, this part I cannot change as it is already fitted.

    The idea is that you adjust the command signal and the stepper will move until both signals are the same values (for this I have used percentage as an output value because the ranges of the 2 inputs are different and can be any values). For this I want the stepper to slow down when it is close to the command value before it stops and then when a new command signal is input it accelerates the stepper up to a given speed until it is close then slows then stops again. Both the command and feedback signals in the real world are actually 4-20mA but I will sort that out in the future once I get the stepper to operate as I plan.

    I have written some code for a T3.6 that appears to operate, in as much as the stepper runs at a constant speed and changes direction based on whether the command signal is higher or lower that the feedback signal. My problem, and there may be many I suspect, is I cannot get the stepper to slow properly or speed up in both directions.

    My coding, below, is far from perfect and I appear to have gotten lost in the numerous 'if' statements, is there another way?. This code will eventually become part of a larger program should I get this part to work properly.

    Code:
    #include <ResponsiveAnalogRead.h>
    
    // Stepper motor is connected to Easydriver board
    byte directionPin = 34;
    byte stepPin = 33;
    
    // Initial Stepper Pulse and Duty cycle
    int pulseWidthMicros = 40;  // microseconds Pulse length
    int microsbetweenSteps = 80; // microseconds Duty Cycle width
    
    // Pins for ADC
    const int Feedback = A9; // 10K Pot connected to 3.3V & AGND
    const int Command = A8;  // 10K Pot connected to 3.3V & AGND
    
    // Real values to introduce differeing datums and ranges as would be in real life.
    int MINA = 932;
    int MAXA = 3203;
    int COML = 400;
    int COMH = 3845;
    
    // Other variables
    double Output, Output1;
    float Ad = 0.0; // Command Signal Range MAXA-MINA
    float Cd = 0.0; // Feedback Signal Range COMH-COML
    
    // Initialise ADC to pins
    ResponsiveAnalogRead value1(Command, true);
    ResponsiveAnalogRead value2(Feedback, true);
    
    void setup() {
      analogReadResolution(12); // Set ADC resolutions to 12 bits
      Serial.begin(115200);
      delay(2000);
      //Set-up Pins for Stepper Motor
      pinMode(directionPin, OUTPUT);
      pinMode(stepPin, OUTPUT);
      // Set-up ADC pins for input
      pinMode(Command, INPUT);
      pinMode(Feedback, INPUT);
      // Calculate Signal ranges
      Ad = (MAXA - MINA) / 100; // example 3203-932= 2270/100=22.7 bits/%
      Cd = (COMH - COML) / 100; // example 3845-400= 3445/100=34.45 bits/%
    
    }
    
    int value3;
    int value4;
    float value5 = 0.0;
    float value6 = 0.0;
    float lastvalue = 0.0;
    
    void loop() {
    
      value1.update(); // Take ADC Reading Command
      value2.update(); // Take ADC Reading Feedback
      value3 = value1.getValue(); // Retrieve actual value
      value4 = value2.getValue(); // Retrieve actual value
      value5 = value3 / Ad; // This is incorrect, calculates % of ADC ouput but
      value6 = value4 / Cd; // doesn't take datum value into account.
    
      // Check if Command is bigger than Feedback signal to set stepper direction
      if (value5 > value6) {
        digitalWriteFast(directionPin, HIGH);
      }
      else
      {
        digitalWriteFast(directionPin, LOW);
      }
      // Check if  micro's between pulses is less than 1999 to operate stepper or stop
      if (microsbetweenSteps < 1999) {
        digitalWriteFast(stepPin, HIGH);
        delayMicroseconds(pulseWidthMicros); // this line is probably unnecessary
        digitalWriteFast(stepPin, LOW);
        delayMicroseconds(microsbetweenSteps);
      }
      // Check if command signal is higher than the feedback signal
      if (value5 > value6) {
        Output1 = value5 - value6;
        Output = 100 - Output1; // Output is inverted from Output1
    
        // This is where I check if the Output is between 98 & 100% so I can slow/quicken the stepper down
        if (Output > 98 && Output < 100) {
    
          // Then decide which direction the signal is going and act
          if (lastvalue < value6) {
            microsbetweenSteps = microsbetweenSteps - 1; // Speed up
            if (microsbetweenSteps < 100) {
              microsbetweenSteps = 100; // prevent duty cycle being to short
            }
          }
          else
          {
            microsbetweenSteps = microsbetweenSteps + 1; // Slow Down
            if (microsbetweenSteps > 5000) {
              microsbetweenSteps = 5000;
            }
          }
        }
        else
        {
          microsbetweenSteps = 100; // When outside 98% - 100% run Stepper quickish
        }
      }
      // The section below is based on the above section
      // Is Feedback > Command signal
      if (value6 > value5) {
        Output1 = value6 - value5;
        Output = 100 - Output1;
    
        if (Output > 98 && Output < 100) {
          if (lastvalue < value6) {
            microsbetweenSteps = microsbetweenSteps - 100;
            if (microsbetweenSteps < 100) {
              microsbetweenSteps = 100;
            }
          }
          else
          {
            microsbetweenSteps = microsbetweenSteps + 100;
            if (microsbetweenSteps > 5000) {
              microsbetweenSteps = 5000;
            }
          }
        }
        else
        {
          microsbetweenSteps = 100;
        }
      }
    
      // Print out values
      Serial.print("Command = ");
      Serial.print(value3);
      Serial.print(" Feedback = ");
      Serial.print(value4);
      Serial.print(" Command2 = ");
      Serial.print(value5);
      Serial.print(" Feedback = ");
      Serial.println(value6);
      Serial.print(" Output = ");
      Serial.print(Output);
      Serial.print(" Output1 = ");
      Serial.print(Output1);
      Serial.print(" Micro's = ");
      Serial.println(microsbetweenSteps);
      Serial.println();
      Serial.print("Last Value = ");
      Serial.println(lastvalue);
    
      lastvalue = value6; // take previous value for checking signal direction
      delay(2);
    }

    I have tried AccelStepper and TeensyStep libraries but both seem to want to be given a number of steps for it to move to rather than based on a second input. There may be others out there that will do just what I want but I haven't found them yet, still looking.

    Any guidance would be appreciated.

    Chris

  2. #2
    Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    31
    Here is a bit of guidance to start with. I haven't looked at your code but basically you have a servo configuration and you probably need to implement a pid control loop.

    More info at the following link:
    https://github.com/br3ttb/Arduino-PID-Library

  3. #3
    Junior Member
    Join Date
    Oct 2016
    Location
    Essex, England
    Posts
    4
    Hello neurofun many thanks for the reply.

    I agree that is what I thought initially.

    I did try PID route but and it was not 100% successful, from memory it would reduce speed and increase speed but only if the error was one way such as command higher than feedback and not vice versa, but I am willing to try again. Now all I need to do is dig that particular piece of code out and have another look. Thanks for your input.

  4. #4
    Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    31
    In retrospect a pid loop might be overkill for your application. I had a look at the AccelStepper lib and that's all you need. It also dramatically simplifies your code.
    Have a look, you will have to fiddle with the parameters in setup to make it behave as you want.
    the code is pretty self explanatory and should work but I have no hardware to test it out. So review it carefully.
    If you have any questions feel free to ask.

    Code:
    #include <ResponsiveAnalogRead.h>
    #include <AccelStepper.h>
    
    AccelStepper stepper(AccelStepper::DRIVER, 33, 34);
    
    // Pins for ADC
    const int Feedback = A9; // 10K Pot connected to 3.3V & AGND
    const int Command = A8;  // 10K Pot connected to 3.3V & AGND
    
    // Real values to introduce differeing datums and ranges as would be in real life.
    int MINA = 932; // Command Signal Range MAXA-MINA
    int MAXA = 3203;
    int COML = 400; // Feedback Signal Range COMH-COML
    int COMH = 3845;
    
    // Other variables
    double error;
    float deadBand;
    float scale;
    
    // Initialise ADC to pins
    ResponsiveAnalogRead value1(Command, true);
    ResponsiveAnalogRead value2(Feedback, true);
    
    void setup() {
      analogReadResolution(12); // Set ADC resolutions to 12 bits
      Serial.begin(115200);
      delay(2000);
      // Set-up ADC pins for input
      pinMode(Command, INPUT);
      pinMode(Feedback, INPUT);
    
      //adjust following paramerters
      stepper.setMaxSpeed(500);
      stepper.setAcceleration(50);
      deadBand = 50.0; //stop moving when error < deadBand
      //scale equals to the maximum possible error , iow the maximum travel distance in steps
      //scale = length of lead screw / pitch of lead screw * steps per revolution
      scale = 100000;
      
      //uncomment to reverse stepper direction
      // stepper.setPinsInverted(true, false, false);
    }
    
    int value3;
    int value4;
    float value5 = 0.0;
    float value6 = 0.0;
    
    void loop() {
      value1.update(); // Take ADC Reading Command
      value2.update(); // Take ADC Reading Feedback
      value3 = value1.getValue(); // Retrieve actual value
      value4 = value2.getValue(); // Retrieve actual value
      value5 = float(value3 - MINA) / float(MAXA - MINA) * scale; // convert command range from MINA->MAXA to 0->scale
      value6 = float(value4 - COML) / float(COMH - COML) * scale; // convert feedback range from COML->COMH to 0->scale
    
      error = value6 - value5;
      if(abs(error) < deadBand ) error = 0;
    
      stepper.move(error);
      stepper.run();
    }

  5. #5
    Junior Member
    Join Date
    Oct 2016
    Location
    Essex, England
    Posts
    4
    Hello neurofun

    That is brilliant.. It functions in both directions then decelerates/accelerates about the Deadband just what I was after. I spent most of the weekend trying PID control and kept getting really lost with the stepper either rotating CW or CCW but couldn't get it to stop and then change direction. Your code is a lot simpler than my original and makes sense when I see it written down, so many many thanks for your help.

    I can now start playing again.

    Thanks again.

  6. #6
    Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    31
    You're welcome. I hope all was not lost and you learned something about pid control over the weekend :)
    Now do yourself a favor and rename your objects, variables to something more explanatory. It will make your code easier to read for everyone, including yourself.
    Code:
    const int commandPin = A8;
    ResponsiveAnalogRead commandAnalogIn(commandPin, true);
    commandAnalogIn.update();
    commandRaw = commandAnalogIn.getValue();
    commandScaled = float(commandRaw - MINA) / float(MAXA - MINA) * scale;
    error = feedbackScaled - commandScaled;

  7. #7
    Junior Member
    Join Date
    Oct 2016
    Location
    Essex, England
    Posts
    4
    The more I learn the more I want to learn.

    I agree with you about naming things so that it is easier to understand, especially for me, so I must try harder.

    Thanks again

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •