diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cbc99c --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Created by https://www.toptal.com/developers/gitignore/api/cmake,visualstudiocode,c++ +# Edit at https://www.toptal.com/developers/gitignore?templates=cmake,visualstudiocode,c++ + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/cmake,visualstudiocode,c++ +.vscode/settings.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 8842b0b..38ce075 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ catkin_package( add_library(${PROJECT_NAME} src/common.cpp src/${PROJECT_NAME}.cpp - src/spiral_stc.cpp + src/boustrophedon_stc.cpp ) add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(${PROJECT_NAME} @@ -65,13 +65,13 @@ catkin_install_python( if (CATKIN_ENABLE_TESTING) catkin_add_gtest(test_common test/src/test_common.cpp test/src/util.cpp src/common.cpp) - catkin_add_gtest(test_spiral_stc test/src/test_spiral_stc.cpp test/src/util.cpp src/spiral_stc.cpp src/common.cpp src/${PROJECT_NAME}.cpp) - add_dependencies(test_spiral_stc ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - target_link_libraries(test_spiral_stc ${catkin_LIBRARIES}) + catkin_add_gtest(test_boustrophedon_stc test/src/test_boustrophedon_stc.cpp test/src/util.cpp src/boustrophedon_stc.cpp src/common.cpp src/${PROJECT_NAME}.cpp) + add_dependencies(test_boustrophedon_stc ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + target_link_libraries(test_boustrophedon_stc ${catkin_LIBRARIES}) find_package(OpenCV) include_directories(${OpenCV_INCLUDE_DIRS}) - target_link_libraries(test_spiral_stc ${OpenCV_LIBRARIES}) + target_link_libraries(test_boustrophedon_stc ${OpenCV_LIBRARIES}) add_rostest(test/${PROJECT_NAME}/test_${PROJECT_NAME}.test) diff --git a/README.md b/README.md index a4b23ea..c0c9e56 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,19 @@ -# Full Coverage Path Planner (FCPP) +# Boustrophedon Full Coverage Path Planner (Modified) +## Acknowledgement + +This package is a modification of the Full Coverage Path Planner package from Nobleo. + +It is modified such that a Boustrophedon Pattern is used to plan the path rather than a Spiral algorithm in the original package. + +Refer to the original package here: http://wiki.ros.org/full_coverage_path_planner ## Overview -This package provides an implementation of a Full Coverage Path Planner (FCPP) using the Backtracking Spiral Algorithm (BSA), see [1]. +This package provides an implementation of a Full Coverage Path Planner (FCPP) using the Boustrophedon Pattern. see [1] and [2]. This packages acts as a global planner plugin to the Move Base package (http://wiki.ros.org/move_base). -![BSA](doc/fcpp_robot_0_5m_plus_tool_0_2m.png) +![BSA](doc/fcpp_modified_boustrophedon.png) The user can configure robot radius and tool radius separately: @@ -15,17 +22,19 @@ The user can configure robot radius and tool radius separately: **Keywords:** coverage path planning, move base -### License +### Authors Apache 2.0 +**Package modified by Ethan Kim, ethanc.kim@uwaterloo.ca, MapaRobo Inc.** + **Author(s): Yury Brodskiy, Ferry Schoenmakers, Tim Clephas, Jerrel Unkel, Loy van Beek, Cesar lopez** **Maintainer: Cesar Lopez, cesar.lopez@nobleo.nl** **Affiliation: Nobleo Projects BV, Eindhoven, the Netherlands** -The Full Coverage Path Planner package has been tested under [ROS] Melodic and Ubuntu 18.04. +The Modified package has been tested under [ROS] Noetic and Ubuntu 20.04. ## Installation @@ -57,8 +66,8 @@ All tests can be run using: #### test_common Unit test that checks the basic functions used by the repository -#### test_spiral_stc -Unit test that checks the basis spiral algorithm for full coverage. The test is performed for different situations to check that the algorithm coverage the accessible map cells. A test is also performed in randomly generated maps. +#### test_boustrophedon_stc +Unit test that checks the basis boustrophedon algorithm for full coverage. The test is performed for different situations to check that the algorithm coverage the accessible map cells. A test is also performed in randomly generated maps. #### test_full_coverage_path_planner.test ROS system test that checks the full coverage path planner together with a tracking pid. A simulation is run such that a robot moves to fully cover the accessible cells in a given map. @@ -131,8 +140,8 @@ The CoverageProgressNode keeps track of coverage progress. It does this by perio ## Plugins -### full_coverage_path_planner/SpiralSTC -For use in move_base(\_flex) as "base_global_planner"="full_coverage_path_planner/SpiralSTC". It uses global_cost_map and global_costmap/robot_radius. +### full_coverage_path_planner/BoustrophedonSTC +For use in move_base(\_flex) as "base_global_planner"="full_coverage_path_planner/BoustrophedonSTC". It uses global_cost_map and global_costmap/robot_radius. #### Parameters @@ -142,18 +151,10 @@ For use in move_base(\_flex) as "base_global_planner"="full_coverage_path_planne ## References -[1] GONZALEZ, Enrique, et al. BSA: A complete coverage algorithm. In: Proceedings of the 2005 IEEE International Conference on Robotics and Automation. IEEE, 2005. p. 2040-2044. - -## Bugs & Feature Requests - -Please report bugs and request features using the [Issue Tracker](https://github.com/nobleo/full_coverage_path_planner/issues). - - -[ROS]: http://www.ros.org -[rviz]: http://wiki.ros.org/rviz -[MBF]: http://wiki.ros.org/move_base_flex +[1] Choset, Howie, and Philippe Pignon. "Coverage path planning: The boustrophedon cellular decomposition." Field and service robotics. Springer, London, 1998. +[2] Zelinsky, Alexander, et al. "Planning paths of complete coverage of an unstructured environment by a mobile robot." Proceedings of international conference on advanced robotics. Vol. 13. 1993. -## Acknowledgments +## Acknowledgments from Original Authors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/full_coverage_path_planner/param/costmap_common_params.yaml b/test/full_coverage_path_planner/param/costmap_common_params.yaml new file mode 100644 index 0000000..a689051 --- /dev/null +++ b/test/full_coverage_path_planner/param/costmap_common_params.yaml @@ -0,0 +1,19 @@ +transform_tolerance: 0.2 + +map_topic: map + +footprint: [[0.46, 0.28], [-0.50, 0.28], [-0.50, -0.28], [0.46, -0.28]] +footprint_padding: 0.05 +inflation_radius: 0.55 + +#observation_sources: point_cloud_sensor +#obstacle_range: 2.5 +#raytrace_range: 3.0 +#point_cloud_sensor: { sensor_frame: d435i_link, +# data_type: PointCloud2, +# topic: /points_filtered, +# marking: true, +# clearing: true, +# expected_update_rate: 1.0 +# max_obstacle_height: 0.75 +# min_obstacle_height: 0.05 } \ No newline at end of file diff --git a/test/full_coverage_path_planner/param/global_costmap_params.yaml b/test/full_coverage_path_planner/param/global_costmap_params.yaml new file mode 100644 index 0000000..e9a0668 --- /dev/null +++ b/test/full_coverage_path_planner/param/global_costmap_params.yaml @@ -0,0 +1,7 @@ +global_costmap: + global_frame: map + robot_base_frame: base_link + update_frequency: 5.0 + publish_frequency: 1.0 + static_map: true + always_send_full_costmap: true \ No newline at end of file diff --git a/test/full_coverage_path_planner/param/local_costmap_params.yaml b/test/full_coverage_path_planner/param/local_costmap_params.yaml index 5147ed1..821db1b 100644 --- a/test/full_coverage_path_planner/param/local_costmap_params.yaml +++ b/test/full_coverage_path_planner/param/local_costmap_params.yaml @@ -1,10 +1,10 @@ local_costmap: global_frame: odom robot_base_frame: base_link - update_frequency: 10.0 + update_frequency: 5.0 publish_frequency: 5.0 width: 5.0 height: 5.0 resolution: 0.05 static_map: false - rolling_window: true + rolling_window: true \ No newline at end of file diff --git a/test/full_coverage_path_planner/param/planners.yaml b/test/full_coverage_path_planner/param/planners.yaml index a59da1f..d47b710 100644 --- a/test/full_coverage_path_planner/param/planners.yaml +++ b/test/full_coverage_path_planner/param/planners.yaml @@ -1,3 +1,3 @@ planners: - - name: 'SpiralSTC' - type: 'full_coverage_path_planner/SpiralSTC' + - name: 'BoustrophedonSTC' + type: 'full_coverage_path_planner/BoustrophedonSTC' diff --git a/test/full_coverage_path_planner/test_full_coverage_path_planner.launch b/test/full_coverage_path_planner/test_full_coverage_path_planner.launch index ffa9c89..91d81c2 100644 --- a/test/full_coverage_path_planner/test_full_coverage_path_planner.launch +++ b/test/full_coverage_path_planner/test_full_coverage_path_planner.launch @@ -1,34 +1,39 @@ - + - + + + + - - + + + - + - + + @@ -49,7 +54,7 @@ - + diff --git a/test/full_coverage_path_planner/test_full_coverage_path_planner_plugin.launch b/test/full_coverage_path_planner/test_full_coverage_path_planner_plugin.launch index 97d0d9d..d61efd5 100644 --- a/test/full_coverage_path_planner/test_full_coverage_path_planner_plugin.launch +++ b/test/full_coverage_path_planner/test_full_coverage_path_planner_plugin.launch @@ -1,7 +1,7 @@ - + @@ -11,13 +11,13 @@ - - + + - + diff --git a/test/src/test_spiral_stc.cpp b/test/src/test_boustrophedon_stc.cpp similarity index 89% rename from test/src/test_spiral_stc.cpp rename to test/src/test_boustrophedon_stc.cpp index 8335255..5d23a07 100644 --- a/test/src/test_spiral_stc.cpp +++ b/test/src/test_boustrophedon_stc.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include cv::Mat drawMap(std::vector > const& grid); @@ -35,15 +35,15 @@ cv::Mat drawPath(const cv::Mat &mapImg, std::list &path); /* - * On a map with nothing on it, spiral_stc should cover all the nodes of the map + * On a map with nothing on it, boustrophedon_stc should cover all the nodes of the map */ -TEST(TestSpiralStc, testFillEmptyMap) +TEST(TestBoustrophedonStc, testFillEmptyMap) { std::vector > grid = makeTestGrid(4, 4, false); Point_t start = {0, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -54,7 +54,7 @@ TEST(TestSpiralStc, testFillEmptyMap) /* * On a map with a single obstacle, all the other nodes should still be visited. */ -TEST(TestSpiralStc, testFillMapWithOneObstacle) +TEST(TestBoustrophedonStc, testFillMapWithOneObstacle) { /* * [s 0 0 0] @@ -67,7 +67,7 @@ TEST(TestSpiralStc, testFillMapWithOneObstacle) Point_t start = {0, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -82,7 +82,7 @@ TEST(TestSpiralStc, testFillMapWithOneObstacle) /* * In a map with 2 obstacles, still the complete map should be covered except for those 2 obstacles */ -TEST(TestSpiralStc, testFillMapWith2Obstacles) +TEST(TestBoustrophedonStc, testFillMapWith2Obstacles) { /* * [s 0 0 0] @@ -96,7 +96,7 @@ TEST(TestSpiralStc, testFillMapWith2Obstacles) Point_t start = {0, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -111,7 +111,7 @@ TEST(TestSpiralStc, testFillMapWith2Obstacles) /* * On a 4x4 map where the opposite right half of the map is blocked, we can cover only the 4x2 reachable nodes */ -TEST(TestSpiralStc, testFillMapWithHalfBlocked) +TEST(TestBoustrophedonStc, testFillMapWithHalfBlocked) { /* * [s 0 1 0] @@ -127,7 +127,7 @@ TEST(TestSpiralStc, testFillMapWithHalfBlocked) Point_t start = {0, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -141,9 +141,9 @@ TEST(TestSpiralStc, testFillMapWithHalfBlocked) /* * On a map with a wall almost blocking off a half of the map, but leaving a gap to the other side, - * spiral_stc should still cover all reachable nodes + * boustrophedon_stc should still cover all reachable nodes */ -TEST(TestSpiralStc, testFillMapWithWall) +TEST(TestBoustrophedonStc, testFillMapWithWall) { /* * [s 0 1 0] @@ -158,7 +158,7 @@ TEST(TestSpiralStc, testFillMapWithWall) Point_t start = {0, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -173,7 +173,7 @@ TEST(TestSpiralStc, testFillMapWithWall) /* * This test case features a very short dead-end */ -TEST(TestSpiralStc, testDeadEnd1) +TEST(TestBoustrophedonStc, testDeadEnd1) { /* * [0 0 1 0] @@ -189,7 +189,7 @@ TEST(TestSpiralStc, testDeadEnd1) Point_t start = {1, 2}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -208,10 +208,10 @@ TEST(TestSpiralStc, testDeadEnd1) /* * This test case is an extension of testDeadEnd1, where the top row is also covered as an obstacle. * The top row is covered but the obstacle from testDeadEnd1 is shifted downwards - * in an attempt to see if SpiralSTC also fails when a dead-end is not on the edge of the map + * in an attempt to see if BoustrophedonSTC also fails when a dead-end is not on the edge of the map * (but below a row of obstacles) */ -TEST(TestSpiralStc, testDeadEnd1WithTopRow) +TEST(TestBoustrophedonStc, testDeadEnd1WithTopRow) { /* * [1 1 1 1] @@ -233,7 +233,7 @@ TEST(TestSpiralStc, testDeadEnd1WithTopRow) Point_t start = {1, 3}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -252,7 +252,7 @@ TEST(TestSpiralStc, testDeadEnd1WithTopRow) /* * This test case features a very short dead-end */ -TEST(TestSpiralStc, testDeadEnd2) +TEST(TestBoustrophedonStc, testDeadEnd2) { /* * [1 0 0 0] @@ -268,7 +268,7 @@ TEST(TestSpiralStc, testDeadEnd2) Point_t start = {3, 0}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -287,7 +287,7 @@ TEST(TestSpiralStc, testDeadEnd2) /* * This test case features a very short dead-end */ -TEST(TestSpiralStc, testDeadEnd3) +TEST(TestBoustrophedonStc, testDeadEnd3) { /* * [0 0 0 0 0 0 1 0 0] @@ -309,7 +309,7 @@ TEST(TestSpiralStc, testDeadEnd3) Point_t start = {5, 2}; int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -328,7 +328,7 @@ TEST(TestSpiralStc, testDeadEnd3) /* * This test case features a very short dead-end */ -TEST(TestSpiralStc, testDeadEnd3WithTopRow) +TEST(TestBoustrophedonStc, testDeadEnd3WithTopRow) { /* * [1 1 1 1 1 1 1 1 1] @@ -359,7 +359,7 @@ TEST(TestSpiralStc, testDeadEnd3WithTopRow) Point_t start = {5, 3}; // NOLINT int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter); @@ -496,7 +496,7 @@ Point_t findStart(std::vector > const& grid) 5. On each coordinate in path, fill pixel at that coordinate in pathImg 6. pathImg and floodfilledImg should be identical */ -TEST(TestSpiralStc, testRandomMap) +TEST(TestBoustrophedonStc, testRandomMap) { // Seed pseudorandom sequence to create *reproducible test unsigned int seed = 12345; @@ -515,7 +515,7 @@ TEST(TestSpiralStc, testRandomMap) cv::Mat mapImg = drawMap(grid); Point_t start = findStart(grid); int multiple_pass_counter, visited_counter; - std::list path = full_coverage_path_planner::SpiralSTC::spiral_stc(grid, + std::list path = full_coverage_path_planner::BoustrophedonSTC::boustrophedon_stc(grid, start, multiple_pass_counter, visited_counter);