AccelStep + 41mm/2A motor losing steps?

Hi - I have a machine here that includes a motor whose sole purpose is to strafe back and forth rapidly at maximum acceleration.

e: you can see the machine being tested here on youtube.

The performance I have with my current setup is perfect: One of the two cores of an ESP32 is dedicated to the Accelstep motor loop, the little 2A motors are surprisingly very strong and fast running through TB6600 drivers with a 36v/7a power supply behind it all.

The driver has a dedicated channel on the power supply, and the TB6600s are set to Quarter step. So it goes 36v/7a power -> TB6600 -> 2A motor

And the motor is driving a threaded linear axis.


My issue is that with prolonged use, the strafing program's boundaries (start and finish points) move over very slowly, so it's going out of bounds (but it seems to think that's where it should be).

(I'm reading about resonance right now, but I'd still like to hear from anyone about what they think is the best way to eliminate the step loss)

I will illustrate the issue:

This is the leadscrew:

|--------------------------------|

This is the programmed boundaries on the leadscrew:

|X-------X-----------------------|

And after about 3 minutes of use, here are the programmed boundaries shifting over:

|-----X-------X------------------|

An ugly way to deal with it would be to occasionally bring the motor back to the bump stop and re-establish a zero point to work from. But this totally breaks up the continuity of the operation I'd like to have.

So I'm just wondering if it's possible that this small 2A motor is losing some steps whilst ripping back and forth at full acceleration.
And if so, would a larger motor (Nema 23) with similar amperage theoretically correct this issue?

(all the leadscrew math and motor setup is correct, no need to double check that stuff)

e: quick note: the acceleration setting on the motor is probably set higher than it's capable of, especially in such a short distance it's traveling back and forth (~20mm). Maybe that's causing issues? Or just the motor's torque capabilities maybe...

I'm slightly out of my depth when it comes to troubleshooting motor performance, so any help is greatly appreciated!

e: here is the motor code that's running on a single core of the esp32...i know it's fairly large and ugly, but the performance is fine. i don't know if that would contribute to the shifting...there's a couple modes of operation there, and the work is all done in blocks (that's the i<20 up top) so one driving job follows another, usually switching between different modes.

stepper2 is the one in question.

e2: i ordered this motor with about 100oz holding torque just in case that's the issue, but i'd still like to hear from anyone who may have an idea about this stuff

I nearly did not respond to this because my dictionary defines "strafe" as "attack with machine gun fire" and I don't like to help with projects that involve weapons.

However your video shows a peaceful coil winder.

I think the word should simply be "move".

To make things easier for other readers this is the code

void blockWinder() {
  for (int i = 0; i < 20; i ++) {
    stepper1.setCurrentPosition(0);

    stepper1.setMaxSpeed(blockSPS[i]);
    stepper1.setAcceleration(autoSets[0]);

    stepper2.setMaxSpeed(MAX_SPD);
    stepper2.setAcceleration(MAX_XCL);
    if (jobComplete != true) {
    stepper2.moveTo(Move2_A[i]);
    stepper2.runToPosition();
    }
    if (stepCancel == true) {break;};
    if (jobComplete == true) {break;};

    stepper2.setMaxSpeed(MaxSpeed2[i]);

    if (dirCW != true) {
      Move1[i] = -Move1[i];
    }

    stepper1.moveTo(Move1[i]);

    if (blockScatter[i] == 1) {
      scatterWind = true;
    } else {
      scatterWind = false;
    };


    if ((dirCW != true) && (scatterWind != true)) {
      while ((stepper1.distanceToGo() < 0) && (jobComplete != true)) {
        if (stepCancel == true) {break;};
        stepper1.run();
        if (stepper2.currentPosition() == Move2_A[i]) {
          stepper2.moveTo(Move2_B[i]);
        }
        if (stepper2.currentPosition() == Move2_B[i]) {
          stepper2.moveTo(Move2_A[i]);
        }
        stepper2.run();
      }
    }

if (jobComplete == true) {break;};

    if ((dirCW == true) && (scatterWind != true)) {
      while ((stepper1.distanceToGo() > 0) && (jobComplete != true)) {
        if (stepCancel == true) {break;};
        stepper1.run();
        if (stepper2.currentPosition() == Move2_A[i]) {
          stepper2.moveTo(Move2_B[i]);
        }
        if (stepper2.currentPosition() == Move2_B[i]) {
          stepper2.moveTo(Move2_A[i]);
        }
        stepper2.run();
      };
    }

if (jobComplete == true) {break;};





    s_pcnt = scatterArea[i];
    s_i_pcnt = scatterInt[i];
    s_cnt = 1;
    s_dist = round(s2_bobbinSpace * s_pcnt);
    s_inc = round(s_dist * s_i_pcnt);
    s_a = s_dist;
    s_b = s_inc - s_a;
    s_aR = -s_dist;
    s_bR = -s_inc - s_aR;

s_end_count = round(s_dist / s_inc);


s_end_A = Move2_A[i] + s_dist;
s_end_B = Move2_B[i] - s_dist;
s_bound_1 = false;
s_bound_2 = true;

    if ((dirCW != true) && (scatterWind == true)) {



      while ((stepper1.distanceToGo() < 0) && (jobComplete != true)) {
        if (stepCancel == true) {break;};
        stepper1.run();

        if ((stepper2.currentPosition() == Move2_A[i]) && (s_bound_2 == true)) {
          s_bound_1 = true;
          s_bound_2 = false;
          s_b_ret = true;
          s_a_ret = false;
          end_bool_L = true;
          end_cnter = 1;
        };
          
        if ((stepper2.currentPosition() == Move2_B[i]) && (s_bound_1 == true)) {
          s_bound_2 = true;
          s_bound_1 = false;
          s_b_ret = true;
          s_a_ret = false;
          end_bool_R = true;
          end_cnter = 1;
        };


        if (s_bound_1 == true) {

if (jobComplete == true) {break;};
        
          if ((stepper2.distanceToGo() == 0) && (s_a_ret == true)) {
            if ((end_bool_L == true) && (end_cnter < s_end_count)) {
              stepper2.move(-(s_dist - (s_inc * end_cnter)));
              end_cnter = end_cnter + 1;
            } else {
            stepper2.move(s_b);
            end_bool_L = false;
            }
            s_a_ret = false;
            s_b_ret = true;
          }
          
          if ((stepper2.distanceToGo() == 0) && (s_b_ret == true)) {
            if ((end_bool_L == true) && (end_cnter < s_end_count)) {
              stepper2.move(s_dist - (s_inc * end_cnter));
            } else {
              stepper2.move(s_a);
              end_bool_L = false;
            }
            s_a_ret = true;
            s_b_ret = false;
          }
        }
        
        if (s_bound_2 == true) {

if (jobComplete == true) {break;};

          if ((stepper2.distanceToGo() == 0) && (s_a_ret == true)) {
            if ((end_bool_R == true) && (end_cnter < s_end_count)) {
              stepper2.move(s_dist - (s_inc * end_cnter));
              end_cnter = end_cnter + 1;
            } else {
            stepper2.move(s_bR);
            end_bool_R = false;
            }
            s_a_ret = false;
            s_b_ret = true;
          }
          if ((stepper2.distanceToGo() == 0) && (s_b_ret == true)) {
            if ((end_bool_R == true) && (end_cnter < s_end_count)) {
              stepper2.move(-(s_dist - (s_inc * end_cnter)));
            } else {
            stepper2.move(s_aR);
            end_bool_R = false;
            }
            s_a_ret = true;
            s_b_ret = false;
          }
        }

        stepper2.run();
      }

    };


    if ((dirCW == true) && (scatterWind == true)) {

      while ((stepper1.distanceToGo() > 0) && (jobComplete != true)) {
        if (stepCancel == true) {break;};
        stepper1.run();

        if ((stepper2.currentPosition() == Move2_A[i]) && (s_bound_2 == true)) {
          s_bound_1 = true;
          s_bound_2 = false;
          s_b_ret = true;
          s_a_ret = false;
          end_bool_L = true;
          end_cnter = 1;
        };
          
        if ((stepper2.currentPosition() == Move2_B[i]) && (s_bound_1 == true)) {
          s_bound_2 = true;
          s_bound_1 = false;
          s_b_ret = true;
          s_a_ret = false;
          end_bool_R = true;
          end_cnter = 1;
        };


        if (s_bound_1 == true) {

if (jobComplete == true) {break;};

          if ((stepper2.distanceToGo() == 0) && (s_a_ret == true)) {
            if ((end_bool_L == true) && (end_cnter < s_end_count)) {
              stepper2.move(-(s_dist - (s_inc * end_cnter)));
              end_cnter = end_cnter + 1;
            } else {
            stepper2.move(s_b);
            end_bool_L = false;
            }
            s_a_ret = false;
            s_b_ret = true;
          }
          
          if ((stepper2.distanceToGo() == 0) && (s_b_ret == true)) {
            if ((end_bool_L == true) && (end_cnter < s_end_count)) {
              stepper2.move(s_dist - (s_inc * end_cnter));
            } else {
              stepper2.move(s_a);
              end_bool_L = false;
            }
            s_a_ret = true;
            s_b_ret = false;
          }
        }
        
        if (s_bound_2 == true) {

if (jobComplete == true) {break;};

          if ((stepper2.distanceToGo() == 0) && (s_a_ret == true)) {
            if ((end_bool_R == true) && (end_cnter < s_end_count)) {
              stepper2.move(s_dist - (s_inc * end_cnter));
              end_cnter = end_cnter + 1;
            } else {
            stepper2.move(s_bR);
            end_bool_R = false;
            }
            s_a_ret = false;
            s_b_ret = true;
          }
          if ((stepper2.distanceToGo() == 0) && (s_b_ret == true)) {
            if ((end_bool_R == true) && (end_cnter < s_end_count)) {
              stepper2.move(-(s_dist - (s_inc * end_cnter)));
            } else {
            stepper2.move(s_aR);
            end_bool_R = false;
            }
            s_a_ret = true;
            s_b_ret = false;
          }
        }

        stepper2.run();
      }
    };
    
if (jobComplete == true) {break;};
if (stepCancel == true) {break;};

    s1_blockAdd = s1_blockAdd + Move1[i];
    if ((blockWinds[i] > 0) && (blockCntr < blocksTotal)) {
      blockCntr = blockCntr + 1;
    };
  };

  stepper2.moveTo(0);
  stepper2.runToPosition();
  windStatus = false;
  scatterWind = false;
  if (stepCancel == true) {
    changeTxtFieldLong(3, lastWindCount);
  };
  changeTxtField(4, "0");
  jobComplete = true;
  stepper1.disableOutputs();
  stepper2.disableOutputs();
  vTaskDelay(10);
};

...R

Sorry, but I'm lazy and I can't quickly make any sense of your code. Can you write a few paragraphs in English that describe how it is intended to work.

And this is only part of a program - how is the function called? What is it working in conjunction with.

Also please use the AutoFormat tool to indent the code consistently.

I note that this line appears several times - repeated code is rarely a good sign.

if (jobComplete == true) {break;};

If you want a motor to move quickly it is more usual to use full steps rather than microsteps. Also positioning will probably be more reliable with full steps.

Rather than moving the motor to an end-stop to re-establish position you could use a non-contact sensor that is triggered roughly in the middle of each pass.

Finally I wonder would it be simpler to write the code without the AccelStepper library. Making your own acceleration code is not that difficult. See this simple acceleration code for an example.

...R

I will have to start using another word...maybe 'alternate', or 'fluctuate'. I only posted the part of the code that's looping and sending instructions motors in question because the rest of the code is over 3000 lines and it would be unfair to try to make someone else figure out.

All that happens is stepper1 is told to go clockwise or counter clockwise (CW/CCW) and stepper2 is told to move between one of 2 main modes of operation - simple back and forth, or a more complex chain of movements. Both of these modes operate within a user-set boundary - usually no more than 20mm.

I switched from whole step to quarter-step to reduce all the horrible rattling and other issues related to acceleration, but now that it appears to be losing steps, I'm considering going the other way with it and trying 1/8th stepping..

Speed isn't the main concern here, accuracy moreso. I ordered a stronger motor with 100oz of holding torque (compared to 64oz on the current ones) too because I have a feeling it's losing steps in the rapid acceleration back and forth many times over and over.

Assuming that the code is accurate in defining the parameters of movement, is that a good way to approach the issue?

jtbennett:
Speed isn't the main concern here, accuracy moreso. I ordered a stronger motor with 100oz of holding torque (compared to 64oz on the current ones) too because I have a feeling it's losing steps in the rapid acceleration back and forth many times over and over.

Don't you see a contradiction between the two phrases I have highlighted?

If this was my problem and I thought the motor was losing steps I would write a short 20 or 30 line program to exercise the motor at the desired speed and check how it performs.

Stepper motors lose torque to a significant extent as the speed increases. Holding torque is only relevant when the motor is stationary. The loss of torque with speed can be counteracted to some extent by increasing the voltage of the motor power supply. The better motor manufacturers publish graphs that show the relationship between torque and speed for different voltages. You may find it helpful to search for some of them on the web.

...R

Robin2:
Don't you see a contradiction between the two phrases I have highlighted?

I see, I should just clarify though - I meant that the solution I'm looking for doesn't have to maintain speed. Really, I'm just looking for a more certain explanation that I can work with. If the solution will reduce the speed on my current setup, then I can always replace the motor something more powerful, no?

I took a look at closed loop motors too, but the price is just silly - plus I'd have to use a different driver and update all of the code.

Robin2:
If this was my problem and I thought the motor was losing steps I would write a short 20 or 30 line program to exercise the motor at the desired speed and check how it performs.

That's a good idea - one of the...fluctuation modes is just a simple back and forth that I can run at a high speed. I'll do that and at least rule out that it's the other mode being entirely responsible. The other mode has a lot more rapid direction reversals, so it's probably just making the issue apparent more quickly.

jtbennett:
That's a good idea - one of the...fluctuation modes is just a simple back and forth that I can run at a high speed. I'll do that and at least rule out that it's the other mode being entirely responsible. The other mode has a lot more rapid direction reversals, so it's probably just making the issue apparent more quickly.

What "other mode" - keep in mind that we know nothing about your project unless you tell us, whereas you have probably been living with it for weeks.

The AccelStepper library is not designed for high step rates. Obviously it will work faster on an ESP32 compared to an Uno or Mega.

Can you be sure that the "system" of the ESP32 does not interfere - I'm not familiar with the inner workings of it. IIRC with an ESP8266 it is necessary to call the WiFi part at regular intervals and that can effect timing.

...R

Robin2:
The AccelStepper library is not designed for high step rates. Obviously it will work faster on an ESP32 compared to an Uno or Mega.

Is there some other library I should be using for this? Sorry, I've never heard that before - I thought it was all down to the processing speed. When you say high step rates, I assume you mean microstepping to finer degrees (1/4 to 1/16 to 1/64, etc)

Robin2:
What "other mode" - keep in mind that we know nothing about your project unless you tell us, whereas you have probably been living with it for weeks.

Been living in it for 10 months actually :expressionless:

I never noticed this straying from the boundaries until now since I'm running a lot of tests with it. It's extremely frustrating and distressing thinking that I might have to change hardware or libraries after I've been designing everything around AccelStepper and the two 2A motors all this time (including a custom circuit with the TB6600s onboard being made now).

If I could just switch to a bigger motor and resolve it, that'd be the most ideal scenario. Right now I'm making a few test modes so I can compare the effects of various levels of acceleration and whatnot because it's difficult to pin down.

I really would like to know, though, if resonance could be responsible for this. The more back and forth commands that run in between the distance from the zero point to the end boundary increases the amount it overshoots the end. When it starts using the simpler sway mode, it doesn't overshoot significantly. The overshoot still occurred with the acceleration turned down significantly....slightly less pronounced though.

I actually noticed that putting my hand on the chassis to reduce vibration lessened the amount of overshooting going on...so I ordered some dampers too. Would I be better off using a smaller microstep setting (1/8 or 1/16) or is that just not gonna work for AccelStep + ESP32?

I guess I'm just gonna have to wait for parts to arrive and keep testing the thing, but I'd really like to figure out exactly what it is still.

Right. firstly microstepping is essential to top performance from a stepper. Never use full steps if you want
performance, you'll get bad resonance issues and vibration and noise and misstepping is much more likely to happen (although carefully tuned mechanical damping can mitigate this significantly).

x8 to x16 microstepping is a reasonable compromise in the first instance.

Secondly if you try to push a motor to its limits in terms of max speed or acceleration it will be unreliable, you
need to find the working limits by experiment for both speed and acceleration, then back them off 20% or so for reliability - and maybe even more after testing.

MarkT:
Right. firstly microstepping is essential to top performance from a stepper. Never use full steps if you want
performance, you'll get bad resonance issues and vibration and noise and misstepping is much more likely to happen (although carefully tuned mechanical damping can mitigate this significantly).

x8 to x16 microstepping is a reasonable compromise in the first instance.

Secondly if you try to push a motor to its limits in terms of max speed or acceleration it will be unreliable, you
need to find the working limits by experiment for both speed and acceleration, then back them off 20% or so for reliability - and maybe even more after testing.

Thanks for responding! I have been doing some more reading and research, and I am convinced that there's some resonance issues (I'm set at 1/4 step, but maybe it's not enough). I believe this to be the issue because I'm seeing an 'overshoot' effect, as well as noticing it's worse when there's a specific vibration/tone coming from the motor.

So I suppose I'm going to try dropping down to 1/8th step.

I ordered some motor dampers to put a load on them and get rid of the vibrations, and I also am considering switching over to the Grbl_ESP32 port, the Marlin port, or this Teacup firmware. Robin2 had mentioned that my AccelStepper library is not a good choice for high pulse rates - I thought it would be fine since the ESP32 has a much higher speed than the Arduinos it normally gets run on, but maybe not.. I'm most familiar with Grbl so I guess I'll commit to that.

I also ordered a higher torque motor (still capable of mounting on NEMA 17 brackets, but not as powerful as generic standard 23 sized motor). I'm really hoping that'll be enough to solve this...does that sound like a good plan?

jtbennett:
Robin2 had mentioned that my AccelStepper library is not a good choice for high pulse rates - I thought it would be fine since the ESP32 has a much higher speed than the Arduinos it normally gets run on, but maybe not.. I'm most familiar with Grbl so I guess I'll commit to that.

I mentioned it only so that you would take it into account. I don't know what the upper limit would be with an ESP32 - you need to experiment to figure that out yourself.

Note that the AccelStepper library will not know whether you are using microsteps or full steps - it just produces step pulses.

I gave you a link earlier to some simple acceleration code.

...R

Robin2:
I mentioned it only so that you would take it into account. I don't know what the upper limit would be with an ESP32 - you need to experiment to figure that out yourself.

Note that the AccelStepper library will not know whether you are using microsteps or full steps - it just produces step pulses.

I gave you a link earlier to some simple acceleration code.

...R

I'll be running every variable possible over the next couple weeks to figure it out - I have some dampers and a larger motor coming, and I'm working on coding a bunch of test sequences.

1/4 step will be noisy and involve vibration, x16 will fare much better. One thing I notice is that belt drive systems are fairly well mechanically damped by their nature compared to lead-screw drive.

MarkT:
1/4 step will be noisy and involve vibration, x16 will fare much better. One thing I notice is that belt drive systems are fairly well mechanically damped by their nature compared to lead-screw drive.

I've read the same thing since the belt serves as a load, but I'm determined to leave as much of the machine in its current state as possible so a damper can be dropped in without affecting anything really.