risitt / kissStepper

Lightweight and fast Arduino and Teensyduino library for controlling a stepper motor driver with a STEP/DIR interface
GNU Lesser General Public License v2.1
12 stars 6 forks source link

Math formula to calculate move time #6

Closed yankobogdan closed 5 years ago

yankobogdan commented 5 years ago

Hi. Firstly huge thanks for this great, fast and reliable library. Im using it in my motion control device. And in software part I need smth that will help me to predict move time. In case of constant speed its easy, but for the acceleration standart phisics formula not useful. Can you help me with math formula for example to calculate moving time when we go from A to B point with start speed C and axxel X?

risitt commented 5 years ago

Hey there,

Sorry for the delay in responding, it's been a busy December! Yes, you can calculate this. It will be approximate, but maybe more than good enough. And perhaps if if I sit down with the math I can figure out a way to calculate it exactly.

There's only one main challenge here: the motor doesn't always have time to accelerate up to the maximum speed. So you have to do some extra math to figure out the actual top speed. You can't (yet) use starting speeds other than 0, so that does simplify things a little bit, but only a little.

All you need to do to figure out how long it will take to accelerate or decelerate is divide the number of steps taken by the average speed during the acceleration. Since acceleration is linear, the average speed is the midpoint between 0 and the top speed, so you can get that by dividing the top speed by 2. But because acceleration and deceleration will take the same time, you only really need to calculate it for one, then multiply by 2. Then add the time taken to move at constant speed. This makes the math really simple, and the hard part is figuring out the top speed.

Here is some code that MIGHT work. Not bug tested, and you might well have some integer overflow problems here that you will need to sort out, but it's a start.


// calculate total distance
uint32_t totalDist = (target > pos) ? (target - pos) : (pos - target);

// get maximum speed (or replace all references to maxSpeed below with your local variable)
uint16_t maxSpeed = mot.getMaxSpeed();

// calculate distance required to accel to maximum speed
uint32_t maxAccelDist = mot.calcMaxAccelDist();

// calculate move time
uint32_t moveTime; // in microseconds
if (maxAccelDist >= (totalDist / 2))
{
    // triangular profile, max speed may not be reached
    uint32_t distAccel = totalDist / 2;
    uint16_t topSpeed = ((uint32_t)maxSpeed * distAccel) / maxAccelDist;
    float microsPerStep = 1000000.0 / topSpeed;
    moveTime = distAccel * microsPerStep; // in microseconds
}
else
{
    // trapezoidal profile, max speed will be reached
    uint32_t distAccel = maxAccelDist;
    uint32_t distRun = totalDist - distAccel*2; // distance at constant speed
    uint16_t topSpeed = maxSpeed; // where maxSpeed is what you previously supplied to mot.setSpeed(...)
    float microsPerStep = 1000000.0 / topSpeed;
    moveTime = (distAccel * microsPerStep) + (distRun * microsPerStep); // in microseconds
}

Of course if you use either stop() or decelerate() after calculating the time, it won't match up with reality.

risitt commented 5 years ago

I've added a getTopSpeed() function to the library. Although not an official release yet, I have tested it and I think it's working quite well.

Just be aware that it IS a bit of an estimate and does suffer from rounding errors, so it's not perfect.

Using this function, the method to calculate how much time (in seconds) it will take to move is this (after calling prepareMove):

uint16_t topSpeed = mot.getTopSpeed();
uint32_t accelDist, runDist, decelDist;
float accelTime, runTime, decelTime, totalTime;

topSpeed = mot.getTopSpeed();
accelDist = mot.getAccelDist();
runDist = mot.getRunDist();
decelDist = mot.getDecelDist();

accelTime = accelDist / (topSpeed / 2.0);
runTime = runDist / (float)topSpeed;
decelTime = decelDist / (topSpeed / 2.0);

totalTime = accelTime + runTime + decelTime;

That's a bit verbose, you can simplify it if you like. You can also change it to calculate integer milliseconds (if you want to avoid floating point math), etc.

I'll close this issue for now. If you have problems, create a new issue and let me know!