waypoints_navi.cpp
Go to the documentation of this file.
00001 
00006 #include "yocs_waypoints_navi/waypoints_navi.hpp"
00007 
00008 /*
00009  * TODO
00010  *  * think about how to best visualise the waypoint(s)/trajectory(ies) which are being executed
00011  *  * add RViz interface to yocs_waypoint_provider
00012  */
00013 
00014 namespace yocs
00015 {
00016 
00017 WaypointsGoalNode::WaypointsGoalNode() : mode_(NONE),
00018                                          state_(IDLE),
00019                                          frequency_(5), // 5 hz
00020                                          close_enough_(0.1), // 10 cm
00021                                          goal_timeout_(60.0), // 60 sec
00022                                          idle_status_update_sent_(false),
00023                                          move_base_ac_("move_base", true)
00024 {}
00025 
00026 WaypointsGoalNode::~WaypointsGoalNode()
00027 {}
00028 
00029 bool WaypointsGoalNode::init()
00030 {
00031   ros::NodeHandle nh;
00032   ros::NodeHandle pnh("~");
00033 
00034   pnh.param("frequency",      frequency_,     1.0);
00035   pnh.param("close_enough",   close_enough_,  0.3);  // close enough to next waypoint
00036   pnh.param("goal_timeout",   goal_timeout_, 30.0);  // maximum time to reach a waypoint
00037   pnh.param("robot_frame",    robot_frame_,    std::string("/base_footprint"));
00038   pnh.param("world_frame",    world_frame_,    std::string("/map"));
00039 
00040   // reset goal way points
00041   waypoints_.clear();
00042   waypoints_it_ = waypoints_.end();
00043 
00044   while ((move_base_ac_.waitForServer(ros::Duration(1.0)) == false) && (ros::ok() == true))
00045   {
00046     ROS_WARN_THROTTLE(1, "Waiting for move_base action server to come up...");
00047   }
00048   waypoints_sub_  = nh.subscribe("waypoints",  1, &WaypointsGoalNode::waypointsCB, this);
00049   trajectories_sub_  = nh.subscribe("trajectories",  1, &WaypointsGoalNode::trajectoriesCB, this);
00050   nav_ctrl_sub_  = nh.subscribe("nav_ctrl", 1, &WaypointsGoalNode::navCtrlCB, this);
00051   status_pub_  = nh.advertise<yocs_msgs::NavigationControlStatus>("nav_ctrl_status", 1, true);
00052 
00053   return true;
00054 }
00055 
00056 void WaypointsGoalNode::waypointsCB(const yocs_msgs::WaypointList::ConstPtr& wps)
00057 {
00058   wp_list_ = *wps;
00059   ROS_INFO_STREAM("Received " << wp_list_.waypoints.size() << " way points.");
00060 }
00061 
00062 void WaypointsGoalNode::trajectoriesCB(const yocs_msgs::TrajectoryList::ConstPtr& trajs)
00063 {
00064   traj_list_ = *trajs;
00065   ROS_INFO_STREAM("Received " << traj_list_.trajectories.size() << " trajectories.");
00066 }
00067 
00068 void WaypointsGoalNode::navCtrlCB(const yocs_msgs::NavigationControl::ConstPtr& nav_ctrl)
00069 {
00070   if (nav_ctrl->control == yocs_msgs::NavigationControl::STOP)
00071   {
00072     if ((state_ == START) || (state_ == ACTIVE))
00073     {
00074       ROS_INFO_STREAM("Stopping current execution ...");
00075       cancelAllGoals();
00076       resetWaypoints();
00077       ROS_INFO_STREAM("Current execution stopped.");
00078       idle_status_update_sent_ = false;
00079       state_ = IDLE;
00080       publishStatusUpdate(yocs_msgs::NavigationControlStatus::CANCELLED);
00081     }
00082     else
00083     {
00084       ROS_WARN_STREAM("Cannot stop way point/trajectory execution, because nothing is being executed.");
00085     }
00086   }
00087   else if ((nav_ctrl->control == yocs_msgs::NavigationControl::START))
00088   {
00089     if ((state_ == IDLE) || (state_ == COMPLETED))
00090     {
00091       resetWaypoints();
00092       // If provided goal is among the way points or trajectories, add the way point(s) to the goal way point list
00093       bool goal_found = false;
00094       for (unsigned int wp = 0; wp < wp_list_.waypoints.size(); ++wp)
00095       {
00096         if (nav_ctrl->goal_name == wp_list_.waypoints[wp].name)
00097         {
00098           geometry_msgs::PoseStamped pose;
00099           pose.header = wp_list_.waypoints[wp].header;
00100           pose.pose = wp_list_.waypoints[wp].pose;
00101           waypoints_.push_back(pose);
00102           waypoints_it_ = waypoints_.begin();
00103           goal_found = true;
00104           ROS_INFO_STREAM("Prepared to navigate to way point '" << nav_ctrl->goal_name << "'.");
00105           continue;
00106         }
00107       }
00108       if (!goal_found)
00109       {
00110         for (unsigned int traj = 0; traj < traj_list_.trajectories.size(); ++traj)
00111         {
00112           if (nav_ctrl->goal_name == traj_list_.trajectories[traj].name)
00113           {
00114             for (unsigned int wp = 0; wp < traj_list_.trajectories[traj].waypoints.size(); ++wp)
00115             {
00116               geometry_msgs::PoseStamped pose;
00117               pose.header = traj_list_.trajectories[traj].waypoints[wp].header;
00118               pose.pose = traj_list_.trajectories[traj].waypoints[wp].pose;
00119               waypoints_.push_back(pose);
00120             }
00121             waypoints_it_ = waypoints_.begin();
00122             goal_found = true;
00123             ROS_INFO_STREAM("Prepared to navigate along the trajectory '" << nav_ctrl->goal_name << "'.");
00124             ROS_INFO_STREAM("# of way points = " << waypoints_.size());
00125           }
00126         }
00127       }
00128       if (goal_found)
00129       {
00130         state_ = START;
00131         mode_  = GOAL;
00132       }
00133       else
00134       {
00135         ROS_WARN_STREAM("Could not find provided way point or trajectory.");
00136       }
00137     }
00138     else
00139     {
00140       ROS_WARN_STREAM("Cannot start way point/trajectory execution, because navigator is currently active. "
00141                       << "Please stop current activity first.");
00142     }
00143   }
00144   else
00145   {
00146     // TODO: handle PAUSE
00147     ROS_WARN_STREAM("'Pause' not yet implemented.");
00148   }
00149 }
00150 
00151 bool WaypointsGoalNode::cancelAllGoals(double timeout)
00152 {
00153   actionlib::SimpleClientGoalState goal_state = move_base_ac_.getState();
00154   if ((goal_state != actionlib::SimpleClientGoalState::ACTIVE) &&
00155       (goal_state != actionlib::SimpleClientGoalState::PENDING) &&
00156       (goal_state != actionlib::SimpleClientGoalState::RECALLED) &&
00157       (goal_state != actionlib::SimpleClientGoalState::PREEMPTED))
00158   {
00159     // We cannot cancel a REJECTED, ABORTED, SUCCEEDED or LOST goal
00160     ROS_WARN("Cannot cancel move base goal, as it has %s state!", goal_state.toString().c_str());
00161     publishStatusUpdate(yocs_msgs::NavigationControlStatus::ERROR);
00162     return true;
00163   }
00164 
00165   ROS_INFO("Canceling move base goal with %s state...", goal_state.toString().c_str());
00166   move_base_ac_.cancelAllGoals();
00167   if (move_base_ac_.waitForResult(ros::Duration(timeout)) == false)
00168   {
00169     ROS_WARN("Cancel move base goal didn't finish after %.2f seconds: %s",
00170              timeout, goal_state.toString().c_str());
00171     publishStatusUpdate(yocs_msgs::NavigationControlStatus::ERROR);
00172     return false;
00173   }
00174 
00175   ROS_INFO("Cancel move base goal succeed. New state is %s", goal_state.toString().c_str());
00176   publishStatusUpdate(yocs_msgs::NavigationControlStatus::CANCELLED);
00177   return true;
00178 }
00179 
00180 void WaypointsGoalNode::resetWaypoints()
00181 {
00182   ROS_DEBUG("Full reset: clear markers, delete waypoints and goal and set state to IDLE");
00183   waypoints_.clear();
00184   waypoints_it_ = waypoints_.end();
00185   goal_  = NOWHERE;
00186   mode_  = NONE;
00187 }
00188 
00189 void WaypointsGoalNode::spin()
00190 {
00191   move_base_msgs::MoveBaseGoal mb_goal;
00192 
00193   ros::Rate rate(frequency_);
00194 
00195   while (ros::ok())
00196   {
00197     rate.sleep();
00198     ros::spinOnce();
00199 
00200     if (state_ == START)
00201     {
00202       if (mode_ == LOOP)
00203       {
00204         if (waypoints_it_ == waypoints_.end())
00205         {
00206           waypoints_it_ = waypoints_.begin();
00207         }
00208       }
00209 
00210       if (waypoints_it_ < waypoints_.end())
00211       {
00212         mb_goal.target_pose.header.stamp = ros::Time::now();
00213         mb_goal.target_pose.header.frame_id = waypoints_it_->header.frame_id;
00214         mb_goal.target_pose.pose = waypoints_it_->pose;
00215 //        mb_goal.target_pose.pose.orientation = tf::createQuaternionMsgFromYaw(0.0);  // TODO use the heading from robot loc to next (front)
00216 
00217         ROS_INFO("New goal: %.2f, %.2f, %.2f",
00218                  mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
00219                  tf::getYaw(mb_goal.target_pose.pose.orientation));
00220         move_base_ac_.sendGoal(mb_goal);
00221 
00222         publishStatusUpdate(yocs_msgs::NavigationControlStatus::RUNNING);
00223 
00224         state_ = ACTIVE;
00225       }
00226       else
00227       {
00228         ROS_ERROR_STREAM("Cannot start execution. Already at the last way point.");
00229         idle_status_update_sent_ = false;
00230         state_ = IDLE;
00231       }
00232 
00233       // TODO: This is a horrible workaround for a problem I cannot solve: send a new goal
00234       // when the previous one has been cancelled return immediately with succeeded state
00235       //
00236       // Marcus: Don't understand this case (yet). Commenting out until we need it.
00237 //        int times_sent = 0;
00238 //        while ((move_base_ac_.waitForResult(ros::Duration(0.1)) == true) &&
00239 //               (move_base_ac_.getState() == actionlib::SimpleClientGoalState::SUCCEEDED))
00240 //        {
00241 //          move_base_ac_.sendGoal(mb_goal);
00242 //          times_sent++;
00243 //        }
00244 //        if (times_sent > 1)
00245 //        {
00246 //          ROS_WARN("Again the strange case of instantaneous goals... (goal sent %d times)", times_sent);
00247 //        }
00248     }
00249     else if (state_ == ACTIVE)
00250     {
00251       actionlib::SimpleClientGoalState goal_state = move_base_ac_.getState();
00252 
00253       // We are still pursuing a goal...
00254       if ((goal_state == actionlib::SimpleClientGoalState::ACTIVE) ||
00255           (goal_state == actionlib::SimpleClientGoalState::PENDING) ||
00256           (goal_state == actionlib::SimpleClientGoalState::RECALLED) ||
00257           (goal_state == actionlib::SimpleClientGoalState::PREEMPTED))
00258       {
00259         // check if we timed out
00260         if ((ros::Time::now() - mb_goal.target_pose.header.stamp).toSec() >= goal_timeout_)
00261         {
00262           ROS_WARN("Cannot reach goal after %.2f seconds; request a new one (current state is %s)",
00263                     goal_timeout_, move_base_ac_.getState().toString().c_str());
00264           if (waypoints_it_ < (waypoints_.end() - 1))
00265           {
00266             ROS_INFO_STREAM("Requesting next way point.");
00267             waypoints_it_++;
00268             state_ = START;
00269           }
00270           else
00271           {
00272             ROS_INFO_STREAM("No more way points to go to.");
00273             state_ = COMPLETED;
00274           }
00275         }
00276         // When close enough to current goal (except for the final one!), go for the
00277         // next waypoint, so we avoid the final slow approach and subgoal obsession
00278         if (waypoints_it_ < (waypoints_.end() - 1))
00279         {
00280           tf::StampedTransform robot_gb, goal_gb;
00281           try
00282           {
00283             tf_listener_.lookupTransform(world_frame_, robot_frame_, ros::Time(0.0), robot_gb);
00284           }
00285           catch (tf::TransformException& e)
00286           {
00287             ROS_WARN("Cannot get tf %s -> %s: %s", world_frame_.c_str(), robot_frame_.c_str(), e.what());
00288             continue;
00289           }
00290 
00291           mtk::pose2tf(mb_goal.target_pose, goal_gb);
00292           double distance = mtk::distance2D(robot_gb, goal_gb);
00293           if (distance <= close_enough_)
00294           {
00295             waypoints_it_++;
00296             state_ = START;
00297             ROS_INFO("Close enough to current goal (%.2f <= %.2f m).", distance, close_enough_);
00298             ROS_INFO_STREAM("Requesting next way point.");
00299           }
00300           else
00301           {
00302             // keep going until get close enough
00303           }
00304         }
00305         else
00306         {
00307           // keep going, since we approaching last way point
00308         }
00309       }
00310       else // actionlib::SimpleClientGoalState::SUCCEEDED, REJECTED, ABORTED, LOST
00311       {
00312         if (goal_state == actionlib::SimpleClientGoalState::SUCCEEDED)
00313         {
00314           ROS_INFO("Go to goal successfully completed: %.2f, %.2f, %.2f",
00315                    mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
00316                    tf::getYaw(mb_goal.target_pose.pose.orientation));
00317           if (waypoints_it_ < (waypoints_.end() - 1))
00318           {
00319             ROS_INFO_STREAM("Requesting next way point.");
00320             waypoints_it_++;
00321             state_ = START;
00322           }
00323           else
00324           {
00325             ROS_INFO_STREAM("Reached final way point.");
00326             state_ = COMPLETED;
00327           }
00328         }
00329         else
00330         {
00331           ROS_ERROR("Go to goal failed: %s.", move_base_ac_.getState().toString().c_str());
00332           if (waypoints_it_ < (waypoints_.end() - 1))
00333           {
00334             ROS_INFO_STREAM("Requesting next way point.");
00335             waypoints_it_++;
00336             state_ = START;
00337           }
00338           else
00339           {
00340             ROS_INFO_STREAM("No more way points to go to.");
00341             state_ = COMPLETED;
00342           }
00343         }
00344       }
00345     }
00346     else if(state_ == COMPLETED)
00347     {
00348       // publish update
00349       publishStatusUpdate(yocs_msgs::NavigationControlStatus::COMPLETED);
00350       idle_status_update_sent_ = false;
00351       state_ = IDLE;
00352     }
00353     else // IDLE
00354     {
00355       if (!idle_status_update_sent_)
00356       {
00357         publishStatusUpdate(yocs_msgs::NavigationControlStatus::IDLING);
00358         idle_status_update_sent_ = true;
00359       }
00360     }
00361   }
00362 }
00363 
00364 bool WaypointsGoalNode::equals(const geometry_msgs::PoseStamped& a, const geometry_msgs::PoseStamped& b)
00365 {
00366   return ((a.pose.position.x == b.pose.position.x) &&
00367           (a.pose.position.y == b.pose.position.y) &&
00368           (a.pose.position.z == b.pose.position.z));
00369   // TODO make decent, with rotation (tk::minAngle, I think) and frame_id and put in math toolkit
00370 }
00371 
00372 bool WaypointsGoalNode::equals(const geometry_msgs::Point& a, const geometry_msgs::Point& b)
00373 {
00374   return ((a.x == b.x) && (a.y == b.y) && (a.z == b.z));
00375 }
00376 
00377 void WaypointsGoalNode::publishStatusUpdate(const uint8_t& status)
00378 {
00379   yocs_msgs::NavigationControlStatus msg;
00380   if (status == yocs_msgs::NavigationControlStatus::IDLING)
00381   {
00382     msg.status = yocs_msgs::NavigationControlStatus::IDLING;
00383     msg.status_desc = "Idling";
00384     status_pub_.publish(msg);
00385   }
00386   else if (status == yocs_msgs::NavigationControlStatus::RUNNING)
00387   {
00388     msg.status = yocs_msgs::NavigationControlStatus::RUNNING;
00389     msg.status_desc = "Navigating to way point.";
00390     status_pub_.publish(msg);
00391   }
00392   else if (status == yocs_msgs::NavigationControlStatus::PAUSED)
00393   {
00394     msg.status = yocs_msgs::NavigationControlStatus::PAUSED;
00395     msg.status_desc = "Navigation on hold.";
00396     status_pub_.publish(msg);
00397   }
00398   else if (status == yocs_msgs::NavigationControlStatus::COMPLETED)
00399   {
00400     msg.status = yocs_msgs::NavigationControlStatus::COMPLETED;
00401     msg.status_desc = "Reached final destination.";
00402     status_pub_.publish(msg);
00403   }
00404   else if (status == yocs_msgs::NavigationControlStatus::CANCELLED)
00405   {
00406     msg.status = yocs_msgs::NavigationControlStatus::CANCELLED;
00407     msg.status_desc = "Navigation cancelled.";
00408     status_pub_.publish(msg);
00409   }
00410   else if (status == yocs_msgs::NavigationControlStatus::ERROR)
00411   {
00412     msg.status = yocs_msgs::NavigationControlStatus::ERROR;
00413     msg.status_desc = "An error occurred.";
00414     status_pub_.publish(msg);
00415   }
00416   else
00417   {
00418     ROS_ERROR_STREAM("Cannot publish unknown status updated!");
00419   }
00420 }
00421 
00422 } // namespace yocs
00423 


yocs_waypoints_navi
Author(s): Jorge Santos Simon
autogenerated on Thu Jun 6 2019 21:47:46