跳到正文

Wiki

map_saver(二維柵格地圖保存)

約 19 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

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對齊、當前保存方式是否取得需要的參數;本次未對真實建圖或導航任務執行保存。