TowerPro SG90 Micro Servo 9g
To control our pan tilt bracket on Alexa M we are using two SG90 Micro Servos. We had a look for a Python library to control these but was only able to locate one which controlled a servo driver board via I2C. Based on this we decided to roll our own.
The Jaycar SG90 Data Sheet only helps with the wiring details. We need the pulse width's required to drive the servo. We found a more useful Data Sheet at MicoPik. From this we can determine the following characteristics:
- Weight: 9 g
- Dimension: 22.2 x 11.8 x 31 mm approx.
- Stall torque: 1.8 kgf·cm
- Operating speed: 0.1 s/60 degree
- Operating voltage: 4.8 V (~5V)
- Dead band width: 10 µs
- Temperature range: 0 ºC – 55 ºC
- Running current with 5V supply (no mechanical load) 220 ±50mA
- Stall current with 5V supply (horn locked) 650 ±80mA
- Idle current with 5V supply 6 ±10mA
More importantly, it tells us that the servo will move to:
- 0 degrees - the centre position with a 1.5 ms pulse;
- + 90 degrees with a ~2 ms pulse; and
- - 90 degrees with a ~1 ms pulse.
Whether + or - 90 degrees is left or right will depend on how you mounted the servo.
Calibrating your Servo
Every servo is different, so you will need to calibrate it for the best performance. From the data sheet, we see that a SG90 expects a frequency of 50 Hz on the control line and the position it moves to depends on the pulse width of the signal.
For the Raspberry Pi we do not have a change pulse width method for PWM, but we can change the Duty Cycle. Note that:
Duty Cycle = Pulse Width * Frequency
Given a 50 Hz frequency we can calculate the required duty cycle for any pulse width. For example:
- We need a 1.5 ms pulse to centre the servo, or a Duty Cycle = 0.0015 * 50 = 0.075 (i.e 7.5%).
- Similarly, 1 ms pulse (- 90 degrees) requires a Duty Cycle = 0.001 * 50 = 5%; and
- 2 ms pulse (+ 90 degrees), Duty Cycle = 0.002 * 50 = 10%
Thus the duty cycle range should be from 5 - 10% with the centre at 7.5%. We will determine the exact numbers for your servo shortly.
Wire up the Pi as shown above. You can use any GPIO pins you want, I used GPIO 5 because it doesn't conflict with the motor driver board and speaker pHat which will also be mounted on Alexa M. Although the servo runs off a nominal 5V, there is no problem controlling it with the 3.3V levels of the Pi since that is still above the logic high threshold.
You can power (Vcc) the servo from the Raspberry Pi 5V pin but do your current calculations first. Use the current draws from the data sheet above. The Pi draws approximately 700 mA from the +5 V supply. You may draw current from the +5 V pins provided the sum of that current and the board's 700 mA doesn't exceed the supply you provide to the board. I'm using a 2A supply so one servo is fine (even stalled).
Python Calibration Program
Load up the following servo_test.py program on your Pi and then run it.
# servo_test.py - Test functionality of SG90 Micro Servo # # Written By: David Such import RPi.GPIO as GPIO import time servo_pin = 5 duty_cycle = 7.5 # Should be the centre for a SG90 # Configure the Pi to use pin names (i.e. BCM) and allocate I/O GPIO.setmode(GPIO.BCM) GPIO.setup(servo_pin, GPIO.OUT) # Create PWM channel on the servo pin with a frequency of 50Hz pwm_servo = GPIO.PWM(servo_pin, 50) pwm_servo.start(duty_cycle) try: while True: duty_cycle = float(input("Enter Duty Cycle (Left = 5 to Right = 10):")) pwm_servo.ChangeDutyCycle(duty_cycle) except KeyboardInterrupt: print("CTRL-C: Terminating program.") finally: print("Cleaning up GPIO...") GPIO.cleanup()
Start with the theoretical duty cycle values above and then gradually move them up and down. Note the values just before the limits, you might have to use 0.5% increments towards the end. For my pan servo, the minimum duty cycle was 3% and the maximum was 11%, with the centre at 7%. Use CTRL-C to exit the program.
You will need to be running idle3 as sudo since we are controlling the GPIO pins.
Once you have the maximum and minimum duty cycle's you can use the following code to create the scan effect shown in the introductory video.
# servo_scan.py - Test functionality of SG90 Micro Servo # # Written By: David Such import RPi.GPIO as GPIO import time MIN_DUTY = 3 MAX_DUTY = 11 CENTRE = MIN_DUTY + (MAX_DUTY - MIN_DUTY) / 2 servo_pin = 5 duty_cycle = CENTRE # Should be the centre for a SG90 # Configure the Pi to use pin names (i.e. BCM) and allocate I/O GPIO.setmode(GPIO.BCM) GPIO.setup(servo_pin, GPIO.OUT) # Create PWM channel on the servo pin with a frequency of 50Hz pwm_servo = GPIO.PWM(servo_pin, 50) pwm_servo.start(duty_cycle) try: while True: pwm_servo.ChangeDutyCycle(MIN_DUTY) time.sleep(0.5) pwm_servo.ChangeDutyCycle(CENTRE) time.sleep(0.5) pwm_servo.ChangeDutyCycle(MAX_DUTY) time.sleep(0.5) pwm_servo.ChangeDutyCycle(CENTRE) time.sleep(0.5) except KeyboardInterrupt: print("CTRL-C: Terminating program.") finally: print("Cleaning up GPIO...") pwm_servo.ChangeDutyCycle(CENTRE) time.sleep(0.5) GPIO.cleanup()
Next up we will create a generalised python Servo class that we can reuse.