)
做了幾年機器人導航我發(fā)現(xiàn)自己反復回到一個最基礎的問題程序里的環(huán)境到底長什么樣。有人用幾何特征有人用拓撲圖但真正到了避障、路徑規(guī)劃、多傳感器融合落地的時候絕大多數(shù)項目最終的落腳點都是一張柵格占用地圖。它把一個連續(xù)世界切成一格一格的判斷每一格回答一個問題——“這里能不能過”。聽起來簡單想把它畫準、存得住、實時更新里面藏著非常多容易低估的坑。這篇文章就把我在實際項目里對柵格占用地圖的理解、踩過的坑和可復用的做法一次性講透。1. 柵格占用地圖把連續(xù)世界拆成一格一格的判斷題1.1 為什么幾乎所有導航系統(tǒng)都繞不開柵格先聊一個最根本的問題我們明明有厘米級精度的激光雷達為什么不用連續(xù)坐標去描述環(huán)境反而要把它離散成網(wǎng)格我的理解是連續(xù)坐標對“定位精確”很友好但對“推理決策”非常不友好。機器人看到一個桌子它不需要記住桌角的精確坐標它真正關(guān)心的是桌子占住的這一塊區(qū)域我的輪子能不能碾過去我的機械臂能不能伸進去。這是一個區(qū)域性的問題而不是一個點的問題。柵格占用地圖正好把環(huán)境表達成“區(qū)域?qū)傩浴?。每一格代表地面上的一小塊面積存儲的是這一小塊被占據(jù)的概率?;蛘哒f是這塊區(qū)域?qū)C器人而言的可通行程度。這種離散化帶來三個實打?qū)嵉暮锰巶鞲衅髟肼暠惶烊蝗萑?。激光打在同一個位置不可能每次都返回完全相同的坐標但打在同一個柵格里結(jié)果就穩(wěn)定得多。更新邏輯統(tǒng)一。不管傳感器是激光、超聲波還是毫米波都能變成“某個網(wǎng)格狀態(tài)變化”的形式融合起來非常簡單。路徑規(guī)劃算法可以直接作用。A*、Dijkstra、DWA這類算法都在離散網(wǎng)格上工作它們不需要理解“墻在幾何上是什么形狀”只需要知道“哪些柵格是占據(jù)的哪些是空閑的”。當然柵格地圖也有缺點最明顯的就是“固定分辨率”和“內(nèi)存增長”。如果你把分辨率定成2厘米那么一個100米乘100米的區(qū)域就是5000乘5000個格兩千萬五百萬個格這還是在二維。如果做成三維體素地圖內(nèi)存問題會更突出。所以柵格占用地圖從來不是一個“拍了腦袋就選”的方案而是一個在表達能力和計算資源之間做權(quán)衡的方案。1.2 一張柵格地圖里到底存了什么柵格地圖的核心數(shù)據(jù)就是一個二維矩陣矩陣的每個元素對應一塊物理區(qū)域。這個元素不只是0和1通常是一個概率值。常見的約定是0表示完全空閑1表示完全占據(jù)0.5表示未知。但實際存儲很少直接用0到1的浮點數(shù)而是用對數(shù)賠率log-odds來保存后面會細說。我自己做項目時第一版最容易犯的錯就是“用0和1畫地圖”。激光打在墻上就把對應格子設成1沒打到的設成0??雌饋頉]毛病但實際一跑就出問題激光打在玻璃上會偶爾穿透返回導致同一面墻這一幀是占據(jù)、下一幀是空閑人走過來的時候一部分點落在人身上另一部分穿過人腿之間的縫隙造成一個區(qū)域被反復翻轉(zhuǎn)。最終地圖上一片麻子。所以柵格地圖的工程實現(xiàn)必須帶“置信度”。一個網(wǎng)格被觀測到一次就判定為墻和被觀測到30次之后判定為墻顯然可信度不一樣。于是占據(jù)柵格地圖的底層邏輯變成我要記錄的是“根據(jù)到目前為止的所有觀測這個格子被占據(jù)的概率有多大”而不是“這一幀激光說了什么”。1.3 分辨率的直覺從地面一塊磚說起理解分辨率最簡單的方式是把它當成地面鋪磚。你把機器人活動區(qū)域鋪成瓷磚每塊瓷磚記錄“站人了嗎”。瓷磚越大整張圖越省內(nèi)存但也有兩個代價一是小障礙物直接消失比如2厘米的線纜在10厘米分辨率下基本不存在二是邊界模糊機器人靠近墻的時候它不知道自己能不能再往前蹭3厘米因為那一格已經(jīng)標記為占據(jù)。那到底怎么選分辨率我個人的經(jīng)驗公式是分辨率至少要達到機器人底盤直徑的十分之一到五分之一。比如一個直徑40厘米的掃地機器人2到4厘米是一個合理的起步值。室內(nèi)環(huán)境0.05米足夠用室外無人車一般用0.1到0.2米因為室外范圍太大0.05米會讓計算量和存儲直接爆掉而且室外激光噪聲遠大于室內(nèi)高分辨率反而容易把噪聲也一塊塊畫進去。關(guān)鍵經(jīng)驗分辨率不是越小越好是“讓地圖能表達出你關(guān)心的最小障礙物”就好。2. 柵格背后的概率更新機制為什么不是“碰到就標記”2.1 一次激光測量到底攜帶了哪些信息激光雷達一幀掃描有幾百甚至幾千個點每個點不只是“有一個點打到東西了”。如果我們樸素地理解每個點會告訴我們一個占據(jù)的位置。但如果只看這些點地圖會非常稀疏因為激光束經(jīng)過的空曠區(qū)域沒有被表達出來。這也是很多新人在第一次自己寫柵格地圖更新時都會漏掉的東西一條激光束從傳感器出發(fā)在命中一個障礙物之前它所有經(jīng)過的格子都應該被標記為“空閑”。只有最后那一段被擊中的位置才應該標記為“占據(jù)”。這個過程在SLAM里叫做射線投射ray casting。想象你手電筒照到一面墻你能說“只有墻上那個亮點是存在的”嗎當然不能。光束經(jīng)過的空間是空的所以機器人能看見墻這本身就是信息。如果把這些途經(jīng)點忽略地圖會變成一堆互不相連的“亮點”路徑規(guī)劃算法只能看見稀疏的幾個障礙點其他地方全是未知。實際結(jié)果就是機器人規(guī)劃出一條“穿墻”路線。所以一次有效的激光測量應該拆成兩部分中間的空閑柵格 終點的占據(jù)柵格。而終點那個柵格也不應該直接設成1而是把它的占據(jù)概率往上抬一點。每多觀測到一次就往“占據(jù)”方向再移動一點。這就是概率更新的雛形。2.2 貝葉斯更新與log-odds一次講透既然每個網(wǎng)格維護的是一個概率那么新的觀測來了之后怎么把這個概率和舊的概率合并這里直接用貝葉斯公式是最自然的思路。假設某個網(wǎng)格被占據(jù)的概率是P(occ)我們想根據(jù)新的傳感器測量z更新它。那么更新后的概率可以寫成P(occ | z) P(z | occ) * P(occ) / P(z)其中P(z | occ)是傳感器模型——也就是“如果這個格子真的被占據(jù)傳感器返回當前這個測量的概率”。這個模型不需要特別精確通常只是一個常量。但真正的工程問題在于如果直接用概率值做多次乘法數(shù)值會快速逼近0或1而且浮點運算是先乘后歸一化長時間運行后精度容易出問題。所以實際代碼里幾乎都用對數(shù)賠率log-odds也叫l(wèi)ogit表示。賠率定義為L(x) log( P(x) / (1 - P(x)) )好處是新的測量可以用加法更新L_new L_old L_measurement其中L_measurement 0 表示“這一幀告訴我這個格子被占據(jù)了”L_measurement 0 表示“這一幀告訴我這個格子是空閑的”0表示未知、無信息這個加法更新推導很簡單本質(zhì)是把貝葉斯公式里的乘積通過取對數(shù)變成加法。工程上還有一個額外好處就是不會因為連續(xù)累乘導致下溢出。我常用的初始化是 L_old 0對應P0.5代表未知。一次“占據(jù)”觀測的增量大概設為0.5到0.9一次“空閑”觀測的增量設為-0.4到-0.7。數(shù)值大小取決于傳感器噪聲的嚴重程度噪聲大增量小一點噪聲小增量可以大一點。這里必須注意一個問題不考慮“阻尼”會讓概率飽和。如果某格已經(jīng)被占據(jù)觀測了100次L值越來越大這時候有一幀因為傳感器誤檢而標記它為空閑這一個-0.7根本拉不動它。所以更新公式后面通常會夾一個上下限L_new clamp(L_old L_measurement, L_min, L_max)我一般取L_max10L_min-10。換算成概率大約是 P0.99995和P0.000045。這樣既保證了對噪聲的魯棒性又不會讓狀態(tài)徹底“焊死”。2.3 多傳感器融合超聲波寬波束怎么處理很多人一開始只用激光雷達后來想加入超聲波或毫米波。這時候要注意不同傳感器的“射線模型”不一樣。激光可以近似成一條極細的射線而超聲波是一個錐形波束它告訴你“這個方向上某個距離內(nèi)有物體”但它無法精確到具體是光束中心還是邊緣返回的。處理寬波束有個常用技巧不要把整個錐形區(qū)域都認為空閑也不要把返回點所在那一格直接標為占據(jù)。而是按角度分成多個子射線中間角度視為真實的射線邊緣角度降低置信度。簡單做法是對同一個網(wǎng)格如果被多個傳感器同時認為是占據(jù)那么占據(jù)概率疊加得更高如果激光認為是占據(jù)而超聲波認為是空閑則要設置傳感器可信度權(quán)重。比如激光權(quán)重0.8超聲波權(quán)重0.3最終更新量就是加權(quán)和。我自己實際項目里超聲波主要是用來補盲區(qū)的所以很少讓它直接參與柵格地圖的更新更多是用它做緊急剎車。真正要融合時記住一個原則無論如何用一個統(tǒng)一的log-odds增量表別把各家傳感器的輸出直接疊加成一個概率值再存。柵格地圖內(nèi)部永遠是log-odds概率值只是給人看的。3. 真實建圖流程從一幀激光到一張完整地圖3.1 坐標系與傳感器模型先對齊再談建圖建圖的輸入通常不只是激光數(shù)據(jù)還包括機器人當前位姿。位姿可能來自里程計、IMU、或者SLAM估計出來的定位結(jié)果。最關(guān)鍵的預處理是將激光點從激光坐標系轉(zhuǎn)換到機器人坐標系再轉(zhuǎn)換到世界系。這個環(huán)節(jié)如果外參標定不準地圖會出現(xiàn)“重影”——比如一面墻激光在前面幾幀看到它在x1.0的位置后來機器人轉(zhuǎn)了一圈再回來因為外參偏了0.1米墻畫在x1.1的位置。這幾乎是新手翻車率最高的點。我見過很多項目建圖算法沒選錯、參數(shù)沒調(diào)錯地圖就是糊最后發(fā)現(xiàn)是激光雷達安裝支架歪了兩毫米在5米外被放大成幾厘米的重影。所以我自己在建圖之前必做三件事靜態(tài)外參標定確認激光相對機器人中心的平移和旋轉(zhuǎn)。時間同步檢查如果里程計和激光時間戳差超過幾十毫秒在機器人高速移動時就會產(chǎn)生偏移。地面點過濾對于三維激光或雷達建二維柵格地圖前要把高于機器人底盤高度的點濾掉只保留“能擋住機器人”的障礙物層面的點。3.2 我自己常用的柵格更新偽代碼做了這么多概念鋪墊上一段最直觀的代碼。下面這個流程可以用在任何語言里核心邏輯完全一樣def update_grid_map(grid_values, grid_logodds, robot_pose, laser_points, params): # 把激光點轉(zhuǎn)到世界坐標 for point in laser_points: world_point robot_to_world(robot_pose, point) # 射線投射從機器人位置到激光點末端先清空沿途格子 cells bresenham(robot_pose.xy, world_point) for cell in cells[:-1]: idx grid_index(cell, params.resolution, params.map_origin) grid_logodds[idx] params.free_increment # 終點是占據(jù)但也需要做范圍檢查 last cells[-1] idx grid_index(last, params.resolution, params.map_origin) grid_logodds[idx] params.hit_increment # 可選將logodds轉(zhuǎn)成概率用于可視化/發(fā)布 for idx in updated_indices: grid_values[idx] sigmoid(grid_logodds[idx])這個代碼里有幾個細節(jié)值得提bresenham用畫線算法找出激光穿越了哪些柵格。注意這個遍歷順序是從傳感器位置出發(fā)到命中點結(jié)束不包括命中點本身。free_increment和hit_increment的值不一定要選成絕對值相等的相反數(shù)。因為一幀激光里空閑觀測的數(shù)量遠多于占據(jù)觀測如果兩者等幅最后地圖會整體偏“空閑”。所以通常hit_increment絕對值比free_increment大一些比如hit0.8free-0.4。每次更新只修改“本幀激光穿過的格子”不需要把整張地圖重算一遍這對性能非常重要。對一個400個點的一幀激光用上面的邏輯更新一次在普通CPU上大約需要幾十微秒到幾百微秒完全來得及實時跑。3.3 從“局部圖”到“全局圖”的拼接思路實際建圖不是單幀更新而是邊移動邊更新。最簡單的全局建圖方法就是把每一幀激光點通過當前位姿變換到全局坐標系然后執(zhí)行上面的柵格更新。這里的核心問題是當前位姿從哪來如果你的機器人只靠輪式里程計那么在平整地面上短距離還是可以的但一打滑就會出現(xiàn)地圖漂移。所以工業(yè)級做法是疊加SLAM實時估計位姿并回環(huán)檢測。地圖本身和SLAM是相互依賴的關(guān)系SLAM用地圖來校正位姿地圖又依賴位姿來更新。這也是為什么Cartographer這類系統(tǒng)會把柵格地圖作為核心后端。我在實際項目中很少自己單獨寫完整的SLAM大部分時候是用現(xiàn)成的SLAM框架構(gòu)建柵格地圖然后導出地圖文件給導航用。構(gòu)建完成之后的關(guān)鍵工作是“地圖后期處理”清理噪聲散點、填充小空洞、把地圖轉(zhuǎn)成導航用的代價地圖。4. 地圖數(shù)據(jù)結(jié)構(gòu)與內(nèi)存優(yōu)化從大數(shù)組到稀疏存儲4.1 一個樸素二維數(shù)組何時會撐爆內(nèi)存先算一筆賬。假設環(huán)境是200米乘200米分辨率0.05米那就是4000乘4000總共1600萬個格子。如果每個格子存一個float4字節(jié)這張圖就要64MB存一個double8字節(jié)就要128MB??雌饋磉€能接受但真實項目里不會只有一層地圖。導航系統(tǒng)通常需要多個圖層靜態(tài)占據(jù)層、代價層、膨脹層、障礙物層、機器人足跡層。單是5層地圖疊在一起64MB就變成了320MB這還沒算地圖以多分辨率保存的占用。對于載具、無人機、室外機器人來說連續(xù)建圖幾小時地圖范圍可能超過1公里這時候直接使用二維數(shù)組存儲會直接爆掉。4.2 實際項目里我用的幾種存儲方案我在實踐中慢慢形成了這樣一個優(yōu)先級稀疏哈希表優(yōu)先只存儲被更新過的格子沒有觀測到的格子不占用空間。一個房間建圖完成后真正被更新過的格子可能只占理論總格數(shù)的20%到40%走廊場景甚至會低于5%。用unordered_map或Python的dict存儲時key是柵格坐標value是log-odds值。缺點是查詢速度比連續(xù)數(shù)組慢但實際跑下來性能差別在毫秒級別完全可以接受。四叉樹/八叉樹四叉樹適合二維柵格八叉樹適合三維體素柵格。核心思路是如果一個區(qū)域內(nèi)所有子格子的狀態(tài)都相同那么在父節(jié)點直接存一個統(tǒng)一值不再往下細分。這在地圖有大片空閑區(qū)域時非常高效。缺點是更新局部格子時需要從根節(jié)點向下遍歷代碼復雜度高一些調(diào)試起來也麻煩。分層金字塔地圖對同一張地圖保存0.05米、0.1米、0.2米等多層級。路徑規(guī)劃時全局規(guī)劃用粗分辨率快速估算局部規(guī)劃用精分辨率細致搜索。這種方案更偏應用層但不少商業(yè)導航產(chǎn)品就是這么干的。4.3 數(shù)據(jù)類型選擇也能省一半內(nèi)存很多人在柵格地圖里存儲概率值都用float甚至double其實完全沒有必要。log-odds值的范圍通常限制在[-10, 10]這個精度用int8_t就足夠表達。把log-odds乘以一個縮放系數(shù)比如10范圍[-100, 100]用單字節(jié)表示。整張1600萬格子的地圖內(nèi)存直接從64MB降到16MB。我曾經(jīng)在一個項目里為了優(yōu)化把所有柵格從float改成了int16_t內(nèi)存降了一半更新速度反而略快因為緩存命中率更高了。這里有一個工程折中的點如果你在嵌入式設備上跑這個優(yōu)化幾乎是必選項。提醒一句別只盯著“地圖本身”的內(nèi)存還要考慮后續(xù)的膨脹層、發(fā)布用ROS消息時的拷貝、可視化圖像編碼。這些中間副本也會吃不少內(nèi)存處理不好會導致系統(tǒng)卡頓。5. 柵格地圖使用中的高頻翻車點與對應避坑方案5.1 分辨率選錯不是細就好也不是粗就好前面提過網(wǎng)格分辨率的選擇公式但實際項目里還有兩個隱藏問題。太細0.02米分辨率下激光束經(jīng)過的格子數(shù)量暴增射線投射計算量跟著漲同時每一個細小噪聲都會被獨立畫成一個“障礙物”。很多清掃機器人在0.02米分辨率下建圖地圖上全是飛點。太粗0.2米分辨率下一個直徑30厘米的障礙物只占一兩格機器人路徑規(guī)劃時哪怕距離膨脹層做得好也容易貼得太近。我調(diào)試時的習慣是先用0.1米分辨率快速驗證整個建圖鏈路是否通暢確認沒有問題后再降到0.05米。這樣可以隔離參數(shù)問題鏈路沒通你調(diào)到0.01米也沒用。5.2 動態(tài)障礙物在靜態(tài)地圖里留下的“幽靈”柵格占用地圖默認假設環(huán)境是靜態(tài)的。但在真實世界里人、車輛、開門、移動的箱子都會出現(xiàn)。如果這些動態(tài)物體被寫進長期更新地圖最后地圖上會出現(xiàn)“鬼影”明明人已經(jīng)走了那面區(qū)域還是顯示占據(jù)。解決這個問題有三個層次前端過濾檢測動態(tài)點不和靜態(tài)地圖更新。判斷方法可以很簡單比如連續(xù)多幀同一個位置被觀測為占據(jù)但位姿一致性偏差很大就判定為動態(tài)點。后端衰減對長期地圖里的占據(jù)概率做時間衰減每隔一段時間把log-odds往0方向拉一點動態(tài)物體的“占據(jù)”特征會慢慢消失。語義分離把動態(tài)障礙物單獨放到一個實時層靜態(tài)地圖層只保留過濾后的靜態(tài)點。這也是自動駕駛里costmap的標準做法。我用得最多的還是第三種。建圖階段關(guān)閉一些動態(tài)層導航階段實時維護障礙物層柵格地圖本身只保存環(huán)境結(jié)構(gòu)。5.3 閾值設置占據(jù)判定為何總是誤觸柵格地圖最終要從概率或log-odds轉(zhuǎn)換成一個“能否通行”的布爾值這就需要兩個閾值占據(jù)閾值和空閑閾值。比如一個格子log-odds大于0.5判定為占據(jù)小于-0.5判定為空閑介于兩者之間判定為未知。如果閾值設得太高動態(tài)噪聲不會觸發(fā)占據(jù)但是真實的細小障礙物也會被漏掉設得太低一片區(qū)域會被誤判為墻。我在實際項目里一般把占據(jù)閾值設為0.7對應的log-odds約0.85空閑閾值設為-0.4對應的log-odds約-0.4。這組參數(shù)跑起來誤判比較少。但是注意這些值不是萬能的。傳感器噪聲低時可以把閾值調(diào)低讓地圖更敏感噪聲高時必須調(diào)高閾值否則會滿地都是障礙。5.4 傳感器外參標定誤差地圖“重影”的根子前面提到了外參這里再深入一點。如果激光雷達相對機器人中心的安裝角度偏了0.5度那么在5米外的障礙物位置就會偏移約4.4厘米。在一張0.05米分辨率的柵格地圖里這幾乎等于讓同一堵墻被畫到兩個相鄰格子上。我建議在建圖之前做一個簡單驗證讓機器人原地旋轉(zhuǎn)觀察同一面墻在地圖上的投影是否穩(wěn)定。如果墻的輪廓在旋轉(zhuǎn)過程中來回擺動那很大概率是外參問題而不是建圖算法問題。很多人在算法參數(shù)里折騰了一天最后發(fā)現(xiàn)是支架歪了。6. 從“畫地圖”到“用地圖”柵格地圖如何真正支撐導航?jīng)Q策6.1 占用柵格地圖到代價地圖的轉(zhuǎn)化柵格地圖本身不直接參與規(guī)劃。路徑規(guī)劃器需要的是一張“代價地圖”每個網(wǎng)格除了已知是否占據(jù)外還要知道“離障礙物多近算危險”。這個危險程度就是膨脹。膨脹的過程很簡單對每個占據(jù)柵格以它為中心按機器人半徑加上安全距離把周圍一圈柵格的代價值從0平滑上升到一個最大值。路徑規(guī)劃搜索時代價高的柵格會被A*算法主動繞開。我在項目里一直強調(diào)不要直接在原始占用柵格地圖上做路徑規(guī)劃。因為原始地圖只有“0/1/未知”沒有考慮機器人半徑和姿態(tài)。就算機器人中心在一格空閑區(qū)域它的機身在旋轉(zhuǎn)時也可能掃到旁邊的障礙物。只有膨脹后的代價地圖才能安全指導導航。6.2 地圖的存儲與傳輸一張地圖最后到底存成什么建圖完成后地圖通常要打包保存下來。工業(yè)界最常見的格式是PGM圖像加YAML配置。PGM里面每個像素對應一個柵格灰度值代表狀態(tài)0表示占據(jù)黑色。254表示空閑白色。205表示未知灰色。YAML配置里記錄分辨率、原點坐標、柵格長度等元數(shù)據(jù)。加載地圖時解析圖像矩陣恢復成log-odds或代價地圖。這里推薦一個保存前的處理對地圖做一次形態(tài)學閉運算把小空洞和空洞填補掉同時清理孤立噪聲點。否則把建圖結(jié)果直接扔給導航會出現(xiàn)很多莫名其妙的“小島障礙物”路徑規(guī)劃器為了繞一個只有一兩格的噪聲點繞路繞大半天。6.3 多機器人共建一張地圖需要注意什么最后聊一個常在多機器人協(xié)同、工廠物流場景出現(xiàn)的問題多個機器人各建各的圖怎么合并成一張統(tǒng)一地圖。理論上只要知道兩個地圖原點之間的相對位姿變換就可以直接疊加。但實際上不同機器人建出的地圖可能存在尺度漂移、角度漂移更不用說各自局部地圖的誤差累積。我做的做法是先提取地圖特征點做粗匹配再用柵格對齊做精配準。坐標變換算出后對重疊區(qū)域做加權(quán)平均——概率值取權(quán)重平均權(quán)重取決于每個機器人局部地圖的置信度。多機器人地圖合并沒有銀彈框架只是輔助真正麻煩的是各機器人定位誤差的統(tǒng)計規(guī)律不一樣這一點必須在合并前就估計出來。7. 我個人調(diào)試柵格地圖的幾條心得寫到這里分享幾個在項目里反復驗證過的經(jīng)驗排序分先后第一先畫一張“夠糙”的地圖再慢工出細活。我第一版建圖永遠用0.2米分辨率、低閾值先看整體結(jié)構(gòu)對不對。一旦整體結(jié)構(gòu)都是歪的調(diào)什么0.05米、調(diào)什么概率增量都是白費功夫。第二把傳感器外參查三遍再動算法參數(shù)。地圖重影、模糊、錯位八成是外參或時間同步問題不是柵格更新公式問題。拿一個固定障礙物做原地旋轉(zhuǎn)測試是效率最高的排查手段。第三地圖更新里別貪心。free和hit增量不要設得太大。我見過有人為了讓地圖快速收斂把hit增量設成5結(jié)果機器人面前十厘米的紙箱被當成墻導航完全卡死。寧可讓地圖收斂慢一點也要保證傳感器誤檢時不會造成災難性后果。第四動態(tài)物體永遠別和靜態(tài)結(jié)構(gòu)混在一個圖層。即使你現(xiàn)在只做靜態(tài)巡檢你的工作場地里也一定會有門、車輛、人。不要等到地圖被“幽靈”弄亂了才后悔一開始就把圖層分開。柵格占用地圖是一個看起來特別基礎、實際又特別深的主題。它跨越了傳感器、概率估計、數(shù)據(jù)結(jié)構(gòu)、數(shù)據(jù)存儲、路徑規(guī)劃好幾個領域。把上面這套邏輯吃透不管以后是用現(xiàn)成導航框架還是自己寫核心模塊都會非常順暢。希望這篇經(jīng)驗總結(jié)能幫你少走一些我當年走過的彎路。