Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
gp.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of Gaussian Process functions
5  * \author Corentin Chauvin-Hameau
6  * \date 2019
7  */
8 
9 #include "gp_nodelet.hpp"
10 #include <iostream>
11 #include <cmath>
12 
13 using namespace std;
14 using Eigen::VectorXf;
15 using Eigen::MatrixXf;
16 
17 
18 namespace mfcpp {
19 
20 
21 void GPNodelet::init_gp()
22 {
23  // Initialise mean and covariance of the GP
24  gp_mean_.resize(size_gp_, 1);
25  gp_cov_ = MatrixXf::Zero(size_gp_, size_gp_);
26 
27  for (unsigned int k = 0; k < size_gp_; k++) {
28  gp_mean_(k) = gp_init_mean_;
29  gp_cov_(k, k) = 1/gp_noise_var_;
30  }
31 
32  // Fill the wall coordinates
33  double x = 0, y = 0;
34  x_coord_.resize(size_gp_);
35  y_coord_.resize(size_gp_);
36 
37  for (int k = 0; k < size_gp_; k++) {
38  x_coord_(k) = x;
39  y_coord_(k) = y;
40  y += delta_y_;
41 
42  if (y > size_wall_y_ + 0.01) {
43  y = 0;
44  x += delta_x_;
45  }
46  }
47 
48  // Initialise C matrix and its inverse
49  gp_C_.resize(size_gp_, size_gp_);
50  double inv_var = 1/gp_noise_var_;
51 
52  for (int k = 0; k < size_gp_; k++) {
53  for (int l = 0; l <= k; l++) {
54  gp_C_(k, l) = matern_kernel(x_coord_(k), y_coord_(k), x_coord_(l), y_coord_(l));
55  gp_C_(l, k) = gp_C_(k, l);
56  }
57 
58  gp_C_(k, k) += inv_var;
59  }
60 
61  gp_C_inv_ = gp_C_.inverse();
62 }
63 
64 
65 void GPNodelet::pop_reordered_idx(
66  unsigned int size_obs, unsigned int size_nobs,
67  unsigned int min_x, unsigned int max_x, unsigned int min_y, unsigned int max_y,
68  vector<unsigned int> &idx_obs , vector<unsigned int> &idx_nobs) const
69 {
70  idx_obs.resize(size_obs);
71  idx_nobs.resize(size_nobs);
72 
73  // Populate observed states indices
74  unsigned int k = 0;
75 
76  for (unsigned int i = min_x; i <= max_x; i++) {
77  for (unsigned int j = min_y; j <= max_y; j++) {
78  idx_obs[k] = i*size_gp_y_ + j;
79  k++;
80  }
81  }
82 
83  // Populate not observed states indices
84  k = 0; // will go through idx_obs
85  unsigned int l = 0; // will go through idx_nobs
86  unsigned int m = 0; // will go through the original state
87 
88  while (m < size_gp_) {
89  if (k < size_obs) {
90  if (idx_obs[k] > m) {
91  idx_nobs[l] = m;
92  l++;
93  } else {
94  k++;
95  }
96  }
97  else {
98  idx_nobs[l] = m;
99  l++;
100  }
101 
102  m++;
103  }
104 }
105 
106 
107 void GPNodelet::notif_changing_pxls(const RectArea &coord)
108 {
109  unsigned int k = 0;
110  float delta_x = size_wall_x_ / (size_img_x_-1);
111  float delta_y = size_wall_y_ / (size_img_y_-1);
112  float x = 0, y = 0;
113 
114  for (unsigned int i = 0; i < size_img_x_; i++) {
115  for (unsigned int j = 0; j < size_img_y_; j++) {
116  if (x >= coord.min_x && x <= coord.max_x && y >= coord.min_y
117  && y <= coord.max_y) {
118  changed_pxl_[k] = true;
119  }
120 
121  y += delta_y;
122  k++;
123  }
124 
125  x += delta_x;
126  y = 0;
127  }
128 }
129 
130 
131 void GPNodelet::build_Kalman_objects(
132  const vector<unsigned int> &idx_obs,
133  const vector<unsigned int> &idx_nobs,
134  VectorXf &mu, VectorXf &mu_obs,
135  MatrixXf &P, MatrixXf &P_obs, MatrixXf &B,
136  MatrixXf &C_obs, MatrixXf &C_obs_inv,
137  VectorXf &x_coord, VectorXf &y_coord) const
138 {
139  unsigned int size_obs = idx_obs.size();
140  unsigned int size_nobs = idx_nobs.size();
141 
142  for (unsigned int k = 0; k < size_obs; k++) {
143  mu_obs(k) = gp_mean_(idx_obs[k]);
144 
145  for (unsigned int l = 0; l <= k; l++) {
146  P_obs(k, l) = gp_cov_(idx_obs[k], idx_obs[l]);
147  P_obs(l, k) = P_obs(k, l);
148 
149  C_obs(k, l) = gp_C_(idx_obs[k], idx_obs[l]);
150  C_obs(l, k) = C_obs(k, l);
151  }
152 
153  for (unsigned int l = 0; l < size_nobs; l++) {
154  B(k, l) = gp_cov_(idx_obs[k], idx_nobs[l]);
155  }
156 
157  x_coord(k) = x_coord_(idx_obs[k]);
158  y_coord(k) = y_coord_(idx_obs[k]);
159  }
160 
161  for (unsigned int k = 0; k < size_gp_; k++) {
162  unsigned int corr_k;
163 
164  if (k < size_obs)
165  corr_k = idx_obs[k];
166  else
167  corr_k = idx_nobs[k - size_obs];
168 
169  mu(k) = gp_mean_(corr_k);
170 
171  for (unsigned int l = 0; l <= k; l++) {
172  unsigned int corr_l;
173 
174  if (l < size_obs)
175  corr_l = idx_obs[l];
176  else
177  corr_l = idx_nobs[l - size_obs];
178 
179  P(k, l) = gp_cov_(corr_k, corr_l);
180  P(l, k) = P(k, l);
181  }
182  }
183 
184  C_obs_inv = C_obs.inverse();
185 }
186 
187 
188 void GPNodelet::update_reordered_gp(
189  const vector<unsigned int> &idx_obs,
190  const vector<unsigned int> &idx_nobs,
191  const VectorXf &mu, const MatrixXf &P,
192  Eigen::VectorXf &gp_mean, Eigen::MatrixXf &gp_cov) const
193 {
194  unsigned int size_obs = idx_obs.size();
195  unsigned int size_nobs = idx_nobs.size();
196 
197  for (unsigned int k = 0; k < size_gp_; k++) {
198  unsigned int corr_k;
199 
200  if (k < size_obs)
201  corr_k = idx_obs[k];
202  else
203  corr_k = idx_nobs[k - size_obs];
204 
205  gp_mean(corr_k) = mu(k);
206 
207  for (unsigned int l = 0; l <= k; l++) {
208  unsigned int corr_l;
209 
210  if (l < size_obs)
211  corr_l = idx_obs[l];
212  else
213  corr_l = idx_nobs[l - size_obs];
214 
215  if (corr_k == corr_l && P(k, l) > gp_cov_thresh_) {
216  gp_cov(corr_k, corr_l) = P(k, l);
217  gp_cov(corr_l, corr_k) = P(l, k);
218  }
219  else if (corr_k == corr_l) {
220  // Prevent the covariance from dropping too low
221  gp_cov(corr_k, corr_l) = gp_cov_thresh_;
222  gp_cov(corr_l, corr_k) = gp_cov_thresh_;
223  }
224  else {
225  // Threshold low off-diagonal values to optimise multiplication performances
226  gp_cov(corr_k, corr_l) = 0;
227  gp_cov(corr_l, corr_k) = 0;
228  }
229  }
230  }
231 }
232 
233 
234 void GPNodelet::update_gp(
235  const vec_f &x_meas, const vec_f &y_meas, const vec_f &z_meas,
236  const vec_f &distances, const vec_f &values,
237  Eigen::VectorXf &gp_mean, Eigen::MatrixXf &gp_cov,
238  vector<unsigned int> &idx_obs,
239  RectArea &obs_coord,
240  bool update_mean) const
241 {
242  // Select the observed part of the state wich is concerned by update
243  // Asumption: the state is not affected far from the measurements
244  if (x_meas.size() == 0)
245  return;
246 
247  float min_x = *std::min_element(x_meas.begin(), x_meas.end());
248  float min_y = *std::min_element(y_meas.begin(), y_meas.end());
249  float max_x = *std::max_element(x_meas.begin(), x_meas.end());
250  float max_y = *std::max_element(y_meas.begin(), y_meas.end());
251 
252  min_x = max(float(0), min_x - radius_obs_); // area on the wall affected by the update
253  max_x = min(float(size_wall_x_), max_x + radius_obs_);
254  min_y = max(float(0), min_y - radius_obs_);
255  max_y = min(float(size_wall_y_), max_y + radius_obs_);
256 
257  unsigned int min_obs_x = min_x / delta_x_; // indices in the original state
258  unsigned int max_obs_x = max_x / delta_x_;
259  unsigned int min_obs_y = min_y / delta_y_;
260  unsigned int max_obs_y = max_y / delta_y_;
261  unsigned int size_obs_x = max_obs_x - min_obs_x + 1; // sizes of the selected state
262  unsigned int size_obs_y = max_obs_y - min_obs_y + 1;
263  unsigned int size_obs = size_obs_x * size_obs_y;
264  unsigned int size_nobs = size_gp_ - size_obs;
265 
266  idx_obs.resize(size_obs, 0);
267  vector<unsigned int> idx_nobs(size_nobs, 0); // complementary to the previous one
268  pop_reordered_idx(size_obs, size_nobs, min_obs_x, max_obs_x, min_obs_y, max_obs_y,
269  idx_obs, idx_nobs);
270 
271  // Notify changing pixels
272  obs_coord.min_x = min_x;
273  obs_coord.max_x = max_x;
274  obs_coord.min_y = min_y;
275  obs_coord.max_y = max_y;
276 
277  // Build mathematical objects that are needed for Kalman update
278  VectorXf mu(size_gp_); // reordered state
279  VectorXf mu_obs(size_obs); // observed part of the state
280  MatrixXf P(size_gp_, size_gp_); // reordered covariance
281  MatrixXf P_obs(size_obs, size_obs); // observed part of the covariance
282  MatrixXf B(size_obs, size_nobs); // off diagonal block matrix in the covariance
283  MatrixXf C_obs(size_obs, size_obs);
284  MatrixXf C_obs_inv(size_obs, size_obs);
285  VectorXf x_coord(size_obs);
286  VectorXf y_coord(size_obs);
287 
288  build_Kalman_objects(idx_obs, idx_nobs, mu, mu_obs, P, P_obs, B, C_obs, C_obs_inv,
289  x_coord, y_coord);
290 
291  // Process input data
292  int size_meas = values.size();
293  VectorXf z(size_meas); // measurement vector
294  MatrixXf R = MatrixXf::Zero(size_meas, size_meas); // covariance on the measurents
295 
296  for (unsigned int k = 0; k < size_meas; k++) {
297  z(k) = values[k];
298  R(k, k) = camera_noise(distances[k]);
299  }
300 
301  // Compute innovation
302  MatrixXf k_meas(size_meas, size_obs);
303 
304  for (unsigned int i = 0; i < size_meas; i++) {
305  for (unsigned int j = 0; j < size_obs; j++) {
306  k_meas(i, j) = matern_kernel(
307  x_meas[i], y_meas[i], x_coord(j), y_coord(j)
308  );
309  }
310  }
311 
312  MatrixXf H_obs = k_meas * C_obs_inv;
313 
314  VectorXf v;
315  if (update_mean)
316  v = z - H_obs * mu_obs;
317 
318  // Compute Kalman gain
319  MatrixXf PHt(size_obs, size_meas);
320  MatrixXf BtHt(size_nobs, size_meas);
321  MatrixXf L(size_gp_, size_meas);
322 
323  PHt = P_obs * H_obs.transpose();
324  BtHt = B.transpose() * H_obs.transpose();
325  L << PHt,
326  BtHt;
327 
328  MatrixXf S = H_obs * PHt + R;
329  MatrixXf S_inv = S.inverse();
330  MatrixXf K = L * S_inv;
331 
332  // Update mean and covariance
333  if (update_mean)
334  mu += K * v;
335  P = P - L * S_inv * L.transpose();
336 
337  update_reordered_gp(idx_obs, idx_nobs, mu, P, gp_mean, gp_cov);
338 
339  // Storing correspondance indices for evaluation
340  min_x = max(float(0), min_x - radius_obs_); // area on the wall affected by the update
341  max_x = min(float(size_wall_x_), max_x + radius_obs_);
342  min_y = max(float(0), min_y - radius_obs_);
343  max_y = min(float(size_wall_y_), max_y + radius_obs_);
344 
345  min_obs_x = min_x / delta_x_; // indices in the original state
346  max_obs_x = max_x / delta_x_;
347  min_obs_y = min_y / delta_y_;
348  max_obs_y = max_y / delta_y_;
349 
350  size_obs = (max_obs_x - min_obs_x + 1) * (max_obs_y - min_obs_y + 1);
351  size_nobs = size_gp_ - size_obs;
352 
353  pop_reordered_idx(size_obs, size_nobs, min_obs_x, max_obs_x, min_obs_y, max_obs_y, idx_obs, idx_nobs);
354 
355 }
356 
357 
358 void GPNodelet::prepare_eval(
359  const std::vector<unsigned int> &idx_obs,
360  const Eigen::VectorXf &gp_mean,
361  Eigen::VectorXf &x_obs, Eigen::VectorXf &y_obs,
362  Eigen::VectorXf &W) const
363 {
364  unsigned int size_obs = idx_obs.size();
365  VectorXf mu_obs(size_obs); // observed part of the state
366  MatrixXf C_inv_obs(size_obs, size_obs); // observed part of the gp_C_inv_ matrix
367  x_obs = VectorXf(size_obs); // x coordinates of the observed state
368  y_obs = VectorXf(size_obs); // y coordinates of the observed state
369 
370  for (unsigned int k = 0; k < size_obs; k++) {
371  mu_obs(k) = gp_mean(idx_obs_[k]);
372  x_obs(k) = x_coord_(idx_obs_[k]);
373  y_obs(k) = y_coord_(idx_obs_[k]);
374 
375  for (unsigned int l = 0; l <= k; l++) {
376  C_inv_obs(k, l) = gp_C_inv_(idx_obs_[k], idx_obs_[l]);
377  C_inv_obs(l, k) = C_inv_obs(k, l);
378  }
379  }
380 
381  W = C_inv_obs * mu_obs;
382 }
383 
384 
385 float GPNodelet::eval_gp(
386  float x, float y,
387  const Eigen::VectorXf &x_obs, const Eigen::VectorXf &y_obs,
388  const Eigen::VectorXf &W) const
389 {
390  int size_obs = x_obs.size();
391  Eigen::VectorXf k_obs(size_obs);
392 
393  for (unsigned int l = 0; l < size_obs; l++) {
394  k_obs(l) = matern_kernel(
395  x, y, x_obs(l), y_obs(l)
396  );
397  }
398 
399  float gp_value = k_obs.dot(W);
400  return (1/(1 + exp(-out_scale_*(gp_value - 0.5))));
401 }
402 
403 
404 };
Definition: common.hpp:23
2D rectangular area
Definition: gp_nodelet.hpp:51
Declaration of a nodelet for Gaussian Process mapping of an algae wall.
std::vector< float > vec_f
Definition: gp_nodelet.hpp:59