Problem with deceleration & stop function

941 views
Skip to first unread message

mike.m...@googlemail.com

unread,
Nov 25, 2014, 8:39:48 AM11/25/14
to accels...@googlegroups.com
Hi all,
Background :.....
I have an old surface grinder that I am attempting to "semi - automate", at present both x & y axes are manual. The x axis table weighs around 65 kg without the magnetic chuck, so cranking this back & forth by hand is quite a chore!!
Accel & decel would be a benefit from the controller - inertia etc.

Initially simply cycling this table between adjustable limit switches/sensors, with adjustable acceleration & max speed would be a huge improvement.
If successful I will look at converting the y axis & then possibly integration of the 2 & possibly even some cnc?

As a complete novice, I have taken code published by Brian Schmalz & modified it to include a pot for speed control & also accel & decel.

I now have 1 di to start the drive in cw  & 1 to start in ccw.
Toggling the opposite di when running, causes the controller to decel to a stop & the accel in the oposite direction up to a speed defined by the analogue input.
All ok so far.

If the analogue input is turned to increase the speed, the motor accelerates as expected.
If however the pot signal is reduced, the motor speed reduces WITHOUT deceleration.
This I find a bit puzzling since the same fragment of code is used for both processes?

If the stop input is activated the motor stops without deceleration.
This may be linked to the point above since I believe deceleration should take place during a stop.


Apologies up front for lots of comments in code text below - very much work in progress.

Any pointers/suggestions would be much appreciated.




Example code below :........................

// so far WORKING  but decel & stop a problem


// The code for this example is shown below:
// Example5 code for Brian Schmalz's Easy Driver Example page

#include <AccelStepper.h>

// Define the stepper and the pins it will use
AccelStepper stepper1(1, 9, 8);
int SetMotorSpeed = 4000;

// Define our three input button pins
#define  LEFT_PIN  4
#define  STOP_PIN  3
#define  RIGHT_PIN 2

// Define our analog pot input pin
#define  SPEED_PIN 0

// Define our maximum and minimum speed in steps per second (scale pot to these)
#define  MAX_SPEED 2000
#define  MIN_SPEED 0.2

void setup() {
// The only AccelStepper value we have to set here is the max speeed, which is higher than we'll ever go
// stepper1.setMaxSpeed(4000.0);
stepper1.setAcceleration(100);
// Set up the three button inputs, with pullups
pinMode(LEFT_PIN, INPUT_PULLUP);
pinMode(STOP_PIN, INPUT_PULLUP);
pinMode(RIGHT_PIN, INPUT_PULLUP);
}

void loop() {
static float current_speed = 0.0;         // Holds current motor speed in steps/second
static int analog_read_counter = 1000;    // Counts down to 0 to fire analog read
static char sign = 0;                     // Holds -1, 1 or 0 to turn the motor on/off and control direction
static int analog_value = 0;              // Holds raw analog value.

/*In order to add acceleration, you'll need to change the sketch in some fundamental ways. 
You'll need to remove the runSpeed() calls (as you suggest) and replace them with run() calls. 
You will also need to set target positions based on which switch is pressed. 
If S3 is pressed, you need to set the target very far positive. 
This will cause the motor to accelerate in a positive direction. 
If the S1 is pressed, you'll want to set the target very far negative. 
For S2 to stop the motion, you will need to compute a target position at exactly 
the right distance from your current position such that the motor starts smoothly decelerating. 
There are probably other ways to do it but this is how I'd start out.
*/


// snips from m macauley author of accelstepper

 //           stepperB.setCurrentPosition(0);
// stepper1.setCurrentPosition(0); this doesnt seem to be needed ??
      
 //            stepperB.setMaxSpeed(SetMotorSpeed);
 //stepper1.setMaxSpeed(SetMotorSpeed);
 stepper1.setMaxSpeed(2000); // this adjusts speed - maybe use analog to raise lower??
 //              stepperB.setAcceleration(100);
 stepper1.setAcceleration(50); // Lowervalue longer accel
 // stepper1.moveTo(1000000); 


// stepper1.run();
// }

// If a switch is pushed down (low), set the sign value appropriately
if (digitalRead(LEFT_PIN) == 0) {
sign = 1;
        stepper1.moveTo(1000000); // set target way positive for using dig start/stop

}
else if (digitalRead(RIGHT_PIN) == 0) {
sign = -1;
        stepper1.moveTo(-1000000); // set target way negative for using dig start/stop
}
else if (digitalRead(STOP_PIN) == 0) {
stepper1.setMaxSpeed(0); // swap sign of speed ref
       //  stepper1.run();
        // write new speed ref
        // check if speed output is zero - if so send stop command
  // sign = 0;  // 
         stepper1.stop();
      // stepper1.setCurrentPosition(0); // added by mm to zero position at stop
        
}

// We only want to read the pot every so often (because it takes a long time we don't
// want to do it every time through the main loop).
if (analog_read_counter > 0) {
analog_read_counter--;
}
else {
analog_read_counter = 500;
// Now read the pot (from 0 to 1023)
analog_value = analogRead(SPEED_PIN);
// Give the stepper a chance to step if it needs to
stepper1.run();
//  And scale the pot's value from min to max speeds
current_speed = sign * ((analog_value/1023.0) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED;
// Update the stepper to run at this new speed
stepper1.setMaxSpeed(sign * current_speed);
Serial.println(current_speed);
               //  Serial.println(distanceToGo);
Serial.println(analog_read_counter);
                Serial.println(stepper1.currentPosition());


}

// This will run the stepper at a constant speed
// stepper1.runSpeed();
        stepper1.run();
}

/*
 Some explanation on this example code: Because reading the analog value takes a (relatively) long period of time, 
and during that time we can't be updating the stepper motor's position (that only happens in the runSpeed() call) 
we only grab a new analog value every 3000 times through the main loop. We do this by using a counter called analog_read_counter, 
and decrementing it each time through the loop until it gets to zero. Then we reload it with 3000, and perform the analog conversion.

We've also inserted a runSpeed() call between the analog conversion and the math necessary to scale the result to MAX_SPEED and MIN_SPEED. 
This is because that math also takes a (relatively) long time, and so we want to give the stepper a chance to step (if it needs to) in between these to time intensive operations.

You can adjust the values of MIN_SPEED and MAX_SPEED to make the range of speeds whatever you want. 
Note that there are only 1024 possible values that the analogRead() call can return, and so there are only that many discrete speeds the motor can take on.

For this example (because we wanted it to be just a fixed speed) we did not use the normal AccelStepper run() call, but rather the runSpeed() call.
*/


Sandy Noble

unread,
Nov 25, 2014, 6:24:50 PM11/25/14
to accels...@googlegroups.com
Hello, I think your sudden stop when the stop pin is hit is because you are setting the maxSpeed to 0. Either use stop() and then decelerate to a halt, OR manually set the speed to 0, but there's no point in doing both. When the maxSpeed is set to 0, then that forces about the fastest deceleration imaginable, so .stop() has nothing to actually decelerate from. The speed is already 0.

So what you want is

stepper1.stop();
stepper1.runToPosition();


Re your other issue: Increasing the maxSpeed and doing run() causes a smooth acceleration to the new speed, whereas _decreasing_ the maxSpeed does not.

I think this is just the nature of the beast. Deceleration is only prompted in one circumstance: The motor is nearly at the target position. So there is no built-in way to decelerate to a new cruising speed. In your case, your motor is not yet near the target position, so it won't decelerate, but you have just set a hard limit on the maxSpeed, so it has to obey. If it were possible to ramp downwards, then you could easily find yourself in a situation where the motor is moving faster than the maxSpeed.

I bet you could do something infernal by calling stop(), then running and waiting until the new speed was reached, then resetting the target and speed and whatnot. That sounds fun.


sn




--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Sandy Noble

Mike McCauley

unread,
Nov 25, 2014, 7:15:59 PM11/25/14
to accels...@googlegroups.com
Hello,

there really should be no need to change the acceleration setting on the fly:
you should preset it to the greatest accel your physical system can handle.

Then just set the target position (occasionally) and call run() as often as
possible.

Cheers.
--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com
Phone +61 7 5598-7474

mike.m...@googlemail.com

unread,
Nov 26, 2014, 2:43:51 AM11/26/14
to accels...@googlegroups.com
Good morning to you both ( Sandy & Mike),
it was pleasing to receive you very prompt, polite & constructive responses.

Sandy 
If my memory serves me correctly when I used the stop() function, there was no decel at all.
Yet the documentation states "Sets a new target position that causes the stepper to stop as quickly as possible, using the current speed and acceleration parameters."
This I find somewhat confusing. However I shall try your suggestion.

Regarding your second point - my background is 40 years in the ac motor control industry, where inertia has to be accommodated in both the accel & decel state. In fact during decel it is hardware critical as physical damage can occur to components due to over current & over voltage conditions.

I accept your description of what happens in accelstepper, but even with an unloaded stepper motor I can easily cause it to stall ( loose control of the motor) by simply reducing the potentiometer value ( speed reference).

I would have thought that in a stepper motor application where precision is critical, it would be paramount that the motor was always kept under control whenever possible?

I am not sure of your involvement is in the development & support of accelstepper but can I suggest that the decel function is modified to try to adhere to the accel/decel param setting at all times ( but I am sure there will be caveats).

For my project, I shall as you suggest attempt to do the "infernal" - I think even a simple smoothing function on the AI might be useful.


MikeM
Re your comment - acceleration rates will be unique to each job, as will be max speed.
These will need to be settable whilst the motor is running & I am monitoring the response, however I agree that some limits will need to be set to cater for the physical limits the hardware can cope with, safety etc.

If setting on the fly is not feasible then setting whilst the motor is stopped would be an option, but being adjustable is definitely a requirement.
Initially I see it being done via a second pot into an AI.


Any further comments/suggestions are most welcome
Many thanks again.
Mike

mike.m...@googlemail.com

unread,
Nov 26, 2014, 4:24:34 AM11/26/14
to accels...@googlegroups.com
Morning again Sandy,
I have been thinking about your comment following :-
"The motor is nearly at the target position. So there is no built-in way to decelerate to a new cruising speed. In your case, your motor is not yet near the target position, so it won't decelerate, but you have just set a hard limit on the maxSpeed, so it has to obey. If it were possible to ramp downwards, then you could easily find yourself in a situation where the motor is moving faster than the maxSpeed."

Can you explain why it is that if I toggle a reverse direction DI - the motor decelerates gracefully to zero speed & then accelerates back up to the desired speed. This happens whether the motor is still accelerating or stable at whatever the set speed is.

If what you say is correct then surely this shouldn't happens.

Pls understand that I am not being pedantic here - I am just trying to understand how it works in order that I can decide my way forward.

BR
Mike


On Tuesday, November 25, 2014 1:39:48 PM UTC, mike.m...@googlemail.com wrote:

mike.m...@googlemail.com

unread,
Nov 26, 2014, 5:01:55 AM11/26/14
to accels...@googlegroups.com
Good morning again,
I have just reactivate the steppper1.stop() in the code fragment below

stepper1.moveTo(-1000000); // set target way negative for using dig start/stop
}
else if (digitalRead(STOP_PIN) == 0) {
stepper1.stop();
}
& the motor does indeed decelerate to a stop, which is great news.
I can only apologise for the confusion, I guess my "geriatric grey cells" are playing tricks with me!!

However there is still the issue of deceleration with a reducing speed reference to solve.

Again many thanks for the input.
BR
Mike




On Tuesday, November 25, 2014 1:39:48 PM UTC, mike.m...@googlemail.com wrote:

Sandy Noble

unread,
Nov 26, 2014, 1:26:41 PM11/26/14
to accels...@googlegroups.com
Not sure, but I guess the reversal goes smoothly because it isn't a deceleration as such, not a change from a speed down to 0, but rather it's an _acceleration_ that happens to be in reverse. Er if that makes any sense.

So what I mean is that it is axiomatic that deceleration in AccelStepper happens at the end of a run, near the target, to bring the stepping rate from a non-zero number to 0. Deceleration is something that AccelStepper starts doing when it sees the end of the run. There are other ways of getting AccelStepper to alter the speed of the motor that result in deceleration. But depending on where you are standing, acceleration might look a lot like deceleration.

So changing from speed 100 (forwards) to speed -100 (backwards) involves a change of 200, and I think this change is treated as an acceleration, and calculated as such. I could be completely wrong mind, I don't really understand the maths enough to be confident.


Changing the maxSpeed during a run isn't a great idea and you'll have to work against some features of AccelStepper to get it working smoothly - it just doesn't expect it. It needs a different kind of mechanism, cruisingSpeed or something like that, that could be set at any time and the motor will accelerate or decelerate up (or down) to it honouring the acceleration parameters it has, and including never being able to go higher than maxSpeed, even if you ask it to cruise faster.

Reducing speed could be achieved by something awful like

newLowerSpeed = (get speed from pot)
long currentTarget = stepper1.targetPosition();
stepper1.stop();
while (stepper1.distanceToGo() != 0) {
    stepper1.run();
    if (abs(stepper1.speed()) == newLowerSpeed) {
        stepper1.moveTo(currentTarget);
        stepper1.setSpeed(newLowerSpeed);
        break;
    }
}
stepper1.run();


Not sure if that'd actually work, or if there's a gotcha in there somewhere.



good luck

sn

mike.m...@googlemail.com

unread,
Nov 27, 2014, 2:59:10 PM11/27/14
to accels...@googlegroups.com
Evening Sandy,
many thanks for the reply,
It looks I may well achieve or indeed exceed my initial expectations with the application.
At the moment I am building the "jogging functions" - so far no problems.

I shall take a look at your suggestion a little later - it does provide me with some ideas to work with.

Again many thanks for your extremely prompt responses

If we dont exchange messages before - I hope you & your family have a great time during the coming festivities
BR
Mike

On Tuesday, November 25, 2014 1:39:48 PM UTC, mike.m...@googlemail.com wrote:

Giovanni Remigi

unread,
Dec 15, 2014, 6:47:24 AM12/15/14
to accels...@googlegroups.com
In case you are still interested in the acceleration/deceleration topic with AccelStepper this is my code to implement the missing functionality.
The method run() is called by an interrupt and it is used to control 2 stepper motors stored into an array "stepper[0]" "stepper[1]".

The targetSpeed[i] is the speed that the stepper should accelerate/decelerate to. oldTargetSpeed[i] is a variable to store the previous speed so to avoid setting the same speed multiple times.
decelerating[i] is a boolean variable set to true when the stepper is decelerating to 0 speed following the stop command.
motorTarget[i] stores the final position where the stepper is aiming to.

So, once you invoke a method move or moveTo you can then change the the targetSpeed to accelerate or decelerate. It seems to work fine for me, but I cannot guarantee it is bug free.


void run()
{
    for (int i = 0; i < 2; i++) {

        if (targetSpeed[i] != oldTargetSpeed[i]) {
            oldTargetSpeed[i] = targetSpeed[i];
            
            if (targetSpeed[i] > stepper[i].speed()) {
                stepper[i].setMaxSpeed(targetSpeed[i]);
                if (decelerating[i]) {
                    stepper[i].moveTo(motorTarget[i]);
                    decelerating[i] = false;
                }
            } else if (targetSpeed[i] < stepper[i].speed()) {
                if (!decelerating[i]) {
                    motorTarget[i] = stepper[i].targetPosition();
                }
                decelerating[i] = true;
                stepper[i].stop();
            }
        }

        if (decelerating[i] && stepper[i].speed() <= targetSpeed[i]) {
            decelerating[i] = false;
            stepper[i].setMaxSpeed(targetSpeed[i]);
            stepper[i].moveTo(motorTarget[i]);
        }

        if (stepper[i].distanceToGo() != 0) {
            stepper[i].run();
        }
    }
}
Reply all
Reply to author
Forward
0 new messages