Introduction: 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.*
We have a be nice policy.
Please be positive and constructive.
Hey, i have another question regarding the servo.
I've noticed that my servo twitches a lot when powered on and stationary. Do you happen to know how i could fix this?
No, I have noticed the same thing and I’m unsure if it’s fluctuating PWM or main power or some type of mechanical issue, but I’d suggest researching the servo you’re using and whether or not raspberry pi’s typically have stable PWM. Sorry I couldn’t be of more help!
I've been able to sort it out, to prevent it from twitching i had to send a signal once, and then stop the pwm. Untill i want to change it again.
Thank you very much for this, everything on this page has been very helpful! However, I'm trying to automatically control the servo motors at definite times for a day (8:00AM, 12:00PM and 4:00PM), would this be possible?
Note that when you don't connect the RPi to the internet, the clock in your RPi will start shifting, but if it's connected to the internet and can sync up to that. You can use a RTC module to fix this problem if needed. (some of those include an alarm function, wich you can use to start a function.
If I were you, I would look at using the code here along with cron. Cron is a tool used to schedule jobs in unix/linux environments. There are tons of resources online to learn how to use it. I highly recommend adding it to your skill set.
I am not sure, but if you were to use my code as a baseline to define some functions and then find out how to check the current time on a RPi, you could create a constantly looping ‘if’ statement that executed my functions when the time input was equal to your desired times. Like I said, I’ve never done anything like that or researched it, but to my knowledge it should be totally possible. Hope this helped!
've heard many times that it's recomended to use an external suply to power the servo, altough you don't do this, why? I ask this because i'm currently working on a project. And it would save me a bit of wiring.
That is certainly a good idea to avoid overloading and possibly causing damage to your RPi, but I checked the current draw and voltage requirements of the servo I was using and made sure it would be okay to use directly from the Pi’s GPIO board. If you are unsure of how to do that, or are using an excessive amounts of servos or other power requirements it’s definitely a good idea to use an external power supply. Just swap the jumpers from where I hooked them up to the 5V and GND pins on the Pi and connect them to the appropriate power source to power your servos instead. Hope this helped!
hi i want to run this code slower for example if i change pwm=GPIO.PWM(03, 50) to pwm=GPIO.PWM(03, 25) how the code would change for correct result?
The second number given as an argument in that statement sets the frequency of modulation in Hz, so your given example would change the PWM signal to cycle 25 times per second instead of my stated 50. If your intention is less cycles per second or minute, that is the correct way to achieve it. If you want the output motor to run slower that is just a matter of lowering the duty cycle, or the percentage you set later in the code. I hope this helped!