diff --git a/.gitignore b/.gitignore index 259148f..7065ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ *.exe *.out *.app + +# clion +.idea/ \ No newline at end of file diff --git a/polygon_coverage_geometry/include/polygon_coverage_geometry/test_comm.h b/polygon_coverage_geometry/include/polygon_coverage_geometry/test_comm.h index d5b3d57..0a92dfd 100644 --- a/polygon_coverage_geometry/include/polygon_coverage_geometry/test_comm.h +++ b/polygon_coverage_geometry/include/polygon_coverage_geometry/test_comm.h @@ -160,6 +160,22 @@ PolygonWithHoles createSophisticatedPolygon() { return poly_with_holes; } +template +Polygon createQiangsun89Polygon() { + Polygon poly; + poly.push_back(Point_2(3572.0, 115.0)); + poly.push_back(Point_2(2724.0, 460.0)); + poly.push_back(Point_2(2660.0, 95.0)); + poly.push_back(Point_2(855.0, 813.0)); + poly.push_back(Point_2(1182.0, 1497.0)); + poly.push_back(Point_2(2925.0, 832.0)); + poly.push_back(Point_2(2905.0, 480.0)); + poly.push_back(Point_2(3608.0, 208.0)); + + return poly; +} + + template bool checkVerticesIdentical(const typename Kernel::Point_2& a, const typename Kernel::Point_2& b) { diff --git a/polygon_coverage_geometry/src/decomposition.cc b/polygon_coverage_geometry/src/decomposition.cc index 9a9bf5b..707a796 100644 --- a/polygon_coverage_geometry/src/decomposition.cc +++ b/polygon_coverage_geometry/src/decomposition.cc @@ -106,6 +106,7 @@ bool computeBestBCDFromPolygonWithHoles(const PolygonWithHoles& pwh, // Get all possible decomposition directions. std::vector directions = findPerpEdgeDirections(pwh); + ROS_DEBUG("Number of perpendicular edge directions: %d", directions.size()); // For all possible rotations: for (const auto& dir : directions) { diff --git a/polygon_coverage_geometry/test/bcd-test.cpp b/polygon_coverage_geometry/test/bcd-test.cpp index 864a302..fad6f37 100644 --- a/polygon_coverage_geometry/test/bcd-test.cpp +++ b/polygon_coverage_geometry/test/bcd-test.cpp @@ -26,7 +26,7 @@ using namespace polygon_coverage_planning; -TEST(BctTest, computeBCD) { +TEST(BcdTest, computeBCD) { // Diamond. PolygonWithHoles diamond(createDiamond()); FT expected_area = diamond.outer_boundary().area(); diff --git a/polygon_coverage_solvers/include/polygon_coverage_solvers/graph_base.h b/polygon_coverage_solvers/include/polygon_coverage_solvers/graph_base.h index 9c51f72..a27b946 100644 --- a/polygon_coverage_solvers/include/polygon_coverage_solvers/graph_base.h +++ b/polygon_coverage_solvers/include/polygon_coverage_solvers/graph_base.h @@ -108,14 +108,6 @@ class GraphBase { // transforming cost into milli int. std::vector> getAdjacencyMatrix() const; - // Preserving three decimal digits. - inline int doubleToMilliInt(double in) const { - return static_cast(std::round(in * kToMilli)); - } - inline double milliIntToDouble(int in) const { - return static_cast(in) * kFromMilli; - } - protected: // Called from addNode. Creates all edges to the node at the back of the // graph. @@ -138,6 +130,7 @@ class GraphBase { size_t goal_idx_; bool is_created_; }; + } // namespace polygon_coverage_planning #include "polygon_coverage_solvers/impl/graph_base_impl.h" diff --git a/polygon_coverage_solvers/include/polygon_coverage_solvers/impl/graph_base_impl.h b/polygon_coverage_solvers/include/polygon_coverage_solvers/impl/graph_base_impl.h index 25478bc..2ad91be 100644 --- a/polygon_coverage_solvers/include/polygon_coverage_solvers/impl/graph_base_impl.h +++ b/polygon_coverage_solvers/include/polygon_coverage_solvers/impl/graph_base_impl.h @@ -20,12 +20,12 @@ #ifndef POLYGON_COVERAGE_SOLVERS_GRAPH_BASE_IMPL_H_ #define POLYGON_COVERAGE_SOLVERS_GRAPH_BASE_IMPL_H_ -#include -#include - #include #include +#include +#include + namespace polygon_coverage_planning { template @@ -324,15 +324,69 @@ Solution GraphBase::reconstructSolution( template std::vector> GraphBase::getAdjacencyMatrix() const { + ROS_DEBUG("Create adjacency matrix."); + // First scale the matrix such that when converting the cost to integers two + // costs can always be differentiated (except for if they are the same). Find + // the minimum absolute difference between any two values to normalize the + // adjacency matrix. + // https://afteracademy.com/blog/minimum-absolute-difference-in-an-array + std::vector sorted_cost; + sorted_cost.reserve(graph_.size() * graph_.size()); + for (size_t i = 0; i < graph_.size(); ++i) { + for (size_t j = 0; j < graph_[i].size(); ++j) { + const EdgeId edge(i, j); + double cost; + if (edgeExists(edge) && getEdgeCost(edge, &cost)) { + sorted_cost.push_back(cost); + } + } + } + sort(sorted_cost.begin(), sorted_cost.end()); + const double equality = 0.000001; // Min. considered cost difference. + const double min_diff_desired = 1.1; // To distinguish integer value costs. + auto min_diff = min_diff_desired; + if (sorted_cost.back() == 0.0) { + ROS_ERROR("Adjacency matrix invalid. Greatest cost is 0.0."); + min_diff = std::numeric_limits::max(); + } + for (size_t i = 0; i < sorted_cost.size() - 1; i++) { + auto diff = std::fabs(sorted_cost[i + 1] - sorted_cost[i]); + if (diff > equality && diff < min_diff) min_diff = diff; + } + + // Only scale with min_diff if this does not overflow the cost. + // Maximum expected cost is number of clusters times max. cost per cluster. + // TODO(rikba): Find a tighter upper bound. + double total_cost_upper_bound = sorted_cost.back() * graph_.size(); + double max_scale = min_diff_desired / min_diff; + double min_scale = + std::numeric_limits::max() / (total_cost_upper_bound + 1.0); + double scale = 1.0; + if (max_scale * total_cost_upper_bound < std::numeric_limits::max()) { + ROS_DEBUG("Optimal scale %.9f applied.", max_scale); + scale = max_scale; + } else { + ROS_DEBUG("Minimum scale %.9f applied.", min_scale); + scale = min_scale; + ROS_WARN_COND( + min_diff < 1.0, + "The adjacency matrix is ill conditioned. Consider removing small " + "details in the polygon to have even decomposition sizes. Loss of " + "optimality!"); + } + + ROS_DEBUG("The minimum cost difference is: %.9f", min_diff); + ROS_INFO("The adjacency matrix scale is: %.9f", scale); + + // Create scale adjacency matrix. std::vector> m(graph_.size(), std::vector(graph_.size())); - for (size_t i = 0; i < m.size(); ++i) { for (size_t j = 0; j < m[i].size(); ++j) { const EdgeId edge(i, j); double cost; if (edgeExists(edge) && getEdgeCost(edge, &cost)) { - m[i][j] = doubleToMilliInt(cost); + m[i][j] = static_cast(cost * scale); } else { m[i][j] = std::numeric_limits::max(); }