Wiki
map_saver(二維柵格地圖保存)
本文由簡體中文內容確定性轉換,並受版本化術語表保護。
1.map_saver(二維柵格地圖保存)
1.1.實車先看:保存哪個話題,文件有沒有保留未知區域
map_saver接收一份OccupancyGrid,寫出圖片及配套地圖YAML。 當前slam_launch啓動map_saver_server可執行程序,其節點名為map_saver;配置這些參數或啓動服務不會自動把地圖保存到磁盤,仍需發起保存。
slam_toolbox → namespace/map(OccupancyGrid)
├─ SaveMap服务请求 → 建图launch中的map_saver
│ → 临时订阅、等待一份地图 → 写图片 + 地图YAML
│
└─ map_saver_cli → 新建一个独立MapSaver
→ 自己订阅、等待一份地图 → 写图片 + 地图YAML
保存的图片 + 地图YAML → map_server → 重新得到OccupancyGrid
map_saver_server、map_saver_cli和節點名map_saver需要區分。 兩個可執行程序使用同一個MapSaver類,但CLI自行保存,不通過正在運行的服務器發送請求;因此當前launch中的5秒參數不會自動傳給另一條CLI命令。保存器實現、CLI入口
| 實車上看到的現象 | 先核對什麼 | 當前配置怎樣理解 |
|---|---|---|
| 建圖結束卻沒出現PGM/YAML | 是否真正調用保存服務或CLI、返回結果與目錄 | 啓動保存器不是自動保存,也不是退出時自動導出二維圖 |
| YAML設了5 s,CLI卻很快等圖失敗 | 保存的是哪個進程及CLI參數 | 未給CLI加載/設置參數時,它用源碼默認2 s,不沿用launch保存器的5 s |
| 保存不到圖,一直等到超時 | map_topic、namespace、QoS及地圖發佈狀態 | 當前每次保存才臨時訂閲一份地圖,True要求兼容的Transient Local發佈端 |
| 改默認閾值,卻看不到這次輸出變化 | SaveMap請求或CLI是否明確指定閾值、map_mode | 請求非零閾值優先;三態與其他模式的編碼不同;地圖若只含0、100、-1,許多門檻變化不改變保存階段分類 |
| 圖片是灰色,重新加載後卻成空地 | trinary寫出的灰205與地圖YAML free_thresh | 當前0.25/0.65組合確有讀寫後的未知身份丟失,已用安裝庫臨時地圖驗證 |
| 保存二維圖後沒有PCD/posegraph | 保存器的輸入與輸出類型 | 本節點只處理OccupancyGrid;PCD、slam_toolbox內部圖狀態需要各自保存 |
本次安裝的nav2_map_server為1.1.20,map_saver/map_io安裝頭文件與該版本一致。當前reality參數與安裝副本一致;simulation段僅use_sim_time=True不同。
1.2.參數
保留髮送的5項原配置。具體保存請求的輸入話題、輸出路徑、模式、格式和請求閾值另列,不能把這些請求字段當成這裏已經寫好的節點參數。
map_saver:
ros__parameters:
use_sim_time: False
save_map_timeout: 5.0
free_thresh_default: 0.25
occupied_thresh_default: 0.65
map_subscribe_transient_local: True
1.2.1.ros__parameters(等待、默認閾值與地圖訂閲)
1.2.1.1.參數表
| 參數名 | 當前值 | 單位 | 實車處理建議 | 作用 | 調大效果 | 調小效果 | 生效條件/備註 |
|---|---|---|---|---|---|---|---|
use_sim_time |
False;仿真True |
不適用,布爾 | 上實物前必須核對並正確配置 | 選擇節點ROS時鐘 | 不適用 | 不適用 | 此鍵在兩套配置都存在,slam_launch會重寫;不改變圖片座標/格距;保存等圖超時不是按/clock走的週期任務 |
save_map_timeout |
5.0;源碼默認2.0 |
s,一次訂閲等圖的等待期限 | 通常保持當前值,不作為優先調試項 | 保存時等待收到一份OccupancyGrid的上限 | 可容忍更慢到圖,也更晚報告等待失敗 | 缺圖時更快失敗,可能來不及取得正常地圖 | 不是每5 s保存,不是完整寫盤操作的總期限;只作用於使用此參數的保存器,CLI需單獨設置 |
free_thresh_default |
0.25 |
無量綱,佔用比例;當前對應25 | 上實物前必須核對並正確配置 | 請求free_thresh為0時的默認值;三態保存把已知值≤25寫為空閒色 | 保存階段更多已知格判空閒、中間灰色減少 | 保存階段更少已知格判空閒、中間灰色增加 | 輸入-1仍先寫未知灰;但當前灰205在0.25下重載成空閒,需看下方驗證;請求非零值覆蓋此默認 |
occupied_thresh_default |
0.65 |
無量綱,佔用比例;當前對應65 | 實物運行時重點觀察,有問題再針對性調整 | 請求occupied_thresh為0時的默認值;三態保存把已知值≥65寫成佔用色 | 保存階段更少已知格判佔用,更多進入中間灰區 | 保存階段更多已知格判佔用,中間灰區減少 | 必須大於有效free門檻、且≤1;非零請求覆蓋默認;灰區重載還受free門檻影響 |
map_subscribe_transient_local |
True |
不適用,QoS耐久性選擇 | 上實物前必須核對並正確配置 | 保存請求期間用Transient Local、Reliable、KeepLast(1)訂閲地圖 | 不適用 | 不適用 | 便於從兼容發佈端取得保留地圖;不是保存文件緩存,也不是“節點重啓不保留地圖”的開關;False仍默認Reliable,改為Volatile、深度10 |
1.2.1.2.參數詳解
1.2.1.2.1.save_map_timeout(等待一份圖,不是整個保存任務計時)
每次保存創建一個臨時OccupancyGrid訂閲,將收到的第一份圖放進future,再用executor.spin_until_future_complete等待,當前超時5 s。成功收到圖後才調用saveMapToFile寫圖片和YAML,後面的文件編碼/寫入不被這個5 s等待包住。訂閲與等待代碼
因此增大它能容忍慢到圖,卻不能修復錯誤topic、namespace、QoS、不存在的發佈者或無效目錄;更不會提高建圖頻率。地圖已通過兼容Transient Local發佈時,晚加入的保存訂閲通常可直接取得最近一份,不必等下一次更新。
該等待使用executor的chrono超時,不能把它當成必須讓仿真/clock前進5 s才結束的定時保存任務。沒有獲得完整圖,函數返回False;“服務調用成功到達”與“響應result=True、文件完整可讀”仍須分開。
1.2.1.2.2.map_subscribe_transient_local(取得最後地圖的條件)
True時採用Transient Local、Reliable、KeepLast(1);False分支以默認QoS深度10訂閲,耐久性為Volatile、可靠性仍Reliable。True可從兼容發佈端取已保留的完整圖;它不讓一個沒有Transient Local能力的發佈端憑空保存歷史消息。
map_server使用Transient Local發佈完整圖,slam_toolbox當前建圖也有自己的地圖發佈配置,應核對實際端點。若可靠性或耐久性不兼容,延長save_map_timeout不保證收到消息。保存訂閲QoS
當前保存器不在啓動後持續緩存所有地圖版本;訂閲在每次保存中創建,取得一份後移除。若發佈端保留的是舊圖,本次可能迅速保存舊圖,等待時間短不證明地圖新鮮或建圖完整。
1.2.1.2.3.free_thresh_default與occupied_thresh_default(請求覆蓋與三態保存邊界)
SaveMap請求的free_thresh或occupied_thresh為0時,保存器分別使用節點default值;非零請求則使用請求值。CLI的--free、--occ寫入其獨立保存器的本次SaveParameters,效果同樣是明確指定保存閾值。0在這裏是“未指定”,不能當成要求精確使用零門檻。
三態保存先將門檻乘100並用rint取整數,再分類。當前0.25/0.65得到25/65:
| 輸入OccupancyGrid的值 | 當前三態保存階段 | 寫出的8位灰度 |
|---|---|---|
| -1,未知 | 先按未知處理,不參與已知值門檻比較 | 205 |
| 0~25,含25 | 空閒 | 254 |
| 26~64 | 中間/不確定 | 205 |
| 65~100,含65 | 佔用 | 0 |
這不是“雷達點高於25 cm”或“障礙強度大於0.65”,也不是inflation_layer的內部代價閾值。輸入應是slam_toolbox的二維佔用圖,不是terrain_map_ext點雲,也不要把具有膨脹顏色的costmap誤當原始場地圖保存。
當前原始地圖若只含-1、0、100,保持合法門檻且不碰端點時,改變門檻常常不改變保存階段的黑/白/灰分類;它仍會改變寫進地圖YAML的閾值,並可能改變重新讀取後的結果。三態保存比較
有效非零門檻應滿足0<free<occupied≤1。參數0會經過默認替代;兩門檻相等或反序會使保存失敗。trinary按門檻編碼圖片,scale主要寫連續灰度及未知透明通道,raw直接編碼佔用數;不能把三態分類效果套給所有模式。保存參數檢查
1.2.1.2.4.已驗證的讀寫問題:trinary、灰205與free=0.25
保存階段的未知/中間格寫成灰度205,並把free_thresh=0.25、occupied_thresh=0.65、negate=0寫入配套YAML。重新加載無透明通道三態圖時,MapIO使用像素佔用比例1−灰度/255,嚴格小於free_thresh便設空閒0:
灰205的占用比例 = 1 − 205 / 255 ≈ 0.196078
0.196078 < 0.25
所以当前三态保存的灰205,在按配套YAML重新读取时变成0
本次沒有隻靠公式推斷:直接調用容器中已安裝的libmap_io,用臨時7×1小地圖保存並重新讀取,得到下面結果。兩組occupied_thresh均為0.65、mode為trinary、圖片為PGM;保存成功,加載狀態均為LOAD_MAP_SUCCESS。
| 輸入7個格子的值 | 保存/加載使用的free門檻 | 重新讀取結果 |
|---|---|---|
| -1、0、25、26、64、65、100 | 0.25,當前配置 | 0、0、0、0、0、100、100 |
| -1、0、25、26、64、65、100 | 0.196,對照配置 | -1、0、-1、-1、-1、100、100 |
第一組不僅未知-1變空閒,中間26/64也經灰205變空閒;不能説“25~65一定會在下一次導航時保持未知”。第二組保留灰205對應的未知身份,但已知25也由空閒改為灰區,因為保存整數free門檻變成rint(19.6)=20。0.196不是隻修元數據、完全保持原分類的無副作用替換。 灰205寫入與YAML門檻、加載灰度判斷
如果任務需要保留未知區,可以在三態重新導出時明確使用經此版本核對的free=0.196,並複核20附近的已知佔用分類;也可以選擇其他合適編碼模式,但必須一起核對map_server和StaticLayer的解釋,不能只改mode字符串。對既有文件應先檢查實際像素和元數據,不能斷言來自任意工具的灰色圖片都有此問題。
這也解釋了前面global_costmap的邊界:頂層track_unknown_space=True只能保留/map消息裏仍為-1的格;若地圖加載已把未知轉為0,代價層和Theta*的allow_unknown不能恢復丟失身份。地圖加載、未知區與全局規劃
本文保留原0.25,沒有自動改工程參數或已有地圖。這裏記錄的是本安裝版本、指定三態讀寫條件下已經驗證的行為;當前實車地圖是否採用相同像素,仍需核對。
1.2.2.保存請求與命令行(不屬於原5項節點參數)
1.2.2.1.保存請求字段
| SaveMap請求字段/CLI選項 | 單位或類型 | 當前用法 |
|---|---|---|
| map_topic/-t | 話題名稱 | 指定OccupancyGrid輸入;空請求退回相對map,namespace為/red_standard_robot1時即該namespace/map;絕對話題可明確來源 |
| map_url/-f | 輸出文件路徑前綴 | 例如目錄/site_01會生成site_01.yaml和site_01.pgm;不是輸入地圖路徑,不應把.yaml再當作前綴後綴填入 |
| image_format/--fmt | 格式名稱 | 未指定時trinary/raw默認pgm,scale默認png;未知透明通道需選支持它的格式 |
| map_mode/--mode | trinary/scale/raw | 未識別值會警告並回退trinary;需要明確自己採用的像素解釋 |
| free_thresh/--free | 無量綱比例 | 0走節點默認;非零覆蓋本次值,不自動改節點default參數 |
| occupied_thresh/--occ | 無量綱比例 | 0走節點默認;非零覆蓋本次值,仍需和free一起滿足關係 |
服務默認名為map_saver/save_map,機械人namespace存在時例如/red_standard_robot1/map_saver/save_map。請求響應result表示保存函數的成功/失敗,不包含PCD或位姿圖。SaveMap接口
接口註釋雖舉file://與package://形式,本次1.1.20的保存實現直接把map_url當文件名前綴交給圖片寫入和文件流,沒有對應URI解析;這裏應使用實際可寫文件系統路徑,不把註釋示例當已經支持的協議。
保存會寫圖片和YAML兩個文件,目錄需要預先存在且可寫;重複同一路徑會覆蓋同名輸出。默認空路徑會生成帶系統時間秒數的map_前綴,仍不保證自動按場地歸檔。文件名生成顯式使用系統時鐘,不由use_sim_time決定。文件名與寫出
1.2.2.2.CLI為何不繼承此處5秒設置
1.1.20的map_saver_cli在當前進程中新建MapSaver,調用on_configure讀取自身參數,然後直接調用saveMapTopicToFile;沒有給另一個map_saver_server發SaveMap請求。若沒有給CLI提供ROS參數或匹配其節點名/namespace的參數文件,它仍使用源碼默認save_map_timeout=2.0,而不是建圖launch中的5.0。
本工程README給出的CLI命令只傳輸出前綴和namespace,沒有傳這份參數文件;因此不要把它當成已使用這裏全部設置。顯式--free/--occ可以改變本次閾值,--ros-args後的-p可以設置該CLI保存器自己的等待與訂閲配置。CLI新建保存器
以下只是後續手動保存示例,未執行。路徑是示例,需要換成實際存在且可寫的目錄;/red_standard_robot1/map也需要按實際機械人修改。此例使用對照門檻0.196,另給CLI獨立名字,避免與已有map_saver同名:
ros2 run nav2_map_server map_saver_cli \
-t /red_standard_robot1/map \
-f /home/tungchiahui/UserFolder/MyMap/site_01/site_01 \
--free 0.196 --occ 0.65 --fmt pgm --mode trinary \
--ros-args -r __ns:=/red_standard_robot1 -r __node:=map_saver_cli \
-p use_sim_time:=False -p save_map_timeout:=5.0 \
-p map_subscribe_transient_local:=True
仿真時按實際時間域設置該CLI的use_sim_time。程序自己的-t、-f、--free等選項應放在--ros-args前;參數文件若採用map_saver分組,而CLI被改名為map_saver_cli,還要調整節點匹配,不能只傳文件就認為所有參數已加載。
1.2.3.文件內容、啓動條件與驗證範圍
1.2.3.1.保存了什麼,哪些還需要另存
| 輸出或數據 | 當前保存器是否負責 | 應怎樣理解 |
|---|---|---|
| 二維圖片與配套地圖YAML | 是 | 寫輸入網格的尺寸/分辨率/原點x、y、yaw及本次mode、閾值;後續由map_server讀取 |
| OccupancyGrid.header.frame_id | 不作為獨立frame_id字段寫進地圖YAML | 後續由map_server自己的frame_id參數設置消息座標名稱;不能靠文件自動恢復所有座標關係 |
| 三維PCD | 否 | 由Point-LIO等點雲保存路徑處理,供small_gicp定位 |
| slam_toolbox的.posegraph/.data | 否 | 由slam_toolbox序列化接口另存,供繼續建圖或其內部定位模式使用 |
| 全套TF、軌跡、車輛包絡與可爬坡能力 | 否 | 二維圖不是完整機械人狀態快照,也不能證明坡道機械可通行 |
保存不新增格距或重新計算地面:輸入是什麼分辨率,輸出就按其元數據記錄。本版本YAML輸出使用Precision(3),非簡單小數的原點/格距需要在保存後複核;不能一概稱為逐位精度不變。圖片中的相對image路徑只寫文件名,通常和YAML成套保存、成套搬移。元數據寫出
1.2.3.2.當前啓動與運行中修改
| 情況 | 當前實現及作用 |
|---|---|
| slam=True | slam_launch啓動map_saver_server,生命週期管理列表為map_saver;提供保存服務,但launch不自動調用保存 |
| slam=False | 當前定位分支啓動map_server和small_gicp,不由這條分支啓動map_saver_server;參數文件裏有分組不代表節點一定運行 |
| reality/simulation | 兩份map_saver段僅時鐘False/True不同,其他4項一致;slam_launch重寫已有use_sim_time |
| map_saver_server | configure時讀4項專用參數並創建SaveMap服務;激活階段建立生命週期bond |
| map_saver_cli | 新建自己的保存器並手動on_configure後直接保存;不依賴遠端服務器是否Active |
| 運行中設置4項專用參數 | 當前沒有動態回調更新已讀取成員;設置成功不保證生效,需要重新配置/重啓;單次閾值可用請求覆蓋 |
MapSaver的SaveMap回調沒有MapServer的Active狀態拒絕檢查;服務在configure階段創建。工程正常由lifecycle_manager_slam管理,但不能根據MapServer行為就斷言此版本保存器在Inactive時一定拒絕請求。保存服務與生命週期
1.2.3.3.本次驗證與實物待檢查
- 本次已核對: nav2_map_server 1.1.20與安裝頭文件;源碼/安裝參數段一致;建圖launch實際啓動map_saver_server、管理map_saver且沒有自動保存調用;CLI獨立對象路徑及默認2 s。
- 本次已離線驗證: 用安裝的libmap_io和臨時7×1地圖,分別以free=0.25、0.196、occupied=0.65完成trinary/PGM保存再讀取;確認前者把未知變空閒、後者保留該未知格,並確認25附近分類也改變。沒有啓動ROS節點、地圖話題或保存服務,臨時圖片與YAML已隨臨時目錄清理。
- 源碼能確認: 5 s只包等圖階段;請求閾值優先、0為默認哨兵;三態包含邊界的保存比較、嚴格邊界的加載比較;QoS和臨時訂閲、路徑前綴、輸出元數據及動態參數限制。
- 實物待核實: 實際map_topic及QoS、消息新鮮度、保存調用返回和文件完整性、真實地圖的像素/未知區重新加載、二維圖與PCD對齊、當前保存方式是否取得需要的參數;本次未對真實建圖或導航任務執行保存。