再一次輪到講自己的paper!耶,宣傳一下自己的工作,順便完成中文博客的解讀 方便大家討論。
-
Title Picture

-
Reference and pictures
paper: https://arxiv.org/abs/2401.16122
code: https://github.com/KTH-RPL/DeFlow
b站視頻: https://www.bilibili.com/video/BV1GH4y1w7LQ
1. Introduction
這個啟發主要是和上一篇 動態障礙物去除 的有一定的聯系,去除完了當然會開始考慮是不是可以有實時識別之類的, 比起只是單純標記1/0 的動和非動分割以外會是什么?然后就發現了 任務:scene flow,其實在2D可能更為人所熟知一些:光流檢測,optical flow,也就是輸入兩幀連續的圖片,輸出其中一張的每個pixel的運動趨勢,NxNx2,其中N為圖片大小,2為x,y兩個方向上的速度

對應的 3D情況下 則是切換為 輸入是兩幀連續的點云幀,輸出一個點云幀內每個點的運動,Nx3,N為點云幀內點的個數,3為x, y, z三個軸上的速度

Motivation
首先關于在自動駕駛的光流任務,我們希望的是能滿足以下兩個點:
- Real Time Running 10Hz
- 能負擔的起大量點云的輸入 32或64線 至少都是6萬個點/幀 起步了 隨便選kitti 一幀 點數是:125883~=12萬;而之前大部分光流論文還停留在max point=8192,然后我當時(2023年8月附近)隨手選了最新cvpr的sota:SCOOP一文,一運行就cuda out of memory;問作者才知道 領域內默認max=8192 number of point

那么Voxelization-based method就是其中大頭 or 唯一選擇了;
接著故事就來到了 啟發DeFlow的點:在查看最近工作(于2023年8月附近查看),閱讀相關資料時發現,很多自監督的paper都聲稱自己超過了 某篇監督的模型效果,也就是Waymo在RA-L發的一篇dataset順帶提出了FastFlow3D(官方閉源,民間有復現);但是實際上 FastFlow3D本身就是參考3D detection那邊網絡框架進行設計的,僅將最后的decoder 連一個 MLPs 用以輸出point flow
在我們的實驗中發現,特別是在resolution用的20cm的時候,效果確實不好,主要原因集中在于下圖2,統計發現如果一個點在動(速度≥0.5m/s),那么絕大多數都是在0.2以下的距離內運動;那么動一動腦筋,我們就想到了 調參,直接把resolution調到10cm不就行了?沒錯!DeFlow 實驗表格 Table III 第三行證明確實直接double kill

那么我們就知道了20cm 的柵格化分辨率下,點都在一個柵格里運動,所以前期pillar encoder 根本無法學出同一個voxel內不同點的feature,而FastFlow3D本身的decoder又是非常簡單的MLP提取,無法實現voxel-to-point feature extraction
Contribution
所以我們的貢獻就以以上為基礎來講述的啦,總結就是:1、提出了一個基于GRU voxel-to-point refinement的decoder;2、同時分析了以下loss function的影響并快速提了一個新的;3、最后實驗到 AV2 官方在線榜單的SOTA
note:所有代碼,各種對比消融實驗 和 刷榜所用的model weight全部都開源供大家下載查閱,歡迎star和follow up:https://github.com/KTH-RPL/DeFlow

2. Method
非常簡單易懂的方法部分,特別配合代碼使用

2.1 Input & Output
輸入是兩幀點云,具體一點 和FastFlow3D還有一系列的3D detection 一樣;我們會先做地面去除,所以實際輸入已經去掉了地面的 P t , P t + 1 P_t, P_{t+1} Pt?,Pt+1?
然后我們要估計的是 P_t 的 flow F,其中根據ego pose信息,我們也專注于預測除pose flow外的,也就是環境內的屬于動態物體帶速度的點

2.2 Decoder
看代碼可能更快一點,論文和圖主要是給了一個insight :
- 從pillar point feature提過來走MLP extend feature channel 作為 更新門 Z_t
- 然后由經過U-Net后的voxel feature 作為initial H_0,之后由再根據迭代次數每次得到更新的 H_{t-1}
此處為對照代碼,方便大家直接對照查看,具體在以下兩個文件:
- decoder:https://github.com/KTH-RPL/DeFlow/blob/main/scripts/network/models/basic/decoder.py
- deflow mode: https://github.com/KTH-RPL/DeFlow/blob/main/scripts/network/models/deflow.py
def forward_single(self, before_pseudoimage: torch.Tensor,after_pseudoimage: torch.Tensor,point_offsets: torch.Tensor,voxel_coords: torch.Tensor) -> torch.Tensor:voxel_coords = voxel_coords.long()after_voxel_vectors = after_pseudoimage[:, voxel_coords[:, 1],voxel_coords[:, 2]].Tbefore_voxel_vectors = before_pseudoimage[:, voxel_coords[:, 1],voxel_coords[:, 2]].T# [N, 64] [N, 64] -> [N, 128]concatenated_vectors = torch.cat([before_voxel_vectors, after_voxel_vectors], dim=1)# [N, 3] -> [N, 64]point_offsets_feature = self.offset_encoder(point_offsets)# [N, 128] -> [N, 128, 1]concatenated_vectors = concatenated_vectors.unsqueeze(2)for itr in range(self.num_iters):concatenated_vectors = self.gru(concatenated_vectors, point_offsets_feature.unsqueeze(2))flow = self.decoder(torch.cat([concatenated_vectors.squeeze(2), point_offsets_feature], dim=1))return flow
然后self.gru則是由這個常規ConvGRU module生成,forward和如下公式 直接對應
H t = Z t ⊙ H t ? 1 + ( 1 ? Z t ) ⊙ H ~ t (3) \mathbf{H}_t=\mathbf{Z}_t \odot \mathbf{H}_{t-1}+\left(1-\mathbf{Z}_t\right) \odot \tilde{\mathbf{H}}_t \tag{3} Ht?=Zt?⊙Ht?1?+(1?Zt?)⊙H~t?(3)
# from https://github.com/weiyithu/PV-RAFT/blob/main/model/update.py
class ConvGRU(nn.Module):def __init__(self, input_dim=64, hidden_dim=128):super(ConvGRU, self).__init__()self.convz = nn.Conv1d(input_dim+hidden_dim, hidden_dim, 1)self.convr = nn.Conv1d(input_dim+hidden_dim, hidden_dim, 1)self.convq = nn.Conv1d(input_dim+hidden_dim, hidden_dim, 1)def forward(self, h, x):hx = torch.cat([h, x], dim=1)z = torch.sigmoid(self.convz(hx))r = torch.sigmoid(self.convr(hx))rh_x = torch.cat([r*h, x], dim=1)q = torch.tanh(self.convq(rh_x))h = (1 - z) * h + z * qreturn h
所以和其他對GRU的用法不同,主要是我們將其用于voxel和point 之間細化特征提取了,當然代碼里也有我第一次的MM TransformerDecoder 和 直接的 LinearDecoder嘗試 hahah;前者太慢了,主要是點太多 我分了batch;后者效果不行,帶代碼就當附帶都留下來了
然后論文里講了以下loss function的設計,過程簡化以下就是:之前的工作一般,在和gt的norm基礎上 都自己給設計不同的權重,比如這里的 σ \sigma σ

結論就是我們這樣設計的,根據ZeroFlow的三種速度劃分,我們不用權重而是直接unified average;實驗部分會說明各個module的進步

OK 方法到這里就結束了,自認為非常直覺性的講故事下來的 hahaha,然后很多實驗在各種角度模塊進行證明我們的statement
3. Experiments
這個是直接抽的leaderboard的表格,具體每個方法的文件 見 https://github.com/KTH-RPL/DeFlow/discussions/2

前三者都是自監督 每篇都說超過了監督方法 FastFlow3D,但實際上只是baseline weak了,或者說他們比不過 FastFlow3D 10cm (0.1m)的分辨率,ZeroFlow XL就是把分辨率降到了0.1 然后加大了網絡;OK leaderboard (test set 只能上傳到在線平臺評估) 的分析就這樣了,知道SOTA就行
接下來的所有評估都是本地的,因為在線平臺有提交次數限制 hahaha;首先貼出 Table III:注意這之中僅改變了decoder,其他loss func, learning rate, 訓練條件均保持一致
這張表格也就是我們說的 我們的decoder提出 不需要細化10cm分辨率;因為這樣GPU的Memory 大大上升了,總得留點給其他模塊用嘛,見FastFlow3D 0.1 第三行
而我們保持了20cm的分辨率 速度和GPU內存使用均無太多上漲的情況下,我們的EPE 3-Way的分數甚至比FastFlow3D 細化10cm的還要好,誤差比原來的 低了33%

Ablation Study
Loss Function:注意此處我們全部使用FastFlow3D的network,僅loss function不同而已

decoder iter number:其實我不太想做這個實驗,但耐不住可能審稿人要問,所以我當時initial是選2/4跑的,畢竟多了降速 hahaha;此處全部使用deflow,僅iteration number不同,所以第二行可以認為是deflow: our decoder+our loss的效果(因為Table III為了對decoder的消融,所以其實我們使用的是fastflow3d提出的loss function;途中有韓國老哥沒看論文,只要結果,所以他跑提供的weight的結果比我好,其實是他看錯了表 lol)

結果可視化
主要就是快速看看就行,code那邊還有10秒 demo視頻可看

4. Conclusion
結論重復了一遍貢獻

然后說了以下future work:自監督的模型訓練,畢竟gt難標呀;歡迎查看最新ECCV2024的工作SeFlow,也就是我當時寫下future work的時候已經在嘗試路上的時候了;同開源(只要我主手的工作都開源 并在文章出版前 code上傳完全能復現論文結果,我的信條 hahaha)
- https://github.com/KTH-RPL/SeFlow
贈人點贊 手有余香 😆;正向回饋 才能更好開放記錄 hhh