1

I'm trying to count rpm of dc and controlling its rpm using PID controller motor which I have done successfully in my code as shown below.

But when I try to count RPM of 2 DC motors simultaneously in a single code motors keeps on misbehaving and i am not able to count the RPM.

import RPi.GPIO as GPIO
import time
import math
import numpy as np

sensorRight = 18 
sensorLeft = 21 

##

Left motor
motorLeft_1 = 12
motorLeft_2 = 20

##Right motor
motorRight_1 = 19
motorRight_2 = 26


GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(sensorLeft,GPIO.IN)
GPIO.setup(sensorRight,GPIO.IN)

GPIO.setup(motorLeft_1,GPIO.OUT)
GPIO.setup(motorLeft_2,GPIO.OUT)
GPIO.setup(motorRight_1,GPIO.OUT)
GPIO.setup(motorRight_2,GPIO.OUT)

pwmLeftForward = GPIO.PWM(motorLeft_1,1000)
pwmLeftForward.start(0)
pwmRightForward = GPIO.PWM(motorRight_1,1000)
pwmRightForward.start(1)

dutyCycleLeft=30
pwmLeftForward.ChangeDutyCycle(dutyCycleLeft) ##Left_motor
GPIO.output(motorLeft_2,False)
time.sleep(0.1)

dutyCycleRight=30
pwmRightForward.ChangeDutyCycle(dutyCycleRight) ##left_motor
GPIO.output(motorRight_2,False)
time.sleep(0.1)

countLeft = 0
rpmLeft = 0
deltaLeft = 0
startLeft = 0
endLeft = 0
rpmLeftDes=60
previousErrorLeft=0
totalErrorLeft=0

countRight = 0
rpmRight = 0
deltaRight = 0
startRight = 0
endRight = 0
previousErrorRight=0
totalErrorRight=0
rpmRightDes = 60

sample=10



def get_rpmLeft(d):
    global countLeft
    global rpmLeft
    global startLeft
    global endLeft
    global previousErrorLeft
    global totalErrorLeft

    if not countLeft:
        startLeft = time.time()
        countLeft = countLeft +1

    else:
        countLeft = countLeft+1

    if countLeft == sample:
        endLeft = time.time()
        deltaLeft = endLeft - startLeft
        deltaLeft = deltaLeft/60
        rpmLeft = (sample/deltaLeft)/341.4
        print("rpmLeft Left =", rpmLeft)

        controller_LeftOut=controller_Left(rpmLeft,previousErrorLeft,totalErrorLeft)
        dutyCycleLeft=controller_LeftOut[0]
        previousErrorLeft=controller_LeftOut[1]
        totalErrorLeft=totalErrorLeft+previousErrorLeft

        pwmLeftForward.ChangeDutyCycle(dutyCycleLeft) ##Left_motor
        GPIO.output(motorLeft_2,False)
        time.sleep(0.1)
        countLeft=0
    return rpmLeft

def get_rpm_right(c):
    global countRight
    global rpmRight
    global startRight
    global endRight
    global previousErrorRight
    global totalErrorRight

    if not countRight:
        startRight = time.time()
        countRight = countRight +1

    else:
        countRight = countRight+1

    if countRight == sample:
        endRight = time.time()
        deltaRight = endRight - startRight
        deltaRight = deltaRight/60
        rpmRight = (sample/deltaRight)/341.4
        print("rpmRight Right =", rpmRight)


        controller_RightOut=controller_Right(rpmRight,previousErrorRight,totalErrorRight)
        dutyCycleRight=controller_RightOut[0]
        previousErrorRight=controller_RightOut[1]
        totalErrorRight=totalErrorRight+previousErrorRight

        pwmRightForward.ChangeDutyCycle(dutyCycleRight) ##left_motor
        GPIO.output(motorRight_2,False)
        time.sleep(0.1)
        countRight=0
    return rpmRight

def controller_Left(rpmLeft,previousErrorLeft,totalErrorLeft):
    global dutyCycleLeft
    kp=0.25
    kd=0.05
    ki=0.2
    errorLeft=rpmLeftDes-rpmLeft

    dutyCycleLeft=kp*errorLeft+kd*(errorLeft-previousErrorLeft)+ki*totalErrorLeft
    if dutyCycleLeft>98:
        dutyCycleLeft=98
    elif dutyCycleLeft< 4:
        dutyCycleLeft= 4

    return dutyCycleLeft,errorLeft

def controller_Right(rpmRight,previousErrorRight,totalErrorRight):
    global dutyCycleRight
    kp=0.25
    kd=0.05
    ki=0.2
    error=rpmRightDes-rpmRight

    dutyCycleRight=kp*error+kd*(error-previousErrorRight)+ki*totalErrorRight
    if dutyCycleRight>98:
        dutyCycleRight=98
    elif dutyCycleRight<1:
        dutyCycleRight=1

    return dutyCycleRight,error

GPIO.add_event_detect(sensorLeft,GPIO.RISING, callback = get_rpmLeft)
GPIO.add_event_detect(sensorRight,GPIO.RISING, callback = get_rpm_right)

try:
    while True:
        time.sleep(0.1)

except KeyboardInterrupt:
    print("QUIT")
    GPIO.cleanup()
  • Could you explain how things are misbehaving? – NomadMaker Dec 18 '19 at 10:30
  • 1
    Whenever I see code (in whatever language) posted without any comments I wonder - but don't feel obliged to make the effort the author didn't. – Milliways Dec 18 '19 at 11:08
  • Hi @Ravinder singh, Welcome and nice to meet you. You remind me in my Arduino Decimilla days using C++ to pid control DC gear motor and quadrature encoder speeddometering. I skimmed your program and found it structured and clear. Obviously you are doing two independent things: (1) pid moving motor, (2) measuring rpm. The measuring part is event driven. So the two parts are clear cuttingly separated. In other words, one can work without the other. Now your mover function is working OK, so you can ask the mover to take a break and use your finger instead to move the motor. / to continue, ... – tlfong01 Dec 18 '19 at 11:55
  • Using optical or Hall effect quadrature encoding or not, you can sense GPIO level change (don't use edge triggering for now) by stupidly looping (don't use interrupt driven call back functions which are difficult to troubleshoot. I usually start with looping to detect level trigger. By hand you can move forward and backward at the trigger point, and by level means no bouncing problem to bother. – tlfong01 Dec 18 '19 at 12:00
  • I know you worry that you will lose face if your bad friends discover that you are not using event driven edge triggering. Me too. So I almost always fakingly tell my bad friends that I don't know nothing about looping and level triggering, because those newbie methods damage my high class brain, ... :) – tlfong01 Dec 18 '19 at 12:05
0

I would like to suggest that you redo your code somewhat.

In your sensor interrupt routines do as little as possible. Just increment the variable countLeft or countRight as applicable. Don't do the PID calculations here. The reason you do this is to allow you to complete an interrupt before another one comes along.

Do the speed calculations in the main code loop. Do your best to work with local variables so that the pwm interrupt routine can't get bad copies of this data. If the calculations are fast enough, it could be done in the pid interrupt routine.

Do the pid calculations in a timer interrupt that goes off every 0.05 seconds or so. Pid loops work best if they are are updated at very regular intervals.

I don't know if python has any functions to synchronize your variables so they don't change on you during one of these interrupts.

The goal is make sure these routines are fast enough to complete and that they don't overwrite variables used by other interrupt routines.

I know how I'd do this in java or c or even rust, but I've never gotten this deep into python.

I hope I'm being clear.

Question: Do you wheel encoders have one or two signals?

| improve this answer | |
  • Problem solved thank you for your valuable suggestion..... – Ravinder singh Dec 20 '19 at 3:42
  • If the problem is solved, would you please answer the question yourself to help others with this problem? – NomadMaker Dec 20 '19 at 4:59
  • wheel encoders which i am using gives two signals but i am using only one other is left open – Ravinder singh Dec 20 '19 at 9:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.