Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
mpc.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of a Model Predictive Controler for an underwater robot
5  * \author Corentin Chauvin-Hameau
6  * \date 2020
7  */
8 
9 #include "mpc_node.hpp"
10 #include "mf_common/common.hpp"
11 #include "osqp.h"
12 #include "osqp_eigen/SparseMatrixHelper.hpp"
13 #include <nav_msgs/Path.h>
14 #include <geometry_msgs/Pose.h>
15 #include <geometry_msgs/PoseArray.h>
16 #include <geometry_msgs/Quaternion.h>
17 #include <geometry_msgs/Point.h>
18 #include <eigen3/Eigen/Dense>
19 #include <eigen3/Eigen/Sparse>
20 #include <vector>
21 
22 using namespace std;
23 using Eigen::VectorXf;
24 using Eigen::VectorXd;
25 using Eigen::MatrixXf;
26 using Eigen::MatrixXd;
27 
28 
29 namespace mfcpp {
30 
31 std::vector<geometry_msgs::Pose> MPCNode::adapt_path(
32  const std::vector<geometry_msgs::Pose> &orig_path,
33  const geometry_msgs::Point &current_position,
34  int nbr_steps,
35  float &spatial_resolution,
36  float &time_resolution,
37  float &desired_speed)
38 {
39  // Find the current position on the path
40  int size_path = orig_path.size();
41  float min_dist = 10000;
42  int idx_path = -1;
43 
44  for (int k = 0; k < size_path; k++) {
45  float d = distance2(current_position, orig_path[k].position);
46 
47  if (d < min_dist) {
48  min_dist = d;
49  idx_path = k;
50  }
51  }
52 
53  // Handle case when the robot is already at the end of the path
54  if (idx_path >= size_path - 1) {
55  return vector<geometry_msgs::Pose>(nbr_steps+1, orig_path[size_path-1]);
56  }
57 
58  // Compute length of the path
59  float path_length = 0;
60 
61  for (int k = idx_path; k < size_path-1; k++) {
62  path_length += distance(orig_path[k], orig_path[k+1]);
63  }
64 
65  // Update resolutions if path is too short
66  if (spatial_resolution*nbr_steps > path_length) {
67  float factor = path_length / (nbr_steps * spatial_resolution);
68  spatial_resolution *= factor;
69  time_resolution *= factor;
70  desired_speed *= factor;
71  }
72 
73  // Interpolate the path to have the right spatial resolution
74  vector<geometry_msgs::Pose> new_path(nbr_steps + 1);
75  new_path[0] = orig_path[idx_path];
76 
77  for (int k = 1; k <= nbr_steps; k++) {
78  float s = 0; // curvilinear abscissa
79  float ds = distance(new_path[k-1], orig_path[idx_path + 1]); // distance to the next waypoint in the path
80 
81  while (s + ds < spatial_resolution && idx_path+2 < size_path) {
82  idx_path++;
83  s += ds;
84  ds = distance(orig_path[idx_path], orig_path[idx_path+1]);
85  }
86 
87  if (s + ds < spatial_resolution) {
88  // Last pose of the path
89  if (k != nbr_steps) {
90  ROS_WARN("[mpc_node] Unexpected path length");
91  }
92 
93  new_path[k] = orig_path[size_path-1];
94  } else {
95  float t = (spatial_resolution-s) / ds;
96  new_path[k] = interpolate(orig_path[idx_path], orig_path[idx_path+1], t);
97  }
98  }
99 
100  return new_path;
101 }
102 
103 
104 template <class VectorT>
105 bool MPCNode::fill_ref_pts(
106  int N, int n, int m,
107  const std::vector<geometry_msgs::Pose> &path,
108  float desired_speed,
109  float last_desired_speed,
110  const RobotModel &robot_model,
111  VectorT &X_ref,
112  VectorT &U_ref)
113 {
114  if (N < 1 || n < 1 || m < 1 || path.size() != N+1) {
115  ROS_WARN("[mpc_node] Wrong input dimensions in fill_ref_pts:");
116  ROS_WARN("[mpc_node] path size: %d ; N+1=%d", (int) path.size(), N+1);
117  return false;
118  }
119 
120  X_ref = VectorT::Zero(n * (N+1));
121  U_ref = VectorT::Zero(m * (N+1));
122 
123  for (int k = 0; k <= N; k++) {
124  double roll, pitch, yaw;
125  to_euler(path[k].orientation, roll, pitch, yaw);
126 
127  X_ref(k*n) = path[k].position.x;
128  X_ref(k*n + 1) = path[k].position.y;
129  X_ref(k*n + 2) = path[k].position.z;
130  X_ref(k*n + 3) = roll;
131  X_ref(k*n + 4) = pitch;
132  X_ref(k*n + 5) = yaw;
133  X_ref(k*n + 6) = desired_speed;
134  }
135 
136  U_ref(0) = robot_model.steady_propeller_speed(last_desired_speed);
137  float desired_prop_speed = robot_model.steady_propeller_speed(desired_speed);
138 
139  for (int k = 0; k <= N; k++) {
140  U_ref(k*m) = desired_prop_speed;
141  }
142 
143  return true;
144 }
145 
146 
147 template <class VectorT>
148 void MPCNode::modulo_ref_state(VectorT &X_ref, const VectorT &x0, int n)
149 {
150  int N = X_ref.rows() / n;
151 
152  // For each three angles
153  for (int k = 0; k < 3; k++) {
154  // Handle initial error
155  while (abs(X_ref(3 + k) - x0(3 + k)) >= M_PI) {
156  if (X_ref(3 + k) - x0(3 + k) >= M_PI)
157  X_ref(3 + k) -= 2*M_PI;
158  else
159  X_ref(3 + k) += 2*M_PI;
160  }
161 
162  // Next reference points
163  for (int l = 1; l < N; l++) {
164  while (abs(X_ref(l*n + 3 + k) - X_ref((l-1)*n + 3 + k)) >= M_PI) {
165  if (X_ref(l*n + 3 + k) - X_ref((l-1)*n + 3 + k) >= M_PI)
166  X_ref(l*n + 3 + k) -= 2*M_PI;
167  else
168  X_ref(l*n + 3 + k) += 2*M_PI;
169  }
170  }
171  }
172 }
173 
174 
175 template <class VectorT, class MatrixT>
176 void MPCNode::fill_ltv_G_H_D(const VectorT &X_ref, const VectorT &U_ref,
177  int N, float dt, float ds,
178  MatrixT &G, MatrixT &H, VectorT &D)
179 {
180  int n = X_ref.rows() / (N+1);
181  int m = U_ref.rows() / (N+1);
182  G = MatrixT::Zero(n*N, m*N);
183  H = MatrixT::Zero(n*N, n);
184  D = VectorT::Zero(n*N);
185 
186  // Linearise the system at each reference point
187  MatrixT Ad_0; // Ad[0] matrix (linearised at first reference point)
188  vector<MatrixT> Ad(N); // Ad matrices
189  vector<MatrixT> Bd(N); // Bd matrices
190 
191  for (int k = 0; k < N; k++) {
192  MatrixT _Ad, _Bd;
193  VectorT x_ref = X_ref.block(k*n, 0, n, 1);
194  VectorT u_ref = U_ref.block(k*m, 0, m, 1);
195  robot_model_.get_lin_discr_matrices(x_ref, u_ref, _Ad, _Bd, dt);
196 
197  Ad[k] = _Ad;
198  Bd[k] = _Bd;
199  }
200 
201  // Build G (column by column)
202  for (int j = 0; j < N; j++) {
203  G.block(0, j*m, n, m) = Bd[j];
204  MatrixT prod = Bd[j]; // product of Ad and Bd: prod = Ad(i)...Ad(j+1)Bd(j)
205 
206  for (int i = j+1; i < N; i++) {
207  prod = Ad[i] * prod;
208  G.block(i*n, j*m, n, m) = prod;
209  }
210  }
211 
212  // Build H
213  H.block(0, 0, n, n) = Ad[0];
214  MatrixT prod = Ad[0];
215 
216  for (int i = 1; i < N; i++) {
217  prod = Ad[i] * prod;
218  H.block(i*n, 0, n, n) = prod;
219  }
220 
221  // Build D
222  VectorT delta; // k-th reference error
223 
224  for (int k = 0; k < N; k++) {
225  // Evaluate the ODE at the k-th reference point
226  RobotModel::state_type x_k_ref(n);
227  RobotModel::input_type u_k_ref(m);
228  RobotModel::state_type _f_k(n);
229 
230  for (int l = 0; l < n; l++)
231  x_k_ref[l] = X_ref(k*n + l);
232  for (int l = 0; l < m; l++)
233  u_k_ref[l] = U_ref(k*m + l);
234 
235  robot_model_.eval_ode(x_k_ref, u_k_ref, _f_k, 0.0);
236 
237  VectorT f_k(n);
238  for (int l = 0; l < n; l++)
239  f_k(l) = _f_k[l];
240 
241  // Compute k-th reference error
242  delta = X_ref.block(k*n, 0, n, 1) + dt*f_k - X_ref.block((k+1)*n, 0, n, 1);
243 
244  // Fill the k-th row of D
245  if (k == 0)
246  D.block(k*n, 0, n, 1) = delta;
247  else
248  D.block(k*n, 0, n, 1) = Ad[k]*D.block((k-1)*n, 0, n, 1) + delta;
249  }
250 }
251 
252 
253 template <class MatrixT>
254 void MPCNode::fill_lti_G(const MatrixT &Ad, const MatrixT &Bd, int N, MatrixT &G)
255 {
256  int n = Bd.rows();
257  int m = Bd.cols();
258  G = MatrixT::Zero(n*N, m*N);
259  MatrixT M = Bd; // Ad^k * Bd
260 
261  for (int k = 0; k < N; k++) {
262  if (k != 0)
263  M = Ad * M;
264 
265  for (int l = 0; l < N-k; l++) {
266  G.block((k+l)*n, l*m, n, m) = M;
267  }
268  }
269 }
270 
271 
272 template <class MatrixT>
273 void MPCNode::fill_lti_H(const MatrixT &Ad, int N, MatrixT &H)
274 {
275  int n = Ad.rows();
276  H = MatrixT(n*N, n);
277  MatrixT M = MatrixT::Identity(n, n);
278 
279  for (int k = 0; k < N; k++) {
280  M = M * Ad;
281  H.block(k*n, 0, n, n) = M;
282  }
283 }
284 
285 
286 template <class MatrixT>
287 void MPCNode::fill_L(const MatrixT &P, const MatrixT &Q_x, int N, MatrixT &L)
288 {
289  int n = P.rows();
290  L = MatrixT::Zero(n*N, n*N);
291 
292  for (int k = 0; k < N-1; k++) {
293  L.block(k*n, k*n, n, n) = Q_x;
294  }
295  L.block((N-1)*n, (N-1)*n, n, n) = P;
296 }
297 
298 
299 template <class MatrixT>
300 void MPCNode::fill_M(const MatrixT &R_u, const MatrixT &R_delta, int N, MatrixT &M)
301 {
302  int m = R_u.rows();
303  M = MatrixT::Zero(m*N, m*N);
304 
305  // Diagonal blocks
306  MatrixT A = 2*R_delta + R_u;
307 
308  for (int k = 0; k < N; k++) {
309  if (k < N-1)
310  M.block(k*m, k*m, m, m) = A;
311  else
312  M.block(k*m, k*m, m, m) = R_delta + R_u;
313  }
314 
315  // Over-diagonal blocks
316  MatrixT B = -R_delta;
317 
318  for (int k = 0; k < N-1; k++) {
319  M.block(k*m, (k+1)*m, m, m) = B;
320  M.block((k+1)*m, k*m, m, m) = B;
321  }
322 }
323 
324 
325 template <class VectorT, class MatrixT>
326 void MPCNode::fill_V(VectorT control_ref, VectorT last_control,
327  const MatrixT &R_delta, int N, VectorT &V)
328 {
329  int m = R_delta.rows();
330  V = VectorT::Zero(m*N);
331  V.block(0, 0, m, 1) = 2*R_delta*(control_ref-last_control);
332 }
333 
334 
335 template <class MatrixT>
336 void MPCNode::fill_lti_LG(const MatrixT &G, const MatrixT &P, const MatrixT &Q_x,
337  int n, int m, int N, MatrixT &LG)
338 {
339  LG = MatrixXf::Zero(n*N, m*N);
340 
341  for (int k = 0; k < N-1; k++) {
342  MatrixXf M = Q_x * G.block(k*n, 0, n, m);
343 
344  for (int l = 0; l < N-k-1; l++) {
345  LG.block((k+l)*n, l*m, n, m) = M;
346  }
347  }
348  for (int l = 0; l < N; l++) {
349  LG.block((N-1)*n, l*m, n, m) = P * G.block((N-1)*n, l*m, n, m);
350  }
351 }
352 
353 
354 template <class VectorT, class MatrixT>
355 void MPCNode::fill_bounds_objs(
356  const MPCBounds &bounds,
357  int n, int N,
358  const VectorT &X0, const VectorT &X_ref, const VectorT &U_ref,
359  const MatrixT &G, const MatrixT &H, const VectorT &D,
360  VectorT &lb, VectorT &ub, MatrixT &Ab)
361 {
362  // Bounds on the state
363  VectorT a = VectorT::Constant(N, -bounds.delta_m);
364  VectorT b = VectorT::Constant(N, bounds.delta_m);
365  MatrixT Kb = MatrixT::Zero(N, n*N);
366 
367  for (int k = 0; k < N; k++) {
368  Kb(k, n-1 + k*n) = 1;
369  }
370 
371  // Bounds on the input
372  int m = bounds.input.size();
373  VectorT c(m), d(m);
374 
375  for (int k = 0; k < m; k++) {
376  c(k) = -bounds.input[k];
377  d(k) = bounds.input[k];
378  }
379  c(0) = 0; // the submarine can't go backwards
380 
381  // Total bounds
382  lb = VectorT::Zero((m+1)*N);
383  ub = VectorT::Zero((m+1)*N);
384  Ab = MatrixT::Zero((m+1)*N, m*N);
385 
386  VectorXf HX0 = H*X0;
387  VectorXf delta(N); // Kb*H*X0 + Kb*X_ref + K_b*D
388 
389  for (int k = 0; k < N; k++) {
390  delta(k) = HX0((k+1)*n - 1) + X_ref((k+2)*n - 1) + D((k+1)*n - 1);
391  }
392 
393  lb.block(0, 0, N, 1) = a - delta;
394  ub.block(0, 0, N, 1) = b - delta;
395 
396  for (int k = 0; k < N; k++) {
397  lb.block(N + k*m, 0, m, 1) = c - U_ref.block((k+1)*m, 0, m, 1);
398  ub.block(N + k*m, 0, m, 1) = d - U_ref.block((k+1)*m, 0, m, 1);
399  }
400 
401  MatrixT KbG(N, m*N);
402  for (int i = 0; i < N; i++) {
403  for (int j = 0; j < m*N; j++) {
404  KbG(i, j) = G((i+1)*n - 1, j);
405  }
406  }
407  Ab.block(0, 0, N, m*N) = KbG;
408  Ab.block(N, 0, m*N, m*N) = MatrixT::Identity(m*N, m*N);
409 }
410 
411 
412 template <class VectorT, class T>
413 bool MPCNode::solve_qp(
414  const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &P,
415  const VectorT &q,
416  const VectorT &lb,
417  const VectorT &ub,
418  const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &Ab,
419  VectorT &solution)
420 {
421  typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> MatrixT;
422  int n = q.rows();
423  int m = ub.rows();
424 
425  // Workspace structures
426  OSQPWorkspace *work;
427  OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
428  OSQPData *data = (OSQPData *)c_malloc(sizeof(OSQPData));
429 
430  // Populate data
431  if (data) {
432  MatrixT P_tri = P.template triangularView<Eigen::Upper>(); // upper part of P
433  Eigen::SparseMatrix<T> _P = P_tri.sparseView();
434  Eigen::SparseMatrix<T> _A = Ab.sparseView();
435 
436  data->P = nullptr;
437  data->A = nullptr;
438 
439  data->n = n;
440  data->m = m;
441  OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(_P, data->P);
442  data->q = const_cast<T*>(q.data());
443  OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(_A, data->A);
444  data->l = const_cast<T*>(lb.data());
445  data->u = const_cast<T*>(ub.data());
446  }
447 
448  // Define solver settings as default
449  if (settings) {
450  osqp_set_default_settings(settings);
451  settings->alpha = 1.0; // Change alpha parameter
452  settings->verbose = false;
453  }
454 
455  // Setup workspace
456  c_int setup_flag = osqp_setup(&work, data, settings);
457 
458  if (setup_flag != 0) {
459  ROS_WARN("[mpc_node] Setup of MPC problem failed");
460  string flag_string = "";
461 
462  switch (setup_flag) {
463  case OSQP_DATA_VALIDATION_ERROR: flag_string = "OSQP_DATA_VALIDATION_ERROR"; break;
464  case OSQP_SETTINGS_VALIDATION_ERROR: flag_string = "OSQP_SETTINGS_VALIDATION_ERROR"; break;
465  case OSQP_LINSYS_SOLVER_LOAD_ERROR: flag_string = "OSQP_LINSYS_SOLVER_LOAD_ERROR"; break;
466  case OSQP_LINSYS_SOLVER_INIT_ERROR: flag_string = "OSQP_LINSYS_SOLVER_INIT_ERROR"; break;
467  case OSQP_NONCVX_ERROR: flag_string = "OSQP_NONCVX_ERROR"; break;
468  case OSQP_MEM_ALLOC_ERROR: flag_string = "OSQP_MEM_ALLOC_ERROR"; break;
469  case OSQP_WORKSPACE_NOT_INIT_ERROR: flag_string = "OSQP_WORKSPACE_NOT_INIT_ERROR"; break;
470  default: break;
471  }
472  ROS_WARN_STREAM("[mpc_node] " << flag_string);
473  qp_warm_start_ = false;
474  return false;
475  }
476 
477  // Warm start (FIXME)
478  qp_warm_start_ = false; // FIXME: warm start leads to big issues
479  if (qp_warm_start_)
480  osqp_warm_start(work, qp_last_primal_.data(), qp_last_dual_.data());
481 
482  // Solve Problem
483  c_int solve_ret = 0; // return code of the solving operation (0: no error)
484  solve_ret = osqp_solve(work);
485 
486  if (solve_ret != 0 || work->info->status_val != OSQP_SOLVED) {
487  ROS_WARN("[mpc_node] Couldn't solve MPC problem");
488  string flag_string = "";
489 
490  switch (work->info->status_val) {
491  case OSQP_DUAL_INFEASIBLE_INACCURATE: flag_string = "OSQP_DUAL_INFEASIBLE_INACCURATE"; break;
492  case OSQP_PRIMAL_INFEASIBLE_INACCURATE: flag_string = "OSQP_PRIMAL_INFEASIBLE_INACCURATE"; break;
493  case OSQP_SOLVED_INACCURATE: flag_string = "OSQP_SOLVED_INACCURATE"; break;
494  case OSQP_SOLVED: flag_string = "OSQP_SOLVED"; break;
495  case OSQP_MAX_ITER_REACHED: flag_string = "OSQP_MAX_ITER_REACHED"; break;
496  case OSQP_PRIMAL_INFEASIBLE: flag_string = "OSQP_PRIMAL_INFEASIBLE"; break;
497  case OSQP_DUAL_INFEASIBLE: flag_string = "OSQP_DUAL_INFEASIBLE"; break;
498  case OSQP_SIGINT: flag_string = "OSQP_SIGINT"; break;
499  case OSQP_NON_CVX: flag_string = "OSQP_NON_CVX"; break;
500  case OSQP_UNSOLVED: flag_string = "OSQP_UNSOLVED"; break;
501  default: break;
502  }
503  ROS_WARN_STREAM("[mpc_node] " << flag_string);
504  qp_warm_start_ = false;
505  return false;
506  }
507 
508  // Parse solution
509  solution = VectorT(n);
510  qp_last_primal_ = VectorT(n);
511  qp_last_dual_ = VectorT(n);
512 
513  for (int k = 0; k < n; k++) {
514  solution(k) = work->solution->x[k];
515  qp_last_primal_(k) = work->solution->x[k];
516  qp_last_dual_(k) = work->solution->y[k];
517  }
518 
519  qp_warm_start_ = true;
520 
521  // Cleanup
522  if (data) {
523  if (data->A) c_free(data->A);
524  if (data->P) c_free(data->P);
525  c_free(data);
526  }
527  if (settings) c_free(settings);
528 }
529 
530 
531 bool MPCNode::compute_control(
532  float &desired_speed,
533  float last_desired_speed,
534  std::vector<float> &command,
535  geometry_msgs::PoseArray &expected_traj)
536 {
537  if (path_.poses.size() < 2) {
538  ROS_WARN("[mpc_node] Path size < 2");
539  return false;
540  }
541 
542  // Parse current state
543  geometry_msgs::Point current_position;
544  current_position.x = state_[0];
545  current_position.y = state_[1];
546  current_position.z = state_[2];
547 
548  int n = state_.size();
549  int m = last_control_.size();
550 
551  // Store the original path for convenience
552  int size_path = path_.poses.size();
553  vector<geometry_msgs::Pose> orig_path(size_path);
554 
555  for (int k = 0; k < size_path; k++) {
556  orig_path[k] = path_.poses[k].pose;
557  }
558 
559  // Get path with suitable length and resolution
560  float spatial_resolution = desired_speed * time_horizon_ / nbr_steps_; // spatial resolution
561  float time_resolution = time_horizon_ / nbr_steps_;
562 
563  vector<geometry_msgs::Pose> new_path = adapt_path(orig_path, current_position,
564  nbr_steps_, spatial_resolution, time_resolution, desired_speed);
565 
566  // Fill intial points
567  VectorXf x0(n), X0(n); // initial state, and initial offset to the reference
568  VectorXf u_m1(m); // last control applied to the robot
569 
570  for (int k = 0; k < n; k++) x0(k) = state_[k];
571  for (int k = 0; k < m; k++) u_m1(k) = last_control_[k];
572 
573  // Fill reference points for MPC optimisation
574  int N = nbr_steps_;
575  VectorXf X_ref, U_ref;
576 
577  if (!fill_ref_pts(N, n, m, new_path, desired_speed, last_desired_speed, robot_model_,
578  X_ref, U_ref))
579  {
580  ROS_WARN("[mpc_node] Reference trajectory could not be filled");
581  return false;
582  }
583 
584  modulo_ref_state(X_ref, x0, n); // change reference orientation to prevent modulo discontinuity
585 
586  X0 = x0 - X_ref.block(0, 0, n, 1);
587  VectorXf x0_ref = X_ref.block(0, 0, n, 1); // first state reference
588  VectorXf u0_ref = U_ref.block(0, 0, m, 1); // first control reference
589 
590  // Build MPC objects
591  MatrixXf G, H; // to express X with respect to U and X0
592  VectorXf D; // idem
593  MatrixXf L, M; // to compute quadratic parts of the cost function
594  VectorXf V; // to compute linear parts of the cost function
595  MatrixXf LG; // product of L and G
596 
597  fill_L(tuning_params_.P, tuning_params_.Q_x, N, L);
598  fill_M(tuning_params_.R_u, tuning_params_.R_delta, N, M);
599  fill_V(u0_ref, u_m1, tuning_params_.R_delta, N, V);
600 
601  if (ltv_mpc_) {
602  // LTV MPC: linearise at each reference point
603  fill_ltv_G_H_D(X_ref, U_ref, N, time_resolution, spatial_resolution, G, H, D);
604  LG = L * G;
605  } else {
606  // LTI MPC: only linearise at first reference point
607  MatrixXf Ad, Bd; // discretised model
608  robot_model_.get_lin_discr_matrices(x0_ref, u0_ref, Ad, Bd, time_resolution);
609 
610  fill_lti_G(Ad, Bd, N, G);
611  fill_lti_H(Ad, N, H);
612  fill_lti_LG(G, tuning_params_.P, tuning_params_.Q_x, n, m, N, LG);
613  D = VectorXf::Zero(n*N);
614  }
615 
616  // Build MPC bounds objects
617  VectorXf lb, ub; // lower and upper bounds
618  MatrixXf Ab; // multiplicative factor in front of U in the bound inequalities
619 
620  fill_bounds_objs(bounds_, n, N, X0, X_ref, U_ref, G, H, D, lb, ub, Ab);
621 
622  // Solve MPC Quadratic Program
623  MatrixXf P = 2*(G.transpose()*LG + M);
624  VectorXf q = 2*LG.transpose()*(H*X0 + D) + V;
625  VectorXf solution;
626 
627  bool solved = solve_qp(P, q, lb, ub, Ab, solution);
628 
629  if (!solved)
630  return false;
631 
632  // Prepare command output
633  command.resize(m);
634  for (int k = 0; k < m; k++)
635  command[k] = solution(k) + U_ref(k);
636 
637  // Compute expected controlled trajectory
638  VectorXf X = G*solution + H*X0 + D + X_ref.block(n, 0, n*N, 1);
639  expected_traj.poses.resize(N);
640 
641  for (int k = 0; k < N; k++) {
642  geometry_msgs::Pose pose;
643  pose.position.x = X(k*n);
644  pose.position.y = X(k*n + 1);
645  pose.position.z = X(k*n + 2);
646  to_quaternion(X(k*n + 3), X(k*n + 4), X(k*n + 5), pose.orientation);
647 
648  expected_traj.poses[k] = pose;
649  }
650 
651 
652  return true;
653 }
654 
655 } // namespace mfcpp
Declaration of a node for Model Predictive Control of an underwater robot.
Definition: common.hpp:23
void to_quaternion(double roll, double pitch, double yaw, tf2::Quaternion &quat)
Converts Roll-Pitch-Yaw Euler angles to a quaternion.
Definition: common.hpp:207
geometry_msgs::Pose interpolate(const geometry_msgs::Pose &pose1, const geometry_msgs::Pose &pose2, float t)
Interpolates linearily two poses.
Definition: common.hpp:93
std::vector< double > state_type
Data type of the state.
Definition: robot_model.hpp:51
Declaration of common functions.
std::vector< double > input
Definition: mpc_node.hpp:61
float distance(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Computes the the euclidean distance between two positions.
Definition: common.hpp:69
Bounds on the MPC problem.
Definition: mpc_node.hpp:58
Class defining the robot model.
Definition: robot_model.hpp:47
std::vector< double > input_type
Data type of the input.
Definition: robot_model.hpp:54
float distance2(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Computes the square of the euclidean distance between two positions.
Definition: common.hpp:61
double steady_propeller_speed(double speed) const
Computes the propeller speed in steady state.
void to_euler(const tf2::Quaternion &quat, double &roll, double &pitch, double &yaw)
Converts a quaternion to Roll-Pitch-Yaw Euler angles.
Definition: common.hpp:180