Recently, I have been trying to make a simple robot arm. One of the first problems I ran into when writing the code was positioning the motors where I wanted them in degrees, so I created the code on this page and today we will look at how it works and how you use it.
The code is at the bottom of the page. The only thing special you need to know to get it to work is how to calculate the SPR of your motor setup, which you can learn about in the Math section.
The setup I am using is an Arduino UNO and a 28BYJ-48 stepper motor wired on pins 8,9,10, and 11. That being said, this post will not to teach you how to use stepper motors with Arduino, instead it will explain how you can get better control of the motors you already have.
The Math
The equation to turn degrees into steps is pretty simple. Once you have replaced all the variables in it, you simply run it, and the number it gives you is the number of steps you need to move your motor.
(Steps_Per_Revolution / 360) * Degrees
The two interesting parts are Steps_Per_Revolution and Degrees.
Degrees is how many degrees you want your motor to move.
Steps_Per_Revolution is how many steps your motor takes to make one complete rotation.
Calculating SPR
The easiest way to find the SPR (Steps_Per_Revolution) of your motor is to look it up online. Though you will get better results, if you search “steps per revolution” for your motor. You will also want to factor in any gears connected to your motor.
For example, one of the motors on my robot is a 28BYJ-48. When you look up the SPR for it on the internet you will eventually find out that it is 2038. Connected to that motor are two gears with a 7 to 41 gear ratio which means the actual SPR of that motor is the result of the following equation.
2038 / (7 / 41)
How to Test
Math is great, but all to often when you start using math in the real world, you find it doesn’t work as nice as it did on paper. Because of this, it is always good to have a way to test that your math is working.
Bellow is the setup that I used. There are three lines marked A, B, and C. What looks like a K is actually supposed to be a line with an arrow pointing at it.
I tested my math by positioning the arms so A and B were next to each other and then told the arm to rotate 180 degrees if everything went correctly A would end up next to C.
The Code
To make this hole process simpler I created the following code. It’s a class that you can use to make all the math easier. It also adds a few extra features that I will explain below.
class step_degree_driver {
private:
bool clockwise = true; // does motor turn clockwise with positive steps
bool flip_rotation = false;
bool last_direction = true;
float last_angle = 0;
float steps_per_degree = 0;
int gear_error = 0;
bool get_dir(int a) {
if (a >= 0) {
return not clockwise;
} else {
return clockwise;
}
}
public:
step_degree_driver(float _steps_per_rotation, bool _clockwise) {
clockwise = _clockwise;
steps_per_degree = double(_steps_per_rotation / 360);
}
void set_gear_error(int _gear_error) {
gear_error = _gear_error;
}
void set_flip_rotation(bool flip) {
flip_rotation = flip;
}
void set_current_angle(float current_angle) {
last_angle = current_angle;
}
int step_error(int steps) {
if (steps && last_direction != get_dir(steps)) {
if (get_dir(steps)) {
return steps + gear_error;
} else {
return steps - gear_error;
}
} else {
return steps;
}
}
int angle(float angle) {
if (flip_rotation) {
if (clockwise) {
angle = angle * -1;
}
} else if (not clockwise) {
angle = angle * -1;
}
int steps = round(steps_per_degree * float(angle - last_angle));
steps = step_error(steps);
last_angle = angle;
last_direction = get_dir(steps);
return steps;
}
int direct_angle(float angle) {
if (flip_rotation) {
if (clockwise) {
angle = angle * -1;
}
} else if (not clockwise) {
angle = angle * -1;
}
int steps = round(steps_per_degree * float(angle));
steps = step_error(steps);
last_angle = angle+last_angle;
last_direction = get_dir(steps);
return steps;
}
};
How do you Use the Code?
First, you need to add the above code to your sketch. Its best if it’s put before the setup function.
Then, you will want to run the following code.
step_degree_driver some_name(SPR,Rotation);
You will need to replace SPR with the SPR of your motor setup. You will also need to set Rotation based on the table below.
Value | reason |
---|---|
true | If your motor moves clockwise when given a positive number of steps. |
false | If your motor moves clockwise when given a negative number of steps. |
There are two ways you can control the motor: direct_angle and angle. For both functions you give them an angle and then they will return the number of steps you need to move your motor.
The direct_angle function will simply calculate the number of steps needed to move your motor.
int steps = some_name.direct_angle(some_angle);
The angle function will actually memorize the last position of the motor and use it in the math for example if your motor starts out at 90 degrees and you tell it to move to 95 degrees your motor will not actually move 95 degrees but instead will move 5 degrees.
int steps = some_name.angle(some_angle);
That is all you need to get the driver to work, but there are a few other settings available.
Starting Position
This only matters ,if you are using the angle function. You can use it to modify what angle it thinks its currently at.
some_name.set_current_angle(180.00);
Gear Error
Because I am using cheap motors in my robot, the gears inside them can cause some slight inaccuracies for my robot the following code helps reduce some of that error.
some_name.set_gear_error(5);
Flipping the Rotation
By default if you told the computer to move 45 degrees, it would move 45 degrees clockwise and if you told it to move -45, it would move counter clockwise. You can reverse this behavior with the setting below.
some_name.set_flip_rotation(true);
One Last Example
Below is the program that I used to test that the code above works. If you run it, open the serial monitor and type in any angle then hit send and the motor will move that many degrees.
#include <Stepper.h>
Stepper motor = Stepper(2038, 11, 9, 10, 8);
class step_degree_driver {
private:
bool clockwise = true; // does motor turn clockwise with positive steps
bool flip_rotation = false;
bool last_direction = true;
float last_angle = 0;
float steps_per_degree = 0;
int gear_error = 0;
bool get_dir(int a) {
if (a >= 0) {
return not clockwise;
} else {
return clockwise;
}
}
public:
step_degree_driver(float _steps_per_rotation, bool _clockwise) {
clockwise = _clockwise;
steps_per_degree = double(_steps_per_rotation / 360);
}
void set_gear_error(int _gear_error) {
gear_error = _gear_error;
}
void set_flip_rotation(bool flip) {
flip_rotation = flip;
}
void set_current_angle(float current_angle) {
last_angle = current_angle;
}
int step_error(int steps) {
if (steps && last_direction != get_dir(steps)) {
if (get_dir(steps)) {
return steps + gear_error;
} else {
return steps - gear_error;
}
} else {
return steps;
}
}
int angle(float angle) {
if (flip_rotation) {
if (clockwise) {
angle = angle * -1;
}
} else if (not clockwise) {
angle = angle * -1;
}
int steps = round(steps_per_degree * float(angle - last_angle));
steps = step_error(steps);
last_angle = angle;
last_direction = get_dir(steps);
return steps;
}
int direct_angle(float angle) {
if (flip_rotation) {
if (clockwise) {
angle = angle * -1;
}
} else if (not clockwise) {
angle = angle * -1;
}
int steps = round(steps_per_degree * float(angle));
steps = step_error(steps);
last_angle = angle+last_angle;
last_direction = get_dir(steps);
return steps;
}
};
step_degree_driver drv(2038, false);
void setup() {
Serial.begin(9600);
Serial.println("running stepper_angle");
motor.setSpeed(5);
drv.set_gear_error(5);
drv.set_current_angle(180.00);
drv.set_flip_rotation(true);
}
void loop() {
if (Serial.available()) {
float direction = Serial.parseFloat();
int steps = drv.direct_angle(direction);
Serial.print(direction);
Serial.print(" : ");
Serial.println(steps);
motor.step(steps);
}
}
If you are interested in building a robot arm you might like to look at How to Control a Robot Arm With Arduino and How to Control Multiple Stepper Motors.