ANOTHER post about controlling (smoothing) a stepper motor

I have these stepper motors and drivers:
https://www.omc-stepperonline.com/index.php?route=product/isearch&search=1+Axis+Stepper+Kit+4.0Nm(566oz.in)+Nema+24+Stepper+Motor

566oz/in - I needed something beefy to drive the mecanum wheels.

Each Axle (2 motors) shares a 24v 7A battery pack. I currently have the drivers set at 1.2A.

Here is some super basic code:

#include <Stepper.h>
int sensorPin = A2;
int sensorValue = 0;
int mappedValue = 0;
int mtrSpeed = 0; 
int currCmd = 1;
int serialCmd = 0;
boolean stationary = true;
String pinConfig = "forward";

//MotorPins
//Motor1 = 6,7
//Motor2 = 8,9
//Motor3 = 22,24
//Motor4 = 51,53

void origPinConfig() { //Orig = Forward
  //Motor1
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  digitalWrite(6, LOW);
  digitalWrite(7, LOW);

  //Motor2
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  digitalWrite(8, HIGH);
  digitalWrite(9, LOW);

  //Motor3
  pinMode(22, OUTPUT);
  pinMode(24, OUTPUT);
  digitalWrite(22, LOW);
  digitalWrite(24, LOW);

  //Motor4
  pinMode(51, OUTPUT);
  pinMode(53, OUTPUT);
  digitalWrite(51, HIGH);
  digitalWrite(53, LOW);

  Serial.println("Starting pin configuration set.");
}

void setup() {
  Serial.begin(9600);
  origPinConfig();
}

//All four wheels forward
void forward() {
  //TODO
  //Accelerate
  //Decelerate
  digitalWrite(7, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(24, HIGH);
  digitalWrite(53, HIGH);
  delayMicroseconds(mtrSpeed);
  digitalWrite(7, LOW);
  digitalWrite(9, LOW);
  digitalWrite(24, LOW);
  digitalWrite(53, LOW);
  delayMicroseconds(mtrSpeed);
}

void loop() {

  if(Serial.available()){
    //Dont care yet
  }
 
  mtrSpeed = 2600;
  
  if(currCmd == 1){
    forward();
  }
}

Here's a 10 second video clip of what the above code produces: Choppy stepper motion - YouTube

You can hear pretty easily that the steppers seem to be stepping too fast, yet still not translating to the RPM I was expecting.

I read Robin's excellent post about Stepper motors some time ago and I thought I had a good understanding of them - until I realized I was writing blocking code. This code will soon listen for a Serial command to change the direction of motion.
When I altered the code to be "non-blocking" the motors are now very choppy. I think it has something to do with the rate at which the Loop method is executed and calls the forward() method.

The drivers are currently set at 400 Steps per revolution. I am trying to get 60RPM - which will translate to 6-7 MPH with these 6" wheels. By my best guess, I am only getting 48-50RPM from this configuration.

I know steppers aren't meant to be "Super-smooth" but you can tell from the video that these are running really rough. If I had this project to do over again, I would probably go with DC motors. I had hoped to achieve a high degree of precision with stepper motors (this is a partially completed autonomous bot).

Any advice on how to "calm/quiet/smooth" these stepper motors?

TIA

Let me say that is a cool-looking gizmo. I dunno what it does, but it looks badass.

For a little more smoothness, turn on the microstepping.
And maybe dial down the current, if possible. Those motors are huge.

Also, you have to get rid of all that digitalWrite() slowness.

If the digitalWrite() function is causing problems then try the digitalWriteFast library. It is almost as fast as port manipulation and much easier to use.

The motion in your video looks pretty good to me, but if you are not using micro-stepping then that should smooth things a lot. The downside is that it will need faster step rates for the same wheel speed.

You need to post your complete program.

Also give some examples of the messages you need to receive using Serial. I presume you are not using blocking Serial functions such as Serial.parseInt()? Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

...R

400 steps per revolution will always be somewhat choppy. Its almost a degree per step. I am working on a gauge and I am using a small geared automotive gauge stepper that has ~1000 steps per revolution and I found that not smooth enough. even though the diameter of my needle is much less than that of your wheels. Adding a microstepping controller with 1/4 steps, now its smooth.

That said; your code is incorrect. You need to give a short pulse to the controller to generate a step, but the length of that pulse shouldnt be variable (and will be MUCH shorter than the 2600 microseconds you're using now, on my controller its 1 microsecond). What should be variable is the time between steps.

And im sure its just proof of concept at this point, but you dont want to control speed, ie, time between steps, the way you're doing it now, using delays. Either use a library like accelstepper or keep track of a timer since your last step, and do a step when you have to, and do nothing when you dont have to. But dont use delay. Especially when microstepping 4 motors.

Stepper motors are a poor choice for main-traction, about an order of magnitude less power efficient
than the usual approach of DC gearmotor + wheel-encoder, and much less efficient on stop-start use as
they consume full power when stationary, whereas DC motor + encoder consumes minimal power on
stationary.

Microstepping is pretty much mandatory for large steppers to keep noise and vibration to sane levels
try x8 or x16 in the first instance. This does mean you have to be able to generate high step rates.
It is possible, with care, to change to lower microstepping ratios at higher speeds (stepper controllers
are designed to do this, the datasheets explain it a bit), which allows faster speed for a given top step pulse rate.

@Robin2:

That is the complete program :slight_smile: I have been back and forth with AccelStepper, MultiStepper, and the method you see above where I don't use a stepper library at all.

The program is "simple" to define, but I have found it is much harder to implement. With the mecanum wheels the bot can move in one of 10 directions. I've given each direction an index between 0-9

0 - Forward (lets call it north)
1 - NorthWest
2 - West
3 - SouthWest
4 - South
5 - SouthEast
6 - East
7 - NorthEast
8 - Rotate Left
9 - Rotate Right
z - stop

Each of these 9 directions has a corresponding combination of wheels to turn (see the attached image)

My plan is to use an ESP8266 to connect to an onboard WIFI network and subscribe to a message queue. When a msg arrives - it is simply a single digit from 0-9 (or z for stop). This is of course the command for the Mega to drive the appropriate motors in the proper direction.

The ESP8266 is physically attached to the Mega via TX2/RX2.

This setup between the ESP8266 and Mega has been built and tested (the ESP8266 is the little white box with the flashing green LCD in the video).

The challenge has been writing the code to listen for the Serial.Available to fire in the loop AND then try to run the motors as smooth as possible in the direction that the serial command indicates.

If time permits, I would love to add a second and third element to the serial message - Duration and Speed. So each message would contain Direction + Duration + Speed. at this point, I am struggling to get the direction right.

Hope that clears it up. Basically I am still at the dang drawing board.

@All,

Thanks for the great feedback and advice.

However, perhaps I am getting frustrated and sloppy - or my google skills are failing me. I don't understand what it means to:

"turn on Microstepping"

Is this done in the code? On the Driver? I have attached an image of the driver's possible DIP switch configurations.

I made some errors early on in this project that I can't change now - for instance the motor + driver combo I selected that has ZERO examples of Arduino code available and a MFG that - very politely - told me they can't help me with any code. I understand, it's not their problem.

Could I trouble someone to perhaps link me to a sample sketch of Microstepping?

I will be sure to share better pics and video when I get this beasty assembled. The portion you saw in the video is just the lower half. There is an upper half that has a torso + arms + head with some Computer vision gear.

Thanks!

With your stepper controller, you simply set the jumpers to define the microstepping. Thats all. Of course, now each time you pulse the stepper, the motor will only turn a fraction of the degrees it would without microstepping, so you have to pulse more often to achieve the same speed. In your current program, that means reducing the delay, but you need to get rid of that delay, see below.

Aside from the microstepping (which is usually configured through IO pins instead of jumpers) your stepper controller is no different than any other, so any example you'll find will apply. PLease be sure to reread my post about pulse length. The manual of your stepper states it needs 1.5uS pulses. Not 2600mS. And I strongly recommend using a library with acceleration support, like accelstepper.

As for running serial comms; there is no problem doing both. You just have to do non blocking serial reads and drive the stepper with non blocking moves, and not regulate the speed by using delay() like you're doing now. Again, see my post above. Using delayMicroseconds for the pulse duration is probably ok, but you shouldnt use delay anywhere else.