Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
planning_nodelet.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of a nodelet for path plannning of an underwater robot
5  * surveying a marine farm
6  * \author Corentin Chauvin-Hameau
7  * \date 2019
8  */
9 
10 #include "planning_nodelet.hpp"
12 #include "mf_sensors_simulator/MultiPoses.h"
13 #include "mf_mapping/UpdateGP.h"
14 #include "mf_common/Float32Array.h"
15 #include "mf_common/Array2D.h"
16 #include "mf_common/Float32Array.h"
17 #include <geometry_msgs/TransformStamped.h>
18 #include <geometry_msgs/PoseArray.h>
19 #include <geometry_msgs/Point.h>
20 #include <visualization_msgs/Marker.h>
21 #include <std_srvs/Empty.h>
22 #include <pluginlib/class_list_macros.h>
23 #include <ros/ros.h>
24 #include <vector>
25 #include <iostream>
26 
27 using namespace std;
28 
29 PLUGINLIB_EXPORT_CLASS(mfcpp::PlanningNodelet, nodelet::Nodelet)
30 
31 
32 namespace mfcpp {
33 
34 /*
35  * Definition of static variables
36  */
37 sig_atomic_t volatile PlanningNodelet::b_sigint_ = 0;
38 ros::Timer PlanningNodelet::main_timer_ = ros::Timer();
39 
40 /*
41  * Definition of member functions
42  */
43 PlanningNodelet::PlanningNodelet():
44  tf_listener_(tf_buffer_)
45 {
46 
47 }
48 
49 
51 
52 
54 {
55  nh_ = getNodeHandle();
56  private_nh_ = getPrivateNodeHandle();
57 
58  // Catch SIGINT (Ctrl+C) stop signal
59  struct sigaction sigIntHandler;
60  sigIntHandler.sa_handler = PlanningNodelet::sigint_handler;
61  sigemptyset(&sigIntHandler.sa_mask);
62  sigIntHandler.sa_flags = 0;
63  sigaction(SIGINT, &sigIntHandler, NULL);
64 
65  // ROS parameters
66  vector<double> model_csts; // model constants
67  vector<double> bnd_input; // boundaries on the input
68  bool initially_disabled; // whether to disable the planner at the beginning
69 
70 
71  private_nh_.param<float>("main_freq", main_freq_, 1.0);
72  private_nh_.param<string>("ocean_frame", ocean_frame_, "ocean");
73  private_nh_.param<string>("wall_frame", wall_frame_, "wall");
74  private_nh_.param<string>("robot_frame", robot_frame_, "base_link");
75  private_nh_.param<string>("camera_frame", camera_frame_, "camera");
76  private_nh_.param<bool>("initially_disabled", initially_disabled, false);
77 
78  private_nh_.param<float>("length_wall", length_wall_, 1.0);
79  private_nh_.param<vector<double>>("model_constants", model_csts, vector<double>(11, 0.0));
80  private_nh_.param<vector<double>>("bnd_input", bnd_input, vector<double>(4, 0.0));
81 
82  private_nh_.param<bool>("horiz_motion", horiz_motion_, true);
83  private_nh_.param<bool>("vert_motion", vert_motion_, true);
84  private_nh_.param<bool>("mult_lattices", mult_lattices_, true);
85  private_nh_.param<int>("nbr_lattices", nbr_lattices_, 1);
86  private_nh_.param<bool>("replan", replan_, false);
87  private_nh_.param<bool>("cart_lattice", cart_lattice_, false);
88  private_nh_.param<float>("plan_speed", plan_speed_, 1.0);
89  private_nh_.param<float>("plan_horizon", plan_horizon_, 1.0);
90  private_nh_.param<int>("lattice_size_horiz", lattice_size_horiz_, 1);
91  private_nh_.param<int>("lattice_size_vert", lattice_size_vert_, 1);
92  private_nh_.param<float>("wall_orientation", wall_orientation_, 0);
93  private_nh_.param<float>("lattice_res", lattice_res_, 0.5);
94  private_nh_.param<vector<float>>("bnd_wall_dist", bnd_wall_dist_, {0.5, 1.0});
95  private_nh_.param<vector<float>>("bnd_depth", bnd_depth_, {0.0, 1.0});
96  private_nh_.param<float>("bnd_pitch", bnd_pitch_, 90.0);
97 
98  private_nh_.param<int>("camera_width", camera_width_, -1);
99  private_nh_.param<int>("camera_height", camera_height_, -1);
100  private_nh_.param<float>("gp_threshold", gp_threshold_, 0.5);
101 
102  private_nh_.param<bool>("linear_path", linear_path_, false);
103  private_nh_.param<float>("path_res", path_res_, 0.1);
104 
105  wall_orientation_ = fabs(wall_orientation_);
106  max_lat_rudder_ = bnd_input[1];
107  max_elev_rudder_ = bnd_input[2];
108  bnd_pitch_ *= M_PI / 180.0; // convert in radian
109 
110  if (!vert_motion_)
111  lattice_size_vert_ = 0;
112  if (!horiz_motion_)
113  lattice_size_horiz_ = 0;
114  if (!mult_lattices_)
115  nbr_lattices_ = 1;
116 
117  // Other variables
118  robot_model_ = RobotModel(model_csts);
119  last_gp_mean_.resize(0);
120  last_gp_cov_.resize(0);
121  x_hit_pt_sel_.resize(0);
122  y_hit_pt_sel_.resize(0);
123  z_hit_pt_sel_.resize(0);
124  state_received_ = false;
125  planner_enabled_ = !initially_disabled;
126  waypoints_.resize(0);
127  path_.poses.resize(0);
128  path_.header.frame_id = ocean_frame_;
129 
130 
131  // ROS subscribers
132  gp_mean_sub_ = nh_.subscribe<mf_common::Float32ArrayConstPtr>("gp_mean", 1, &PlanningNodelet::gp_mean_cb, this);
133  gp_cov_sub_ = nh_.subscribe<mf_common::Array2DConstPtr>("gp_cov", 1, &PlanningNodelet::gp_cov_cb, this);
134  state_sub_ = nh_.subscribe<mf_common::Float32Array>("state", 1, &PlanningNodelet::state_cb, this);
135 
136  // ROS publishers
137  lattice_pub_ = nh_.advertise<visualization_msgs::Marker>("wp_lattice", 0);
138  lattice_pose_pub_ = nh_.advertise<geometry_msgs::PoseArray>("wp_pose_array", 0);
139  path_pub_ = nh_.advertise<nav_msgs::Path>("path", 0);
140 
141  // ROS services
142  ray_multi_client_ = nh_.serviceClient<mf_sensors_simulator::MultiPoses>("raycast_multi");
143  update_gp_client_ = nh_.serviceClient<mf_mapping::UpdateGP>("update_gp");
144  disable_serv_ = nh_.advertiseService("disable_planner", &PlanningNodelet::disable_cb, this);
145  enable_serv_ = nh_.advertiseService("enable_planner", &PlanningNodelet::enable_cb, this);
146 
147 
148  // Main loop
149  main_timer_ = private_nh_.createTimer(
150  ros::Duration(1/main_freq_), &PlanningNodelet::main_cb, this
151  );
152 }
153 
154 
155 void PlanningNodelet::main_cb(const ros::TimerEvent &timer_event)
156 {
157  if (!ros::ok() || ros::isShuttingDown() || b_sigint_)
158  return;
159 
161  // Check whether a plan has been determined for the full wall
162  if (!replan_) {
163  bool plan_needed = check_planning_needed();
164 
165  if (!plan_needed)
166  return;
167  }
168 
169  clock_t start = clock();
170  cout << "\n=== Starting plannning... ===" << endl;
171 
172  bool success = plan_trajectory();
173 
174  cout << "Ellapsed time: " << (clock() - start) / (double)CLOCKS_PER_SEC << endl;
175 
176  if (success) {
177  path_pub_.publish(path_);
179  }
180  }
181 }
182 
183 
185 {
186  b_sigint_ = 1;
187  main_timer_.stop();
188 
189  raise(SIGTERM);
190 }
191 
193 {
194  // Get position of last planned waypoint in wall frame
195  if (waypoints_.size() == 0)
196  return true;
197 
198  geometry_msgs::Point pos_wall;
199  geometry_msgs::Point pos_ocean = waypoints_.back().position;
200  tf2::doTransform(pos_ocean, pos_wall, wall_ocean_tf_);
201 
202  // Check whether the position is after the end of the wall
203  if ((pos_wall.z > 0 && pos_wall.y >= length_wall_) || (pos_wall.z < 0 && pos_wall.y <= 0))
204  {
205  return false;
206  } else {
207  return true;
208  }
209 }
210 
211 
213 {
214  // Prepare lattice Rviz message
215  unsigned int n = lattice_.size();
216 
217  visualization_msgs::Marker marker;
218  marker.header.stamp = ros::Time::now();
219  marker.header.frame_id = ocean_frame_;
220  marker.ns = "lattice";
221  marker.pose.orientation.w = 1.0;
222  marker.color.r = 0.0;
223  marker.color.g = 1.0;
224  marker.color.b = 0.0;
225  marker.color.a = 1.0;
226  marker.type = visualization_msgs::Marker::POINTS;
227  marker.action = visualization_msgs::Marker::ADD;
228  marker.scale.x = 0.05;
229  marker.scale.y = 0.05;
230  marker.points.resize(n);
231  marker.colors.resize(n);
232 
233  // Publish the non-selected viewpoints
234  for (unsigned int k = 0; k < n; k++) {
235  marker.points[k].x = lattice_[k].position.x;
236  marker.points[k].y = lattice_[k].position.y;
237  marker.points[k].z = lattice_[k].position.z;
238 
239  marker.colors[k].r = 0.0;
240  marker.colors[k].g = 0.0;
241  marker.colors[k].b = 1.0;
242  marker.colors[k].a = 1.0;
243  }
244 
245  lattice_pub_.publish(marker);
246 
247  // Publish the corresponding poses
248  geometry_msgs::PoseArray msg;
249  msg.header.frame_id = ocean_frame_;
250  msg.poses = lattice_;
251  lattice_pose_pub_.publish(msg);
252 
253  // Publish the selected viewpoints
254  marker.ns = "selected_vp";
255  marker.points.resize(nbr_lattices_);
256  marker.colors.resize(nbr_lattices_);
257 
258  for (int k = 0; k < nbr_lattices_; k++) {
259  marker.points[k].x = selected_vp_[k].position.x;
260  marker.points[k].y = selected_vp_[k].position.y;
261  marker.points[k].z = selected_vp_[k].position.z;
262 
263  marker.colors[k].r = 0.0;
264  marker.colors[k].g = 1.0;
265  marker.colors[k].b = 0.0;
266  marker.colors[k].a = 1.0;
267  }
268 
269  lattice_pub_.publish(marker);
270 
271  // Publish the hitpoints of the selected viewpoint
272  marker.ns = "hitpoints";
273  marker.header.frame_id = ocean_frame_;
274  marker.color.r = 0.0;
275  marker.color.g = 1.0;
276  marker.color.b = 1.0;
277  marker.color.a = 1.0;
278 
279  int n_pts = x_hit_pt_sel_.size();
280  marker.points.resize(n_pts);
281  for (unsigned int k = 0; k < n_pts; k++) {
282  marker.points[k].x = x_hit_pt_sel_[k];
283  marker.points[k].y = y_hit_pt_sel_[k];
284  marker.points[k].z = z_hit_pt_sel_[k];
285  }
286 
287  lattice_pub_.publish(marker);
288 }
289 
290 
292 {
293  geometry_msgs::TransformStamped t1, t2, t3, t4, t5, t6, t7;
294 
295  try {
296  t1 = tf_buffer_.lookupTransform(wall_frame_, robot_frame_, ros::Time(0));
297  t2 = tf_buffer_.lookupTransform(robot_frame_, wall_frame_, ros::Time(0));
298  t3 = tf_buffer_.lookupTransform(ocean_frame_, robot_frame_, ros::Time(0));
299  t4 = tf_buffer_.lookupTransform(robot_frame_, ocean_frame_, ros::Time(0));
300  t5 = tf_buffer_.lookupTransform(ocean_frame_, wall_frame_, ros::Time(0));
301  t6 = tf_buffer_.lookupTransform(wall_frame_, ocean_frame_, ros::Time(0));
302  t7 = tf_buffer_.lookupTransform(ocean_frame_, camera_frame_, ros::Time(0));
303  }
304  catch (tf2::TransformException &ex) {
305  NODELET_WARN("[planning_nodelet] %s", ex.what());
306  return false;
307  }
308 
309  wall_robot_tf_ = t1;
310  robot_wall_tf_ = t2;
311  ocean_robot_tf_ = t3;
312  robot_ocean_tf_ = t4;
313  ocean_wall_tf_ = t5;
314  wall_ocean_tf_ = t6;
315  ocean_camera_tf_ = t7;
316  return true;
317 }
318 
319 
320 void PlanningNodelet::gp_mean_cb(const mf_common::Float32ArrayConstPtr msg)
321 {
322  last_gp_mean_ = msg->data;
323 }
324 
325 
326 void PlanningNodelet::gp_cov_cb(const mf_common::Array2DConstPtr msg)
327 {
328  last_gp_cov_ = array_to_vector2D(msg->data);
329 }
330 
331 
332 void PlanningNodelet::state_cb(const mf_common::Float32Array msg)
333 {
334  state_ = RobotModel::state_type(msg.data.begin(), msg.data.end());
335  state_received_ = true;
336 }
337 
338 
340  std_srvs::Empty::Request &req,
341  std_srvs::Empty::Response &res)
342 {
343  ROS_INFO("[planning_nodelet] Planner disabled");
344  planner_enabled_ = false;
345 
346  path_.poses.resize(0);
347  waypoints_.resize(0);
348 
349  return true;
350 }
351 
352 
354  std_srvs::Empty::Request &req,
355  std_srvs::Empty::Response &res)
356 {
357  ROS_INFO("[planning_nodelet] Planner enabled");
358  planner_enabled_ = true;
359  return true;
360 }
361 
362 
363 
364 } // namespace mfcpp
int nbr_lattices_
Number of lattices for multi-lattices planning.
float plan_horizon_
Horizon (m) of the planning (for each lattice)
static void sigint_handler(int s)
SINGINT (Ctrl+C) callback to stop the nodelet properly.
std::vector< float > x_hit_pt_sel_
X coordinates of the hit points for the selected viewpoint (in ocean frame)
bool vert_motion_
Whether to allow motion in the vertical plane.
geometry_msgs::TransformStamped robot_ocean_tf_
Transform from robot to ocean frames.
ros::Subscriber state_sub_
Subscriber for the state of the robot.
Definition: common.hpp:23
Declaration of physical underwater robot model.
float path_res_
Spatial resolution (m) of the planned trajectory.
geometry_msgs::TransformStamped wall_ocean_tf_
Transform from wall to ocean frames.
ros::Subscriber gp_mean_sub_
Subscriber for the mean of the Gaussian Process.
float wall_orientation_
Orientation of the wall (absolute value)
float lattice_res_
Resolution (m) of the waypoints lattice.
bool enable_cb(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
Service server callback to enable planning.
tf2_ros::Buffer tf_buffer_
Buffer for tf2.
std::string camera_frame_
Camera frame.
int camera_width_
Number of pixels of the camera along width (-1 for actual camera size)
ros::NodeHandle nh_
Node handler (for topics and services)
geometry_msgs::TransformStamped ocean_camera_tf_
Transform from ocean to camera frames.
bool disable_cb(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
Service server callback to disable planning.
std::vector< std::vector< float > > array_to_vector2D(const std::vector< mf_common::Float32Array > &array)
Converts a custom ROS array to a 2D std vector.
std::vector< float > last_gp_mean_
Last mean of the Gaussian Process.
nav_msgs::Path path_
Interpolated path between the waypoints (in ocean frame)
void state_cb(const mf_common::Float32Array msg)
Callback for the state of the robot.
float main_freq_
Frequency of the main loop.
ros::Publisher path_pub_
Publisher for the path.
geometry_msgs::TransformStamped robot_wall_tf_
Transform from robot to wall frames.
ros::NodeHandle private_nh_
Private node handler (for parameters)
std::vector< geometry_msgs::Pose > lattice_
Lattice of possible waypoints in ocean frame.
geometry_msgs::TransformStamped ocean_wall_tf_
Transform from ocean to wall frames.
bool plan_trajectory()
Main function to compute a trajectory plan.
Definition: planning.cpp:812
std::vector< double > state_type
Data type of the state.
Definition: robot_model.hpp:51
void gp_mean_cb(const mf_common::Float32ArrayConstPtr msg)
Callback for Gaussian Process mean.
void pub_lattice_markers()
Publishes the waypoints lattice markers.
ros::ServiceClient update_gp_client_
Service client for updating the Gaussian Process.
std::vector< geometry_msgs::Pose > waypoints_
Waypoints to follow (in ocean frame)
ros::Subscriber gp_cov_sub_
Subscriber for the covariance of the Gaussian Process.
std::vector< std::vector< float > > last_gp_cov_
Last covariance of the Gaussian Process.
RobotModel::state_type state_
State of the robot (in ocean frame)
bool cart_lattice_
Whether to create a cartesian lattice, or use motion model instead.
std::vector< geometry_msgs::Pose > selected_vp_
Selected view points in the lattice (in ocean frame)
bool check_planning_needed()
Checks whether planning is needed or not.
bool horiz_motion_
Whether to allow motion in the horizontal plane.
std::vector< float > bnd_depth_
Bounds on the depth (in wall frame) for waypoint selection.
std::vector< float > z_hit_pt_sel_
Z coordinates of the hit points for the selected viewpoint (in ocean frame)
std::vector< float > y_hit_pt_sel_
Y coordinates of the hit points for the selected viewpoint (in ocean frame)
std::string ocean_frame_
Ocean frame.
Class defining the robot model.
Definition: robot_model.hpp:47
float max_elev_rudder_
Maximum angle of the elevation rudder.
ros::ServiceClient ray_multi_client_
Service client for raycasting at several camera poses.
static sig_atomic_t volatile b_sigint_
Whether SIGINT signal has been received.
virtual void onInit()
Function called at beginning of nodelet execution.
geometry_msgs::TransformStamped wall_robot_tf_
Transform from wall to robot frames.
static ros::Timer main_timer_
Timer callback for the main function.
RobotModel robot_model_
Robot model.
int lattice_size_vert_
Size of the lattice in the vertical direction (half size when single-lattice planning) ...
int lattice_size_horiz_
Size of the lattice in the horizontal direction (half size when single-lattice planning) ...
std::string wall_frame_
Wall frame.
bool linear_path_
Whether to create a linear path, or a spline one.
int camera_height_
Number of pixels of the camera along height (-1 for actual camera size)
float plan_speed_
Planned speed (m/s) of the robot.
std::string robot_frame_
Robot frame.
float max_lat_rudder_
Maximum angle of the lateral rudder.
bool get_tf()
Gets tf transforms.
bool planner_enabled_
Whether the planner should run.
bool replan_
Whether to replan from the current robot pose, or to plan from the last planned pose.
ros::ServiceServer disable_serv_
Service server to disable planning.
bool state_received_
Whether the state of the robot has been received.
Declaration of a nodelet for path plannning of an underwater robot surveying a marine farm...
ros::Publisher lattice_pub_
Publisher for the waypoints lattice.
bool mult_lattices_
Whether to use multi-lattices planning.
geometry_msgs::TransformStamped ocean_robot_tf_
Transform from ocean to robot frames.
Nodelet for path planning of an underwater robot surveying a marine farm.
void main_cb(const ros::TimerEvent &timer_event)
Main callback which is called by a timer.
ros::ServiceServer enable_serv_
Service server to enable planning.
void gp_cov_cb(const mf_common::Array2DConstPtr msg)
Callback for Gaussian Process covariance.
ros::Publisher lattice_pose_pub_
Publisher for the waypoints lattice poses.
std::vector< float > bnd_wall_dist_
Bounds on the distance to the wall for waypoint selection.