Intro: Servo Motor Control With Raspberry Pi
Use a Raspberry Pi 3 and Python Scripts to control a servo motor.
Step 1: Materials and Tools
- Raspberry Pi 3 (RPi)
- Jumper Wires, Male to Female (M/F)
- Servo Motor
**In this project, it is necessary to access the Raspberry Pi desktop. This can be done by plugging a monitor, keyboard and mouse into the RPi or by using an SSH connection.**
No tools are necessary for this project as none of the connections are permanent and use jumper wires and a breadboard. If you want to make a permanent, more durable version, simply make the same connections with a soldering iron and some wire.
Step 2: Background Info.
**This step is all background info. If you don't care about how this is done and just want to do it without learning, skip to Step 3.**
This project uses Python scripts run on a Raspberry Pi microcontroller to send GPIO PWM outputs to a servo motor to set its angle. If all that sounds confusing, don't worry, I'm about to explain it.
First things first; a Raspberry Pi is an open-source credit card sized computer with 40 open GPIO pins. GPIO stands for "General Purpose Input/Output", which means these pins can either send electrical signals to drive hardware or receive them and read sensor data. We're using them as outputs, to send signals to a servo motor. Nothing special.
A servo motor is a type of DC motor that, upon receiving a signal of a certain frequency, can rotate itself to any angle from 0-180 degrees. Its 90 degree position is generally referred to as 'neutral' position, because it can rotate equally in either direction from that point.
The way a servo motor reads the information it's being sent is by using an electrical signal called PWM. PWM stands for "Pulse Width Modulation". That just means sending ON electrical signals for a certain amount of time, followed by an OFF period, repeated hundreds of times a second. The amount of time the signal is on sets the angle the servo motor will rotate to. In most servos, the expected frequency is 50Hz, or 3000 cycles per minute. Servos will set to 0 degrees if given a signal of .5 ms, 90 when given 1.5 ms, and 180 when given 2.5ms pulses. This translates to about 2.5-12.5% duty in a 50Hz PWM cycle.
We'll be sending PWM signals from one GPIO pin on the RPi, and powering it from the GPIO board, so three wires will run from the servo to the RPi.
Now that we know what's going on, it's time to wire it up.
Step 3: Hardware Setup
The hardware for this project is very simple.
I recommend creating something to go on the end of the servo to better show its rotation, but that's totally unnecessary.
The only thing that you have to do is plug the three wires from the servo into the GPIO board. Refer to the diagram above for the pin numbers.
Plug a M/F jumper into each of the holes on the end of the servo cord, then plug the one coming off the red wire into pin #2, the one coming off of the brown into pin #6, and the one coming out of the yellow wire into pin #3. That's all there is to it, and if those instructions aren't clear enough, just look above at the pictures.
Step 4: Software Setup
First, we need to open a program on the Pi to write our code. We're going to use IDLE 2, so go to the top left of your desktop, click Menu, click Programming, and click Python 2(IDLE). You should see a blank text editor with an untitled document. You should not see a console with a shell prompt (ie. '>>>"). IF you do, click File, then New.
The first thing we need to do is import the GPIO module. So, on the first line, type exactly, CaSe sensitive,
import RPi.GPIO as GPIO
this imports the GPIO module
next, we need a command called 'sleep', so write
from time import sleep
next we need to name all of the pins, so set the naming mode by writing
this sets the names to board mode, which just names the pins according to the numbers in the middle of the diagram above.
Now we need an output to send our PWM signal on, so write
Now setup PWM on pin #3 at 50Hz
Then start it with 0 duty cycle so it doesn't set any angles on startup
Now, to set the angle of the servo, we need to send a specific signal to it. This can differ from servo to servo, as normally it's from 2.5-12.5%, and on the ones I'm using it's 2-12%. Regardless, it will be a 10% window, so to calculate the duty cycle for your desired angle, divide by 18, then add the lowest available value, in this case 2.
So, for 90 degrees, divide by 18, which is 5, then add 2, and you get 7. So on this servo 7% duty is 90 degrees.
As you can see, this math is not very friendly and would be tedious to do every time you wanted to set an angle, so in order to simplify that we're going to write a function in Python that does the math automatically then sets the angle.
So, first define a function. You can name it whatever you like.
duty = angle / 18 + 2
Now that probably looks like a lot of confusing code, so let me explain everything I did.
- The first line sets up a function called 'SetAngle' that we can call later in the code and give our input as an angle.
- The second line (which needs to be indented inside the function) sets a variable equal to our angle divided by 18 and 2 added like I showed above
- The third line turns on the pin for output
- The fourth line changes the duty cycle to match what we calculated
- The fifth line waits 1 second so the servo has time to make the turn. Depending on the speed of your servo you might need longer, or you might not need this long
- The sixth line turns off the pin
- And the seventh line changes the duty back to 0 so we aren't continuously sending inputs to the servo
Now in your code you can call the function, by writing
to tell the servo to turn to 90 degrees.
So, in your code, call a few angles, and when we run the code we'll see how they run on the servo.
At the end of your code, make sure to write
And that's it! You now have a code that can set your servo to any angle. Press F5, then save to test your code!
*Note: you may receive an error that says the selected GPIO channels are already in use. This won't affect your project, and you can make the warnings stop appearing by writing "GPIO.setwarnings(False)" to your code.*