Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
planning.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of path planning functions
5  * \author Corentin Chauvin-Hameau
6  * \date 2019
7  */
8 
9 #include "planning_nodelet.hpp"
10 #include "lattice.hpp"
11 #include "mf_common/common.hpp"
12 #include "mf_common/spline.hpp"
13 #include "mf_sensors_simulator/MultiPoses.h"
14 #include "mf_mapping/UpdateGP.h"
15 #include <visualization_msgs/Marker.h>
16 #include <nav_msgs/Path.h>
17 #include <geometry_msgs/Quaternion.h>
18 #include <geometry_msgs/Pose.h>
19 #include <geometry_msgs/PoseStamped.h>
20 #include <tf2/LinearMath/Scalar.h>
21 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
22 #include <eigen3/Eigen/Dense>
23 #include <cmath>
24 #include <vector>
25 #include <iostream>
26 
27 using namespace std;
28 using Eigen::Vector3f;
29 
30 
31 namespace mfcpp {
32 
33 vector<mf_common::Float32Array> PlanningNodelet::vector2D_to_array(
34  const vector<vector<float>> &vector2D)
35 {
36  int n = vector2D.size();
37  vector<mf_common::Float32Array> out(n);
38 
39  for (int k = 0; k < n; k++) {
40  out[k].data = vector2D[k];
41  }
42 
43  return out;
44 }
45 
46 
47 vector<vector<float>> PlanningNodelet::array_to_vector2D(
48  const vector<mf_common::Float32Array> &array)
49 {
50  int n = array.size();
51  vector<vector<float>> out(n);
52 
53  for (int k = 0; k < n; k++) {
54  out[k] = array[k].data;
55  }
56 
57  return out;
58 }
59 
60 
61 vector<vector<float>> PlanningNodelet::array_to_vector2D(
62  const mf_common::Array2D &array)
63 {
64  int n = array.data.size();
65  vector<vector<float>> out(n);
66 
67  for (int k = 0; k < n; k++) {
68  out[k] = array.data[k].data;
69  }
70 
71  return out;
72 }
73 
74 
75 Eigen::Vector3f PlanningNodelet::get_wall_orientation(float &yaw_wall)
76 {
77  double roll, pitch, yaw;
78  to_euler(ocean_robot_tf_.transform.rotation, roll, pitch, yaw);
79 
80  yaw_wall = wall_orientation_;
81 
82  while (fabs(yaw_wall - yaw) > M_PI_2) {
83  if (yaw_wall - yaw > M_PI_2)
84  yaw_wall -= M_PI;
85  else
86  yaw_wall += M_PI;
87  }
88 
89  return Eigen::Vector3f(cos(yaw_wall), sin(yaw_wall), 0.0);
90 }
91 
92 
93 Eigen::Vector3f PlanningNodelet::get_wall_orientation()
94 {
95  float foo;
96  return get_wall_orientation(foo);
97 }
98 
99 
100 void PlanningNodelet::generate_cart_lattice(float max_lat_angle, float max_elev_angle,
101  float horizon, float resolution, Lattice &lattice)
102 {
103  int n_x = horizon / resolution; // size of the lattice in x direction
104  int n_y = horizon * sin(max_lat_angle) / resolution; // half size in y direction
105  int n_z = horizon * sin(max_elev_angle) / resolution; // half size in z direction
106  unsigned int n_lattice = n_x * (2*n_y + 1) * (2*n_z + 1); // total size of the lattice
107 
108  lattice.nodes.clear();
109  lattice.nodes.reserve(n_lattice);
110  float x = 0;
111 
112  for (int i = 0; i < n_x; i++) {
113  x += resolution;
114  float y = -n_y * resolution;
115 
116  for (int j = -n_y; j <= n_y; j++) {
117  float z = -n_z * resolution;
118 
119  for (int k = -n_z; k <= n_z; k++) {
120  // Compute point angles with respect to the x axis
121  float lat_angle = atan2(y, x);
122  float elev_angle = atan2(z, x);
123 
124  if (abs(lat_angle) <= max_lat_angle && abs(elev_angle) <= max_elev_angle) {
125  geometry_msgs::Pose pose;
126  pose.position.x = x;
127  pose.position.y = y;
128  pose.position.z = z;
129 
130  // Account for rotation needed to reach the waypoint
131  tf2::Quaternion q_orig, q_rot, q_new;
132  q_orig.setRPY(0, 0, 0);
133  q_rot.setRPY(0.0, -elev_angle, lat_angle);
134 
135  q_new = q_rot * q_orig;
136  q_new.normalize();
137  tf2::convert(q_new, pose.orientation);
138 
139  lattice.nodes.emplace_back(new LatticeNode());
140  lattice.nodes.back()->pose = pose;
141  }
142 
143  z += resolution;
144  }
145 
146  y += resolution;
147  }
148  }
149 }
150 
151 
152 void PlanningNodelet::generate_lattice(Lattice &lattice)
153 {
154  float duration = plan_horizon_/plan_speed_;
155  int n_horiz = 2*lattice_size_horiz_ + 1;
156  int n_vert = 2*lattice_size_vert_ + 1;
157  lattice.nodes.clear();
158  lattice.nodes.resize(n_horiz*n_vert);
159  int counter = 0;
160 
161  for (int k = -lattice_size_horiz_; k <= lattice_size_horiz_; k++) {
162  for (int l = -lattice_size_vert_; l <= lattice_size_vert_; l++) {
163  // Apply a command during a fixed duration
164  float prop_speed = robot_model_.steady_propeller_speed(plan_speed_); // constant speed command
165  float vert_angle = max_elev_rudder_ * l / max(lattice_size_vert_, 1); // constant vertical angle command
166  float ref_horiz_angle = max_lat_rudder_ * k / max(lattice_size_horiz_, 1); // reference horizontal angle
167 
168  RobotModel::state_type state = state_;
169  int N = 10;
170  for (int cnt = 0; cnt < N; cnt++) {
171  float t = float(cnt) / N;
172  float current_horiz_angle = ref_horiz_angle*(-2*pow(t, 3) + 4*pow(t,2) - 1); // cubic interpolation for horizontal angle
173  RobotModel::input_type input = {prop_speed, current_horiz_angle, vert_angle, 0};
174 
175  robot_model_.integrate(state, input, 0.0, duration/N, duration/N/10);
176  }
177 
178  // Transform the predicted pose from ocean frame to robot frame
179  geometry_msgs::Pose pose, lattice_pose;
180  pose.position.x = state[0];
181  pose.position.y = state[1];
182  pose.position.z = state[2];
183 
184  float wall_orientation = wall_orientation_;
185 
186  while (fabs(wall_orientation - state[5]) > M_PI_2) {
187  if (wall_orientation - state[5] > M_PI_2)
188  wall_orientation -= M_PI;
189  else
190  wall_orientation += M_PI;
191  }
192 
193  to_quaternion(state[3], state[4], wall_orientation, pose.orientation);
194 
195  tf2::doTransform(pose, lattice_pose, robot_ocean_tf_);
196 
197  lattice.nodes[counter] = std::shared_ptr<LatticeNode>(new LatticeNode());
198  lattice.nodes[counter]->pose = lattice_pose;
199  counter++;
200  }
201  }
202 }
203 
204 
205 void PlanningNodelet::generate_lattices(
206  const RobotModel::state_type &init_state,
207  std::vector<Lattice> &lattices)
208 {
209  // Get wall orientation
210  float yaw_wall;
211  Eigen::Vector3f orientation_wall = get_wall_orientation(yaw_wall);
212  geometry_msgs::Quaternion quati; // orientation in ocean frame
213  to_quaternion(0, 0, yaw_wall, quati);
214 
215  geometry_msgs::Quaternion orientation_vp; // orientation of viewpoints in wall frame
216  tf2::doTransform(quati, orientation_vp, wall_ocean_tf_);
217 
218  // Get robot initial position (in wall frame)
219  geometry_msgs::Point p_ocean, p_wall; // position in ocean and wall frames
220  p_ocean.x = init_state[0];
221  p_ocean.y = init_state[1];
222  p_ocean.z = init_state[2];
223  tf2::doTransform(p_ocean, p_wall, wall_ocean_tf_);
224 
225  bool backwards = (p_wall.z < 0); // whether the robot is going backwards or forwards (with respect to the wall)
226 
227  // Generate lattices in wall frame
228  int nbr_lattices = lattices.size();
229  int nbr_nodes = lattice_size_vert_*lattice_size_horiz_; // number of nodes per lattice
230  float y = p_wall.y;
231  float dy = plan_horizon_;
232 
233  float margin = 0.01; // margin to prevent the points to be filtered out
234  float dx = fabs(bnd_depth_[1] - bnd_depth_[0] - 2*margin) / (lattice_size_vert_-1); // vertical increment
235  float dz = fabs(bnd_wall_dist_[1] - bnd_wall_dist_[0] - 2*margin) / (lattice_size_horiz_-1); // horizontal increment
236 
237  if (backwards) {
238  dy = -dy;
239  dz = -dz;
240  }
241 
242  for (int k = 0; k < nbr_lattices; k++) {
243  lattices[k].nodes.clear();
244  lattices[k].nodes.reserve(nbr_nodes);
245  y += dy;
246  float x = bnd_depth_[0] + margin/(lattice_size_vert_-1);
247 
248  for (int l = 0; l < lattice_size_vert_; l++) {
249  float z = bnd_wall_dist_[0] + margin/(lattice_size_horiz_-1);
250 
251  if (backwards)
252  z = -z;
253 
254  for (int m = 0; m < lattice_size_horiz_; m++) {
255  // Create a new node to add to the lattice
256  shared_ptr<LatticeNode> new_node (new LatticeNode());
257  new_node->pose.position.x = x;
258  new_node->pose.position.y = y;
259  new_node->pose.position.z = z;
260  new_node->pose.orientation = orientation_vp;
261 
262  lattices[k].nodes.emplace_back(std::move(new_node));
263 
264  z += dz;
265  }
266 
267  x += dx;
268  }
269  }
270 
271  // Transform lattice from wall to robot frame
272  for (int k = 0; k < nbr_lattices; k++) {
273  for (int l = 0; l < nbr_nodes; l++) {
274  geometry_msgs::Pose pose_tmp;
275  tf2::doTransform(lattices[k].nodes[l]->pose, pose_tmp, robot_wall_tf_);
276  lattices[k].nodes[l]->pose = pose_tmp;
277  }
278  }
279 }
280 
281 
282 void PlanningNodelet::filter_lattice(Lattice &lattice_in, Lattice &lattice_out)
283 {
284  lattice_out.nodes.reserve(lattice_in.nodes.size());
285 
286  for (int k = 0; k < lattice_in.nodes.size(); k++) {
287  bool position_ok = false;
288  bool pitch_ok = false;
289  bool orientation_ok = false;
290 
291  // Transform point in wall and ocean frames
292  geometry_msgs::Pose vp1; // viewpoint expressed in wall frame
293  geometry_msgs::Pose vp2; // viewpoint expressed in ocean frame
294  tf2::doTransform(lattice_in.nodes[k]->pose, vp1, wall_robot_tf_);
295  tf2::doTransform(lattice_in.nodes[k]->pose, vp2, ocean_robot_tf_);
296 
297  // Check position bounds
298  if (fabs(vp1.position.z) >= bnd_wall_dist_[0] && fabs(vp1.position.z) <= bnd_wall_dist_[1]
299  && vp1.position.x >= bnd_depth_[0] && vp1.position.x <= bnd_depth_[1]) {
300  position_ok = true;
301  }
302 
303  if (vp1.position.z * wall_robot_tf_.transform.translation.z < 0.0)
304  position_ok = false; // the vp is on the wrong side of the wall
305 
306  // Check pitch angle of the viewpoint (not being to vertically inclined)
307  double roll, pitch, yaw;
308  to_euler(vp2.orientation, roll, pitch, yaw);
309 
310  if (abs(pitch) <= bnd_pitch_)
311  pitch_ok = true;
312 
313  // Check orientation of the viewpoint (not going backwards)
314  Eigen::Vector3f orientation_vp;
315  get_orientation(roll, pitch, yaw, orientation_vp); // orientation of the viewpoint
316  Eigen::Vector3f orientation_wall = get_wall_orientation(); // orientation of the wall
317 
318  if (orientation_vp.dot(orientation_wall) >= 0.0)
319  orientation_ok = true;
320 
321  // Add the viewpoints if it passed all the checks
322  if (position_ok && pitch_ok && orientation_ok) {
323  lattice_out.nodes.emplace_back(std::move(lattice_in.nodes[k]));
324  }
325  }
326 }
327 
328 
329 void PlanningNodelet::transform_lattices(
330  std::vector<Lattice> &lattices,
331  const geometry_msgs::TransformStamped &transform)
332 {
333  for (int k = 0; k < lattices.size(); k++) {
334  for (int l = 0; l < lattices[k].nodes.size(); l++) {
335  geometry_msgs::Pose tmp;
336  tf2::doTransform(lattices[k].nodes[l]->pose, tmp, transform);
337  lattices[k].nodes[l]->pose = tmp;
338  }
339  }
340 }
341 
342 
343 void PlanningNodelet::add_node(
344  const RobotModel::state_type &state,
345  const geometry_msgs::TransformStamped &frame_ocean_tf,
346  Lattice &lattice)
347 {
348  geometry_msgs::Pose p_ocean, p_frame;
349  p_ocean.position.x = state[0];
350  p_ocean.position.y = state[1];
351  p_ocean.position.z = state[2];
352  to_quaternion(state[3], state[4], state[5], p_ocean.orientation);
353  tf2::doTransform(p_ocean, p_frame, frame_ocean_tf);
354 
355  shared_ptr<LatticeNode> new_node (new LatticeNode());
356  new_node->pose = p_frame;
357  new_node->speed = state[6];
358  lattice.nodes.emplace_back(std::move(new_node));
359 }
360 
361 
362 void PlanningNodelet::connect_lattices(
363  const RobotModel::state_type &init_state,
364  std::vector<Lattice> &lattices_in,
365  std::vector<Lattice> &lattices_out)
366 {
367  // Transform the lattice in wall frame
368  int nbr_lattices = lattices_in.size();
369  transform_lattices(lattices_in, wall_robot_tf_);
370 
371  // Create a lattice for the initial state (in wall frame)
372  Lattice initial_lattice;
373  add_node(init_state, wall_ocean_tf_, initial_lattice);
374 
375  // Handle lattices one by one
376  for (int k = 0; k < nbr_lattices; k++)
377  {
378  Lattice* next_lattice = &(lattices_in[k]); // lattice to check reachability
379  Lattice* prev_lattice; // lattice from which propagate motion
380  if (k == 0)
381  prev_lattice = &initial_lattice;
382  else
383  prev_lattice = &(lattices_out[k-1]);
384 
385  vector<bool> reached(next_lattice->nodes.size(), false); // whether each node of lattice k has been reached
386 
387  // Propagate motion from nodes of the previous lattice
388  float wall_yaw;
389  Eigen::Vector3f wall_orientation = get_wall_orientation(wall_yaw); // wall orientation in ocean frame
390  vector<int> lat_mult = {-1, -1, 1, 1}; // multipliers for the maximum rudder commands
391  vector<int> elev_mult = {-1, 1, -1, 1};
392  float prop_speed = robot_model_.steady_propeller_speed(plan_speed_); // propeller speed command
393 
394  for (int l = 0; l < prev_lattice->nodes.size(); l++ )
395  {
396  // Transform viewpoint in ocean frame
397  geometry_msgs::Pose pose_ocean;
398  tf2::doTransform(prev_lattice->nodes[l]->pose, pose_ocean, ocean_wall_tf_);
399 
400  // Parse node state
401  RobotModel::state_type init_state = to_state(pose_ocean, state_.size());
402  init_state[6] = prev_lattice->nodes[l]->speed;
403 
404  // Go through extreme control inputs
405  vector<geometry_msgs::Pose> propag_poses(4); // propagated poses (in wall frame)
406  float propag_speed = 1000; // min of the propagated longitudinal speeds
407 
408  for (int m = 0; m < 4; m++) {
409  RobotModel::state_type state = init_state;
410  Eigen::Vector3f origin; origin << state[0], state[1], state[2]; // origin of motion
411  Eigen::Vector3f current_pos;
412 
413  float elev_angle = max_elev_rudder_ * elev_mult[m]; // elevation rudder angle command
414  float lat_angle = max_lat_rudder_ * lat_mult[m]; // lateral rudder angle command
415  RobotModel::input_type input = {prop_speed, lat_angle, elev_angle, 0};
416  float duration = (plan_horizon_ / plan_speed_) / 5; // duration of each integration
417 
418  // Integrate ODE
419  float distance = 0.0; // moved distance in wall direction
420  float max_distance = 0.0; // max moved distance
421  Vector3f best_pos; // position corresponding to the max moved distance
422  int safe_cnt = 0; // counter to go out of the integration loop (the robot could u-turn and never reach the next lattice)
423 
424  do {
425  robot_model_.integrate(state, input, 0.0, duration, duration/5);
426  current_pos << state[0], state[1], state[2];
427  distance = wall_orientation.dot(current_pos - origin);
428 
429  if (distance > max_distance ) {
430  best_pos = current_pos;
431  max_distance = distance;
432  }
433  } while (distance < plan_horizon_ && safe_cnt++ < 20);
434 
435  // Transform propagated pose in wall frame
436  pose_ocean.position.x = best_pos(0);
437  pose_ocean.position.y = best_pos(1);
438  pose_ocean.position.z = best_pos(2);
439  to_quaternion(0, 0, wall_yaw, pose_ocean.orientation); // viewpoints parallel to the wall
440 
441  tf2::doTransform(pose_ocean, propag_poses[m], wall_ocean_tf_);
442 
443  if (state[6] < propag_speed)
444  propag_speed = state[6];
445  }
446 
447  // Get extreme achievable coordinates
448  vector<float> x_values(4), z_values(4); // coordinates of the 4 propagated points
449  for (int m = 0; m < 4; m++) {
450  x_values[m] = propag_poses[m].position.x;
451  z_values[m] = propag_poses[m].position.z;
452  }
453 
454  float min_x = *(std::min_element(x_values.begin(), x_values.end()));
455  float max_x = *(std::max_element(x_values.begin(), x_values.end()));
456  float min_z = *(std::min_element(z_values.begin(), z_values.end()));
457  float max_z = *(std::max_element(z_values.begin(), z_values.end()));
458 
459  // Check viewpoints that can be reached in the next lattice
460  for (int m = 0; m < next_lattice->nodes.size(); m++) {
461  geometry_msgs::Point node_pos = next_lattice->nodes[m]->pose.position;
462 
463  if (node_pos.x >= min_x && node_pos.x <= max_x && node_pos.z >= min_z && node_pos.z <= max_z) {
464  prev_lattice->nodes[l]->next.emplace_back(next_lattice->nodes[m]);
465  reached[m] = true;
466  next_lattice->nodes[m]->speed = propag_speed;
467  }
468  }
469  }
470 
471  // Add reachable nodes in the lattice
472  for (int l = 0; l < next_lattice->nodes.size(); l++) {
473  if (reached[l]) {
474  lattices_out[k].nodes.emplace_back(lattices_in[k].nodes[l]);
475  }
476  }
477  }
478 
479  // Transform the lattice back in robot frame
480  transform_lattices(lattices_out, robot_wall_tf_);
481 
482  // TODO: remove nodes of the first lattice that doesn't lead to the right tree depth
483  // ...
484 }
485 
486 
487 bool PlanningNodelet::compute_lattice_gp(Lattice &lattice, ros::Time stamp)
488 {
489  // Compute the corresponding camera orientation for each viewpoint
490  int size_lattice = lattice.nodes.size();
491  vector<geometry_msgs::Pose> poses(size_lattice); // camera orientations
492 
493  for (int k = 0; k < size_lattice; k++) {
494  tf2::Quaternion q_orig, q_rot, q_new;
495  tf2::convert(lattice.nodes[k]->pose.orientation, q_orig);
496  q_rot.setRPY(M_PI_2, 0.0, 0.0);
497 
498  q_new = q_orig * q_rot;
499  q_new.normalize();
500  tf2::convert(q_new, poses[k].orientation);
501 
502  poses[k].position = lattice.nodes[k]->pose.position;
503  }
504 
505  // Transform the orientation in camera frame
506  geometry_msgs::TransformStamped camera_robot_tf;
507 
508  try {
509  camera_robot_tf = tf_buffer_.lookupTransform(camera_frame_, robot_frame_, stamp);
510  }
511  catch (tf2::TransformException &ex) {
512  NODELET_WARN("[planning_nodelet] %s", ex.what());
513  return false;
514  }
515 
516  for (int k = 0; k < size_lattice; k++) {
517  geometry_msgs::Pose transf_pose;
518  tf2::doTransform(poses[k], transf_pose, camera_robot_tf);
519 
520  poses[k] = transf_pose;
521  }
522 
523  // Get camera measurement for each viewpoint of the lattice
524  mf_sensors_simulator::MultiPoses camera_srv;
525  camera_srv.request.stamp = stamp;
526  camera_srv.request.pose_array.header.frame_id = robot_frame_;
527  camera_srv.request.pose_array.poses = poses;
528  camera_srv.request.n_pxl_height = camera_height_;
529  camera_srv.request.n_pxl_width = camera_width_;
530 
531  if (ray_multi_client_.call(camera_srv)) {
532  if (!camera_srv.response.is_success) {
533  NODELET_WARN("[planning_nodelet] Call to raycast_multi service didn't give output ");
534  return false;
535  }
536  } else {
537  NODELET_WARN("[planning_nodelet] Failed to call raycast_multi service");
538  return false;
539  }
540 
541  // Store camera hit points
542  for (int k = 0; k < size_lattice; k++) {
543  lattice.nodes[k]->camera_pts_x = camera_srv.response.results[k].x;
544  lattice.nodes[k]->camera_pts_y = camera_srv.response.results[k].y;
545  lattice.nodes[k]->camera_pts_z = camera_srv.response.results[k].z;
546  }
547 
548  // Update the GP covariance for each viewpoint
549  mf_mapping::UpdateGP gp_srv;
550  gp_srv.request.stamp = stamp;
551  gp_srv.request.use_internal_mean = true;
552  gp_srv.request.use_internal_cov = true;
553  gp_srv.request.update_mean = false;
554  gp_srv.request.return_cov_diag = true;
555  gp_srv.request.eval_gp = false;
556 
557  gp_srv.request.meas.resize(size_lattice);
558  for (int k = 0; k < size_lattice; k++) {
559  gp_srv.request.meas[k] = camera_srv.response.results[k];
560  }
561 
562  if (update_gp_client_.call(gp_srv)) {
563  for (int k = 0; k < size_lattice; k++) {
564  lattice.nodes[k]->gp_cov = gp_srv.response.new_cov_diag[k].data;
565  }
566  } else {
567  NODELET_WARN("[planning_nodelet] Failed to call update_gp service");
568  return false;
569  }
570 
571  return true;
572 }
573 
574 
575 void PlanningNodelet::compute_info_gain(Lattice &lattice)
576 {
577  int size_gp = last_gp_mean_.size();
578 
579  for (int k = 0; k < lattice.nodes.size(); k++) {
580  lattice.nodes[k]->info_gain = 0.0;
581 
582  for (int l = 0; l < size_gp; l++) {
583  float cov_diff = last_gp_cov_[l][l] - lattice.nodes[k]->gp_cov[l];
584 
585  float weight = 0.0;
586  if (last_gp_mean_[l] > gp_threshold_)
587  weight = 100;
588 
589  lattice.nodes[k]->info_gain += weight * cov_diff;
590  }
591  }
592 }
593 
594 
595 std::vector<LatticeNode*> PlanningNodelet::select_viewpoints(const std::vector<Lattice> &lattices)
596 {
597  float best_info_gain = 0.0;
598  vector<LatticeNode*> selected_vp;
599 
600  for (int k = 0; k < lattices[0].nodes.size(); k++) {
601  float info_gain;
602  vector<LatticeNode*> vp;
603 
604  select_viewpoints(lattices[0].nodes[k].get(), info_gain, vp);
605 
606  if (info_gain > best_info_gain) {
607  selected_vp = vp;
608  best_info_gain = info_gain;
609  }
610  }
611 
612  return selected_vp;
613 }
614 
615 
616 void PlanningNodelet::select_viewpoints(
617  LatticeNode* node,
618  float &info_gain,
619  std::vector<LatticeNode*> &selected_vp)
620 {
621  // Terminating condition
622  if (node->next.size() == 0) {
623  info_gain = node->info_gain;
624  selected_vp.resize(1);
625  selected_vp[0] = node;
626 
627  return;
628  }
629 
630  // Select best next node
631  float best_info_gain = 0.0;
632 
633  for (int k = 0; k < node->next.size(); k++) {
634  float info_gain;
635  vector<LatticeNode*> vp;
636 
637  select_viewpoints(node->next[k].get(), info_gain, vp);
638 
639  if (info_gain > best_info_gain) {
640  selected_vp = vp;
641  best_info_gain = info_gain;
642  }
643  }
644 
645  // Prepare output
646  info_gain = best_info_gain + node->info_gain;
647  selected_vp.insert(selected_vp.begin(), node);
648 }
649 
650 
651 void PlanningNodelet::fill_display_obj(
652  const std::vector<Lattice> &lattices,
653  const std::vector<LatticeNode*> &selected_vp)
654 {
655  // Fill selected viewpoints poses and camera hitpoints
656  selected_vp_.resize(nbr_lattices_);
657  x_hit_pt_sel_.resize(0);
658  y_hit_pt_sel_.resize(0);
659  z_hit_pt_sel_.resize(0);
660 
661  for (int k = 0; k < nbr_lattices_; k++) {
662  // Fill selected pose
663  geometry_msgs::Pose pose;
664  tf2::doTransform(selected_vp[k]->pose, pose, ocean_robot_tf_);
665  selected_vp_[k] = pose;
666 
667  // Fill camera hit points
668  x_hit_pt_sel_.insert(x_hit_pt_sel_.end(), selected_vp[k]->camera_pts_x.begin(), selected_vp[k]->camera_pts_x.end());
669  y_hit_pt_sel_.insert(y_hit_pt_sel_.end(), selected_vp[k]->camera_pts_y.begin(), selected_vp[k]->camera_pts_y.end());
670  z_hit_pt_sel_.insert(z_hit_pt_sel_.end(), selected_vp[k]->camera_pts_z.begin(), selected_vp[k]->camera_pts_z.end());
671  }
672 
673  for (int k = 0; k < x_hit_pt_sel_.size(); k++) {
674  // Transform the hit points in ocean frame
675  geometry_msgs::Point pose, tmp;
676  pose.x = x_hit_pt_sel_[k];
677  pose.y = y_hit_pt_sel_[k];
678  pose.z = z_hit_pt_sel_[k];
679 
680  tf2::doTransform(pose, tmp, ocean_camera_tf_);
681 
682  x_hit_pt_sel_[k] = tmp.x;
683  y_hit_pt_sel_[k] = tmp.y;
684  z_hit_pt_sel_[k] = tmp.z;
685  }
686 
687  // Build lattice of non-selected viewpoints (in ocean frame)
688  lattice_.resize(0);
689 
690  for (int k = 0; k < nbr_lattices_; k++) {
691  for (int l = 0; l < lattices[k].nodes.size(); l++) {
692  if (lattices[k].nodes[l].get() != selected_vp[k]) {
693  geometry_msgs::Pose pose;
694  tf2::doTransform(lattices[k].nodes[l]->pose, pose, ocean_robot_tf_);
695  lattice_.emplace_back(pose);
696  }
697  }
698  }
699 }
700 
701 
702 void PlanningNodelet::generate_path(
703  const std::vector<geometry_msgs::Pose> &selected_vp,
704  nav_msgs::Path &path,
705  std::vector<geometry_msgs::Pose> &waypoints)
706 {
707  path.poses.resize(0);
708  waypoints.resize(1);
709 
710  if (waypoints_.size() == 0 | replan_)
711  waypoints[0] = tf_to_pose(ocean_robot_tf_);
712  else
713  waypoints[0] = waypoints_.back();
714 
715  waypoints.insert(waypoints.end(), selected_vp_.begin(), selected_vp_.end());
716 
717  if (linear_path_) {
718  for (int k = 0; k < nbr_lattices_; k++) {
719  nav_msgs::Path intermed_path = straight_line_path(waypoints[k], waypoints[k+1], path_res_);
720 
721  if (k == 0)
722  path.poses = intermed_path.poses;
723  else
724  path.poses.insert(path.poses.end(), intermed_path.poses.begin()+1, intermed_path.poses.end());
725  }
726  } else {
727  path = spline_path(waypoints, path_res_, plan_speed_);
728  }
729 }
730 
731 
732 nav_msgs::Path PlanningNodelet::straight_line_path(const geometry_msgs::Pose &start,
733  const geometry_msgs::Pose &end, float resolution)
734 {
735  // Convert input
736  tf2::Vector3 p1, p2;
737  tf2::convert(start.position, p1);
738  tf2::convert(end.position, p2);
739 
740  tf2::Quaternion q1, q2;
741  tf2::convert(start.orientation, q1);
742  tf2::convert(end.orientation, q2);
743 
744  // Prepare interpolation
745  nav_msgs::Path path;
746  float d = tf2::tf2Distance(p1, p2);
747  int n = d/resolution;
748 
749  path.header.frame_id = ocean_frame_;
750  path.header.stamp = ros::Time::now();
751  path.poses.resize(n + 1);
752 
753  // Interpolation
754  for (int k = 0; k <= n; k++) {
755  geometry_msgs::PoseStamped new_pose;
756  tf2::toMsg( p1 + (p2-p1) * (float(k)/n), new_pose.pose.position);
757  tf2::convert((q1 + (q2-q1) * (float(k)/n)).normalize(), new_pose.pose.orientation);
758 
759  path.poses[k] = new_pose;
760  }
761 
762  return path;
763 }
764 
765 
766 nav_msgs::Path PlanningNodelet::spline_path(
767  const std::vector<geometry_msgs::Pose> &waypoints,
768  float resolution,
769  float speed)
770 {
771  int n = waypoints.size();
772  vector<Eigen::Vector3f> p(n); // waypoints positions
773  vector<Eigen::Vector3f> o(n); // waypoints orientations
774 
775  for (int k = 0; k < n; k++) {
776  p[k] << waypoints[k].position.x, waypoints[k].position.y, waypoints[k].position.z;
777 
778  double roll, pitch, yaw;
779  to_euler(waypoints[k].orientation, roll, pitch, yaw);
780  o[k] << cos(yaw), sin(yaw), sin(pitch);
781  }
782 
783  Spline spline(p, o, speed);
784 
785  nav_msgs::Path path;
786  path.poses.resize(0);
787  path.header.frame_id = ocean_frame_;
788  bool last_reached = false;
789  float t = 0;
790 
791  while (!last_reached) {
792  Eigen::Vector3f p;
793  Eigen::Vector3f o;
794 
795  spline.evaluate(t, p, o, last_reached);
796  o = o / o.norm();
797 
798  geometry_msgs::PoseStamped pose;
799  pose.pose.position.x = p(0);
800  pose.pose.position.y = p(1);
801  pose.pose.position.z = p(2);
802  to_quaternion(0.0, -asin(o(2)), atan2(o(1), o(0)), pose.pose.orientation);
803 
804  path.poses.emplace_back(pose);
805  t += resolution / speed;
806  }
807 
808  return path;
809 }
810 
811 
812 bool PlanningNodelet::plan_trajectory()
813 {
814  // Check for Gaussian Process received
815  int size_gp = last_gp_cov_.size();
816 
817  if (last_gp_cov_.size() == 0 || last_gp_mean_.size() == 0)
818  return false;
819 
820  // Generate a lattice of possible waypoints (in robot frame)
821  vector<Lattice> lattices_tmp(nbr_lattices_);
822 
823  if (mult_lattices_)
824  {
826 
827  if (waypoints_.size() == 0 || replan_) {
828  state = state_;
829  } else {
830  state = to_state(waypoints_.back(), state_.size());
831  }
832 
833  generate_lattices(state, lattices_tmp);
834  }
835  else if (cart_lattice_)
836  {
837  float lat_turn_radius = robot_model_.lat_turn_radius(plan_speed_, max_lat_rudder_);
838  float elev_turn_radius = robot_model_.elev_turn_radius(plan_speed_, max_lat_rudder_);
839 
840  float max_lat_angle = plan_horizon_ / (2 * lat_turn_radius);
841  float max_elev_angle = plan_horizon_ / (2 * elev_turn_radius);
842 
843  if (!horiz_motion_) max_lat_angle = 0;
844  if (!vert_motion_) max_elev_angle = 0;
845 
846  generate_cart_lattice(max_lat_angle, max_elev_angle, plan_horizon_, lattice_res_, lattices_tmp[0]);
847  }
848  else
849  {
850  generate_lattice(lattices_tmp[0]);
851  }
852 
853  // Filter the lattices (remove viewpoints that are not geometrically acceptable)
854  vector<Lattice> lattices_tmp2(nbr_lattices_);
855 
856  for (int k = 0; k < nbr_lattices_; k++) {
857  filter_lattice(lattices_tmp[k], lattices_tmp2[k]);
858  }
859 
860  // Connect lattices and remove viewpoints that are not dynamically reachable
861  vector<Lattice> lattices(nbr_lattices_);
862  connect_lattices(state_, lattices_tmp2, lattices);
863 
864  for (int k = 0; k < nbr_lattices_; k++) {
865  if (lattices[k].nodes.size() == 0) {
866  NODELET_WARN("[planning_nodelet] Lattice %d is empty", k);
867  return false;
868  }
869  }
870 
871  // Update the GP covariance for each viewpoint
872  ros::Time stamp = ocean_robot_tf_.header.stamp; // time at which to fetch the ROS transforms
873 
874  for (int k = 0; k < nbr_lattices_; k++) {
875  bool ret = compute_lattice_gp(lattices[k], stamp);
876 
877  if (!ret) {
878  NODELET_WARN("[planning_nodelet] Error when computing GP covariance ");
879  return false;
880  }
881  }
882 
883  // Compute information gains and select best viewpoints
884  for (int k = 0; k < nbr_lattices_; k++) {
885  compute_info_gain(lattices[k]);
886  }
887 
888  vector<LatticeNode*> selected_vp = select_viewpoints(lattices);
889 
890  if (selected_vp.size() != nbr_lattices_) {
891  NODELET_WARN("[planning_nodelet] Not the right number of selected viewpoints (%d against %d)",
892  (int)selected_vp.size(), (int)nbr_lattices_
893  );
894  return false;
895  }
896 
897  // Fill objects for display purposes
898  fill_display_obj(lattices, selected_vp);
899 
900  // Generate a spline trajectory (in ocean frame) between the selected points
901  nav_msgs::Path new_path;
902  vector<geometry_msgs::Pose> waypoints; // new viewpoints in ocean frame
903  generate_path(selected_vp_, new_path, waypoints);
904 
905  if (new_path.poses.size() <= 1) {
906  NODELET_WARN("[planning_nodelet] Computed path to small (size: %d)", (int)new_path.poses.size());
907  return false;
908  }
909 
910  // Append the path to the previous path
911  path_.header.stamp = ros::Time::now();
912 
913  if (replan_) {
914  path_.poses = new_path.poses;
915  waypoints_ = waypoints;
916  }
917  else {
918  if (path_.poses.size() == 0) {
919  path_.poses.emplace_back(new_path.poses[0]);
920  waypoints_.emplace_back(waypoints[0]);
921  }
922 
923  path_.poses.insert(path_.poses.end(), new_path.poses.begin()+1, new_path.poses.end());
924  waypoints_.insert(waypoints_.end(), waypoints.begin()+1, waypoints.end());
925  }
926 
927  return true;
928 }
929 
930 
931 } // namespace mfcpp
float info_gain
Information gain fo this viewpoint.
Definition: lattice.hpp:31
Definition: common.hpp:23
std::vector< std::shared_ptr< LatticeNode > > nodes
Nodes of the lattice.
Definition: lattice.hpp:57
Class to represent a lattice.
Definition: lattice.hpp:54
void evaluate(float t, Eigen::Vector3f &position, Eigen::Vector3f &orientation, bool &last_reached)
Evaluates the spline at a specific time instant (assuming constant speed)
Definition: spline.cpp:63
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
std::vector< double > state_type
Data type of the state.
Definition: robot_model.hpp:51
Declaration of common functions.
Declaration of a class for 3D spline interpolation.
std::vector< std::shared_ptr< LatticeNode > > next
Nodes in the next lattice that can be reached from this node.
Definition: lattice.hpp:29
float distance(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Computes the the euclidean distance between two positions.
Definition: common.hpp:69
std::vector< double > input_type
Data type of the input.
Definition: robot_model.hpp:54
RobotModel::state_type to_state(const geometry_msgs::Pose &pose, int state_size)
Converts a pose into a state message.
Declaration of a nodelet for path plannning of an underwater robot surveying a marine farm...
Declaration of useful classes for planning with lattices.
void get_orientation(float roll, float pitch, float yaw, VectorT &orientation)
Get orientation vector (x vector of the frame) given by Roll-Pitch-Yaw.
Definition: common.hpp:256
Class to represent a node (viewpoint) of a lattice.
Definition: lattice.hpp:24
Class for spline interpolation.
Definition: spline.hpp:24
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