动手学无人驾驶(3):基于激光雷达3D多目标追踪
上一篇博客介紹了無人駕駛中的車輛檢測算法(YOLO模型),該檢測是基于圖像進行的2D目標檢測。在無人駕駛環境感知傳感器中還有另一種重要的傳感器:激光雷達。今天就介紹一篇無人駕駛中基于激光雷達目標檢測的3D多目標追蹤論文,使用的數據集也是KITTI數據集,使用的目標檢測算法是PointRCNN。該3D多目標追蹤系統不需要使用GPU且取得了最快的處理速度(214.7FPS)。
論文地址:A Baseline for 3D Multi-Object Tracking
Github地址:https://github.com/xinshuoweng/AB3DMOT
大家有時間可以看看這個視頻,這是論文作者此前分享的一個3D多目標追蹤報告,視頻地址:https://www.bilibili.com/video/BV1DD4y1Q7JG/
3D Multi-Object Tracking: A Baseline and New Evaluation Metrics
文章目錄
- 1.概述
- 2.方法
- A. 3D 目標檢測
- B. 3D 卡爾曼濾波---狀態預測
- C. 數據關聯
- D. 3D卡爾曼濾波-狀態更新
- E. Birth and Death內存
- 3. 測試結果
1.概述
無論是在自動駕駛或輔助機器人等實時應用中,3D多目標追蹤(MOT)都是非常重要的技術環節。然而,在近期的一些3D多目標追蹤研究當中更多的都是關注于追蹤系統的準確率而忽略了其計算成本和復雜性。
在本文中作者提出了一種簡單而又準確的實時3D MOT基線系統。本文使用了現成的3D目標檢測器從LiDAR點云數據中獲取對應目標3D bounding box。 然后,將3D卡爾曼濾波器和匈牙利算法結合起來進行目標狀態估計和數據關聯。為了評估提出的基線系統,論文在原有的KITTI官方2D MOT評價指標上進行了擴展,提出了三個新的3D MOT評價指標,本文主要貢獻有:
- 設計了一種簡單而又準確的實時3D MOT系統;
- 對KITTI官方2D MOT評價指標進行擴展,提出了新的3D MOT評價指標;
- 在KITTI評價工具上獲得了最優的3D MOT性能,在2D MOT評價工具上獲得了最快的處理速度(如下圖所示)。
2.方法
3D MOT的目的是對一段連續數據中的3D 檢測目標進行關聯。在本系統中,需要使用當前幀中的檢測結果以及上一幀中的關聯軌跡。系統結構如下圖所示,由5個部分組成:
(A)3D檢測模塊用于對LiDAR點云數據進行目標檢測;
(B)3D卡爾曼濾波模塊用于預測關聯軌跡當前幀的狀態;
(C)數據關聯模塊用于對檢測模塊輸出結果和卡爾曼濾波模塊輸出的軌跡進行匹配關聯;
(D)3D卡爾曼濾波根據檢測結果更新匹配軌跡的狀態;
(E)birth和death內存模塊用于生成新的軌跡和刪除未匹配的目標軌跡。
除了3D目標檢測塊以外,3D MOT系統不需要進行訓練,可直接使用。
A. 3D 目標檢測
在這里,直接使用在KITTI 3D數據及上訓練好的3D 檢測模型(即:PointRCNN)作為目標檢測模塊。
下面是一些術語規定,假設當前為ttt時刻,3D檢測模塊輸出是:Dt={Dt1,Dt2,...,Dtnt}D_{t}=\{{D_{t}^{1}, D_{t}^{2}, ...,D_{t}^{n_{t}}}\}Dt?={Dt1?,Dt2?,...,Dtnt??},(ntn_tnt?為ttt時刻中檢測目標數量)。每一個檢測目標向量表示形式為:(x,y,z,θ,l,w,h,s)(x, y, z,\theta, l, w, h, s)(x,y,z,θ,l,w,h,s),即目標中心坐標(x,y,z)(x,y,z)(x,y,z),目標尺寸(l,w,h)(l,w,h)(l,w,h),偏航角θ\thetaθ以及置信度sss
B. 3D 卡爾曼濾波—狀態預測
為了預測目標從上一幀到當前幀的軌跡狀態,這里選擇使用的運動模型為恒速運動模型,忽略了相鄰幀內的目標位置移動。
具體地,我們使用11維向量來表示目標軌跡的狀態T=(x,y,z,θ,l,w,h,s,vx,vy,vz)T=(x, y, z, \theta, l, w, h, s, v_{x}, v_{y,} v_{z})T=(x,y,z,θ,l,w,h,s,vx?,vy,?vz?),附加的3個變量表示目標在3個維度上的運動速度。
在每一幀,所有t-1幀的關聯軌跡為Tt?1={Tt?11,Tt?12,?,Tt?1mt?1}T_{t-1}=\left\{T_{t-1}^{1}, T_{t-1}^{2}, \cdots, T_{t-1}^{m_{t-1}}\right\}Tt?1?={Tt?11?,Tt?12?,?,Tt?1mt?1??}(mt?1m_{t-1}mt?1?是t-1時刻的軌跡數量)將會被傳播到下一幀ttt,用TestT_{est}Test?表示。由恒速運動模型可以推出:
xest=x+vxyest=y+vyzest=z+vzx_{est}=x+v_x \quad y_{est}=y+v_y \quad z_{est}=z+v_z xest?=x+vx?yest?=y+vy?zest?=z+vz?
因此,Tt?1T_{t-1}Tt?1?中的每個軌跡Tt?1iT^i_{t-1}Tt?1i?傳播到t幀中的預測狀態將是Testi=(xest,yest,zest,θ,l,w,h,s,vx,vy,vz)T_{\mathrm{est}}^{i}=\left(x_{\mathrm{est}}, y_{\mathrm{est}}, z_{\mathrm{est}}, \theta, l, w, h, s, v_{x}, v_{y}, v_{z}\right)Testi?=(xest?,yest?,zest?,θ,l,w,h,s,vx?,vy?,vz?),軌跡數據將會用于之后的關聯模塊。
3D 卡爾曼濾波的代碼如下:
class KalmanBoxTracker(object):"""This class represents the internel state of individual tracked objects observed as bbox."""count = 0def __init__(self, bbox3D, info):"""Initialises a tracker using initial bounding box."""# define constant velocity modelself.kf = KalmanFilter(dim_x=10, dim_z=7) self.kf.F = np.array([[1,0,0,0,0,0,0,1,0,0], # state transition matrix[0,1,0,0,0,0,0,0,1,0],[0,0,1,0,0,0,0,0,0,1],[0,0,0,1,0,0,0,0,0,0], [0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,1,0,0,0,0],[0,0,0,0,0,0,1,0,0,0],[0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,0,1,0],[0,0,0,0,0,0,0,0,0,1]]) self.kf.H = np.array([[1,0,0,0,0,0,0,0,0,0], # measurement function,[0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0],[0,0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,1,0,0,0,0],[0,0,0,0,0,0,1,0,0,0]])# # with angular velocity# self.kf = KalmanFilter(dim_x=11, dim_z=7) # self.kf.F = np.array([[1,0,0,0,0,0,0,1,0,0,0], # state transition matrix# [0,1,0,0,0,0,0,0,1,0,0],# [0,0,1,0,0,0,0,0,0,1,0],# [0,0,0,1,0,0,0,0,0,0,1], # [0,0,0,0,1,0,0,0,0,0,0],# [0,0,0,0,0,1,0,0,0,0,0],# [0,0,0,0,0,0,1,0,0,0,0],# [0,0,0,0,0,0,0,1,0,0,0],# [0,0,0,0,0,0,0,0,1,0,0],# [0,0,0,0,0,0,0,0,0,1,0],# [0,0,0,0,0,0,0,0,0,0,1]]) # self.kf.H = np.array([[1,0,0,0,0,0,0,0,0,0,0], # measurement function,# [0,1,0,0,0,0,0,0,0,0,0],# [0,0,1,0,0,0,0,0,0,0,0],# [0,0,0,1,0,0,0,0,0,0,0],# [0,0,0,0,1,0,0,0,0,0,0],# [0,0,0,0,0,1,0,0,0,0,0],# [0,0,0,0,0,0,1,0,0,0,0]])# self.kf.R[0:,0:] *= 10. # measurement uncertainty# state uncertainty, give high uncertainty to the unobservable initial velocities, covariance matrixself.kf.P[7:, 7:] *= 1000. self.kf.P *= 10.# self.kf.Q[-1,-1] *= 0.01 # process uncertaintyself.kf.Q[7:, 7:] *= 0.01self.kf.x[:7] = bbox3D.reshape((7, 1))self.time_since_update = 0self.id = KalmanBoxTracker.countKalmanBoxTracker.count += 1self.history = []self.hits = 1 # number of total hits including the first detectionself.hit_streak = 1 # number of continuing hit considering the first detectionself.first_continuing_hit = 1self.still_first = Trueself.age = 0self.info = info # other info associateddef update(self, bbox3D, info): """ Updates the state vector with observed bbox."""self.time_since_update = 0self.history = []self.hits += 1self.hit_streak += 1 # number of continuing hitif self.still_first:self.first_continuing_hit += 1 # number of continuing hit in the fist time######################### orientation correctionif self.kf.x[3] >= np.pi: self.kf.x[3] -= np.pi * 2 # make the theta still in the rangeif self.kf.x[3] < -np.pi: self.kf.x[3] += np.pi * 2new_theta = bbox3D[3]if new_theta >= np.pi: new_theta -= np.pi * 2 # make the theta still in the rangeif new_theta < -np.pi: new_theta += np.pi * 2bbox3D[3] = new_thetapredicted_theta = self.kf.x[3]if abs(new_theta - predicted_theta) > np.pi / 2.0 and abs(new_theta - predicted_theta) < np.pi * 3 / 2.0: # if the angle of two theta is not acute angleself.kf.x[3] += np.pi if self.kf.x[3] > np.pi: self.kf.x[3] -= np.pi * 2 # make the theta still in the rangeif self.kf.x[3] < -np.pi: self.kf.x[3] += np.pi * 2# now the angle is acute: < 90 or > 270, convert the case of > 270 to < 90if abs(new_theta - self.kf.x[3]) >= np.pi * 3 / 2.0:if new_theta > 0: self.kf.x[3] += np.pi * 2else: self.kf.x[3] -= np.pi * 2######################### # flipself.kf.update(bbox3D)if self.kf.x[3] >= np.pi: self.kf.x[3] -= np.pi * 2 # make the theta still in the rageif self.kf.x[3] < -np.pi: self.kf.x[3] += np.pi * 2self.info = infodef predict(self): """Advances the state vector and returns the predicted bounding box estimate."""self.kf.predict() if self.kf.x[3] >= np.pi: self.kf.x[3] -= np.pi * 2if self.kf.x[3] < -np.pi: self.kf.x[3] += np.pi * 2self.age += 1if (self.time_since_update > 0):self.hit_streak = 0self.still_first = Falseself.time_since_update += 1self.history.append(self.kf.x)return self.history[-1]def get_state(self):"""Returns the current bounding box estimate."""return self.kf.x[:7].reshape((7, ))C. 數據關聯
為了將檢測結果DtD_tDt?與預測軌跡TestT_{est}Test?進行匹配,我們將使用匈牙利算法。
維度為(mt?1,nt)(m_{t-1}, n_t)(mt?1?,nt?)的矩陣將用來計算每對DtD_tDt?和TestT_{est}Test?的3DIoU3D IoU3DIoU或目標中心距離。然后,使用匈牙利算法來處理二分圖匹配問題。除此之外,當3DIoU3D IoU3DIoU小于IoUminIoU_{min}IoUmin?時或目標中心距離大于distmaxdist_{max}distmax?時,將拒絕進行匹配。數據關聯模塊的輸出將分為4類:
Tmatch={Tmatch1,Tmatch2,..,Tmatchwt}Dmatch={Dmatch1,Dmatch2,..,Dmatchwt}Tunmatch={Tunmatch1,Tunmatch2,..,Tunmatchmt?1?wt}Dunmatch={Dunmatch1,Dunmatch2,..,Dunmatchnt?wt}T_{match}=\{{T^1_{match},T^2_{match},..,T^{wt}_{match}}\}\\ D_{match}=\{{D^1_{match},D^2_{match},..,D^{wt}_{match}}\}\\ T_{unmatch}=\{{T^1_{unmatch},T^2_{unmatch},..,T^{m_{t-1}-wt}_{unmatch}}\}\\ D_{unmatch}=\{{D^1_{unmatch},D^2_{unmatch},..,D^{nt-wt}_{unmatch}}\}Tmatch?={Tmatch1?,Tmatch2?,..,Tmatchwt?}Dmatch?={Dmatch1?,Dmatch2?,..,Dmatchwt?}Tunmatch?={Tunmatch1?,Tunmatch2?,..,Tunmatchmt?1??wt?}Dunmatch?={Dunmatch1?,Dunmatch2?,..,Dunmatchnt?wt?}
這里wtw_twt?是匹配目標數量。
D. 3D卡爾曼濾波-狀態更新
由于TmatchT_{match}Tmatch?的不確定性,我們根據檢測結果DmatchD_{match}Dmatch?對TmatchT_{match}Tmatch?整個狀態空間的每個軌跡進行更新,最后獲得ttt幀時刻的關聯軌跡Tt={Tt1,Tt2,?,Ttwt}T_{t}=\left\{T_{t}^{1}, T_{t}^{2}, \cdots, T_{t}^{w_{t}}\right\}Tt?={Tt1?,Tt2?,?,Ttwt??}。
根據貝葉斯規則,更新后的每一條軌跡Ttk=(x′,y′,z′,θ′,l′,w′,h′,s′,vx′,vy,′vz′)T_{t}^k=(x', y', z', \theta', l', w', h',s', v_{x}', v_{y,}' v_{z}')Ttk?=(x′,y′,z′,θ′,l′,w′,h′,s′,vx′?,vy,′?vz′?)是狀態空間TmatchkT_{match}^kTmatchk?和DmatchkD_{match}^kDmatchk?的加權和,這的權重是由軌跡和檢測的不確定性所決定的。
除此之外,我們發現原始的加權和對確定目標方向效果一般。對于匹配目標TtkT_{t}^kTtk?,其檢測結果上的方向和運動軌跡方向可能完全相反,相差π\piπ個相位。但事實上,目標都是平緩移動,不可能在一幀中改變移動方向。從結果上來看,TtkT_t^kTtk?目標的加權軌跡方向應該位于DmatchkD_{match}^kDmatchk?和TmatchkT_{match}^kTmatchk?之間。這往往將會導致比較低的3DIoU3D IoU3DIoU值。為此,我們提出一個方向修正技術,當兩者相位相差超過π/2\pi/2π/2時,我們在TmatchkT_{match}^kTmatchk?上添加π\piπ個相位,以便TmatchkT_{match}^kTmatchk?和DmatchkD_{match}^kDmatchk?方向能大致一致。
E. Birth and Death內存
因為現存的目標可能會消失以及新的目標可能會進入畫面,因此需要一個新的模塊來管理出現和消失的軌跡。一方面,我們認為所有不匹配的檢測可能會進入幀中,為了避免假陽性追蹤,當在BirminBir_{min}Birmin?中持續檢測到DunmatchpD_{unmatch}^pDunmatchp?時,將會生成 一條新的軌跡TnewpT_{new}^pTnewp?。一旦新的軌跡產生,我們將初始化TnewpT_{new}^pTnewp?作為最新的測量DunmatchpD_{unmatch}^pDunmatchp?,三個方向的速度初始化為0。
另一方面,我們認為所有不匹配的軌跡可能會離開,為了避免刪除真陽性的軌跡,我們在AgemaxAge_{max}Agemax?幀中持續追蹤不匹配軌跡TunmatchqT_{unmatch}^qTunmatchq?直到從關聯軌跡刪除。
3. 測試結果
在KITTI上的追蹤結果如下,這里只給出了可視化結果,詳細結果可查閱論文。
總結
以上是生活随笔為你收集整理的动手学无人驾驶(3):基于激光雷达3D多目标追踪的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全球股市开始下跌,大宗商品却大涨,意味着
- 下一篇: 浦发信用卡白金卡额度多少 visa白金卡