I am trying to update the speed of two stepper motors in real time using a potentiometer. I have debugged with the serial monitor and am getting the desired speeds to print dozens of times per second. While my motors do change direction in real time according to the value of the potentiometer (postive/negative for CW/CCW), they are only stepping about once per second.
I have set their accelerations to 1000. In my loop, I call setSpeed() for each stepper based on conditions of the potentiometer in. The last statement of my loop is
stepper1.runSpeed();
stepper2.runSpeed();
I have ran other online examples, so I know my steppers are wired correctly and can certainly rotate faster than 1 step per second.
Am I correctly implementing setSpeed() and runSpeed()? As I said, they are responsive to the direction but not the speed.
Lol I figured it would be overwhelming with my lack of comments. I find it funny that my loop() just so happens to begin on line 42. My analog reading is from a joystick.
It looks like an awful lot of floating point math per loop(). How often do you actually move your potentiometers? I think you could improve performance significantly if you rate-limited the updating to something matching a human interface time (10Hz?) and perhaps further limited the updating to only happen with changes in the inputs.
Also, Arduino has atan2(y,x):
atan2f()
float atan2f
(
float
__y,
float
__x
)
The atan2f() function computes the principal value of the arc tangent of __y / __x, using the signs of both arguments to determine the quadrant of the return value. The returned value is in the range [-pi, +pi] radians.
The joystick will be moved almost constantly, so I need constant updating. I am happy with how many times I am getting serial monitor updates of the math (dozens per second). I just don't see how speed() gives the correct speed in the monitor, yet my stepper motors update at a seemingly constant rate of about once per second (in the correct direction).
With these two changes, the runspeed()s can potentially happen much faster than dozens of Hz, decoupling the stepping from the IO and allowing faster speeds.
I would be concerned about the effect of calling stepperX.setSpeed(...) umpteen times per second, and whether each call was restarting/resetting some process/calculations/variables in the library over and over and over again.
I think I understand, but I don't think it would work with my low quality joystick as the values change constantly even without movement. I do plan on upgrading though
This was my initial fear. Perhaps acceleration is restarting at zero with every call of setSpeed()? So the motor is always restarting its acceleration from zero or something like that
Poll the motor and step it if a step is due, implementing a constant speed as set by the most recent call to setSpeed(). You must call this as frequently as possible, but at least once per step interval,
I am 99% confident this is due to my misunderstanding of how the AccelStepper library works... particularly the .setSpeed() and .runSpeed() functions. Why?
the motors spin as expected with various online example codes... both directions, fast/slow... so they are wired correctly.
in the end of my loop(), I print the stepper1.speed(), stepper2.speed(), and these give my desired RPM in the correct directions... so it is not my conditional statement logic.
the serial log updates dozens of times per second (enough for my purposes), so it is not a matter of processor speed/expensive calculations.
When I power my circuit, the motors make an idle running noise and take just a single step every few seconds or so (not often) in seemingly random intervals.
My only guess would be that I am not giving the steppers enough time to accelerate, so with every update, they start with a velocity of zero (dozens of times per second, briefly accelerating to one step before restarting a fraction of a second later). Would millis() work around this? Here is my code (I call runSpeed() at the very end):
#include <AccelStepper.h>
// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL2WIRE, 19, 18); // left stepper
AccelStepper stepper2(AccelStepper::FULL2WIRE, 17, 16); // right stepper
#define PI 3.14159
//pins
const int x_pin = 34; //should be 36??????
const int y_pin = 35; //should be 39??????
//const int SW = 34;
//vars for analog readings
float x = 0; //left right
float y = 0; //up down
//vars for speeds/directions etc
float hypotenuse;
float p1;
float p2;
float q1;
float q2;
float var1;
const int deadzone = 11;
void setup() {
Serial.begin(115200);
stepper1.setMaxSpeed(1024.0);
stepper1.setAcceleration(1000.0);
stepper2.setMaxSpeed(1024.0);
stepper2.setAcceleration(1000.0);
// Set ADC resolution to 10 bits
analogReadResolution(10);
}
void loop() {
x = -(analogRead(x_pin) - 473); // xpin reads 0 to 1023 where 512 is middle (stationary)
y = (analogRead(y_pin) - 473); // ypin reads 0 to 1023 where 512 is middle (stationary)
hypotenuse = sqrt((x * x) + (y * y));
if (hypotenuse > deadzone) {
if (x > 0 && y > 0) { // first quadrant
if (x > y) {
p1 = (PI / 4 + atan(y / x)) / (PI / 2); // % for RIGHT motor
p2 = 1 - p1; // % for LEFT motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(-var1 * p1); //negative for counterclockwise
stepper1.setSpeed(-var1 * p2); //negative for counterclockwise
} else {
p1 = (PI / 4 + atan(x / y)) / (PI / 2); // % for RIGHT motor
p2 = 1 - p1; // % for LEFT motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(-var1 * p1); //negative for counterclockwise
stepper1.setSpeed(var1 * p2); //positive for clockwise
}
}
else if (x < 0 && y > 0) { // second quadrant
if (y > -x) {
p1 = (PI / 4 + atan(-x / y)) / (PI / 2); // % for right motor
p2 = 1 - p1; // % for left motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(-var1 * p1); //negative for counterclockwise
stepper1.setSpeed(var1 * p2); //positive for clockwise
} else {
p1 = (PI / 4 + atan(y / -x)) / (PI / 2); // % for RIGHT motor
p2 = 1 - p1; // % for LEFT motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(var1 * p1); //positive for clockwise
stepper1.setSpeed(var1 * p2); //positive for clockwise
}
}
else if (x < 0 && y < 0) { // third quadrant
if (-x > -y) {
p1 = (PI / 4 + atan(-y / -x)) / (PI / 2); // % for RIGHT motor
p2 = 1 - p1; // % for LEFT motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(var1 * p1); //positive for clockwise
stepper1.setSpeed(var1 * p2); //positive for clockwise
} else {
p1 = (PI / 4 + atan(-x / -y)) / (PI / 2); // % for right motor
p2 = 1 - p1; // % for left motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(var1 * p1); //positive for clockwise
stepper1.setSpeed(-var1 * p2); //negative for counterclockwise
}
}
else if (x > 0 && y < 0) { // forth quadrant
if (-y > x) {
p1 = (PI / 4 + atan(x / -y)) / (PI / 2); // % for right motor
p2 = 1 - p1; // % for left motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(-var1 * p1); //negative for counterclockwise
stepper1.setSpeed(var1 * p2); //positive for clockwise
} else {
p1 = (PI / 4 + atan(-y / x)) / (PI / 2); // % for RIGHT motor
p2 = 1 - p1; // % for LEFT motor
q1 = p1 * p1;
q2 = p2 * p2;
var1 = sqrt(hypotenuse * hypotenuse / (q1 + q2));
stepper2.setSpeed(-var1 * p1); //negative for counterclockwise
stepper1.setSpeed(-var1 * p2); //negative for counterclockwise
}
}
else if (x == 0) {
if (y > 0) {
stepper2.setSpeed(-hypotenuse);
stepper1.setSpeed(hypotenuse);
} else {
stepper2.setSpeed(hypotenuse);
stepper1.setSpeed(-hypotenuse);
}
}
else if (y == 0) {
if (x > 0) {
stepper2.setSpeed(hypotenuse);
stepper1.setSpeed(hypotenuse);
} else {
stepper2.setSpeed(-hypotenuse);
stepper1.setSpeed(-hypotenuse);
}
}
}
else { // joystick in "deadzone" (no movement)
stepper1.setSpeed(0);
stepper2.setSpeed(0);
}
stepper1.runSpeed();
stepper2.runSpeed();
Serial.print("LR: ");
Serial.print(x);
Serial.print(" UD: ");
Serial.println(y);
Serial.println(hypotenuse);
Serial.println(var1);
Serial.print("Left motor speed: ");
Serial.println(stepper1.speed());
Serial.print("Right motor speed: ");
Serial.println(stepper2.speed());
}
AccelStepper is primarily designed to be used in a positional mode; ie. request to move a number of steps forward or reverse.
Continuous rotation mode is kind of a bonus mode, if you just want to run the stepper continuously (although the code for that is somewhat trivial and doesn't need a library).
Using setspeed() and creating your own acceleration profile is probably well outside the range of expected usage. I haven't checked but I guess every time you call setspeed() it calculates the time to the next step and restarts a timer. If you repeatedly call setspeed(), it will never (or rarely) get to the point where it does a step.
Perhaps if you call setpeed() less frequently, e.g. 10 times a second, you might get better results. Do you even need to use AccelStepper anyway?