-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenvironment.cc
More file actions
323 lines (282 loc) · 9.32 KB
/
environment.cc
File metadata and controls
323 lines (282 loc) · 9.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
* environment.hh (C) Inaki Rano 2022
*
* Definition of environment class for the 2D multi-robot simulator
* and the functions related to the environment: distance(), angle()
* and wrap()
*
*/
#include "base.hh"
#include "environment.hh"
namespace mrs {
// Pointer to the environment initilised to null until an
// environment object is created
Environment *Environment::m_envp = NULL;
// Function to compute distances returns Euclidean distance if there
// is no environment object created
float distance(const Position2d & p0, const Position2d & p1)
{
if ((Environment::m_envp == NULL) || Environment::m_envp->m_isEuclidean)
return (p0-p1).norm();
return Environment::m_envp->dist(p0, p1);
}
// Function to compute angle, returns normal angle if there is no
// environment object created
float angle(const Position2d & p0, const Position2d & p1)
{
if ((Environment::m_envp == NULL) || Environment::m_envp->m_isEuclidean) {
Position2d diff(p1 - p0);
return atan2(diff[1], diff[0]);
}
return Environment::m_envp->ang(p0, p1);
}
// Wrap the point to fall again in the environment
Position2d wrap(const Position2d & p)
{
if ((Environment::m_envp == NULL) || Environment::m_envp->m_isEuclidean)
return p;
return Environment::m_envp->wrap(p);
}
// Function to calculate average of a vector of points
Position2d average(const std::vector<Position2d> & pts)
{
// Calculate standard average
Position2d mP(Position2d::Zero());
for (unsigned int ii = 0; ii < pts.size(); ii++)
mP += pts[ii];
mP /= (float)pts.size();
if ((Environment::m_envp == NULL) || Environment::m_envp->m_isEuclidean)
return mP;
// Search for the true average in a spherical topology. This is an
// adaptation of what I understood from
//
// https://www.youtube.com/watch?v=cYVmcaRAbJg
//
// For angular an variables and a discrete set of angles (a1,
// a2,...,aN) the real average on the circle can be selected among
// the following mean angle candidates:
//
// am_k = 1/N * \Sum_i(ai) + k*2*pi/N
//
// for k=1,2,...,N. The actual mean will be the one minimising the
// sum of the distances to the original angles, i.e. fulfilling
// the equation of the mean in a manyfold (M):
//
// am = argmin_{a\in M}\Sum_i d(a_i,a)
int sz(pts.size());
std::vector<Position2d> mPcandidates;
Position2d mPc;
for (unsigned int ii = 0; ii < sz; ii++) {
mPc[0] = wrapVar(mP[0] + ii * Environment::m_envp->m_size[0] / sz,
Environment::m_envp->m_size[0]);
for (unsigned int jj = 0; jj < sz; jj++) {
mPc[1] = wrapVar(mP[1] + jj * Environment::m_envp->m_size[1] / sz,
Environment::m_envp->m_size[1]);
mPcandidates.push_back(mPc);
}
}
float minDist((float)sz * Environment::m_envp->m_size.norm());
for (unsigned int ii = 0; ii < mPcandidates.size(); ii++)
{
float currDist(0);
for (unsigned int jj = 0; jj < pts.size(); jj++)
currDist += distance(pts[jj], mPcandidates[ii]);
if (currDist < minDist) {
minDist = currDist;
mP = mPcandidates[ii];
}
}
return mP;
}
// Function to calculate average of a vector of angles
float averageAngle(const std::vector<float> & angs)
{
// Calculate standard average
float mAng(0);
for (unsigned int ii = 0; ii < angs.size(); ii++)
mAng += angs[ii];
mAng /= (float)angs.size();
// Search for the true average in a circle. This is an adaptation
// of what I understood from:
//
// https://www.youtube.com/watch?v=cYVmcaRAbJg
//
// For angular an variables and a discrete set of angles (a1,
// a2,...,aN) the real average on the circle can be selected among
// the following mean angle candidates:
//
// am_k = 1/N * \Sum_i(ai) + k*2*pi/N
//
// for k=1,2,...,N. The actual mean will be the one minimising the
// sum of the distances to the original angles, i.e. fulfilling
// the equation of the mean in a manyfold (M):
//
// am = argmin_{a\in M}\Sum_i d(a_i,a)
int sz(angs.size());
float minDist((float)sz * 2 * M_PI);
float mAngCandidate(0), currMean(mAng);
for (unsigned int ii = 0; ii < sz; ii++) {
float currDist(0);
mAngCandidate = wrap2pi(mAng + ii * 2 * M_PI /(float)sz);
for (unsigned int jj = 0; jj < angs.size(); jj++)
currDist += fabs(wrap2pi(angs[jj] - mAngCandidate));
if (currDist < minDist) {
minDist = currDist;
currMean = mAngCandidate;
}
}
return currMean;
}
// Function to calculate average of a vector of points using a grid
// search (gradient descent might lead to faster results but it
// might also get trapped in local minima, as the sum of the
// distances might be non-convex)
float averageAngleW(const std::vector<float> & angs,
const std::vector<float> & w)
{
if (angs.size() != w.size())
{
std::cerr << "averageAngleW(): Weight error!" << std::endl;
}
// Copy vector of angles
Eigen::VectorXf A(angs.size());
for (unsigned int ii = 0; ii < A.size(); ii++)
A[ii] = angs[ii];
// Copy vector of weights
Eigen::VectorXf W(w.size());
for (unsigned int ii = 0; ii < A.size(); ii++)
{
W[ii] = w[ii];
if (W[ii] < 0)
std::cerr << "averageAngleW(): Warning negative weight!"
<< std::endl;
}
// Number of points to evaluate the distortion
int sz(10 * angs.size());
// Mean weighted angle and previous mean weighted angle
float mAngPrev(0), mAng(0);
// Search range width
float delta(2*M_PI);
// Search range
float range[2] = {-M_PI, M_PI};
Eigen::VectorXf mAngCandidates(sz);
Eigen::VectorXf distortion(sz);
bool meanImproves(true);
float tol(1e-6);
while (meanImproves) {
// Select candidate angles
for (unsigned int ii = 0; ii < sz; ii++)
mAngCandidates[ii] = wrap2pi(range[0] + ii * delta / sz +
delta / (2 * sz));
// Calculate distortion for the candidate means
for (unsigned int ii = 0; ii < sz; ii++)
{
Eigen::VectorXf aux(A - mAngCandidates[ii] *
Eigen::VectorXf::Ones(A.size()));
for (unsigned int jj = 0; jj < aux.size(); jj++)
aux[jj] = wrap2pi(aux[jj]);
aux = aux.array().pow(2);
distortion[ii] = W.transpose() * aux;
}
// Find the point of min distotion and new weighted average
mAngPrev = mAng;
unsigned int idx(0);
mAng = mAngCandidates[idx];
for (unsigned int ii = 1; ii < sz; ii++)
if (distortion[ii] < distortion[idx]) {
idx = ii;
mAng = mAngCandidates[idx];
}
meanImproves = (fabs(wrap2pi(mAng - mAngPrev)) >= tol);
delta /= 2.0;
range[0] = wrap2pi(mAng - delta / 2);
range[1] = wrap2pi(mAng + delta / 2);
}
return mAng;
}
// Constructor sets the limit points, size and pointer to the
// environment. Checks validity of the arguments and that there are
// no more than one environment object
Environment::Environment(const Position2d & min, const Position2d & max,
bool isEuclidean)
{
assert(m_envp == NULL);
assert(min.size() == max.size());
for (unsigned int ii = 0; ii < min.size(); ii++)
assert(min[ii] < max[ii]);
m_min = min;
m_max = max;
m_size = max - min;
m_isEuclidean = isEuclidean;
m_envp = this;
}
bool
Environment::isFree(const Position2d & x, double r,
const SwarmPtr swarm) const
{
// If no swarm is passed as an argument in only checks that the
// point is within the limits of the environment
if (swarm == nullptr) {
if ((x[0] > m_min[0]) && (x[0] <= m_max[0]) &&
(x[1] > m_min[1]) && (x[1] <= m_max[1]))
return true;
return false;
}
else
for (std::vector<RobotPtr>::const_iterator iit = swarm->begin();
iit != swarm->end(); ++iit)
{
double lim(r + (*iit)->settings().radius);
if (dist(x, (*iit)->position()) < lim)
return false;
}
return true;
}
Position2d
Environment::random() const
{
return Position2d(0.5 * (m_min + m_max) +
Position2d::Random().cwiseProduct(0.5 * m_size));
}
Position2d
Environment::random(float r, const SwarmPtr swarm, int & trials) const
{
Position2d p;
bool isPFree(true);
do {
p = random();
isPFree = isFree(p, r, swarm);
} while ((trials-- > 0) &&(!isPFree));
return p;
}
float
Environment::dist(const Position2d & p0, const Position2d & p1) const
{
Position2d diff(p0 - p1);
for (unsigned int ii = 0; ii < diff.size(); ii++)
diff[ii] = wrapVar(diff[ii], m_size[ii]);
return diff.norm();
}
float
Environment::ang(const Position2d & p1, const Position2d & p0) const
{
Position2d diff(p0 - p1);
for (unsigned int ii = 0; ii < diff.size(); ii++)
diff[ii] = wrapVar(diff[ii], m_size[ii]);
return atan2(diff[1], diff[0]);;
}
Position2d
Environment::wrap(const Position2d & p) const
{
Position2d wp(p);
for (unsigned int ii = 0; ii < p.size(); ii++)
wp[ii] = wrapVar(p[ii], m_size[ii]);
return wp;
}
std::ostream & operator<< (std::ostream & ofd, const mrs::Environment & env)
{
ofd << "Environment: " << "minP: " << env.min().transpose() << std::endl;
ofd << "Environment: " << "maxP: " << env.max().transpose() << std::endl;
return ofd;
}
}