Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
planning_logic.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of a base class node managing high level planning logic
5  * \author Corentin Chauvin-Hameau
6  * \date 2020
7  */
8 
9 #include "planning_logic.hpp"
10 #include "mf_common/common.hpp"
11 #include "mf_common/spline.hpp"
12 #include <nav_msgs/Path.h>
13 #include <nav_msgs/Odometry.h>
14 #include <geometry_msgs/PoseStamped.h>
15 #include <geometry_msgs/Pose.h>
16 #include <std_srvs/Empty.h>
17 #include <ros/package.h>
18 #include <ros/ros.h>
19 #include <eigen3/Eigen/Dense>
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25 
26 using std::cout;
27 using std::endl;
28 using std::string;
29 using std::vector;
30 
31 
32 namespace mfcpp {
33 
34 
36  nh_("~")
37 {
38  init_node();
39 }
40 
41 
43 {
44 
45 }
46 
47 
49 {
50  // ROS parameters
51  string path_frame; // frame in which the path is expressed
52  string wp_file_name; // relative path of the file containing the waypoints
53  string transition_file_name; // relative path of the file containing the points delimiting the transitions
54  float plan_res; // spatial resolution (m) of the planned trajectory
55  bool initially_disabled; // whether to disable the planner at the beginning
56 
57  nh_.param<float>("main_freq", main_freq_, 1.0);
58  nh_.param<string>("path_frame", path_frame, "ocean");
59  nh_.param<string>("wp_file_name", wp_file_name, "resources/path.txt");
60  nh_.param<string>("transition_file_name", transition_file_name, "resources/transition.txt");
61  nh_.param<float>("plan_res", plan_res, 1.0);
62  nh_.param<bool>("initially_disabled", initially_disabled, false);
63 
64  // Other variables
65  transition_ = false;
66  odom_received_ = false;
67  path_loaded_ = false;
68  transition_pts_loaded_ = false;
69 
70  path_.header.frame_id = path_frame;
71  load_path(wp_file_name, plan_res, path_);
72  load_transition_pts(transition_file_name);
73 
74  if (initially_disabled) {
75  transition_ = true;
76  transition_pts_.erase(transition_pts_.begin());
77  } else {
78  transition_ = false;
79  }
80 
81  // ROS subscribers
82  odom_sub_ = nh_.subscribe<nav_msgs::Odometry>("odom", 1, &PlanningLogic::odom_cb, this);
83 
84  // ROS publishers
85  path_pub_ = nh_.advertise<nav_msgs::Path>("path", 0);
86 
87  // ROS services
88  disable_planner_client_ = nh_.serviceClient<std_srvs::Empty>("disable_planner");
89  enable_planner_client_ = nh_.serviceClient<std_srvs::Empty>("enable_planner");
90 }
91 
92 
94 {
96  ROS_ERROR("[planning_logic] Couldn't read transition path, shutting down...");
97  return;
98  }
99 
100  ros::Rate loop_rate(main_freq_);
101 
102  while (ros::ok()) {
103  ros::spinOnce();
104 
105  // Check position of the robot
106  if (odom_received_ && transition_pts_.size() > 0) {
107  geometry_msgs::Pose pose = find_closest(path_.poses, odom_.pose.pose);
108  Eigen::Vector3f pos(pose.position.x, pose.position.y, pose.position.z);
109  float distance = (pos - transition_pts_[0]).norm();
110 
111  if (distance < 0.5) {
112  std_srvs::Empty srv;
113  double srv_called;
114 
115  if (transition_)
116  srv_called = enable_planner_client_.call(srv);
117  else
118  srv_called = disable_planner_client_.call(srv);
119 
120  if (srv_called) {
121  transition_pts_.erase(transition_pts_.begin());
123  } else
124  ROS_WARN("[planning_logic] Enable/Disable call to planner failed");
125  }
126  }
127 
128  // Publish the path if needed
129  if (transition_) {
130  path_.header.stamp = ros::Time::now();
131  path_pub_.publish(path_);
132  }
133 
134  loop_rate.sleep();
135  }
136 }
137 
138 
139 void PlanningLogic::load_path(string file_name, float resolution, nav_msgs::Path &path)
140 {
141  // Open path file
142  string package_path = ros::package::getPath("mf_navigation");
143  string file_path = package_path + '/' + file_name;
144 
145  std::ifstream file(file_path.c_str());
146 
147  if (!file.is_open()) {
148  ROS_WARN("[planning_logic] Path file couldn't be opened at: %s", file_path.c_str());
149  return;
150  }
151 
152  // Read the waypoints
153  vector<Eigen::Vector3f> p(0); // waypoints positions
154  vector<Eigen::Vector3f> o(0); // waypoints orientations
155  int k = 0;
156 
157  for (string line; std::getline(file, line); ) {
158  if (!line.empty()) {
159  p.emplace_back(Eigen::Vector3f());
160  o.emplace_back(Eigen::Vector3f());
161  std::istringstream in(line);
162  in >> p[k](0) >> p[k](1) >> p[k](2) >> o[k](0) >> o[k](1) >> o[k](2);
163 
164  k++;
165  }
166  }
167 
168  // Interpolate the path
169  Spline spline(p, o, 1.0);
170 
171  path_.poses.resize(0);
172  bool last_reached = false;
173  float t = 0;
174 
175  while (!last_reached) {
176  Eigen::Vector3f p;
177  Eigen::Vector3f o;
178 
179  spline.evaluate(t, p, o, last_reached);
180  o = o / o.norm();
181 
182  geometry_msgs::PoseStamped pose;
183  pose.pose.position.x = p(0);
184  pose.pose.position.y = p(1);
185  pose.pose.position.z = p(2);
186  to_quaternion(0.0, -asin(o(2)), atan2(o(1), o(0)), pose.pose.orientation);
187 
188  path_.poses.emplace_back(pose);
189  t += resolution;
190  }
191 
192  // Cleaning
193  file.close();
194  path_loaded_ = true;
195 }
196 
197 
198 void PlanningLogic::load_transition_pts(std::string file_name)
199 {
200  // Open path file
201  string package_path = ros::package::getPath("mf_navigation");
202  string file_path = package_path + '/' + file_name;
203 
204  std::ifstream file(file_path.c_str());
205 
206  if (!file.is_open()) {
207  ROS_WARN("[planning_logic] Path file couldn't be opened at: %s", file_path.c_str());
208  return;
209  }
210 
211  // Read the waypoints
212  transition_pts_.resize(0);
213  int k = 0;
214 
215  for (string line; std::getline(file, line); ) {
216  if (!line.empty()) {
217  transition_pts_.emplace_back(Eigen::Vector3f());
218  std::istringstream in(line);
219  in >> transition_pts_[k](0) >> transition_pts_[k](1) >> transition_pts_[k](2);
220 
221  k++;
222  }
223  }
224 
225  // Cleaning
226  file.close();
227  transition_pts_loaded_ = true;
228 }
229 
230 
231 geometry_msgs::Pose PlanningLogic::find_closest(
232  const std::vector<geometry_msgs::PoseStamped> &path,
233  const geometry_msgs::Pose &pose)
234 {
235  if (path.size() == 0)
236  return geometry_msgs::Pose();
237 
238  float min_dist = distance2(path[0].pose.position, pose.position);
239  int best_k = 0;
240 
241  for (int k = 1; k < path.size(); k++) {
242  float d = distance2(path[k].pose.position, pose.position);
243 
244  if (d < min_dist) {
245  min_dist = d;
246  best_k = k;
247  }
248  }
249 
250  return path[best_k].pose;
251 }
252 
253 
254 void PlanningLogic::odom_cb(const nav_msgs::Odometry msg)
255 {
256  odom_ = msg;
257  odom_received_ = true;
258 }
259 
260 
261 } // namespace mfcpp
262 
263 
264 
265 /**
266  * \brief Main function called before node initialisation
267  */
268 int main(int argc, char **argv)
269 {
270  ros::init(argc, argv, "planning_logic");
271  mfcpp::PlanningLogic planning_logic;
272  planning_logic.run_node();
273 
274  return 0;
275 }
Base class for a node managing high level planning logic.
Definition: common.hpp:23
void load_transition_pts(std::string file_name)
Loads the points delimiting the transitions.
bool transition_pts_loaded_
Whether the transition points have been loaded.
nav_msgs::Odometry odom_
Odometry of the robot.
std::vector< Eigen::Vector3f > transition_pts_
Points marking start and end of transition paths.
ros::ServiceClient enable_planner_client_
Client for enabling the path planner.
ros::Subscriber odom_sub_
Subscriber for robot&#39;s odometry.
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
void run_node()
Runs the node.
Declaration of a base class node managing high level planning logic.
void odom_cb(const nav_msgs::Odometry msg)
Callback for the odometry.
void init_node()
Initialises the node and its parameters.
void load_path(std::string file_name, float resolution, nav_msgs::Path &path)
Loads a path from a text file.
Declaration of common functions.
ros::Publisher path_pub_
Publisher for a path.
Declaration of a class for 3D spline interpolation.
int main(int argc, char **argv)
Main function called before node initialisation.
float distance(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Computes the the euclidean distance between two positions.
Definition: common.hpp:69
bool transition_
Whether the robot is following a transition path.
ros::ServiceClient disable_planner_client_
Client for disabling the path planner.
bool odom_received_
Whether the odometry has been received.
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
ros::NodeHandle nh_
Node handler.
geometry_msgs::Pose find_closest(const std::vector< geometry_msgs::PoseStamped > &path, const geometry_msgs::Pose &pose)
Finds the closest pose in a path.
bool path_loaded_
Whether the path was loaded with success.
Class for spline interpolation.
Definition: spline.hpp:24
nav_msgs::Path path_
Path to publish.