Steppers Stalling

Hi - I have the following setup:

  • 32v / 10A power supply
  • 2 x TB6600 drivers (the kind with 6 pins), set @ 2.5A & 1/8 stepping (1600 steps per revolution)
  • 2 x Nema23 motors, 1.8deg, 2.9A
  • ESP32 shield controlling, dedicated one of the cores to running just the motors

I’ve been having problems with complete stalls, and it’s possible I’m missing steps (rpm count coming up short consistently).

It’s particularly worse when both motors are running simultaneously - one of them spinning one direction constantly, the other going forward and reverse rapidly.

I noticed it occurs at particular points of rpm speed, so I believe that resonance points may be the cause, but when I increase the microststepping, the motor’s top speed drops down and I need the torque for the rapid back and forth.

Both motors are attached to a sheet metal chassis - is it possible that resonance issues can occur between the motors simply from vibrational transfer?

I have ordered some shielded cables to try, and I read something inverted pins being a possible cause of lost steps, but I just wanted to ask some smarter people here if there are any glaring issues.

I also recently changed the wiring to single core 22awg, I didn’t have these problems when using stranded 22awg wire...though the sheet metal chassis is new too so who knows.

Why not go back to that wire then?

1 Like

I'll be trying that tomorrow, but that's just one change that was made lately, I switched from a plastic chassis to a sheet metal one and increased the size of the power supply. Is it theoretically a culprit to use single core wire over stranded wire (same overall gauge)?

Pages found with a Google search for "current capacity solid vs. stranded wire" seem to favor solid wire for carrying current.

Attack one thing at the time. Possibly during the change of cables, reconnecting, something else tricked You. Work systematically, one thing at the time.

@groundFungus damn...I'm so tired of wires. I read that shielded wires are better between the motor and driver to avoid any signal distortion, so I ordered some of those. I also read that the shielding should be grounded at the driver/power supply end only. Does that mean just running that ground straight to the power supply, or should I connect it to the same driver terminal as the motor ground?

@Railroader I am feeling very frustrated with the whole thing, you're right. Maybe you can help me with the most suspicious part here though: when operating a single motor (motor one, constant clockwise rotation), I get a stall at higher RPMs (I reduced it to an 800 RPM limit in the software now and that seems to avoid it). When I run motor one with motor two (rapid CW-CCW motion) simultaneously, for some reason only motor one stalls but not motor two.

Quoting: I read that shielded wires are better between the motor and driver.....
Where on earth have You been reading that? Shielded cables are used for signals, not power.

Quoting: I get a stall at higher RPMs....

Post the code! Use code tags!

Steppers started bang on have a lot lower highest RPM than steppers accelerated in a controlled way.

I do not know about shielding stepper power wires. I have not heard of that being done.

In applications that call for shielded cable, the shield and ground should be separate conductors. The shield should not carry current nor be the return for a signal. Only one end of the shield should be connected to ground to prevent current flow in the shield.

From your description of the problem, my opinion is that if you use the microstepping to mitigate resonance and can't get enough torque, then you need a higher supply voltage for the motors that you have (up to the max for your drivers) or stronger stepper motors.

1 Like

OK tomorrow I'm gonna try some stuff out and come back to annoy you people again when it probably doesn't work.

I just can't understand why motor 2's motion would cause motor 1 to stall. Motor 2 is the one using a lot of torque for rapid back and forth motion, motor 1 just spins up with 4000 step acceleration (in accel stepper) to 800rpms and somehow seems to affected along the way. Without load too.

e: while I'm posting, do you have any suggestions for power supply & driver types (maybe motor size too?) that'd definitely give the torque needed for rapid back-and-forth movement with 1/16 or 1/32 microstepping on a 4-start lead screw? I was under the impression that what I had would do it.

Your response here concerns me. The wire shield should go directly to the controller ground which goes directly to the power supply ground.
You mention a "motor ground". What is this? Is this a wire from the motor frame to ground? If so, why?
Paul

OK I'm back. I did a number of things today:

  • Changed the wires
  • Switched drivers and motors to rule out anything faulty
  • Removed the motors from their chassis mounts to operate them without any vibrations or load
  • Varied RPMs during operation

Here's a couple videos of the thing in action:

  1. Manual mode - only Motor 1 is spinning in a single direction - works fine under 800 RPMs, no stalls.
  2. Auto mode - Motor 1 spins in a single direction again, whilst Motor 2 strafes back and forth at short intervals. Motor 1 experiences stalling - I've noted that the stalls seem to occur at particular changeover points where the run commands (in a loop) end after a parameter is met and the program switches to the next loop til a new parameter is met. If I slow the RPMs of Motor 1 down significantly, I can feel the little pauses occurring in Motor 1 while the loop switches. I think it's possible that there's just a few microseconds of time between the loops where Motor 1 isn't receiving a command, and the position is overrun and the whole thing loses its place (I have only basic understanding of how stepper motors function).

So I am now starting to think it's a software issue and would like some help if possible.

Here's an example of what's happening programmatically while that auto mode is running:


    case PATTERN_R1:
              for (int i = 0; i < s_end_count; i ++) {
                  stepper2.moveTo(Move_2_A + (s_inc * i));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_L1;}
                    stepper2.moveTo(Move_2_A);
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_L1;}
                }
              Motor_State = PATTERN_R2;
    break;
    case PATTERN_R2:
               if ((Move_2_B - stepper2.currentPosition()) < s_dist) {
                  stepper2.moveTo(Move_2_B);
                } else {
                  stepper2.moveTo(stepper2.currentPosition() + s_dist);
                };
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    stepper1.run();
                    stepper2.run();
                  }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_R3;}
                    stepper2.moveTo(stepper2.currentPosition() - (s_dist - s_inc));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    stepper1.run();
                    stepper2.run();
                  }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_R3;}
    break;
    
    case PATTERN_R3:            
              for (int i = s_end_count; i > 0; i --) {
                  stepper2.moveTo(Move_2_B - (s_inc * i));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_R1;}
                    stepper2.moveTo(Move_2_B);
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_R1;}
                }
              Motor_State = PATTERN_L1;
    break;
    case PATTERN_L1:
              for (int i = 0; i < s_end_count; i ++) {
                  stepper2.moveTo(Move_2_B - (s_inc * i));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_R1;}
                    stepper2.moveTo(Move_2_B);
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_R1;}
                }
              Motor_State = PATTERN_L2;
    break;
    case PATTERN_L2:

    if ((stepper2.currentPosition() - Move_2_A) < s_dist) {
                  stepper2.moveTo(Move_2_A);
                } else {
                  stepper2.moveTo(stepper2.currentPosition() - s_dist);
                };

                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                  }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_L3;}
                    stepper2.moveTo(stepper2.currentPosition() + (s_dist - s_inc));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_A) {
                    stepper1.run();
                    stepper2.run();
                  }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_A) {Motor_State = PATTERN_L3;}
    break;
    case PATTERN_L3:
              for (int i = s_end_count; i > 0; i --) {
                  stepper2.moveTo(Move_2_A + (s_inc * i));
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_R3;}
                    stepper2.moveTo(Move_2_A);
                  while (stepper2.distanceToGo() != 0 && stepper1.distanceToGo() != 0 && stepper2.currentPosition() != Move_2_B) {
                    stepper1.run();
                    stepper2.run();
                    }
                    if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
                    if (stepper2.currentPosition() == Move_2_B) {Motor_State = PATTERN_R3;}
                }
              Motor_State = PATTERN_R1;
    break;

That's on one of the ESP32's cores operating independently. Ignoring the math and whatnot, you can see it jumps from case to case / loop to loop. The other core is dedicated to receiving serial commands from a touchscreen panel, so it's hard for me to just assign a core to run one motor independently from the other.

My first thought is to try to stop all this case switching business and at least have all the loops occur sequentially...I'm really not a great programmer, or engineer, or anything like that, so I'm sort of just trying things out. I won't be able to get to it til tomorrow so in the meantime, any insight or ideas that may seem worth investigating would be very helpful. Thank you!

Edit: sorry, I forgot to mention that in that video of the auto mode, you see two modes happening - first there's a simple back and forth strafing on Motor 2 that works fine with that 800rpm ceiling on Motor 1. The next is the problem mode, where there's rapid back and forth motion on Motor 2 that stalls Motor 1. The code for the functional simple strafing mode looks like this:

    case OSCILLATE_R:

            while ((stepper1.distanceToGo() != 0) && (cancel_run == false)) {
              stepper2.moveTo(Move_2_B);
              while (stepper2.distanceToGo() != 0) {
                  stepper1.run();
                  stepper2.run();
                  };
              stepper2.moveTo(Move_2_A);
              while (stepper2.distanceToGo() != 0) {
                  stepper1.run();
                  stepper2.run();
                  };
                  };
              if (cancel_run == true) {Motor_State = CANCELLED;}
              if (stepper1.distanceToGo() == 0) {Motor_State = SELECTOR_A;}
    break;

Two loops, but no case switching.

videos not public.

The symptoms should strongly like resonanace issues, extra mechanical damping is one solution to consider, especially if this isn't belt-driven (rubber belts are useful for damping stepper resonance).

Videos are set to public, dunno why you couldn't see them. I disconnected them from the chassis and tested them without load, even held one up in my hand so they weren't vibrating the same surface. Are you saying one motor's resonance points can affect the other's?

After communicating with some people on CNCZone, it seems like my 32v power supply and TB6600 drivers seem to be a bad choice for the RPMs I want out of this thing. So I'll be upgrading to a 48v supply and DM556T drivers. In case anyone else is running into similar problems.

I think vimeo was being slow to make them available then - works now.

You know that a bare stepper not connected to a load is the most resonant confuguration and thus usually will have strong mid-band resonance issues. Whereas any mechanical system with damping (belt-drive, rubber-coupling) will be less prone to resonance. Microstepping reduces resonance by reducing the step transient size.

Resonance is not strongly affected by supply voltage, that affects the available torque at speed and top speed mainly.

I wonder if a solution might be to use a belt drive with a pulley ratio that will give the output shaft rotation rates at reasonable stepper rotation rates. That would help with resonances, too.

@MarkT Normally, Motor 2 is connected to a leadscrew, while Motor 1 is driving a wheel directly. The thing that throws me off is that Motor 2's direction changes are the cause of Motor 1's stalling.

@groundFungus I would like to leave the wheel on Motor 1 driving directly for simplicity, but if it's possible that it's leaving itself more susceptible to resonance peaks of the other motor, I suppose I'll have to find out the hard way after I install new drivers and a higher voltage power supply.

I'm not sure how the stepper library you are using handles things, but could it be that there's just not enough time gap between pulses to fit both in? As in when motor 2 pulse is being sent, it's blocking at the time motor 1 pulse is required.

A logic analyser would show you exactly what is going on.