Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map editing tools and improved local mapping #92

Draft
wants to merge 43 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
53778a9
Add method to crop map at voxel resolution and update crop_map_operation
victorreijgwart Nov 12, 2024
c6759c6
Add option to use most recent available ROS TF, ignoring timestamps
victorreijgwart Nov 13, 2024
3bbd1b8
Update config validation schemas for CropMapOperation
victorreijgwart Nov 13, 2024
097b6dc
Extend automatic unit conversions with additional units
victorreijgwart Nov 13, 2024
6fb3d81
Implement map decay operation for temporally local mapping
victorreijgwart Nov 29, 2024
f46ea6c
Add option to multi-thread crop_map and decay_map operations
victorreijgwart Nov 29, 2024
7bd403b
Minor refactoring for consistency
victorreijgwart Dec 7, 2024
8e1e10a
Draft method to resample map into another coordinate frame
victorreijgwart Dec 7, 2024
f3034d7
Prune the blocks after dense sampling to restore sparsity
victorreijgwart Dec 7, 2024
dd42058
Make sure all config getters are marked const
victorreijgwart Dec 7, 2024
e87d0e5
Improve map transform method
victorreijgwart Dec 7, 2024
e6afc67
Add occupancy map to PLY conversion script
victorreijgwart Dec 11, 2024
435e45f
Expose method to convert node indices to their center point in Python
victorreijgwart Dec 11, 2024
b656e0a
Add Python methods to get all occupied leaf nodes or cell center points
victorreijgwart Dec 11, 2024
fff8b77
Build the C++ Library in Release mode by default
victorreijgwart Dec 11, 2024
1092dc2
Merge branch 'feature/map_conversions' into feature/improve_local_map…
victorreijgwart Dec 12, 2024
7bf1525
Merge branch 'feature/default_build_release' into feature/improve_loc…
victorreijgwart Dec 12, 2024
21c91e2
Preselect last loaded map in file dialog to enable easy reloads in Rviz
victorreijgwart Dec 12, 2024
fa8a4eb
Make sure cell sizes are updated when map min cell width changes
victorreijgwart Dec 12, 2024
f37f7f9
Update local test script to include C++ examples and Python API
victorreijgwart Dec 6, 2024
a815bc0
Add map edit module to Python API and expose crop_to_sphere methods
victorreijgwart Dec 12, 2024
43003a4
Expose map multiplication method and add exponential forgetting example
victorreijgwart Dec 12, 2024
ba9ae88
Expose map transformation method
victorreijgwart Dec 12, 2024
c3b5f4b
Generalize sampling method to also allow summing, not only overwriting
victorreijgwart Dec 13, 2024
565864c
Separate definitions from declarations for readability
victorreijgwart Dec 13, 2024
7392f41
Add method to sum (merge) maps together
victorreijgwart Dec 13, 2024
a908112
Add map editing examples for C++ API
victorreijgwart Dec 13, 2024
c72e9ae
Reduce code duplication by using sampler to implement transform
victorreijgwart Dec 13, 2024
10e80fb
Explicitly use default copy and move constructors for QueryAccelerators
victorreijgwart Dec 14, 2024
1728931
Refactor and move AABB to utils/geometry to accommodate new colliders
victorreijgwart Dec 15, 2024
d0db0de
Make C++ examples more consistent
victorreijgwart Dec 15, 2024
1c90971
Minor refactoring
victorreijgwart Dec 15, 2024
759669b
Add method to sum values to all cells within a given shape
victorreijgwart Dec 15, 2024
68bf7a9
Expose methods to sum shapes into map through Python API
victorreijgwart Dec 16, 2024
4d6d707
Generalize map crop method to other shapes
victorreijgwart Dec 18, 2024
e1384ba
Improve intersection tests
victorreijgwart Dec 18, 2024
e0700f1
Expose new crop functions in Python
victorreijgwart Dec 18, 2024
3d81e74
Update Python examples
victorreijgwart Dec 18, 2024
c19f5bb
Speed up masked sums using multi-resolution
victorreijgwart Dec 18, 2024
345f198
Update crop method argument names to be consistent with sum
victorreijgwart Dec 18, 2024
8f1f01b
Add Python example on generating random obstacle maps
victorreijgwart Dec 18, 2024
28803ee
Add Rviz voxel filter option that includes occupied|unknown surfaces
victorreijgwart Dec 30, 2024
12b4728
Update random map generation script
victorreijgwart Dec 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/python_api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Python API
.. automethod:: pywavemap.convert.height_to_cell_width
.. automethod:: pywavemap.convert.point_to_nearest_index
.. automethod:: pywavemap.convert.point_to_node_index
.. automethod:: pywavemap.convert.node_index_to_center_point

.. automodule:: pywavemap.logging
.. automethod:: pywavemap.logging.set_level
Expand Down
3 changes: 2 additions & 1 deletion examples/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ else ()
endif ()

# Add each set of examples
add_subdirectory(edit)
add_subdirectory(conversions)
add_subdirectory(io)
add_subdirectory(queries)
add_subdirectory(planning)
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ add_executable(occupancy_to_esdf occupancy_to_esdf.cc)
set_wavemap_target_properties(occupancy_to_esdf)
target_link_libraries(occupancy_to_esdf PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)

add_executable(occupancy_to_ply occupancy_to_ply.cc)
set_wavemap_target_properties(occupancy_to_ply)
target_link_libraries(occupancy_to_ply PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)
94 changes: 94 additions & 0 deletions examples/cpp/conversions/occupancy_to_ply.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#include <filesystem>
#include <fstream>
#include <vector>

#include <glog/logging.h>
#include <wavemap/core/common.h>
#include <wavemap/core/map/hashed_wavelet_octree.h>
#include <wavemap/core/map/map_base.h>
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int argc, char** argv) {
// Initialize GLOG
google::InitGoogleLogging(argv[0]);
google::InstallFailureSignalHandler();
FLAGS_alsologtostderr = true;
FLAGS_colorlogtostderr = true;

// Read params
if (argc != 2) {
LOG(ERROR)
<< "Please supply a path to an occupancy map as the first argument.";
return EXIT_FAILURE;
}
const std::filesystem::path map_file_path = argv[1];

// Load the occupancy map
MapBase::Ptr occupancy_map;
if (!io::fileToMap(map_file_path, occupancy_map)) {
LOG(WARNING) << "Could not open map for reading: " << map_file_path;
return EXIT_FAILURE;
}

// Convert each occupied leaf node to high-res points and add to pointcloud
std::vector<Point3D> pointcloud;
constexpr FloatingPoint kOccupancyThreshold = 0.01f;
const FloatingPoint min_cell_width = occupancy_map->getMinCellWidth();
occupancy_map->forEachLeaf([min_cell_width, &pointcloud](
const OctreeIndex& node_index,
FloatingPoint node_log_odds) {
if (kOccupancyThreshold < node_log_odds) {
if (node_index.height == 0) {
const Point3D center =
convert::indexToCenterPoint(node_index.position, min_cell_width);
pointcloud.emplace_back(center);
} else {
const Index3D node_min_corner =
convert::nodeIndexToMinCornerIndex(node_index);
const Index3D node_max_corner =
convert::nodeIndexToMaxCornerIndex(node_index);
for (const Index3D& index : Grid(node_min_corner, node_max_corner)) {
const Point3D center =
convert::indexToCenterPoint(index, min_cell_width);
pointcloud.emplace_back(center);
}
}
}
});

// Create the PLY output file
const std::filesystem::path ply_file_path =
std::filesystem::path(map_file_path).replace_extension(".ply");
LOG(INFO) << "Creating PLY file: " << ply_file_path;
std::ofstream ply_file(ply_file_path,
std::ofstream::out | std::ofstream::binary);
if (!ply_file.is_open()) {
LOG(WARNING) << "Could not open file for writing. Error: "
<< strerror(errno);
return EXIT_FAILURE;
}

// Write the PLY header
// clang-format off
ply_file << "ply\n"
"format ascii 1.0\n"
"comment The voxel size is " << min_cell_width << " meters.\n"
"element vertex " << pointcloud.size() << "\n"
"property float x\n"
"property float y\n"
"property float z\n"
"end_header"
<< std::endl;
// clang-format on

// Add the points
for (const Point3D& point : pointcloud) {
ply_file << point.x() << " " << point.y() << " " << point.z() << "\n";
}
ply_file.flush();

// Close the file and communicate whether writing succeeded
ply_file.close();
return static_cast<bool>(ply_file) ? EXIT_SUCCESS : EXIT_FAILURE;
}
20 changes: 20 additions & 0 deletions examples/cpp/edit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Binaries
add_executable(crop_map crop_map.cc)
set_wavemap_target_properties(crop_map)
target_link_libraries(crop_map PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)

add_executable(multiply_map multiply_map.cc)
set_wavemap_target_properties(multiply_map)
target_link_libraries(multiply_map PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)

add_executable(sum_map sum_map.cc)
set_wavemap_target_properties(sum_map)
target_link_libraries(sum_map PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)

add_executable(transform_map transform_map.cc)
set_wavemap_target_properties(transform_map)
target_link_libraries(transform_map PUBLIC
wavemap::wavemap_core wavemap::wavemap_io)
32 changes: 32 additions & 0 deletions examples/cpp/edit/crop_map.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <memory>

#include <wavemap/core/common.h>
#include <wavemap/core/utils/edit/crop.h>
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Load the map
const std::filesystem::path home_dir = CHECK_NOTNULL(getenv("HOME"));
const std::filesystem::path input_map_path = home_dir / "your_map.wvmp";
MapBase::Ptr map_base;
bool success = io::fileToMap(input_map_path, map_base);
CHECK(success);

// Downcast it to a concrete map type
auto map = std::dynamic_pointer_cast<HashedWaveletOctree>(map_base);
CHECK_NOTNULL(map);

// Crop the map
const Point3D t_W_center{-2.2, -1.4, 0.0};
const FloatingPoint radius = 3.0;
const Sphere cropping_sphere{t_W_center, radius};
auto thread_pool = std::make_shared<ThreadPool>(); // Optional
edit::crop(*map, cropping_sphere, 0, thread_pool);

// Save the map
const std::filesystem::path output_map_path =
home_dir / "your_map_cropped.wvmp";
success &= io::mapToFile(*map, output_map_path);
CHECK(success);
}
30 changes: 30 additions & 0 deletions examples/cpp/edit/multiply_map.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <memory>

#include <wavemap/core/common.h>
#include <wavemap/core/utils/edit/multiply.h>
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Load the map
const std::filesystem::path home_dir = CHECK_NOTNULL(getenv("HOME"));
const std::filesystem::path input_map_path = home_dir / "your_map.wvmp";
MapBase::Ptr map_base;
bool success = io::fileToMap(input_map_path, map_base);
CHECK(success);

// Downcast it to a concrete map type
auto map = std::dynamic_pointer_cast<HashedWaveletOctree>(map_base);
CHECK_NOTNULL(map);

// Use the multiply method to implement exponential forgetting
const FloatingPoint decay_factor = 0.9;
auto thread_pool = std::make_shared<ThreadPool>(); // Optional
edit::multiply(*map, decay_factor, thread_pool);

// Save the map
const std::filesystem::path output_map_path =
home_dir / "your_map_decayed.wvmp";
success &= io::mapToFile(*map, output_map_path);
CHECK(success);
}
52 changes: 52 additions & 0 deletions examples/cpp/edit/sum_map.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <memory>

#include <wavemap/core/common.h>
#include <wavemap/core/utils/edit/crop.h>
#include <wavemap/core/utils/edit/sum.h>
#include <wavemap/core/utils/edit/transform.h>
#include <wavemap/core/utils/shape/aabb.h>
#include <wavemap/core/utils/shape/sphere.h>
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Load the map
const std::filesystem::path home_dir = CHECK_NOTNULL(getenv("HOME"));
const std::filesystem::path input_map_path = home_dir / "your_map.wvmp";
MapBase::Ptr map_base;
bool success = io::fileToMap(input_map_path, map_base);
CHECK(success);

// Downcast it to a concrete map type
auto map = std::dynamic_pointer_cast<HashedWaveletOctree>(map_base);
CHECK_NOTNULL(map);

// Crop the map
const Point3D t_W_center{-2.2, -1.4, 0.0};
const FloatingPoint radius = 3.0;
const Sphere cropping_sphere{t_W_center, radius};
auto thread_pool = std::make_shared<ThreadPool>(); // Optional
edit::crop(*map, cropping_sphere, 0, thread_pool);

// Create a translated copy
Transformation3D T_AB;
T_AB.getPosition() = {5.0, 5.0, 0.0};
auto map_translated = edit::transform(*map, T_AB, thread_pool);

// Merge them together
edit::sum(*map, *map_translated);

// Set a box in the map to free
AABB<Point3D> box{{6.f, 6.f, -2.f}, {10.f, 10.f, 2.f}};
edit::sum(*map, box, -1.f, thread_pool);

// Set a sphere in the map to occupied
Sphere<Point3D> sphere{{8.f, 8.f, 0.f}, 1.5f};
edit::sum(*map, sphere, 2.f, thread_pool);

// Save the map
const std::filesystem::path output_map_path =
home_dir / "your_map_merged.wvmp";
success &= io::mapToFile(*map, output_map_path);
CHECK(success);
}
33 changes: 33 additions & 0 deletions examples/cpp/edit/transform_map.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <memory>

#include <wavemap/core/common.h>
#include <wavemap/core/utils/edit/transform.h>
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Load the map
const std::filesystem::path home_dir = CHECK_NOTNULL(getenv("HOME"));
const std::filesystem::path input_map_path = home_dir / "your_map.wvmp";
MapBase::Ptr map_base;
bool success = io::fileToMap(input_map_path, map_base);
CHECK(success);

// Downcast it to a concrete map type
auto map = std::dynamic_pointer_cast<HashedWaveletOctree>(map_base);
CHECK_NOTNULL(map);

// Define a transformation that flips the map upside down, for illustration
Transformation3D T_AB;
T_AB.getRotation() = Rotation3D{0.f, 1.f, 0.f, 0.f};

// Transform the map
auto thread_pool = std::make_shared<ThreadPool>(); // Optional
map = edit::transform(*map, T_AB, thread_pool);

// Save the map
const std::filesystem::path output_map_path =
home_dir / "your_map_transformed.wvmp";
success &= io::mapToFile(*map, output_map_path);
CHECK(success);
}
7 changes: 4 additions & 3 deletions examples/cpp/io/load_map_from_file.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Create a smart pointer that will own the loaded map
wavemap::MapBase::Ptr loaded_map;
MapBase::Ptr loaded_map;

// Load the map
const bool success =
wavemap::io::fileToMap("/path/to/your/map.wvmp", loaded_map);
const bool success = io::fileToMap("/path/to/your/map.wvmp", loaded_map);
CHECK(success);
}
6 changes: 4 additions & 2 deletions examples/cpp/io/load_map_from_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

#include <wavemap/io/stream_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Create a smart pointer that will own the loaded map
wavemap::MapBase::Ptr loaded_map;
MapBase::Ptr loaded_map;

// Create an input stream for illustration purposes
std::istrstream input_stream{""};

// Load the map
const bool success = wavemap::io::streamToMap(input_stream, loaded_map);
const bool success = io::streamToMap(input_stream, loaded_map);
CHECK(success);
}
8 changes: 5 additions & 3 deletions examples/cpp/io/save_map_to_file.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <wavemap/io/file_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Create an empty map for illustration purposes
wavemap::HashedWaveletOctreeConfig config;
wavemap::HashedWaveletOctree map(config);
HashedWaveletOctreeConfig config;
HashedWaveletOctree map(config);

// Save the map
const bool success = wavemap::io::mapToFile(map, "/path/to/your/map.wvmp");
const bool success = io::mapToFile(map, "/path/to/your/map.wvmp");
CHECK(success);
}
8 changes: 5 additions & 3 deletions examples/cpp/io/save_map_to_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

#include <wavemap/io/stream_conversions.h>

using namespace wavemap; // NOLINT
int main(int, char**) {
// Create an empty map for illustration purposes
wavemap::HashedWaveletOctreeConfig config;
wavemap::HashedWaveletOctree map(config);
HashedWaveletOctreeConfig config;
HashedWaveletOctree map(config);

// Create an output stream for illustration purposes
std::ostrstream output_stream;

// Save the map
bool success = wavemap::io::mapToStream(map, output_stream);
bool success = io::mapToStream(map, output_stream);
output_stream.flush();
success &= output_stream.good();
CHECK(success);
}
16 changes: 16 additions & 0 deletions examples/python/edit/crop_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
import numpy as np
import pywavemap as wave

# Load the map
user_home = os.path.expanduser('~')
input_map_path = os.path.join(user_home, "your_map.wvmp")
your_map = wave.Map.load(input_map_path)

# Crop the map
cropping_sphere = wave.Sphere(center=np.array([-2.2, -1.4, 0.0]), radius=3.0)
wave.edit.crop(your_map, cropping_sphere)

# Save the map
output_map_path = os.path.join(user_home, "your_map_cropped.wvmp")
your_map.store(output_map_path)
Loading
Loading