Search code examples
pythonraspberry-picontinuousservo

Controlling continuous servo using python in Raspberry Pi,but the continuous servo can't stop


I'm trying to control a continuous servo(DF15RSMG) using python in Raspberry Pi, but the continuous servo can't stop. Code as follows:

import RPi.GPIO as GPIO  
import time  
import signal  
import atexit  

atexit.register(GPIO.cleanup)    

GPIO.setmode(GPIO.BCM)  
GPIO.setup(17, GPIO.OUT, initial=False)  
p = GPIO.PWM(17,50) #50HZ  
p.start(0)  
time.sleep(2)  

while(True):  
  for i in range(0,181,10):  
    p.ChangeDutyCycle(2.5 + 10 * i / 180)  
    time.sleep(0.02)                       
    p.ChangeDutyCycle(0)                    
    time.sleep(0.2)  

  for i in range(181,0,-10):  
    p.ChangeDutyCycle(2.5 + 10 * i / 180)  
    time.sleep(0.02)  
    p.ChangeDutyCycle(0)  
    time.sleep(0.2)  

Code above is work for SG90 Servo.Having seen this question Raspberry pi servo doesn't stop ,but I still haven't know how to solve this problem,What should I do?


Solution

  • I think you may have multiple problems - for example you appear to calculate the duy cycle using integer arithmetic. To test this, add a print i,i/180, 2.5+10*i/180 statement in each loop. With python 2.7 that I am using, the duty cycle you are requesting jumps in units of 1 every second time round the loop because the calculation 10*i/180 is made using integer arithmetic. All you have to do is change the width calculation to use e.g. 10.0*i/180 - but more on that later.

    However looking at your code it does appear to briefly set the PWM to 7.5% so the motor should stop for 0.2s.

    Also there is no need to set the duty cycle back to 0 after each setting - not sure why you do that.

    And 0.2s isn't much time to wait between speed increments (so you may be missing the stopped period), make it slower might make it easier to see what is going on.

    Most importantly, for a continuous movement servo it is the speed of the servo which is controlled by the pulse width - so the servo goes 'forwards' faster as the drive pulse width increases above the nominal 'zero' width, and faster backwards as the pulse width reduces narrower than the zero width.

    So the speed of the servo for a pulse width w milliseconds is calculated something like:

    speed% = (w-1.5)*100
    

    where w varies between 0.5 and 2.5 milliseconds. 100% forward speed needs a pulse width of 2.5ms, and 100% reverse (i.e. -100%) speed needs a pulse width of 0.5ms. With a repeat interval of 20ms these correspond to 2.5-12.5%.

    NOTE that the servo will have a small dead band around 1.5ms so that it will stop over input pulse width range of perhaps 1.45-1.55ms otherwise it would be very difficult to get an exact zero speed from it.

    So, to stop this servo, set the pulse width to the 'zero' width 1.5ms and leave it running at that, and the servo will not rotate. No problem with the while true - that keeps the pulses going which where needed for classic analogue servos. This servo is a digital servo so can have faster repetition rate so you could use 5ms, for example, which gives you more resolution of the pulse width varying from 10-50%. And as a digital servo it appears to only need a single pulse to set its speed, you could also work without the 20ms repeat rate, just generate a pulse when you want to change the speed.

    Anyway back to your code, basically, for 20ms repetitions and nominal width of 1.5ms, you need to set the duty cycle to 7.5% and the servo will stop. Your code should increase and reduce around this to make the servo go backwards and forwards.

    My reference is this info on amazon.co.uk https://www.amazon.com/DFRobot-DF15RSMG-Degree-Standard-Servo/dp/B014L5CBBA which was the top search result.

    I haven't the hardware to test this, but something like this should work better, I'm assuming the initialization code you use works:

    import RPi.GPIO as GPIO  
    import time  
    import signal  
    import atexit  
    
    atexit.register(GPIO.cleanup)    
    
    GPIO.setmode(GPIO.BCM)  
    GPIO.setup(17, GPIO.OUT, initial=False)  
    p = GPIO.PWM(17,50) #50HZ  
    p.start(0)  
    time.sleep(2)  
    
    STEPS=10    # the number of steps either side of nominal
    NOMINAL=7.5 # the 'zero' PWM %age
    RANGE=1.0   # the maximum variation %age above/below NOMINAL
    
    while(True):  
        # loop first over "forward" ramp up/down, then reverse.
        for direction in [+1.0,-1.0]:
            # step from 0 to 100% then back to just above zero
            # (next time round the loop will do the 0)
            for step in list(range(STEPS+1))+list(range(STEPS-1,0,-1)):
                dutycycle = NOMINAL + direction*RANGE*step/STEPS
                print direction, step, dutycycle
                p.ChangeDutyCycle(dutycycle)  
                time.sleep(1.0)  
    

    Final comment - if you want to find out more about what your code is doing, the very simple step of doing separate calculation of the duty cycle allows you to add a print statement without writing the calculation twice. TBH this is a reason I'm not a great fan of python's very powerful one-liner constructs like list comprehensions: they're great once you get them working, but for beginners when they don't work they rob you of the ability to see what's happening inside. Far better to simply use a few more lines of code and a for loop, adding prints if there's a problem, commenting the prints out once your loop is working.

    FINAL final thing - if you want the servo to stop when you exit your code, give it a pulse of the zero width 1.5ms, otherwise it won't stop. To make sure the servo gets this pulse do a sleep for at least 20ms after setting it:

    p.ChangeDutyCycle(NOMINAL)
    time.sleep(0.1)