Coverage Control Library
Loading...
Searching...
No Matches
simul_explore_exploit.h
Go to the documentation of this file.
1/*
2 * This file is part of the CoverageControl library
3 *
4 * Author: Saurav Agarwal
5 * Contact: sauravag@seas.upenn.edu, agr.saurav1@gmail.com
6 * Repository: https://github.com/KumarRobotics/CoverageControl
7 *
8 * Copyright (c) 2024, Saurav Agarwal
9 *
10 * The CoverageControl library is free software: you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or (at your
13 * option) any later version.
14 *
15 * The CoverageControl library is distributed in the hope that it will be
16 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
18 * Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * CoverageControl library. If not, see <https://www.gnu.org/licenses/>.
22 */
23
33#ifndef CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_SIMUL_EXPLORE_EXPLOIT_H_
34#define CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_SIMUL_EXPLORE_EXPLOIT_H_
35
36#include <omp.h>
37
38#include <algorithm>
39#include <fstream>
40#include <iostream>
41#include <queue>
42#include <random>
43#include <set>
44#include <vector>
45
48#include "CoverageControl/extern/lsap/Hungarian.h"
52
53namespace CoverageControl {
55 private:
56 Parameters const params_;
57 size_t num_robots_ = 0;
58 CoverageSystem &env_;
59 Voronoi voronoi_;
60 std::vector<VoronoiCell> voronoi_cells_;
61 PointVector robot_global_positions_;
62 PointVector goals_, actions_;
63 MapType const &exploration_map_; // Binary map: true for unexplored locations
64 MapType const &explored_idf_map_;
65
66 std::vector<queue_t> frontiers_;
67 size_t num_frontiers_ = 1;
68
69 int recompute_goal_steps_ = 0, recompute_goal_counter_ = 0;
70 bool is_converged_ = false;
71 bool trigger_exploit_ = false;
72 bool first_step_ = true;
73 std::vector<int> robot_status_; // 0: exploring, 1: exploit
74 std::random_device
75 rd_; // Will be used to obtain a seed for the random number engine
76 std::mt19937 gen_;
77 std::uniform_real_distribution<> rand_dist_;
78 double exploration_threshold_ = 0.8;
79 double exploit_threshold_ = 0;
80 double too_close_factor_ = 6.0;
81 double sensor_area_ = 0;
82 double local_win_ = 0;
83 double time_step_dist_ = 0;
84 double eps = 0.0001;
85 double better_threshold_ = 2;
86 int num_exploring_robots_ = 0;
87 std::vector<int> exploit_robots_;
88
89 public:
90 OracleSimulExploreExploit(Parameters const &params, size_t const &num_robots,
91 CoverageSystem &env)
92 : params_{params},
93 num_robots_{num_robots},
94 env_{env},
95 exploration_map_{env.GetSystemExplorationMap()},
96 explored_idf_map_{env.GetSystemExploredIDFMap()} {
97 num_exploring_robots_ = num_robots_;
98 voronoi_cells_.resize(num_robots_);
99
100 robot_global_positions_ = env_.GetRobotPositions();
101 actions_.resize(num_robots_);
102 goals_ = robot_global_positions_;
103 sensor_area_ = params_.pSensorSize * params_.pSensorSize;
104 local_win_ = params_.pLocalMapSize / 2;
105 exploit_threshold_ = 0.2 * local_win_ * local_win_;
106
107 robot_status_.resize(num_robots_, 0);
108 frontiers_.resize(num_robots_);
109
110 std::srand(0);
111 gen_ = std::mt19937(
112 rd_()); // Standard mersenne_twister_engine seeded with rd_()
113
114 time_step_dist_ =
115 params_.pMaxRobotSpeed * params_.pTimeStep * params_.pResolution;
116 rand_dist_ = std::uniform_real_distribution<>(0, 2 * M_PI);
117 ComputeGoals();
118 }
119
120 PointVector GetActions() { return actions_; }
121
122 std::vector<int> GetRobotStatus() { return robot_status_; }
123
124 void SetGoals(PointVector const &goals) { goals_ = goals; }
125
126 auto GetGoals() { return goals_; }
127 std::vector<VoronoiCell> GetVoronoiCells() { return voronoi_cells_; }
128
130 robot_global_positions_ = env_.GetRobotPositions();
131 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
132 robot_status_[iRobot] = 0;
133
134 MapUtils::MapBounds index, offset;
136 robot_global_positions_[iRobot], local_win_,
137 params_.pWorldMapSize, index, offset);
138 double unexplored =
139 exploration_map_
140 .block(index.left + offset.left, index.bottom + offset.bottom,
141 offset.width, offset.height)
142 .count();
143 double idf_value =
144 explored_idf_map_
145 .block(index.left + offset.left, index.bottom + offset.bottom,
146 offset.width, offset.height)
147 .sum();
148
149 if (unexplored < exploit_threshold_ and idf_value > exploit_threshold_) {
150 robot_status_[iRobot] = 1;
151 for (size_t jRobot = 0; jRobot < iRobot; ++jRobot) {
152 double goal_dist = (goals_[iRobot] - goals_[jRobot]).norm();
153 if (goal_dist < too_close_factor_ * params_.pSensorSize *
154 params_.pResolution and
155 env_.GetExplorationRatio() < exploration_threshold_) {
156 robot_status_[jRobot] = 0;
157 }
158 }
159 }
160 }
161 }
162
163 double GetFrontierGoal(Point2 const &pos, Point2 &best_goal,
164 queue_t &frontiers) {
165 best_goal = pos;
166 double best_bcr = 0, best_benefit = 0;
167 double pos_x_lim = std::min(pos[0] + 2 * time_step_dist_,
168 static_cast<double>(params_.pWorldMapSize));
169 double pos_y_lim = std::min(pos[1] + 2 * time_step_dist_,
170 static_cast<double>(params_.pWorldMapSize));
171 for (double pos_x = std::max(pos[0] - 2 * time_step_dist_, eps);
172 pos_x < pos_x_lim; pos_x += 1 * params_.pResolution) {
173 for (double pos_y = std::max(pos[1] - 2 * time_step_dist_, eps);
174 pos_y < pos_y_lim; pos_y += 1 * params_.pResolution) {
175 Point2 qpos{pos_x, pos_y};
176 double dist = (qpos - pos).norm();
177 /* if(dist < 2 * params_.pResolution) { continue; } */
178 dist = std::max(dist, time_step_dist_);
179 MapUtils::MapBounds index, offset;
180 MapUtils::ComputeOffsets(params_.pResolution, qpos, params_.pSensorSize,
181 params_.pWorldMapSize, index, offset);
182 double unexplored =
183 exploration_map_
184 .block(index.left + offset.left, index.bottom + offset.bottom,
185 offset.width, offset.height)
186 .count();
187 double benefit = unexplored;
188 if (unexplored > 0.1 * sensor_area_) {
189 double idf_value =
190 explored_idf_map_
191 .block(index.left + offset.left, index.bottom + offset.bottom,
192 offset.width, offset.height)
193 .count();
194 benefit += 2 * idf_value;
195 }
196 double bcr = benefit / dist;
197
198 if (frontiers.size() < num_frontiers_) {
199 frontiers.push(Frontier(qpos, bcr));
200 } else {
201 auto worst_frontier = frontiers.top();
202 if (worst_frontier.value < bcr) {
203 frontiers.pop();
204 frontiers.push(Frontier(qpos, bcr));
205 }
206 }
207
208 double bcr_diff = bcr - best_bcr;
209 bool update = false;
210 if (bcr_diff < -kEps) {
211 continue;
212 }
213 if (std::abs(bcr_diff) < kEps) {
214 Point2 unit_best_goal = best_goal.normalized();
215 Point2 unit_qpos = qpos.normalized();
216 Point2 unit_curr_pos = pos.normalized();
217 if (unit_best_goal.dot(unit_curr_pos) >
218 unit_qpos.dot(unit_curr_pos)) {
219 update = true;
220 }
221 }
222
223 if (bcr_diff > kEps or update == true) {
224 best_bcr = bcr;
225 best_goal = qpos;
226 best_benefit = benefit;
227 }
228 }
229 }
230 /* std::cout << "Best goal: " << best_bcr << " " << best_goal[0] << " " <<
231 * best_goal[1] << std::endl; */
232 return best_benefit;
233 }
234
236 /* Exploration Robots */
238
239#pragma omp parallel for num_threads(num_robots_)
240 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
241 frontiers_[iRobot] = queue_t();
242 if (robot_status_[iRobot] == 0) {
243 double best_benefit =
244 GetFrontierGoal(robot_global_positions_[iRobot], goals_[iRobot],
245 frontiers_[iRobot]);
246 if (best_benefit < better_threshold_) {
247 robot_status_[iRobot] = 1;
248 }
249 /* std::cout << iRobot << " " << robot_status_[iRobot] << " " <<
250 * (goals_[iRobot] - robot_global_positions_[iRobot]).norm() << " " <<
251 * goals_[iRobot][0] << " " << goals_[iRobot][1] << std::endl; */
252 }
253 }
254
255 /* Exploiting Robots */
256 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
257 if (robot_status_[iRobot] != 0) {
258 exploit_robots_.push_back(iRobot);
259 }
260 }
261#pragma omp parallel for num_threads(num_robots_)
262 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
263 if (robot_status_[iRobot] != 0) { // 1 is for exploit
264 Point2 const &pos = robot_global_positions_[iRobot];
265 MapUtils::MapBounds index, offset;
267 params_.pLocalMapSize, params_.pWorldMapSize,
268 index, offset);
269
270 MapType robot_local_map = explored_idf_map_.block(
271 index.left + offset.left, index.bottom + offset.bottom,
272 offset.width, offset.height);
273
274 /* Point2 map_translation(params_.pLocalMapSize *
275 * params_.pResolution/2., params_.pLocalMapSize *
276 * params_.pResolution/2.); */
277 Point2 map_translation(
278 (index.left + offset.left) * params_.pResolution,
279 (index.bottom + offset.bottom) * params_.pResolution);
280 /* map_translation -= Point2(offset.left, offset.bottom) *
281 * params_.pResolution; */
282
283 PointVector robot_neighbors_pos;
284 for (size_t jRobot = 0; jRobot < num_robots_; ++jRobot) {
285 if (iRobot == jRobot or robot_status_[jRobot] == 0) {
286 continue;
287 }
288 Point2 relative_pos =
289 robot_global_positions_[jRobot] - robot_global_positions_[iRobot];
290 if (relative_pos.norm() < params_.pCommunicationRange) {
291 robot_neighbors_pos.push_back(robot_global_positions_[jRobot]);
292 }
293 }
294 PointVector robot_positions(robot_neighbors_pos.size() + 1);
295
296 robot_positions[0] = robot_global_positions_[iRobot] - map_translation;
297 /* std::cout << "Voronoi: " << robot_positions[0][0] << " " <<
298 * robot_positions[0][1] << std::endl; */
299 int count = 1;
300 for (Point2 const &pos : robot_neighbors_pos) {
301 robot_positions[count] = pos - map_translation;
302 ++count;
303 }
304 Point2 map_size(offset.width, offset.height);
305 Voronoi voronoi(robot_positions, robot_local_map, map_size,
306 params_.pResolution, true, 0);
307 auto vcell = voronoi.GetVoronoiCell();
308 goals_[iRobot] =
309 vcell.centroid() + robot_positions[0] + map_translation;
310 if (goals_[iRobot][0] < 0 or
311 goals_[iRobot][0] > params_.pWorldMapSize or
312 goals_[iRobot][1] < 0 or
313 goals_[iRobot][1] > params_.pWorldMapSize) {
314 std::cout << "Goal out of bounds: " << goals_[iRobot][0] << " "
315 << goals_[iRobot][1] << std::endl;
316 std::cout << robot_global_positions_[iRobot][0] << " "
317 << robot_global_positions_[iRobot][1] << std::endl;
318 std::cout << vcell.centroid()[0] << " " << vcell.centroid()[1]
319 << std::endl;
320 std::cout << map_translation[0] << " " << map_translation[1]
321 << std::endl;
322 std::cout << robot_local_map.sum() << std::endl;
323 throw std::runtime_error(
324 "Goal out of bounds: this should not have happened for convex "
325 "environments");
326 }
327 }
328 }
329
330 /* int num_exploring_robots = num_robots_ - exploit_robots_.size(); */
331 /* std::cout << "Number of exploring_robots: " << num_exploring_robots << "
332 * with ratio: "<< env_.GetExplorationRatio() * 100<< std::endl; */
333 /* std::cout << "Exploration ratio: " << env_.GetExplorationRatio() * 100 <<
334 * std::endl; */
335 }
336
338 robot_global_positions_ = env_.GetRobotPositions();
339 goals_ = robot_global_positions_;
340 if (first_step_ == true) {
341 first_step_ = false;
342 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
343 double ang = rand_dist_(gen_);
344 goals_[iRobot] = robot_global_positions_[iRobot] +
345 time_step_dist_ * Point2{cos(ang), sin(ang)};
346 }
347
348 } else {
349 ComputeGoals();
350 }
351 is_converged_ = true;
352 num_exploring_robots_ = 0;
353 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
354 if (robot_status_[iRobot] == 0) {
355 ++num_exploring_robots_;
356 }
357 }
358
359 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
360 actions_[iRobot] = Point2(0, 0);
361 Point2 diff = goals_[iRobot] - robot_global_positions_[iRobot];
362 double dist = diff.norm();
363 double speed = dist / params_.pTimeStep;
364 speed = std::min(params_.pMaxRobotSpeed, speed);
365 Point2 direction(diff);
366 direction.normalize();
367 actions_[iRobot] = speed * direction;
368 /* printf("Step: %ld, %d, (%f, %f) \t (%f, %f)\n", iRobot,
369 * robot_status_[iRobot], goals_[iRobot][0], goals_[iRobot][1],
370 * actions_[iRobot][0], actions_[iRobot][1]); */
371 if (dist <= kEps) {
372 continue;
373 }
374 if (num_exploring_robots_ == 0) {
375 if (env_.CheckOscillation(iRobot)) {
376 continue;
377 }
378 }
379 is_converged_ = false;
380 }
381 return 0;
382 }
383
384 bool IsConverged() { return is_converged_; }
385
386 PointVector GetFrontiers(size_t const &robot_id) const {
387 PointVector frontier_points;
388 auto robot_frontiers = frontiers_[robot_id];
389 while (!robot_frontiers.empty()) {
390 auto point = robot_frontiers.top();
391 frontier_points.push_back(point.pt);
392 robot_frontiers.pop();
393 }
394 return frontier_points;
395 }
396
398 PointVector frontier_points;
399 for (auto robot_frontiers : frontiers_) {
400 while (!robot_frontiers.empty()) {
401 auto point = robot_frontiers.top();
402 frontier_points.push_back(point.pt);
403 robot_frontiers.pop();
404 }
405 }
406 return frontier_points;
407 }
408};
409
410} /* namespace CoverageControl */
411#endif // CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_SIMUL_EXPLORE_EXPLOIT_H_
Contains the abstract class for coverage control algorithms.
The CoverageSystem class is the main class for the coverage control library.
PointVector GetRobotPositions(bool force_no_noise=false)
Get the global positions of all robots.
bool CheckOscillation(size_t const robot_id) const
double GetFrontierGoal(Point2 const &pos, Point2 &best_goal, queue_t &frontiers)
OracleSimulExploreExploit(Parameters const &params, size_t const &num_robots, CoverageSystem &env)
PointVector GetFrontiers(size_t const &robot_id) const
Class to store parameters.
Definition parameters.h:48
double pTimeStep
Each time step corresponds to pTimeStep seconds.
Definition parameters.h:140
double pCommunicationRange
Radius of communication (in meters)
Definition parameters.h:134
Class for computing Voronoi cells.
Definition voronoi.h:116
The file contains the CoverageSystem class, which is the main class for the coverage control library.
double const kEps
Definition constants.h:48
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > MapType
Definition typedefs.h:48
std::vector< Point2 > PointVector
Definition typedefs.h:51
Eigen::Vector2d Point2
Definition typedefs.h:44
std::priority_queue< Frontier, std::vector< Frontier >, FrontierCompare > queue_t
Definition typedefs.h:86
Utility functions for transforming maps.
void ComputeOffsets(double const resolution, Point2 const &pos, int const submap_size, int const map_size, MapBounds &index, MapBounds &offset)
Definition map_utils.h:59
Namespace for the CoverageControl library.
Contains parameters.
A struct to store a frontier points and a value.
Definition typedefs.h:69
Contains typedefs for the library.