waypoints_navi.cpp
Go to the documentation of this file.
1 
7 
8 /*
9  * TODO
10  * * think about how to best visualise the waypoint(s)/trajectory(ies) which are being executed
11  * * add RViz interface to yocs_waypoint_provider
12  */
13 
14 namespace yocs
15 {
16 
18  state_(IDLE),
19  frequency_(5), // 5 hz
20  close_enough_(0.1), // 10 cm
21  goal_timeout_(60.0), // 60 sec
22  idle_status_update_sent_(false),
23  move_base_ac_("move_base", true)
24 {}
25 
27 {}
28 
30 {
31  ros::NodeHandle nh;
32  ros::NodeHandle pnh("~");
33 
34  pnh.param("frequency", frequency_, 1.0);
35  pnh.param("close_enough", close_enough_, 0.3); // close enough to next waypoint
36  pnh.param("goal_timeout", goal_timeout_, 30.0); // maximum time to reach a waypoint
37  pnh.param("robot_frame", robot_frame_, std::string("/base_footprint"));
38  pnh.param("world_frame", world_frame_, std::string("/map"));
39 
40  // reset goal way points
41  waypoints_.clear();
42  waypoints_it_ = waypoints_.end();
43 
44  while ((move_base_ac_.waitForServer(ros::Duration(1.0)) == false) && (ros::ok() == true))
45  {
46  ROS_WARN_THROTTLE(1, "Waiting for move_base action server to come up...");
47  }
48  waypoints_sub_ = nh.subscribe("waypoints", 1, &WaypointsGoalNode::waypointsCB, this);
49  trajectories_sub_ = nh.subscribe("trajectories", 1, &WaypointsGoalNode::trajectoriesCB, this);
50  nav_ctrl_sub_ = nh.subscribe("nav_ctrl", 1, &WaypointsGoalNode::navCtrlCB, this);
51  status_pub_ = nh.advertise<yocs_msgs::NavigationControlStatus>("nav_ctrl_status", 1, true);
52 
53  return true;
54 }
55 
56 void WaypointsGoalNode::waypointsCB(const yocs_msgs::WaypointList::ConstPtr& wps)
57 {
58  wp_list_ = *wps;
59  ROS_INFO_STREAM("Received " << wp_list_.waypoints.size() << " way points.");
60 }
61 
62 void WaypointsGoalNode::trajectoriesCB(const yocs_msgs::TrajectoryList::ConstPtr& trajs)
63 {
64  traj_list_ = *trajs;
65  ROS_INFO_STREAM("Received " << traj_list_.trajectories.size() << " trajectories.");
66 }
67 
68 void WaypointsGoalNode::navCtrlCB(const yocs_msgs::NavigationControl::ConstPtr& nav_ctrl)
69 {
70  if (nav_ctrl->control == yocs_msgs::NavigationControl::STOP)
71  {
72  if ((state_ == START) || (state_ == ACTIVE))
73  {
74  ROS_INFO_STREAM("Stopping current execution ...");
77  ROS_INFO_STREAM("Current execution stopped.");
79  state_ = IDLE;
80  publishStatusUpdate(yocs_msgs::NavigationControlStatus::CANCELLED);
81  }
82  else
83  {
84  ROS_WARN_STREAM("Cannot stop way point/trajectory execution, because nothing is being executed.");
85  }
86  }
87  else if ((nav_ctrl->control == yocs_msgs::NavigationControl::START))
88  {
89  if ((state_ == IDLE) || (state_ == COMPLETED))
90  {
92  // If provided goal is among the way points or trajectories, add the way point(s) to the goal way point list
93  bool goal_found = false;
94  for (unsigned int wp = 0; wp < wp_list_.waypoints.size(); ++wp)
95  {
96  if (nav_ctrl->goal_name == wp_list_.waypoints[wp].name)
97  {
98  geometry_msgs::PoseStamped pose;
99  pose.header = wp_list_.waypoints[wp].header;
100  pose.pose = wp_list_.waypoints[wp].pose;
101  waypoints_.push_back(pose);
102  waypoints_it_ = waypoints_.begin();
103  goal_found = true;
104  ROS_INFO_STREAM("Prepared to navigate to way point '" << nav_ctrl->goal_name << "'.");
105  continue;
106  }
107  }
108  if (!goal_found)
109  {
110  for (unsigned int traj = 0; traj < traj_list_.trajectories.size(); ++traj)
111  {
112  if (nav_ctrl->goal_name == traj_list_.trajectories[traj].name)
113  {
114  for (unsigned int wp = 0; wp < traj_list_.trajectories[traj].waypoints.size(); ++wp)
115  {
116  geometry_msgs::PoseStamped pose;
117  pose.header = traj_list_.trajectories[traj].waypoints[wp].header;
118  pose.pose = traj_list_.trajectories[traj].waypoints[wp].pose;
119  waypoints_.push_back(pose);
120  }
121  waypoints_it_ = waypoints_.begin();
122  goal_found = true;
123  ROS_INFO_STREAM("Prepared to navigate along the trajectory '" << nav_ctrl->goal_name << "'.");
124  ROS_INFO_STREAM("# of way points = " << waypoints_.size());
125  }
126  }
127  }
128  if (goal_found)
129  {
130  state_ = START;
131  mode_ = GOAL;
132  }
133  else
134  {
135  ROS_WARN_STREAM("Could not find provided way point or trajectory.");
136  }
137  }
138  else
139  {
140  ROS_WARN_STREAM("Cannot start way point/trajectory execution, because navigator is currently active. "
141  << "Please stop current activity first.");
142  }
143  }
144  else
145  {
146  // TODO: handle PAUSE
147  ROS_WARN_STREAM("'Pause' not yet implemented.");
148  }
149 }
150 
152 {
154  if ((goal_state != actionlib::SimpleClientGoalState::ACTIVE) &&
158  {
159  // We cannot cancel a REJECTED, ABORTED, SUCCEEDED or LOST goal
160  ROS_WARN("Cannot cancel move base goal, as it has %s state!", goal_state.toString().c_str());
161  publishStatusUpdate(yocs_msgs::NavigationControlStatus::ERROR);
162  return true;
163  }
164 
165  ROS_INFO("Canceling move base goal with %s state...", goal_state.toString().c_str());
167  if (move_base_ac_.waitForResult(ros::Duration(timeout)) == false)
168  {
169  ROS_WARN("Cancel move base goal didn't finish after %.2f seconds: %s",
170  timeout, goal_state.toString().c_str());
171  publishStatusUpdate(yocs_msgs::NavigationControlStatus::ERROR);
172  return false;
173  }
174 
175  ROS_INFO("Cancel move base goal succeed. New state is %s", goal_state.toString().c_str());
176  publishStatusUpdate(yocs_msgs::NavigationControlStatus::CANCELLED);
177  return true;
178 }
179 
181 {
182  ROS_DEBUG("Full reset: clear markers, delete waypoints and goal and set state to IDLE");
183  waypoints_.clear();
184  waypoints_it_ = waypoints_.end();
185  goal_ = NOWHERE;
186  mode_ = NONE;
187 }
188 
190 {
191  move_base_msgs::MoveBaseGoal mb_goal;
192 
193  ros::Rate rate(frequency_);
194 
195  while (ros::ok())
196  {
197  rate.sleep();
198  ros::spinOnce();
199 
200  if (state_ == START)
201  {
202  if (mode_ == LOOP)
203  {
204  if (waypoints_it_ == waypoints_.end())
205  {
206  waypoints_it_ = waypoints_.begin();
207  }
208  }
209 
210  if (waypoints_it_ < waypoints_.end())
211  {
212  mb_goal.target_pose.header.stamp = ros::Time::now();
213  mb_goal.target_pose.header.frame_id = waypoints_it_->header.frame_id;
214  mb_goal.target_pose.pose = waypoints_it_->pose;
215 // mb_goal.target_pose.pose.orientation = tf::createQuaternionMsgFromYaw(0.0); // TODO use the heading from robot loc to next (front)
216 
217  ROS_INFO("New goal: %.2f, %.2f, %.2f",
218  mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
219  tf::getYaw(mb_goal.target_pose.pose.orientation));
220  move_base_ac_.sendGoal(mb_goal);
221 
222  publishStatusUpdate(yocs_msgs::NavigationControlStatus::RUNNING);
223 
224  state_ = ACTIVE;
225  }
226  else
227  {
228  ROS_ERROR_STREAM("Cannot start execution. Already at the last way point.");
229  idle_status_update_sent_ = false;
230  state_ = IDLE;
231  }
232 
233  // TODO: This is a horrible workaround for a problem I cannot solve: send a new goal
234  // when the previous one has been cancelled return immediately with succeeded state
235  //
236  // Marcus: Don't understand this case (yet). Commenting out until we need it.
237 // int times_sent = 0;
238 // while ((move_base_ac_.waitForResult(ros::Duration(0.1)) == true) &&
239 // (move_base_ac_.getState() == actionlib::SimpleClientGoalState::SUCCEEDED))
240 // {
241 // move_base_ac_.sendGoal(mb_goal);
242 // times_sent++;
243 // }
244 // if (times_sent > 1)
245 // {
246 // ROS_WARN("Again the strange case of instantaneous goals... (goal sent %d times)", times_sent);
247 // }
248  }
249  else if (state_ == ACTIVE)
250  {
252 
253  // We are still pursuing a goal...
254  if ((goal_state == actionlib::SimpleClientGoalState::ACTIVE) ||
258  {
259  // check if we timed out
260  if ((ros::Time::now() - mb_goal.target_pose.header.stamp).toSec() >= goal_timeout_)
261  {
262  ROS_WARN("Cannot reach goal after %.2f seconds; request a new one (current state is %s)",
264  if (waypoints_it_ < (waypoints_.end() - 1))
265  {
266  ROS_INFO_STREAM("Requesting next way point.");
267  waypoints_it_++;
268  state_ = START;
269  }
270  else
271  {
272  ROS_INFO_STREAM("No more way points to go to.");
273  state_ = COMPLETED;
274  }
275  }
276  // When close enough to current goal (except for the final one!), go for the
277  // next waypoint, so we avoid the final slow approach and subgoal obsession
278  if (waypoints_it_ < (waypoints_.end() - 1))
279  {
280  tf::StampedTransform robot_gb, goal_gb;
281  try
282  {
284  }
285  catch (tf::TransformException& e)
286  {
287  ROS_WARN("Cannot get tf %s -> %s: %s", world_frame_.c_str(), robot_frame_.c_str(), e.what());
288  continue;
289  }
290 
291  mtk::pose2tf(mb_goal.target_pose, goal_gb);
292  double distance = mtk::distance2D(robot_gb, goal_gb);
293  if (distance <= close_enough_)
294  {
295  waypoints_it_++;
296  state_ = START;
297  ROS_INFO("Close enough to current goal (%.2f <= %.2f m).", distance, close_enough_);
298  ROS_INFO_STREAM("Requesting next way point.");
299  }
300  else
301  {
302  // keep going until get close enough
303  }
304  }
305  else
306  {
307  // keep going, since we approaching last way point
308  }
309  }
310  else // actionlib::SimpleClientGoalState::SUCCEEDED, REJECTED, ABORTED, LOST
311  {
313  {
314  ROS_INFO("Go to goal successfully completed: %.2f, %.2f, %.2f",
315  mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
316  tf::getYaw(mb_goal.target_pose.pose.orientation));
317  if (waypoints_it_ < (waypoints_.end() - 1))
318  {
319  ROS_INFO_STREAM("Requesting next way point.");
320  waypoints_it_++;
321  state_ = START;
322  }
323  else
324  {
325  ROS_INFO_STREAM("Reached final way point.");
326  state_ = COMPLETED;
327  }
328  }
329  else
330  {
331  ROS_ERROR("Go to goal failed: %s.", move_base_ac_.getState().toString().c_str());
332  if (waypoints_it_ < (waypoints_.end() - 1))
333  {
334  ROS_INFO_STREAM("Requesting next way point.");
335  waypoints_it_++;
336  state_ = START;
337  }
338  else
339  {
340  ROS_INFO_STREAM("No more way points to go to.");
341  state_ = COMPLETED;
342  }
343  }
344  }
345  }
346  else if(state_ == COMPLETED)
347  {
348  // publish update
349  publishStatusUpdate(yocs_msgs::NavigationControlStatus::COMPLETED);
350  idle_status_update_sent_ = false;
351  state_ = IDLE;
352  }
353  else // IDLE
354  {
356  {
357  publishStatusUpdate(yocs_msgs::NavigationControlStatus::IDLING);
359  }
360  }
361  }
362 }
363 
364 bool WaypointsGoalNode::equals(const geometry_msgs::PoseStamped& a, const geometry_msgs::PoseStamped& b)
365 {
366  return ((a.pose.position.x == b.pose.position.x) &&
367  (a.pose.position.y == b.pose.position.y) &&
368  (a.pose.position.z == b.pose.position.z));
369  // TODO make decent, with rotation (tk::minAngle, I think) and frame_id and put in math toolkit
370 }
371 
372 bool WaypointsGoalNode::equals(const geometry_msgs::Point& a, const geometry_msgs::Point& b)
373 {
374  return ((a.x == b.x) && (a.y == b.y) && (a.z == b.z));
375 }
376 
377 void WaypointsGoalNode::publishStatusUpdate(const uint8_t& status)
378 {
379  yocs_msgs::NavigationControlStatus msg;
380  if (status == yocs_msgs::NavigationControlStatus::IDLING)
381  {
382  msg.status = yocs_msgs::NavigationControlStatus::IDLING;
383  msg.status_desc = "Idling";
384  status_pub_.publish(msg);
385  }
386  else if (status == yocs_msgs::NavigationControlStatus::RUNNING)
387  {
388  msg.status = yocs_msgs::NavigationControlStatus::RUNNING;
389  msg.status_desc = "Navigating to way point.";
390  status_pub_.publish(msg);
391  }
392  else if (status == yocs_msgs::NavigationControlStatus::PAUSED)
393  {
394  msg.status = yocs_msgs::NavigationControlStatus::PAUSED;
395  msg.status_desc = "Navigation on hold.";
396  status_pub_.publish(msg);
397  }
398  else if (status == yocs_msgs::NavigationControlStatus::COMPLETED)
399  {
400  msg.status = yocs_msgs::NavigationControlStatus::COMPLETED;
401  msg.status_desc = "Reached final destination.";
402  status_pub_.publish(msg);
403  }
404  else if (status == yocs_msgs::NavigationControlStatus::CANCELLED)
405  {
406  msg.status = yocs_msgs::NavigationControlStatus::CANCELLED;
407  msg.status_desc = "Navigation cancelled.";
408  status_pub_.publish(msg);
409  }
410  else if (status == yocs_msgs::NavigationControlStatus::ERROR)
411  {
412  msg.status = yocs_msgs::NavigationControlStatus::ERROR;
413  msg.status_desc = "An error occurred.";
414  status_pub_.publish(msg);
415  }
416  else
417  {
418  ROS_ERROR_STREAM("Cannot publish unknown status updated!");
419  }
420 }
421 
422 } // namespace yocs
423 
void pose2tf(const geometry_msgs::Pose &pose, tf::Transform &tf)
bool equals(const geometry_msgs::PoseStamped &a, const geometry_msgs::PoseStamped &b)
bool cancelAllGoals(double timeout=2.0)
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
#define ROS_WARN_THROTTLE(rate,...)
const geometry_msgs::PoseStamped NOWHERE
void publish(const boost::shared_ptr< M > &message) const
void navCtrlCB(const yocs_msgs::NavigationControl::ConstPtr &nav_ctrl)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
bool waitForResult(const ros::Duration &timeout=ros::Duration(0, 0))
static double getYaw(const Quaternion &bt_q)
yocs_msgs::TrajectoryList traj_list_
ros::Subscriber trajectories_sub_
std::vector< geometry_msgs::PoseStamped >::iterator waypoints_it_
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
actionlib::SimpleActionClient< move_base_msgs::MoveBaseAction > move_base_ac_
#define ROS_WARN(...)
yocs_msgs::WaypointList wp_list_
void publishStatusUpdate(const uint8_t &status)
double distance2D(double ax, double ay, double bx, double by)
std::string toString() const
void trajectoriesCB(const yocs_msgs::TrajectoryList::ConstPtr &trajs)
ros::Subscriber nav_ctrl_sub_
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
enum yocs::WaypointsGoalNode::@1 state_
void waypointsCB(const yocs_msgs::WaypointList::ConstPtr &wps)
ROSCPP_DECL bool ok()
tf::TransformListener tf_listener_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
#define ROS_WARN_STREAM(args)
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
bool sleep()
std::vector< geometry_msgs::PoseStamped > waypoints_
void sendGoal(const Goal &goal, SimpleDoneCallback done_cb=SimpleDoneCallback(), SimpleActiveCallback active_cb=SimpleActiveCallback(), SimpleFeedbackCallback feedback_cb=SimpleFeedbackCallback())
#define ROS_INFO_STREAM(args)
geometry_msgs::PoseStamped goal_
static Time now()
enum yocs::WaypointsGoalNode::@0 mode_
ros::Publisher status_pub_
SimpleClientGoalState getState() const
#define ROS_ERROR_STREAM(args)
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
ros::Subscriber waypoints_sub_
#define ROS_DEBUG(...)


yocs_waypoints_navi
Author(s): Jorge Santos Simon
autogenerated on Mon Jun 10 2019 15:54:12