直方圖濾波的算法思想在於:它把整個狀態空間dom(x(t))切分爲互不相交的部分、、,使得:python
而後定義一個新的狀態空間,當且僅當時,,因爲是一個離散的狀態空間,咱們就能夠採用離散貝葉斯算法計算。是對的近似,它給出x(t)在每個的機率,劃分的越精細,計算的結果就越精確,固然精確的代價是計算成本的上升。git
以下圖所示,無人駕駛汽車在一維的寬度爲5m的世界重複循環,由於世界是循環的,因此若是無人駕駛汽車到了最右側,再往前走一步,它就又回到了最左側的位置。github
自動駕駛汽車上安裝有Sensor能夠檢測車輛當前所在位置的顏色,但Sensor自己的存在必定檢測錯誤率,即Sensor對顏色的檢測不是100%準確的;算法
無人駕駛汽車以自認爲1m/step的恆定速度向右運動,車輛運動自己也存在偏差,即向車輛發出的控制命令是向右移動2m,而實際的車輛運動結果多是待在原地不動,可能向右移動1m,也可能向右移動3m。app
從數學的語言來描述這個問題:機器人只有一個狀態變量:位置,。應用直方圖濾波(Histogram Filter),對狀態空間作以下分解:dom
因而獲得一個新的狀態空間,它是對連續狀態空間的近似,在某一時刻車輛只能在這些離散狀態中的一個。ide
雖然車輛自認爲在向右運動,每一步運動1m,咱們假設存在5%的機率,無人駕駛汽車仍待在原地沒動;存在90%的機率車輛在向右移動1m;存在5%的機率無人駕駛汽車在向右運動2m。oop
無人駕駛汽車的Sensor假設90%的機率感知結果是正確的,還有10%的狀況下它感知的結果是錯誤的。idea
咱們用一個5維的向量來描述t時刻,無人駕駛汽車位於第1個格子、第2個格子、第3個格子、第4個格子、第5個格子的機率。spa
無人駕駛汽車對本身所在位置一無所知,假設它連續三次【運動-向右走一步】-【觀測】,三次觀測結果分別是orange、blue、orange,咱們一步步無人駕駛汽車是如何經過【運動】-【觀測】過程逐步確認本身的位置的。
t=0時刻
沒有任何先驗知識,無人車不知道本身在哪裏,因此在各個位置機率相等:
t=1時刻
首先向右走1m,用運動模型進行位置預測
能夠看出無人車雖然向前運動一步,但它仍然對本身所在位置一無所知。這也和咱們的認知相同,剛開始徹底不知道本身在哪裏,走了一步天然也徹底不知道本身在哪裏。
再用更新模型經過Sensor感知環境,更新當前位置的置信度。
orange的顏色感知信息使得傳感器認爲本身極可能位於第二個和第五個方格中。
t=2時刻
運動模型-向右走1m
更新模型-sensor環境感知
t=3時刻
運動模型-向右走1m
感知更新模型-sensor環境感知
能夠看到通過三次的運動和觀測後,無人駕駛汽車已經有73%的機率確認本身位於第二個網格中,事實再通過三次的運動觀測,無人駕駛汽車能夠有94%的機率確認本身的位置。
1D的直方圖濾波能夠很好的幫助咱們理解直方圖濾波的原理以及在如何應用在自動駕駛的定位過程當中。可是1D的直方圖濾波在實際應用中幾乎是不存在的,因此咱們從更偏向應用的角度,看看2D直方圖濾波在自動駕駛定位中是如何工做的。
首先定義一張二維地圖,R和G表明地圖塊的顏色:R爲紅色,G爲綠色。每一個地圖塊的大小根據實際應用而定,好比0.0125m*0.125m、0.025m*0.025m等。地圖塊越小,定位精度越高,可是地圖數據量和計算量也就越大;反之,地圖塊越大,定位精度越低,但數據量和計算量也相應較低。
grid = [ [R,G,G,G,R,R,R],
[G,G,R,G,R,G,R],
[G,R,G,G,G,G,R],
[R,R,G,R,G,G,G],
[R,G,R,G,R,R,R],
[G,R,R,R,G,R,G],
[R,R,R,G,R,G,G],
]
t=0時刻,車輛不知道本身處於地圖中的具體位置,轉化爲數學表述,就是車輛在各個地圖塊的置信度相同,代碼以下:
def initialize_beliefs(grid): height = len(grid) width = len(grid[0]) area = height * width belief_per_cell = 1.0 / area beliefs = [] for i in range(height): row = [] for j in range(width): row.append(belief_per_cell) beliefs.append(row) return beliefs
初始置信度以下:
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
0.020 0.020 0.020 0.020 0.020 0.020 0.020
置信度的可視化以下,紅色星星位置爲車輛的真實初始實際位置,藍色圈大小表明置信度的高低,藍色圈越大,置信度越高,藍色圈越小,置信度越低。t=0時刻,車輛不肯定本身的位置,因此各個位置的置信度相等。
車輛運動模型簡化爲x、y兩個方向的運動,同時因爲運動的不肯定性,須要對運動後的位置增長几率性信息。
代碼以下:
def move(dy, dx, beliefs, blurring): height = len(beliefs) width = len(beliefs[0]) new_G = [[0.0 for i in range(width)] for j in range(height)] for i, row in enumerate(beliefs): for j, cell in enumerate(row): new_i = (i + dy ) % height new_j = (j + dx ) % width new_G[int(new_i)][int(new_j)] = cell return blur(new_G, blurring)
觀測更新的過程當中,當觀測的Color等於地圖塊的Color時,hit=1, bel=beliefs[i][j] * p_hit;當觀測到的Color不等於地圖塊的Color時,hit=0, bel=beliefs[i][j] * p_miss。
代碼以下:
def sense(color, grid, beliefs, p_hit, p_miss): new_beliefs = [] height = len(grid) width = len(grid[0]) # loop through all grid cells for i in range(height): row = [] for j in range(width): hit = (color == grid[i][j]) row.append(beliefs[i][j] * (hit * p_hit + (1-hit) * p_miss)) new_beliefs.append(row) s = sum(map(sum, new_beliefs)) for i in range(height): for j in range(width): new_beliefs[i][j] = new_beliefs[i][j] / s return new_beliefs
單次直方圖濾波定位過程當中,先進行觀測更新,再進行運動更新。
def run(self, num_steps=1): for i in range(num_steps): self.sense() dy, dx = self.random_move() self.move(dy,dx)
設置運動更新的不肯定度爲0.1,觀測更新的錯誤率:每隔100次觀測出現一次觀測錯誤,車輛的真實初始位置爲(3,3),注意,這個真實位置車輛本身並不知道,咱們只是爲了仿真而設置的值。
blur = 0.1 p_hit = 100.0 init_pos = (3,3) simulation = sim.Simulation(grid, blur, p_hit, init_pos) simulation.run(1) simulation.show_beliefs() show_rounded_beliefs(simulation.beliefs)
通過一次直方圖濾波定位以後,各個位置的置信度已經發生了變化。
0.003 0.002 0.036 0.002 0.037 0.003 0.038
0.003 0.037 0.002 0.002 0.001 0.002 0.037
0.038 0.038 0.003 0.036 0.002 0.002 0.003
0.038 0.004 0.038 0.003 0.037 0.038 0.038
0.003 0.038 0.039 0.038 0.003 0.037 0.003
0.038 0.038 0.038 0.003 0.037 0.003 0.003
0.038 0.003 0.002 0.002 0.038 0.038 0.038
置信度的可視化效果以下。能夠看到,車輛已經對本身的置信度有了必定的認知,可是仍是有大量的可能位置須要進一步確認。
連續執行直方圖濾波100次,各個位置置信度的數值以下:
0.008 0.000 0.000 0.000 0.000 0.016 0.016
0.032 0.001 0.000 0.000 0.001 0.032 0.833
0.016 0.000 0.000 0.000 0.000 0.025 0.017
0.001 0.000 0.000 0.000 0.000 0.000 0.000
0.000 0.000 0.000 0.000 0.000 0.000 0.000
0.000 0.000 0.000 0.000 0.000 0.000 0.000
0.000 0.000 0.000 0.000 0.000 0.000 0.000
置信度的可視化效果以下,能夠看到,車輛已經83.3%的機率能夠肯定本身所處的位置了。
車輛狀態的定義以下:
$$\begin{aligned}\mathbf{{x}}_{t}&=\begin{bmatrix}\text{x} \\\text{y} \\\theta \\\text{v}\end{bmatrix}\\\end{aligned}$$
其中,(x,y)是車輛的位置,是車輛的朝向,v是車輛的運動速度,咱們假設車輛是勻速運動的,是車輛運動的角速度。
車輛運動方程以下,其實就是:
$x_{t+1} = x_t + v * cos(\theta) * \Delta t$
$y_{t+1} = y_t + v * sin(\theta) * \Delta t$
$\theta_{t+1} = \theta_t + \omega * \Delta t$
寫成矩陣形式:
$$x_{t+1} =\begin{bmatrix}1.0 & 0.0 & 0.0 & 0.0 \\0.0 & 1.0 & 0.0 & 0.0 \\0.0 & 0.0 & 1.0 & 0.0 \\0.0 & 0.0 & 0.0 & 0.0 \\\end{bmatrix} *\begin{bmatrix}\text{x} \\\text{y} \\\theta \\\text{v}\end{bmatrix}+ \begin{bmatrix}\Delta t cos(\theta) & 0.0 \\ \Delta t sin(\theta) & 0.0 \\0.0 & \Delta t \\1.0 & 0.0 \\\end{bmatrix} *\begin{bmatrix}v \\\omega \\\end{bmatrix}$$
車輛的運動模型代碼以下:
def motion_model(x, u): F = np.array([[1.0, 0, 0, 0], [0, 1.0, 0, 0], [0, 0, 1.0, 0], [0, 0, 0, 0]]) B = np.array([[DT * math.cos(x[2, 0]), 0], [DT * math.sin(x[2, 0]), 0], [0.0, DT], [1.0, 0.0]]) x = F @ x + B @ u return x
運動更新的過程與前面談到的車輛運動模型一致,車輛運動有不肯定性,因此增長了Gaussian Filter用來處理不肯定性。還有一個細節,就是車輛運動距離和直方圖濾波的分塊地圖之間的轉換關係:
x_shift = $\Delta$ x / map_x_resolution
y_shift = $\Delta$ y / map_y_resolution
代碼以下:
# grid_map是網格地圖,u=(v,w)是車輛運動的控制參數,yaw是車輛朝向 def motion_update(grid_map, u, yaw): # DT是時間間隔 grid_map.dx += DT * math.cos(yaw) * u[0] grid_map.dy += DT * math.sin(yaw) * u[0] # grid_map.xy_reso是地圖分辨率 x_shift = grid_map.dx // grid_map.xy_reso y_shift = grid_map.dy // grid_map.xy_reso if abs(x_shift) >= 1.0 or abs(y_shift) >= 1.0: # map should be shifted grid_map = map_shift(grid_map, int(x_shift), int(y_shift)) grid_map.dx -= x_shift * grid_map.xy_reso grid_map.dy -= y_shift * grid_map.xy_reso # MOTION_STD是車輛運動不肯定性的標準差 grid_map.data = gaussian_filter(grid_map.data, sigma=MOTION_STD) return grid_map
這個例子中經過測量車輛到LandMark的距離來肯定自身的位置,LandMark的位置都是已知的。
2D 直方圖濾波定位算法
def calc_gaussian_observation_pdf(gmap, z, iz, ix, iy, std): # predicted range x = ix * gmap.xy_reso + gmap.minx y = iy * gmap.xy_reso + gmap.miny d = math.sqrt((x - z[iz, 1]) ** 2 + (y - z[iz, 2]) ** 2) # likelihood pdf = (1.0 - norm.cdf(abs(d - z[iz, 0]), 0.0, std)) return pdf #z=[(車輛到Landmark的測量距離,Landmark的x座標,Landmark的y座標),...],z是全部Landmark測量距離和位置的集合,std是測量偏差的標準差 def observation_update(gmap, z, std): for iz in range(z.shape[0]): for ix in range(gmap.xw): for iy in range(gmap.yw): gmap.data[ix][iy] *= calc_gaussian_observation_pdf( gmap, z, iz, ix, iy, std) # 機率歸一化 gmap = normalize_probability(gmap) return gmap
設置地圖和測量相關參數:
DT = 0.1 # time tick [s] MAX_RANGE = 10.0 # maximum observation range MOTION_STD = 1.0 # standard deviation for motion gaussian distribution RANGE_STD = 3.0 # standard deviation for observation gaussian distribution # grid map param XY_RESO = 0.5 # xy grid resolution MINX = -15.0 MINY = -5.0 MAXX = 15.0 MAXY = 25.0 # Landmark Position RF_ID = np.array([[10.0, 0.0], [10.0, 10.0], [0.0, 15.0], [-5.0, 20.0]]) # 車輛的初始位置(for simulation) xTrue = np.zeros((4, 1))
經過Observation模擬自動駕駛車輛對各個LandMark的觀測結果和車輛速度的偏差。
def observation(xTrue, u, RFID): xTrue = motion_model(xTrue, u) z = np.zeros((0, 3)) for i in range(len(RFID[:, 0])): dx = xTrue[0, 0] - RFID[i, 0] dy = xTrue[1, 0] - RFID[i, 1] d = math.sqrt(dx ** 2 + dy ** 2) if d <= MAX_RANGE: # add noise to range observation dn = d + np.random.randn() * NOISE_RANGE zi = np.array([dn, RFID[i, 0], RFID[i, 1]]) z = np.vstack((z, zi)) # add noise to speed ud = u[:, :] ud[0] += np.random.randn() * NOISE_SPEED return xTrue, z, ud
執行車輛定位流程:
while SIM_TIME >= time: time += DT print("Time:", time) u = calc_input() yaw = xTrue[2, 0] # Orientation is known xTrue, z, ud = observation(xTrue, u, RF_ID) grid_map = histogram_filter_localization(grid_map, u, z, yaw)
定位效果以下:
圖片來源:https://github.com/AtsushiSakai/PythonRobotics
1.http://www.deepideas.net/robo...
2.https://github.com/AtsushiSakai/PythonRobotics