如何设置PID控制器控制的伺服器的初始角度

问题描述

我正在遵循此tutorial,并添加了扫描程序。一旦检测到对象,扫描循环就会中断,并从教程恢复为跟踪模式。问题在于,跟踪模式的认位置是激活时的认位置,该位置通常不在扫描过程中对象所在的FOV范围内。

伺服角由PID控制器更新。因此,我需要找到一种方法来将初始值设置为在激活跟踪模式之前扫描与伺服之间的最后角度,然后从PID控制器进行更新以跟踪对象。我从教程资料库中处理的主要文档是manager.py:

import logging
from multiprocessing import Value,Process,Manager,Queue

import pantilthat as pth
import signal
import sys
import time
import RPi.GPIO as GPIO

from rpi_deep_pantilt.detect.util.visualization import visualize_Boxes_and_labels_on_image_array
from rpi_deep_pantilt.detect.camera import run_pantilt_detect
from rpi_deep_pantilt.control.pid import PIDController

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(8,GPIO.OUT)

logging.basicConfig()
LOGLEVEL = logging.getLogger().getEffectiveLevel()

RESOLUTION = (320,320)

SERVO_MIN = -90
SERVO_MAX = 90

CENTER = (
    RESOLUTION[0] // 2,RESOLUTION[1] // 2
)


# function to handle keyboard interrupt
def signal_handler(sig,frame):
    # print a status message
    print("[INFO] You pressed `ctrl + c`! Exiting...")

    # disable the servos
    pth.servo_enable(1,False)
    pth.servo_enable(2,False)
    GPIO.output(8,GPIO.LOW)

    # exit
    sys.exit()

def in_range(val,start,end):
    # determine the input value is in the supplied range
    return (val >= start and val <= end)


def set_servos(pan,tilt,scan):
    # signal trap to handle keyboard interrupt
    signal.signal(signal.SIGINT,signal_handler)
    
    pn = 90
    tt = 25
    
    while scan.value == 't':     
        print('Scanning')
            
        pth.pan(pn)
        pth.tilt(tt)
        
        pn = pn-1
        
        if pn <= -90:
            pn = 90
        
        time.sleep(0.1)
        
        continue
        
    pan.value = -1*pn
    tilt.value = tt
    
    g=0
    
            
    while True:
#        if g<1:
#            pan.value = -1*pn
#            tilt.value = tt
        
        pan_angle = -1 * pan.value
        tilt_angle = tilt.value
        
        if g<6:
            print(pan_angle)
            print(tilt_angle)
        g=g+1
        
        # if the pan angle is within the range,pan
        if in_range(pan_angle,SERVO_MIN,SERVO_MAX):
            pth.pan(pan_angle)
        else:
            logging.info(f'pan_angle not in range {pan_angle}')

        if in_range(tilt_angle,SERVO_MAX):
            pth.tilt(tilt_angle)
        else:
            logging.info(f'tilt_angle not in range {tilt_angle}')
            
 
    
    
def pid_process(output,p,i,d,Box_coord,origin_coord,action):
    # signal trap to handle keyboard interrupt
    signal.signal(signal.SIGINT,signal_handler)

    # create a PID and initialize it
    p = PIDController(p.value,i.value,d.value)
    p.reset()
    

    # loop indefinitely
    while True:
        error = origin_coord - Box_coord.value
        output.value = p.update(error)
        # logging.info(f'{action} error {error} angle: {output.value}')
    

def pantilt_process_manager(
    model_cls,labels=('Raspi',),rotation=0
):
    
    pth.servo_enable(1,True)
    pth.servo_enable(2,True)
    with Manager() as manager:

        
        scan = manager.Value('c','t')
        
        # set initial bounding Box (x,y)-coordinates to center of frame
        center_x = manager.Value('i',0)
        center_y = manager.Value('i',0)

        center_x.value = RESOLUTION[0] // 2
        center_y.value = RESOLUTION[1] // 2
        

        # pan and tilt angles updated by independent PID processes
        pan = manager.Value('i',0)
        tilt = manager.Value('i',0)

        # PID gains for panning
        pan_p = manager.Value('f',0.05)
        # 0 time integral gain until inferencing is faster than ~50ms
        pan_i = manager.Value('f',0.1)
        pan_d = manager.Value('f',0)

        # PID gains for tilting
        tilt_p = manager.Value('f',0.15)
        # 0 time integral gain until inferencing is faster than ~50ms
        tilt_i = manager.Value('f',0.2)
        tilt_d = manager.Value('f',0)

        detect_processr = Process(target=run_pantilt_detect,args=(center_x,center_y,labels,model_cls,rotation,scan))

        pan_process = Process(target=pid_process,args=(pan,pan_p,pan_i,pan_d,center_x,CENTER[0],'pan'))

        tilt_process = Process(target=pid_process,args=(tilt,tilt_p,tilt_i,tilt_d,CENTER[1],'tilt'))

        servo_process = Process(target=set_servos,scan))
        
        
        detect_processr.start()
        pan_process.start()
        tilt_process.start()
        servo_process.start()
        
        detect_processr.join()
        pan_process.join()
        tilt_process.join()
        servo_process.join()

if __name__ == '__main__':
    pantilt_process_manager()

我在set_servos函数添加代码显示我要完成的工作。但是,它不起作用,因为一旦伺服器移至最后一个扫描位置,就会出现以下循环,它们将恢复为认的初始pan.value / tilt.value角度,通常角度约为-10deg。平移,倾斜10度。

这是我运行该脚本时的终端脚本,它在5度(水平)和25度(倾斜)处检测到该对象:

$ rpi-deep-pantilt track Raspi
Scanning
Scanning
Scanning
Scanning
Scanning
Scanning
Object Detected
5
25
5
25
5
12.55306363105774
-0.28837611675262453
12.55306363105774
-0.28837611675262453
12.55306363105774
-0.28837611675262453
12.656980562210084
Object Detected
^C[INFO] You pressed `ctrl + c`! Exiting...

这是PID脚本,对我来说这很乱:

# import necessary packages
import time


class PIDController:
    def __init__(self,kP=1,kI=0,kD=0):

        # initialize gains
        self.kP = kP
        self.kI = kI
        self.kD = kD

    def reset(self):
        # intialize the current and prevIoUs time
        self.time_curr = time.time()
        self.time_prev = self.time_curr

        # initialize the prevIoUs error
        self.error_prev = 0

        # initialize the term result variables
        self.cP = 0
        self.cI = 0
        self.cD = 0

    def update(self,error,sleep=0.01):

        time.sleep(sleep)
        # grab the current time and calculate delta time / error
        self.time_curr = time.time()
        time_delta = self.time_curr - self.time_prev
        error_delta = error - self.error_prev

        # proportional term
        self.cP = error

        # integral term
        self.cI += error * time_delta

        # derivative term and prevent divide by zero
        self.cD = (error_delta / time_delta) if time_delta > 0 else 0

        # save prevIoUs time and error for the next update
        self.time_prev = self.time_curr
        self.error_prev = error

        # sum the terms and return
        return sum([
            self.kP * self.cP,self.kI * self.cI,self.kD * self.cD]
        )

有趣的一点是,一旦物体被跟踪并被相机遗失,它就不会恢复到任何位置,它会一直停留在原来的位置,直到再次检测到为止。因此,我不明白为什么传递初始p / t值时,直到再次拾取物体之前,它都不会停留在该位置。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)