Skip to content

Commit dfa8325

Browse files
committed
Save/Load gridmaps
Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
1 parent a7bb5b9 commit dfa8325

6 files changed

Lines changed: 315 additions & 4 deletions

File tree

easynav_gridmap_maps_builder/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,27 @@ find_package(rclcpp_lifecycle REQUIRED)
1616
find_package(sensor_msgs REQUIRED)
1717
find_package(easynav_common REQUIRED)
1818
find_package(std_msgs REQUIRED)
19+
find_package(std_srvs REQUIRED)
1920
find_package(geometry_msgs REQUIRED)
2021
find_package(grid_map_ros REQUIRED)
2122
find_package(grid_map_msgs REQUIRED)
22-
23+
find_package(ament_index_cpp REQUIRED)
24+
find_package(yaml_cpp_vendor REQUIRED)
25+
find_package(cv_bridge REQUIRED)
2326

2427
set(dependencies
2528
rclcpp
2629
rclcpp_lifecycle
2730
sensor_msgs
2831
std_msgs
32+
std_srvs
2933
geometry_msgs
3034
easynav_common
3135
grid_map_ros
3236
grid_map_msgs
37+
ament_index_cpp
38+
yaml_cpp_vendor
39+
cv_bridge
3340
)
3441

3542
include_directories(include)

easynav_gridmap_maps_builder/include/easynav_gridmap_maps_builder/GridmapMapsBuilderNode.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929

3030
#include "sensor_msgs/msg/point_cloud2.hpp"
3131
#include "grid_map_msgs/msg/grid_map.hpp"
32+
#include "std_srvs/srv/trigger.hpp"
33+
34+
#include "grid_map_ros/grid_map_ros.hpp"
3235

3336
#include "easynav_common/types/Perceptions.hpp"
3437

@@ -103,7 +106,15 @@ class GridmapMapsBuilderNode : public rclcpp_lifecycle::LifecycleNode
103106
*/
104107
void register_handler(std::shared_ptr<PerceptionHandler> handler);
105108

109+
protected:
110+
bool save_gridmap(const std::string & filename, const grid_map::GridMap & map);
111+
bool load_gridmap(const std::string & filename, grid_map::GridMap & map);
112+
113+
grid_map::GridMap map_;
114+
std::string map_path_;
115+
106116
private:
117+
107118
/// Name of the sensor topic to subscribe to (e.g., point clouds).
108119
std::string sensor_topic_;
109120

@@ -122,6 +133,11 @@ class GridmapMapsBuilderNode : public rclcpp_lifecycle::LifecycleNode
122133
/// Publisher for the processed grid map.
123134
rclcpp_lifecycle::LifecyclePublisher<grid_map_msgs::msg::GridMap>::SharedPtr pub_;
124135

136+
/**
137+
* @brief Service for saving current map to disk.
138+
*/
139+
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr savemap_srv_;
140+
125141
/// Registered perception handlers by sensor name.
126142
std::map<std::string, std::shared_ptr<PerceptionHandler>> handlers_;
127143
};

easynav_gridmap_maps_builder/package.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
<depend>rclcpp</depend>
1313
<depend>sensor_msgs</depend>
1414
<depend>std_msgs</depend>
15+
<depend>std_srvs</depend>
1516
<depend>geometry_msgs</depend>
1617
<depend>easynav_common</depend>
1718
<depend>grid_map_ros</depend>
1819
<depend>grid_map_msgs</depend>
19-
20+
<depend>ament_index_cpp</depend>
21+
<depend>yaml_cpp_vendor</depend>
22+
<depend>cv_bridge</depend>
23+
2024
<test_depend>ament_lint_auto</test_depend>
2125
<test_depend>ament_lint_common</test_depend>
2226

easynav_gridmap_maps_builder/src/easynav_gridmap_maps_builder/GridmapMapsBuilderNode.cpp

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,22 @@
2323
#include "rclcpp/macros.hpp"
2424
#include "rclcpp_lifecycle/lifecycle_node.hpp"
2525

26+
#include "yaml-cpp/yaml.h"
27+
2628
#include "lifecycle_msgs/msg/transition.hpp"
2729
#include "lifecycle_msgs/msg/state.hpp"
2830

29-
#include <grid_map_ros/grid_map_ros.hpp>
31+
#include "grid_map_ros/grid_map_ros.hpp"
32+
#include "grid_map_cv/grid_map_cv.hpp"
33+
#include "cv_bridge/cv_bridge.hpp"
34+
#include "opencv2/highgui/highgui.hpp"
3035

3136
#include "easynav_gridmap_maps_builder/GridmapMapsBuilderNode.hpp"
3237
#include "easynav_common/types/PointPerception.hpp"
3338

39+
#include "ament_index_cpp/get_package_share_directory.hpp"
40+
#include "ament_index_cpp/get_package_prefix.hpp"
41+
3442
namespace easynav
3543
{
3644

@@ -50,9 +58,48 @@ GridmapMapsBuilderNode::GridmapMapsBuilderNode(const rclcpp::NodeOptions & optio
5058
declare_parameter("perception_default_frame", "map");
5159
}
5260

61+
std::string package_name, map_path_file;
62+
if (!has_parameter("package")) {
63+
declare_parameter("package", package_name);
64+
}
65+
66+
if (!has_parameter("map_path_file")) {
67+
declare_parameter("map_path_file", "map_path_file");
68+
}
69+
70+
// if (package_name != "" && map_path_file != "") {
71+
// std::string pkgpath;
72+
// try {
73+
// pkgpath = ament_index_cpp::get_package_share_directory(package_name);
74+
// map_path_ = pkgpath + "/" + map_path_file;
75+
// } catch(ament_index_cpp::PackageNotFoundError & ex) {
76+
// return std::unexpected("Package " + package_name + " not found. Error: " + ex.what());
77+
// }
78+
//
79+
// if (!load_gridmap(map_path_, map_)) {
80+
// return std::unexpected("File [" + map_path_ + "] not found");
81+
// }
82+
// }
83+
5384
pub_ = this->create_publisher<grid_map_msgs::msg::GridMap>(
5485
"map_builder_gridmap/gridmap", rclcpp::QoS(1).transient_local().reliable());
5586

87+
// savemap_srv_ = node->create_service<std_srvs::srv::Trigger>(
88+
// node->get_name() + std::string("/") + plugin_name + "/savemap",
89+
// [this](
90+
// const std::shared_ptr<std_srvs::srv::Trigger::Request> request,
91+
// std::shared_ptr<std_srvs::srv::Trigger::Response> response)
92+
// {
93+
// (void)request;
94+
// if (!save_gridmap(map_path_, map_)) {
95+
// response->success = false;
96+
// response->message = "Failed to save map to: " + map_path_;
97+
// } else {
98+
// response->success = true;
99+
// response->message = "Map successfully saved to: " + map_path_;
100+
// }
101+
// });
102+
//
56103
register_handler(std::make_shared<PointPerceptionHandler>());
57104
}
58105

@@ -236,4 +283,83 @@ GridmapMapsBuilderNode::register_handler(std::shared_ptr<PerceptionHandler> hand
236283
handlers_[handler->group()] = handler;
237284
}
238285

286+
287+
bool
288+
GridmapMapsBuilderNode::save_gridmap(const std::string & filename, const grid_map::GridMap & map)
289+
{
290+
std::string dir = std::filesystem::path(filename).parent_path();
291+
YAML::Emitter yaml;
292+
yaml << YAML::BeginMap;
293+
yaml << YAML::Key << "layers" << YAML::Value << YAML::BeginSeq;
294+
295+
for (const std::string & layer : map.getLayers()) {
296+
// Compute min and max values of the layer
297+
float min_val = std::numeric_limits<float>::max();
298+
float max_val = std::numeric_limits<float>::lowest();
299+
for (grid_map::GridMapIterator it(map); !it.isPastEnd(); ++it) {
300+
if (!map.isValid(*it, layer)) continue;
301+
float value = map.at(layer, *it);
302+
min_val = std::min(min_val, value);
303+
max_val = std::max(max_val, value);
304+
}
305+
306+
// Save layer image
307+
cv::Mat image;
308+
grid_map::GridMapCvConverter::toImage<unsigned char, 1>(map, layer, CV_8UC1, min_val, max_val, image);
309+
std::string image_filename = layer + ".pgm";
310+
std::string image_path = dir + "/" + image_filename;
311+
cv::imwrite(image_path, image);
312+
313+
yaml << YAML::BeginMap;
314+
yaml << YAML::Key << "name" << YAML::Value << layer;
315+
yaml << YAML::Key << "min" << YAML::Value << min_val;
316+
yaml << YAML::Key << "max" << YAML::Value << max_val;
317+
yaml << YAML::EndMap;
318+
}
319+
yaml << YAML::EndSeq;
320+
yaml << YAML::Key << "resolution" << YAML::Value << map.getResolution();
321+
yaml << YAML::Key << "length" << YAML::Value << YAML::Flow << std::vector<double>{map.getLength().x(), map.getLength().y()};
322+
yaml << YAML::Key << "position" << YAML::Value << YAML::Flow << std::vector<double>{map.getPosition().x(), map.getPosition().y()};
323+
yaml << YAML::EndMap;
324+
325+
std::ofstream file_out(filename);
326+
if (!file_out) return false;
327+
file_out << yaml.c_str();
328+
return true;
329+
}
330+
331+
bool
332+
GridmapMapsBuilderNode::load_gridmap(const std::string & filename, grid_map::GridMap & map)
333+
{
334+
std::ifstream file_in(filename);
335+
if (!file_in) return false;
336+
337+
YAML::Node config = YAML::LoadFile(filename);
338+
if (!config["layers"]) return false;
339+
340+
map.setFrameId("map");
341+
342+
auto resolution = config["resolution"].as<double>();
343+
auto length = config["length"].as<std::vector<double>>();
344+
auto position = config["position"].as<std::vector<double>>();
345+
346+
map.setGeometry(grid_map::Length(length[0], length[1]), resolution, grid_map::Position(position[0], position[1]));
347+
348+
std::string dir = std::filesystem::path(filename).parent_path();
349+
350+
for (const auto & layer_node : config["layers"]) {
351+
std::string layer_name = layer_node["name"].as<std::string>();
352+
float min_val = layer_node["min"].as<float>();
353+
float max_val = layer_node["max"].as<float>();
354+
355+
std::string image_path = dir + "/" + layer_name + ".pgm";
356+
cv::Mat image = cv::imread(image_path, cv::IMREAD_GRAYSCALE);
357+
if (image.empty()) return false;
358+
359+
grid_map::GridMapCvConverter::addLayerFromImage<unsigned char, 1>(image, layer_name, map, min_val, max_val);
360+
}
361+
362+
return true;
363+
}
364+
239365
} // namespace easynav

easynav_gridmap_maps_builder/tests/CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,12 @@ target_link_libraries(gridmap_maps_builder_tests
88
)
99
ament_target_dependencies(gridmap_maps_builder_tests ${dependencies})
1010

11-
11+
ament_add_gtest(gridmap_maps_load_save_tests gridmap_maps_load_save_tests.cpp)
12+
target_include_directories(gridmap_maps_load_save_tests PUBLIC
13+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
14+
$<INSTALL_INTERFACE:include>
15+
)
16+
target_link_libraries(gridmap_maps_load_save_tests
17+
${PROJECT_NAME}
18+
)
19+
ament_target_dependencies(gridmap_maps_load_save_tests ${dependencies})

0 commit comments

Comments
 (0)