Coverage Control Library
Loading...
Searching...
No Matches
oracle_bang_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
30#ifndef CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_ORACLE_BANG_EXPLORE_EXPLOIT_H_
31#define CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_ORACLE_BANG_EXPLORE_EXPLOIT_H_
32
33#include <omp.h>
34#include <algorithm>
35#include <fstream>
36#include <iostream>
37#include <queue>
38#include <random>
39#include <set>
40#include <vector>
41
44#include "CoverageControl/extern/lsap/Hungarian.h"
47
48namespace CoverageControl {
49
51 private:
52 Parameters const params_;
53 size_t num_robots_ = 0;
54 CoverageSystem &env_;
55 Voronoi voronoi_;
56 std::vector<VoronoiCell> voronoi_cells_;
57 PointVector robot_global_positions_;
58 PointVector goals_, actions_;
59 MapType const &exploration_map_; // Binary map: true for unexplored locations
60 MapType const &explored_idf_map_;
61
62 int recompute_goal_steps_ = 0, recompute_goal_counter_ = 0;
63 bool is_converged_ = false;
64 bool trigger_exploit_ = false;
65 bool first_step_ = true;
66 std::vector<int> robot_status_; // 0: exploring, 1: exploit
67 std::vector<int> random_goal_counter_;
68 std::random_device
69 rd_; // Will be used to obtain a seed for the random number engine
70 std::mt19937 gen_;
71 std::uniform_real_distribution<> rand_dist_;
72 double exploration_threshold_ = 0.8;
73 double exploit_threshold_ = 0;
74 double too_close_factor_ = 6.0;
75 double sensor_area_ = 0;
76 double local_win_ = 0;
77 double time_step_dist_ = 0;
78 double eps = 0.0001;
79 double better_threshold_ = 2;
80
81 public:
82 OracleBangExploreExploit(Parameters const &params, size_t const &num_robots,
83 CoverageSystem &env)
84 : params_{params},
85 num_robots_{num_robots},
86 env_{env},
87 exploration_map_{env.GetSystemExplorationMap()},
88 explored_idf_map_{env.GetSystemExploredIDFMap()} {
89 voronoi_cells_.resize(num_robots_);
90
91 robot_global_positions_ = env_.GetRobotPositions();
92 actions_.resize(num_robots_);
93 goals_ = robot_global_positions_;
94 sensor_area_ = params_.pSensorSize * params_.pSensorSize;
95 local_win_ = params_.pLocalMapSize / 2;
96 exploit_threshold_ = 0.2 * local_win_ * local_win_;
97
98 robot_status_.resize(num_robots_, 0);
99 random_goal_counter_.resize(num_robots_, 10);
100
101 std::srand(0);
102 gen_ = std::mt19937(
103 rd_()); // Standard mersenne_twister_engine seeded with rd_()
104
105 time_step_dist_ =
106 params_.pMaxRobotSpeed * params_.pTimeStep * params_.pResolution;
107 rand_dist_ =
108 std::uniform_real_distribution<>(-time_step_dist_, time_step_dist_);
109 ComputeGoals();
110 }
111
112 PointVector GetActions() { return actions_; }
113
114 std::vector<int> GetRobotStatus() { return robot_status_; }
115
116 void SetGoals(PointVector const &goals) { goals_ = goals; }
117
118 auto GetGoals() { return goals_; }
119 std::vector<VoronoiCell> GetVoronoiCells() { return voronoi_cells_; }
120
122 robot_global_positions_ = env_.GetRobotPositions();
123 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
124 robot_status_[iRobot] = 0;
125
126 MapUtils::MapBounds index, offset;
128 robot_global_positions_[iRobot], local_win_,
129 params_.pWorldMapSize, index, offset);
130 double unexplored =
131 exploration_map_
132 .block(index.left + offset.left, index.bottom + offset.bottom,
133 offset.width, offset.height)
134 .count();
135 double idf_value =
136 explored_idf_map_
137 .block(index.left + offset.left, index.bottom + offset.bottom,
138 offset.width, offset.height)
139 .sum();
140
141 if (unexplored < exploit_threshold_ and idf_value > exploit_threshold_) {
142 robot_status_[iRobot] = 1;
143 for (size_t jRobot = 0; jRobot < iRobot; ++jRobot) {
144 double goal_dist = (goals_[iRobot] - goals_[jRobot]).norm();
145 if (goal_dist < too_close_factor_ * params_.pSensorSize *
146 params_.pResolution and
147 env_.GetExplorationRatio() < exploration_threshold_) {
148 robot_status_[jRobot] = 0;
149 }
150 }
151 }
152 }
153 }
154
155 double GetFrontierGoal(Point2 const &pos, Point2 &best_goal) {
156 best_goal = pos;
157 double best_bcr = 0, best_benefit = 0;
158 double pos_x_lim = std::min(pos[0] + time_step_dist_,
159 static_cast<double>(params_.pWorldMapSize));
160 double pos_y_lim = std::min(pos[1] + time_step_dist_,
161 static_cast<double>(params_.pWorldMapSize));
162 for (double pos_x = std::max(pos[0] - time_step_dist_, eps);
163 pos_x < pos_x_lim; pos_x += 1 * params_.pResolution) {
164 for (double pos_y = std::max(pos[1] - time_step_dist_, eps);
165 pos_y < pos_y_lim; pos_y += 1 * params_.pResolution) {
166 Point2 qpos{pos_x, pos_y};
167 double dist = (qpos - pos).norm();
168 /* if(dist < 2 * params_.pResolution) { continue; } */
169 dist = std::max(dist, time_step_dist_);
170 MapUtils::MapBounds index, offset;
171 MapUtils::ComputeOffsets(params_.pResolution, qpos, params_.pSensorSize,
172 params_.pWorldMapSize, index, offset);
173 double unexplored =
174 exploration_map_
175 .block(index.left + offset.left, index.bottom + offset.bottom,
176 offset.width, offset.height)
177 .count();
178 double benefit = unexplored;
179 if (unexplored > 0.1 * sensor_area_) {
180 double idf_value =
181 explored_idf_map_
182 .block(index.left + offset.left, index.bottom + offset.bottom,
183 offset.width, offset.height)
184 .count();
185 benefit += 2 * idf_value;
186 }
187
188 double bcr = benefit / dist;
189 double bcr_diff = bcr - best_bcr;
190 bool update = false;
191 if (bcr_diff < -kEps) {
192 continue;
193 }
194 if (std::abs(bcr_diff) < kEps) {
195 Point2 unit_best_goal = best_goal.normalized();
196 Point2 unit_qpos = qpos.normalized();
197 Point2 unit_curr_pos = pos.normalized();
198 if (unit_best_goal.dot(unit_curr_pos) >
199 unit_qpos.dot(unit_curr_pos)) {
200 update = true;
201 }
202 }
203
204 if (bcr_diff > kEps or update == true) {
205 best_bcr = bcr;
206 best_goal = qpos;
207 best_benefit = benefit;
208 }
209 }
210 }
211 /* std::cout << "Best goal: " << best_bcr << " " << best_goal[0] << " " <<
212 * best_goal[1] << std::endl; */
213 return best_benefit;
214 }
215
216 double GetFrontierGoal1(Point2 const &pos, Point2 &best_goal) {
217 best_goal = pos;
218 double best_angle = 0;
219 double best_bcr = 0, best_benefit = 0, best_idf_benefit = 0;
220 for (double rad = time_step_dist_; rad <= 20 * time_step_dist_;
221 rad += time_step_dist_) {
222 for (double angle = 0; angle < 2 * M_PI; angle += M_PI / 180) {
223 Point2 qpos = pos + rad * Point2{std::cos(angle), std::sin(angle)};
224 std::cout << "rad: " << rad << " " << angle << std::endl;
225 qpos[0] = std::min(qpos[0], static_cast<double>(params_.pWorldMapSize));
226 qpos[1] = std::min(qpos[1], static_cast<double>(params_.pWorldMapSize));
227 qpos[0] = std::max(qpos[0], eps);
228 qpos[1] = std::max(qpos[1], eps);
229 std::cout << "qpos: " << qpos[0] << " " << qpos[1] << std::endl;
230 double dist = (qpos - pos).norm();
231 /* dist = std::max(dist, time_step_dist_); */
232
233 MapUtils::MapBounds index, offset;
234 MapUtils::ComputeOffsets(params_.pResolution, qpos, params_.pSensorSize,
235 params_.pWorldMapSize, index, offset);
236 double unexplored =
237 exploration_map_
238 .block(index.left + offset.left, index.bottom + offset.bottom,
239 offset.width, offset.height)
240 .count();
241 double unexplored_benefit = unexplored;
242 double idf_benefit = 0;
243 double idf_value =
244 explored_idf_map_
245 .block(index.left + offset.left, index.bottom + offset.bottom,
246 offset.width, offset.height)
247 .sum();
248 if (unexplored > 0.1 * sensor_area_) {
249 idf_benefit = 2.5 * idf_value;
250 }
251 double benefit = unexplored_benefit + idf_benefit;
252 double bcr = benefit / dist;
253 double bcr_diff = bcr - best_bcr;
254 bool update = false;
255 if (bcr_diff < -better_threshold_) {
256 continue;
257 }
258 if (std::abs(bcr_diff) < better_threshold_) {
259 if (best_angle > angle) {
260 update = true;
261 }
262 }
263 if (bcr_diff > better_threshold_ or update == true) {
264 best_bcr = bcr;
265 best_goal = qpos;
266 best_benefit = benefit;
267 /* best_unexplored_benefit = unexplored_benefit; */
268 best_idf_benefit = idf_benefit;
269 best_angle = angle;
270 }
271 }
272 if (best_benefit > better_threshold_) {
273 break;
274 }
275 if (best_idf_benefit > 0.1) {
276 break;
277 }
278 }
279 /* std::cout << "Best goal: " << best_bcr << " " << best_goal[0] << " " <<
280 * best_goal[1] << " " << best_unexplored_benefit << " " <<
281 * best_idf_benefit << std::endl; */
282 return best_benefit;
283 }
284
286 /* Exploration Robots */
288
289#pragma omp parallel for num_threads(num_robots_)
290 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
291 if (robot_status_[iRobot] == 0) {
292 double best_benefit =
293 GetFrontierGoal(robot_global_positions_[iRobot], goals_[iRobot]);
294 if (best_benefit < better_threshold_) {
295 robot_status_[iRobot] = 1;
296 }
297 /* std::cout << iRobot << " " << robot_status_[iRobot] << " " <<
298 * (goals_[iRobot] - robot_global_positions_[iRobot]).norm() << " " <<
299 * goals_[iRobot][0] << " " << goals_[iRobot][1] << std::endl; */
300 }
301 }
302
303 /* Exploiting Robots */
304 PointVector exploit_robots_pos;
305 std::vector<int> exploit_robots;
306 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
307 if (robot_status_[iRobot] != 0) {
308 exploit_robots_pos.push_back(robot_global_positions_[iRobot]);
309 exploit_robots.push_back(iRobot);
310 }
311 }
312
313 Voronoi exploit_voronoi(
314 exploit_robots_pos, explored_idf_map_,
315 Point2(params_.pWorldMapSize, params_.pWorldMapSize),
316 params_.pResolution);
317 auto exploit_voronoi_cells = exploit_voronoi.GetVoronoiCells();
318
319 for (size_t iCell = 0; iCell < exploit_robots.size(); ++iCell) {
320 size_t iRobot = exploit_robots[iCell];
321 auto goal = exploit_voronoi_cells[iCell].centroid();
322 goals_[iRobot] = goal;
323 }
324
325 int num_exploring_robots = num_robots_ - exploit_robots.size();
326 std::cout << "Number of exploring_robots: " << num_exploring_robots
327 << std::endl;
328 std::cout << "Exploration ratio: " << env_.GetExplorationRatio() * 100
329 << std::endl;
330
331 /* if (num_exploring_robots == 0) { */
332 /* goals_ = LloydOffline(params_.pLloydNumTries,
333 * params_.pLloydMaxIterations, */
334 /* num_robots_, explored_idf_map_,
335 * params_.pWorldMapSize, */
336 /* params_.pResolution, robot_global_positions_); */
337 /* trigger_exploit_ = true; */
338 /* } */
339 }
340
342 if (first_step_ == true) {
343 first_step_ = false;
344 robot_global_positions_ = env_.GetRobotPositions();
345 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
346 goals_[iRobot] = robot_global_positions_[iRobot] +
347 Point2{rand_dist_(gen_), rand_dist_(gen_)};
348 }
349
350 } else if (trigger_exploit_ == false) {
351 ComputeGoals();
352 }
353 /* env_.StepRobotsToGoals(goals_, actions_); */
354 is_converged_ = true;
355 for (size_t iRobot = 0; iRobot < num_robots_; ++iRobot) {
356 actions_[iRobot] = Point2(0, 0);
357 Point2 diff = goals_[iRobot] - robot_global_positions_[iRobot];
358 double dist = diff.norm();
359 double speed = dist / params_.pTimeStep;
360 speed = std::min(params_.pMaxRobotSpeed, speed);
361 Point2 direction(diff);
362 direction.normalize();
363 actions_[iRobot] = speed * direction;
364 /* printf("Step: %ld, %d, (%f, %f) \t (%f, %f)\n", iRobot,
365 * robot_status_[iRobot], goals_[iRobot][0], goals_[iRobot][1],
366 * actions_[iRobot][0], actions_[iRobot][1]); */
367 if (actions_[iRobot].norm() > eps) {
368 is_converged_ = false;
369 }
370 }
371 return 0;
372 }
373
374 bool IsConverged() { return is_converged_; }
375};
376
377} /* namespace CoverageControl */
378#endif // CPPSRC_CORE_INCLUDE_COVERAGECONTROL_ALGORITHMS_ORACLE_BANG_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.
OracleBangExploreExploit(Parameters const &params, size_t const &num_robots, CoverageSystem &env)
double GetFrontierGoal1(Point2 const &pos, Point2 &best_goal)
double GetFrontierGoal(Point2 const &pos, Point2 &best_goal)
Class to store parameters.
Definition parameters.h:48
double pTimeStep
Each time step corresponds to pTimeStep seconds.
Definition parameters.h:140
Class for computing Voronoi cells.
Definition voronoi.h:116
auto GetVoronoiCells() const
Definition voronoi.h:185
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
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.
Contains typedefs for the library.