PID control

|—平滑化算法算法

|—PID控制—|—P控制器編程編程

                     |—PD控制編程dom

                     |—PID控制編程優化

                     |—參數優化spa

                     |—實驗P、PD、PID對減少系統偏差的做用code

這裏討論怎麼將路徑轉變成行動指令(生成平滑的路徑),用到PID去控制。blog

從出發點到目的地,實際的路徑不能像以前的path plan(藍色),沒有car能立轉90度,也不能像綠色斜轉45度,紅色路徑最好溫和地移動。get

 

平滑化算法it

將路徑規劃找出來的每一個小格點設爲x0~xi~ xn-1(未平滑化),並設置值與xi相等的變量yi,而後作兩項優化使每項平方差最小io

if只作第一項優化,結果仍會是原始路徑由於步驟1已經設置的是兩變量相減爲0,而if只作第二項優化會使得相鄰平滑點愈來愈近,最後除了一個原始點get nothing。

能夠看出這兩項式子是互相鬥爭的,使y i更接近xi點意味着路徑趨向於塊狀而不光滑(如上面的藍色路徑);使y i彼此靠近使得路徑更平滑,但更不真實於原始路徑(像以上綠色路徑)。

 

那如何作到使兩個式子都最小化?能夠經過最小化一個線性組合使得兩個式子都最小化,

c1相對c2越大就越平滑,相反就越接近一個原點,找到這兩個鬥爭的平衡點就能獲得如上面紅線同樣的平滑路徑。

 

模擬情形說明兩項優化後路徑爲何會平滑:

當把y移動遠離x(可對除起始目的點的其餘全部y點),產生y彼此之間距離越近更平滑的新的路徑,同時第二個式子偏差變小,但新的路徑會使得第一個式子偏差變大,但依靠權重c1和c2的設置能獲得理想的路徑,這個優化只優化中間的點,起始點和目的地點x,y一直相等。

 

如何作?

使用梯度降低,每一次迭代都沿着能減少偏差的方向邁一小步,即每一步都對yi(除y0和yn-1)進行調整使得組合式子最小化:(這個算法紅色部分本來是-,但這個模型裏是+)

make code:

from math import *
path = [[0, 0],
        [0, 1],
        [0, 2],
        [1, 2],
        [2, 2],
        [3, 2],
        [4, 2],
        [4, 3],
        [4, 4]]
def smooth(path, weight_data = 0.5, weight_smooth = 0.1, tolerance = 0.000001):#tolerance是偏差容忍變量 
#將路徑深度複製到newpath
newpath 
= [[0 for col in range(len(path[0]))] for row in range(len(path))]    for i in range(len(path)):         for j in range(len(path[0])):             newpath[i][j] = path[i][j]

#迭代那兩條表達式應用到除0和n外的每一個路徑點上
#迭代不會中止直到某次更新後整體變化比容忍偏差值小
change = tolerance while (change >= tolerance): change = 0 for i in range(1,len(path)­1): for j in range(len(path[0])): d1 = weight_data * (path[i][j] - ­newpath[i][j]) d2 = weight_smooth * (newpath[i­1][j] + newpath[i+1][j] - 2 * newpath[i][j])             change += abs(d1 + d2)             newpath[i][j] += d1 + d2 return newpath 

 

PID控制

假設車有可轉向的前輪和固定的後輪,以固定速率向前行駛,但願它走平滑的路徑,但只能控制前輪轉向角度。三種選擇:1.固定轉向量(這會走圓圈)2.隨機轉向命令,按橫切偏差的某個比例來設置轉向角度(橫切偏差CTE:車輛與參考航線間的垂直距離)3.讓轉向與橫切偏差成比例

應該選第三種,偏差越大就越朝着參考航線設置一個更大的轉向角,隨着接近航線,轉向角會變小,最終挨着航線。

這種轉向控制叫作P controller,應用這種控制到達航線後仍會以一個小轉角繼續行駛而後再,因此這種轉向控制下的行駛軌跡以下圖(臨界穩定):

 

#使用以前robot類,編寫P控制器迭代100次機器人運動
#包含robot類中的__init__()、set()、set_noise()、move()、_repr_()
#轉向角 steer = -tau * crosstrack_error(CTE)import random
import numpy as np
import matplotlib.pyplot as plt

import proportional

def run(param):     #控制參數param就是轉向角度與CTE之比,換句話說param = tau = steer/CTE
    myrobot = robot()
    myrobot.set(0.0, 1.0, 0.0)
    speed = 1.0 # motion distance is equalt to speed (we assume time = 1)
    N = 100 

    for i in range(N):
         crosstrack_error = myrobot.y
         steer = -param * crosstrack_error
         myrobot = myroobo.move(steer,speed)
         print myrobot, steer 

#run(0.1)

 

 下一個問題是有辦法避免越界嗎?用PD控制

在PD控制裏,轉向角度alpha不只由控制參數tau_p和橫切偏差CTE決定,也會與CTE關於時間的導數有關,

這裏間隔時間設爲1,那麼:

這個式子中,它會注意到CTE隨着時間的過去變小,當減少到必定程度它會反方向使車輪向上轉動,不會衝過 x軸而是平緩地接近參考航線。

因此如今不是像在P控制器裏以CTE的比例控制steer,還加入了第二個常數tau_d和CTE的微分項

def run(param1=0.2, param2=3.0):
    myrobot = robot()
    myrobot.set(0.0, 1.0, 0.0)
    speed = 1.0 
    N = 100
    crosstrack_error = myrobot.y
    for i in range(N):
        diff_crosstrack_error = myrobot.y ­ crosstrack_error   #微分項
        crosstrack_error = myrobot.y
        steer = ­ param1 * crosstrack_errorparam2 * diff_crosstrack_error
        myrobot = myrobot.move(steer, speed)
        print myrobot, steer

#run(0.2,3.0)

 

 

這裏在初始時都是假定車輪對正前,但其實實際中車輪在開始時會有誤差角度,而這個問題P、PD都解決不了,(P會沿着一個常數震盪,PD會貼合一個常數),帶來系統誤差的問題,爲了解決這個問題,引入PID。

假如One people以系統偏差(由前輪轉向誤差帶來的持續的巨大誤差)的軌跡行駛,一段時間後他發現沒有接近預想軌跡,因而他開始往右打方向盤回到預想軌跡,這段時間的誤差能夠用CTE基於時間的積分來衡量。而後,來編寫新的控制器,它會根據CTE比例、CTE微分的某個比例、以及會受到CTE積分的比例影響,CTE積分就是歷史全部觀察到的CTE的和,它的累積能夠矯正方向。

def run(param1, param2, param3):
     myrobot = robot()
     myrobot.set(0.0, 1.0, 0.0)
    speed = 1.0  #假定速度是1
    N = 100
    myrobot.set_steering_drift(10.0 / 180.0 * pi) # 初始轉向(形成系統誤差)
    int_crosstrack_error = 0 #新變量
    for i in range(N):
        int_crosstrack_error += crosstrack_error
        diff_crosstrack_error = myrobot.y ­ crosstrack_error
        crosstrack_error = myrobot.y
        steer = ­ param1 * crosstrack_error
                      -­ param2 * diff_crosstrack_error
    ­                  - param3 * int_crosstrack_error
        myrobot = myrobot.move(steer, speed)
        print myrobot, steer
    
#run(0.2,3.0,0.004)

 

參數優化

使用一種稱爲「Twiddle」的算法,每次只調整一個參數直到找到最佳參數集合。

從一個參數向量 p = [0, 0, 0]開始,p將表明迄今爲止最佳猜想參數集合,還將初始化一個向量dp = [1,1,1],表明想嘗試的潛在變化。首先,在p中選擇一個參數(好比這個例子中的p [0],儘管它能夠是任何參數),而且經過dp中的對應值增長該參數(例如,若是p [0] == 1和dp [0] == 0.5,那麼咱們p [0]爲1.5)。而後在run()中調用p,結果賦給best_error,而後遍歷p,首先把p增長一個探索值,把這個值賦給error,留下更小的值更新best_error,而後把dp的值稍微增大(自乘1.1),不然減掉兩個單位dp(由於以前加了一個單位),若是兩個都失敗了,p[i]變回原來的值,而且下降探索範圍dp[i]的值(bug應該是減dp[i],好比自乘0.9)。只要dp之和大於閥值就會一直循環以上步驟。

 

#這裏的params是三維向量
def run(params, printflag = False):
     myrobot = robot()
     myrobot.set(0.0, 1.0, 0.0)
     speed = 1.0
     err = 0.0
     int_crosstrack_error = 0.0
     N = 100
     myrobot.set_steering_drift(10.0 / 180.0 * pi)  #設置了10度的起始轉向偏差
     crosstrack_error = myrobot.y

     for i in range(N * 2):  #迭代2*N次PID
         diff_crosstrack_error = myrobot.y ­ crosstrack_error
         crosstrack_error = myrobot.y
         int_crosstrack_error += crosstrack_error
         steer = ­ params[0] * crosstrack_error  \
            ­       - params[1] * diff_crosstrack_error \
            ­       - int_crosstrack_error * params[2]
         myrobot = myrobot.move(steer, speed)
         if i >= N:  #在N次之後纔開始統計,想觀察CTE更顯著的變化
              err += (crosstrack_error ** 2)
         if printflag:
              print myrobot, steer
     return err / float(N)  #返回平均偏差

def twiddle(tol = 0.2): 
     n_params = 3
     dparams = [1.0 for row in range(n_params)]
     params = [0.0 for row in range(n_params)]
     best_error = run(params)
     n = 0
     while sum(dparams) > tol:
           for i in range(len(params)):
               params[i] += dparams[i]
               err = run(params)
               if err < best_error:
                   best_error = err
                   dparams[i] *= 1.1
               else:
                   params[i] ­= 2.0 * dparams[i]
                   err = run(params)
                   if err < best_error:
                       best_error = err
                       dparams[i] *= 1.1
                   else:
                       params[i] += dparams[i]
                       dparams[i] *= 0.9+= 1
     print ‘Twiddle #’, n, params, ‘ ­> ‘, best_error
     return run(params)

 

實驗

下面嘗試把積分項去掉,獲得的是0積分增益,可是偏差比上面的那個偏差大一些,說明積分項對於讓偏差接近與0是必要的

而後嘗試一下,令dparams[1] = 0,把微分項去掉,出現了0.55的偏差!即便去掉drift偏差也是巨大的。

實驗在只有P狀況下偏差是0.1,PD下接近於0(沒有drift狀況下),而I(積分)是針對於有系統偏差的機器人。

 

尋路器、平滑器、控制器能夠應對大部分機器人的應用了,谷歌的還往裏面加了料,它考慮了轉向輪自有慣性的狀況,

相關文章
相關標籤/搜索