Servo Issues while using PWM with Raspberry Pi
Question:
My setup is simply the servo JX-PDI-6221MG being directly activated with raspberry pi’s PWM through GPIO library. The servo is brand new, I followed this tutorial and set duty cycle values between 0 and 100. The angles it makes have no pattern, it seems to go one way from 20 to 50 and them from 60 to 90 the other.
I decide to loop through all possible duty cycles (code is below) and I confirm that the variations aren’t smooth and it doesn’t look precise. After trying to set fix values again it suddenly starts to spin in multiple directions, even without I changing anything. Not even GPIO.cleanup()
or killing python related processes made it stop.
Is my Raspberry Pi getting jitter and being very imprecise or was I a victim of falsification?
Code:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11,GPIO.OUT)
pwm=GPIO.PWM(11,330) #which is the same as shown in datasheet
pwm.start(0)
for i in range(0,100):
pwm.ChangeDutyCycle(i)
time.sleep(0.02)
for i in range(0,100):
i = 100 - i
pwm.ChangeDutyCycle(i)
time.sleep(0.02)`
Answers:
AFAIK, only GPIO 18 can be used by GPIO library for hardware PWM. try changing tht GPIO.OUT to that.
Also try using pigpio library which allows ANY GPIO pin to do hardware timed PWM.
Servos generally need precise timing, so on a non-realtime device like the Raspberry Pi that means you need to use the hardware PWM pins. RPi.GPIO only supports software emulated PWM, so you need to use another library, e.g. pigpio.
This is because that Raspberry Pi is a non-Realtime controller.
For that, try to use pigpio, this library includes a support for hardware PWM.
Quick example:
import sys
import time
import random
import pigpio
NUM_GPIO=32
MIN_WIDTH=1000
MAX_WIDTH=2000
step = [0]*NUM_GPIO
width = [0]*NUM_GPIO
used = [False]*NUM_GPIO
pi = pigpio.pi()
if not pi.connected:
exit()
if len(sys.argv) == 1:
G = [4]
else:
G = []
for a in sys.argv[1:]:
G.append(int(a))
for g in G:
used[g] = True
step[g] = random.randrange(5, 25)
if step[g] % 2 == 0:
step[g] = -step[g]
width[g] = random.randrange(MIN_WIDTH, MAX_WIDTH+1)
print("Sending servos pulses to GPIO {}, control C to stop.".
format(' '.join(str(g) for g in G)))
while True:
try:
for g in G:
pi.set_servo_pulsewidth(g, width[g])
# print(g, width[g])
width[g] += step[g]
if width[g]<MIN_WIDTH or width[g]>MAX_WIDTH:
step[g] = -step[g]
width[g] += step[g]
time.sleep(0.1)
except KeyboardInterrupt:
break
print("nTidying up")
for g in G:
pi.set_servo_pulsewidth(g, 0)
pi.stop()
You can take a look on Servo Code from Python examples at pigpio website.
Or, check out the support of pigpio in gpiozero.
My setup is simply the servo JX-PDI-6221MG being directly activated with raspberry pi’s PWM through GPIO library. The servo is brand new, I followed this tutorial and set duty cycle values between 0 and 100. The angles it makes have no pattern, it seems to go one way from 20 to 50 and them from 60 to 90 the other.
I decide to loop through all possible duty cycles (code is below) and I confirm that the variations aren’t smooth and it doesn’t look precise. After trying to set fix values again it suddenly starts to spin in multiple directions, even without I changing anything. Not even GPIO.cleanup()
or killing python related processes made it stop.
Is my Raspberry Pi getting jitter and being very imprecise or was I a victim of falsification?
Code:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11,GPIO.OUT)
pwm=GPIO.PWM(11,330) #which is the same as shown in datasheet
pwm.start(0)
for i in range(0,100):
pwm.ChangeDutyCycle(i)
time.sleep(0.02)
for i in range(0,100):
i = 100 - i
pwm.ChangeDutyCycle(i)
time.sleep(0.02)`
AFAIK, only GPIO 18 can be used by GPIO library for hardware PWM. try changing tht GPIO.OUT to that.
Also try using pigpio library which allows ANY GPIO pin to do hardware timed PWM.
Servos generally need precise timing, so on a non-realtime device like the Raspberry Pi that means you need to use the hardware PWM pins. RPi.GPIO only supports software emulated PWM, so you need to use another library, e.g. pigpio.
This is because that Raspberry Pi is a non-Realtime controller.
For that, try to use pigpio, this library includes a support for hardware PWM.
Quick example:
import sys
import time
import random
import pigpio
NUM_GPIO=32
MIN_WIDTH=1000
MAX_WIDTH=2000
step = [0]*NUM_GPIO
width = [0]*NUM_GPIO
used = [False]*NUM_GPIO
pi = pigpio.pi()
if not pi.connected:
exit()
if len(sys.argv) == 1:
G = [4]
else:
G = []
for a in sys.argv[1:]:
G.append(int(a))
for g in G:
used[g] = True
step[g] = random.randrange(5, 25)
if step[g] % 2 == 0:
step[g] = -step[g]
width[g] = random.randrange(MIN_WIDTH, MAX_WIDTH+1)
print("Sending servos pulses to GPIO {}, control C to stop.".
format(' '.join(str(g) for g in G)))
while True:
try:
for g in G:
pi.set_servo_pulsewidth(g, width[g])
# print(g, width[g])
width[g] += step[g]
if width[g]<MIN_WIDTH or width[g]>MAX_WIDTH:
step[g] = -step[g]
width[g] += step[g]
time.sleep(0.1)
except KeyboardInterrupt:
break
print("nTidying up")
for g in G:
pi.set_servo_pulsewidth(g, 0)
pi.stop()
You can take a look on Servo Code from Python examples at pigpio website.
Or, check out the support of pigpio in gpiozero.