abstract_controller_execution.cpp
Go to the documentation of this file.
00001 /*
00002  *  Copyright 2018, Magazino GmbH, Sebastian Pütz, Jorge Santos Simón
00003  *
00004  *  Redistribution and use in source and binary forms, with or without
00005  *  modification, are permitted provided that the following conditions
00006  *  are met:
00007  *
00008  *  1. Redistributions of source code must retain the above copyright
00009  *     notice, this list of conditions and the following disclaimer.
00010  *
00011  *  2. Redistributions in binary form must reproduce the above
00012  *     copyright notice, this list of conditions and the following
00013  *     disclaimer in the documentation and/or other materials provided
00014  *     with the distribution.
00015  *
00016  *  3. Neither the name of the copyright holder nor the names of its
00017  *     contributors may be used to endorse or promote products derived
00018  *     from this software without specific prior written permission.
00019  *
00020  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  *  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031  *  POSSIBILITY OF SUCH DAMAGE.
00032  *
00033  *  abstract_controller_execution.cpp
00034  *
00035  *  authors:
00036  *    Sebastian Pütz <spuetz@uni-osnabrueck.de>
00037  *    Jorge Santos Simón <santos@magazino.eu>
00038  *
00039  */
00040 
00041 #include "mbf_abstract_nav/abstract_controller_execution.h"
00042 #include <mbf_msgs/ExePathResult.h>
00043 
00044 namespace mbf_abstract_nav
00045 {
00046 
00047   const double AbstractControllerExecution::DEFAULT_CONTROLLER_FREQUENCY = 100.0; // 100 Hz
00048 
00049   AbstractControllerExecution::AbstractControllerExecution(
00050       const std::string name,
00051       const mbf_abstract_core::AbstractController::Ptr& controller_ptr,
00052       const ros::Publisher& vel_pub,
00053       const ros::Publisher& goal_pub,
00054       const TFPtr &tf_listener_ptr,
00055       const MoveBaseFlexConfig &config,
00056       boost::function<void()> setup_fn,
00057       boost::function<void()> cleanup_fn) :
00058     AbstractExecutionBase(name, setup_fn, cleanup_fn),
00059       controller_(controller_ptr), tf_listener_ptr(tf_listener_ptr), state_(INITIALIZED),
00060       moving_(false), max_retries_(0), patience_(0), vel_pub_(vel_pub), current_goal_pub_(goal_pub),
00061       calling_duration_(boost::chrono::microseconds(static_cast<int>(1e6 / DEFAULT_CONTROLLER_FREQUENCY)))
00062   {
00063     ros::NodeHandle nh;
00064     ros::NodeHandle private_nh("~");
00065 
00066     // non-dynamically reconfigurable parameters
00067     private_nh.param("robot_frame", robot_frame_, std::string("base_link"));
00068     private_nh.param("map_frame", global_frame_, std::string("map"));
00069     private_nh.param("mbf_tolerance_check", mbf_tolerance_check_, false);
00070     private_nh.param("dist_tolerance", dist_tolerance_, 0.1);
00071     private_nh.param("angle_tolerance", angle_tolerance_, M_PI / 18.0);
00072     private_nh.param("tf_timeout", tf_timeout_, 1.0);
00073 
00074     // dynamically reconfigurable parameters
00075     reconfigure(config);
00076   }
00077 
00078   AbstractControllerExecution::~AbstractControllerExecution()
00079   {
00080   }
00081 
00082   bool AbstractControllerExecution::setControllerFrequency(double frequency)
00083   {
00084     // set the calling duration by the moving frequency
00085     if (frequency <= 0.0)
00086     {
00087       ROS_ERROR("Controller frequency must be greater than 0.0! No change of the frequency!");
00088       return false;
00089     }
00090     calling_duration_ = boost::chrono::microseconds(static_cast<int>(1e6 / frequency));
00091     return true;
00092   }
00093 
00094   void AbstractControllerExecution::reconfigure(const MoveBaseFlexConfig &config)
00095   {
00096     boost::lock_guard<boost::mutex> guard(configuration_mutex_);
00097     // Timeout granted to the controller. We keep calling it up to this time or up to max_retries times
00098     // If it doesn't return within time, the navigator will cancel it and abort the corresponding action
00099     patience_ = ros::Duration(config.controller_patience);
00100 
00101     setControllerFrequency(config.controller_frequency);
00102 
00103     max_retries_ = config.controller_max_retries;
00104   }
00105 
00106 
00107   bool AbstractControllerExecution::start()
00108   {
00109     setState(STARTED);
00110     if (moving_)
00111     {
00112       return false; // thread is already running.
00113     }
00114     moving_ = true;
00115     return AbstractExecutionBase::start();
00116   }
00117 
00118 
00119   void AbstractControllerExecution::setState(ControllerState state)
00120   {
00121     boost::lock_guard<boost::mutex> guard(state_mtx_);
00122     state_ = state;
00123   }
00124 
00125 
00126   typename AbstractControllerExecution::ControllerState
00127   AbstractControllerExecution::getState()
00128   {
00129     boost::lock_guard<boost::mutex> guard(state_mtx_);
00130     return state_;
00131   }
00132 
00133   void AbstractControllerExecution::setNewPlan(const std::vector<geometry_msgs::PoseStamped> &plan)
00134   {
00135     if (moving_)
00136     {
00137       // This is fine on continuous replanning
00138       ROS_DEBUG("Setting new plan while moving");
00139     }
00140     boost::lock_guard<boost::mutex> guard(plan_mtx_);
00141     new_plan_ = true;
00142 
00143     plan_ = plan;
00144   }
00145 
00146 
00147   bool AbstractControllerExecution::hasNewPlan()
00148   {
00149     boost::lock_guard<boost::mutex> guard(plan_mtx_);
00150     return new_plan_;
00151   }
00152 
00153 
00154   std::vector<geometry_msgs::PoseStamped> AbstractControllerExecution::getNewPlan()
00155   {
00156     boost::lock_guard<boost::mutex> guard(plan_mtx_);
00157     new_plan_ = false;
00158     return plan_;
00159   }
00160 
00161 
00162   bool AbstractControllerExecution::computeRobotPose()
00163   {
00164     bool tf_success = mbf_utility::getRobotPose(*tf_listener_ptr, robot_frame_, global_frame_,
00165                                                 ros::Duration(tf_timeout_), robot_pose_);
00166     // would be 0 if not, as we ask tf listener for the last pose available
00167     robot_pose_.header.stamp = ros::Time::now();
00168     if (!tf_success)
00169     {
00170       ROS_ERROR_STREAM("Could not get the robot pose in the global frame. - robot frame: \""
00171                            << robot_frame_ << "\"   global frame: \"" << global_frame_ << std::endl);
00172       message_ = "Could not get the robot pose";
00173       outcome_ = mbf_msgs::ExePathResult::TF_ERROR;
00174       return false;
00175     }
00176     return true;
00177   }
00178 
00179 
00180   uint32_t AbstractControllerExecution::computeVelocityCmd(const geometry_msgs::PoseStamped& robot_pose,
00181                                                            const geometry_msgs::TwistStamped& robot_velocity,
00182                                                            geometry_msgs::TwistStamped& vel_cmd,
00183                                                            std::string& message)
00184   {
00185     return controller_->computeVelocityCommands(robot_pose, robot_velocity, vel_cmd, message);
00186   }
00187 
00188 
00189   void AbstractControllerExecution::setVelocityCmd(const geometry_msgs::TwistStamped &vel_cmd)
00190   {
00191     boost::lock_guard<boost::mutex> guard(vel_cmd_mtx_);
00192     vel_cmd_stamped_ = vel_cmd;
00193     if (vel_cmd_stamped_.header.stamp.isZero())
00194       vel_cmd_stamped_.header.stamp = ros::Time::now();
00195     // TODO what happen with frame id?
00196     // TODO Add a queue here for handling the outcome, message and cmd_vel values bundled,
00197     // TODO so there should be no loss of information in the feedback stream
00198   }
00199 
00200 
00201   geometry_msgs::TwistStamped AbstractControllerExecution::getVelocityCmd()
00202   {
00203     boost::lock_guard<boost::mutex> guard(vel_cmd_mtx_);
00204     return vel_cmd_stamped_;
00205   }
00206 
00207 
00208   ros::Time AbstractControllerExecution::getLastPluginCallTime()
00209   {
00210     boost::lock_guard<boost::mutex> guard(lct_mtx_);
00211     return last_call_time_;
00212   }
00213 
00214 
00215   bool AbstractControllerExecution::isPatienceExceeded()
00216   {
00217     boost::lock_guard<boost::mutex> guard(lct_mtx_);
00218     return !patience_.isZero() && (ros::Time::now() - last_call_time_ > patience_);
00219   }
00220 
00221 
00222   bool AbstractControllerExecution::isMoving()
00223   {
00224     return moving_;
00225   }
00226 
00227   bool AbstractControllerExecution::reachedGoalCheck()
00228   {
00229     // check whether the controller plugin returns goal reached or if mbf should check for goal reached.
00230     return controller_->isGoalReached(dist_tolerance_, angle_tolerance_) || (mbf_tolerance_check_
00231         && mbf_utility::distance(robot_pose_, plan_.back()) < dist_tolerance_
00232         && mbf_utility::angle(robot_pose_, plan_.back()) < angle_tolerance_);
00233   }
00234 
00235   bool AbstractControllerExecution::cancel()
00236   {
00237     cancel_ = true;
00238     // returns false if cancel is not implemented or rejected by the recovery behavior (will run until completion)
00239     if(!controller_->cancel())
00240     {
00241       ROS_WARN_STREAM("Cancel controlling failed or is not supported by the plugin. "
00242                           << "Wait until the current control cycle finished!");
00243       return false;
00244     }
00245     return true;
00246   }
00247 
00248 
00249   void AbstractControllerExecution::run()
00250   {
00251     start_time_ = ros::Time::now();
00252 
00253     // init plan
00254     std::vector<geometry_msgs::PoseStamped> plan;
00255     if (!hasNewPlan())
00256     {
00257       setState(NO_PLAN);
00258       moving_ = false;
00259       ROS_ERROR("robot navigation moving has no plan!");
00260     }
00261 
00262     ros::Time last_valid_cmd_time = ros::Time();
00263     int retries = 0;
00264     int seq = 0;
00265 
00266     try
00267     {
00268       while (moving_ && ros::ok())
00269       {
00270         boost::chrono::thread_clock::time_point loop_start_time = boost::chrono::thread_clock::now();
00271 
00272         if(cancel_){
00273           setState(CANCELED);
00274           condition_.notify_all();
00275           moving_ = false;
00276           return;
00277         }
00278 
00279         // update plan dynamically
00280         if (hasNewPlan())
00281         {
00282           plan = getNewPlan();
00283 
00284           // check if plan is empty
00285           if (plan.empty())
00286           {
00287             setState(EMPTY_PLAN);
00288             condition_.notify_all();
00289             moving_ = false;
00290             return;
00291           }
00292 
00293           // check if plan could be set
00294           if(!controller_->setPlan(plan))
00295           {
00296             setState(INVALID_PLAN);
00297             condition_.notify_all();
00298             moving_ = false;
00299             return;
00300           }
00301           current_goal_pub_.publish(plan.back());
00302         }
00303 
00304         // compute robot pose and store it in robot_pose_
00305         computeRobotPose();
00306 
00307         // ask planner if the goal is reached
00308         if (reachedGoalCheck())
00309         {
00310           ROS_DEBUG_STREAM_NAMED("abstract_controller_execution", "Reached the goal!");
00311           setState(ARRIVED_GOAL);
00312           // goal reached, tell it the controller
00313           condition_.notify_all();
00314           moving_ = false;
00315           // if not, keep moving
00316         }
00317         else
00318         {
00319           setState(PLANNING);
00320 
00321           // save time and call the plugin
00322           lct_mtx_.lock();
00323           last_call_time_ = ros::Time::now();
00324           lct_mtx_.unlock();
00325 
00326           // call plugin to compute the next velocity command
00327           geometry_msgs::TwistStamped cmd_vel_stamped;
00328           geometry_msgs::TwistStamped robot_velocity;   // TODO pass current velocity to the plugin!
00329           outcome_ = computeVelocityCmd(robot_pose_, robot_velocity, cmd_vel_stamped, message_ = "");
00330 
00331           if (outcome_ < 10)
00332           {
00333             setState(GOT_LOCAL_CMD);
00334             vel_pub_.publish(cmd_vel_stamped.twist);
00335             last_valid_cmd_time = ros::Time::now();
00336             retries = 0;
00337           }
00338           else
00339           {
00340             boost::lock_guard<boost::mutex> guard(configuration_mutex_);
00341             if (max_retries_ >= 0 && ++retries > max_retries_)
00342             {
00343               setState(MAX_RETRIES);
00344               moving_ = false;
00345             }
00346             else if (!patience_.isZero() && ros::Time::now() - last_valid_cmd_time > patience_
00347                      && ros::Time::now() - start_time_ > patience_)
00348             {
00349               // patience limit enabled and running controller for more than patience without valid commands
00350               setState(PAT_EXCEEDED);
00351               moving_ = false;
00352             }
00353             else
00354             {
00355               setState(NO_LOCAL_CMD); // useful for server feedback
00356             }
00357             // could not compute a valid velocity command -> stop moving the robot
00358             publishZeroVelocity(); // command the robot to stop; we still feedback command calculated by the plugin
00359           }
00360 
00361           // set stamped values; timestamp and frame_id should be set by the plugin; otherwise setVelocityCmd will do
00362           cmd_vel_stamped.header.seq = seq++; // sequence number
00363           setVelocityCmd(cmd_vel_stamped);
00364           condition_.notify_all();
00365         }
00366 
00367         boost::chrono::thread_clock::time_point end_time = boost::chrono::thread_clock::now();
00368         boost::chrono::microseconds execution_duration =
00369             boost::chrono::duration_cast<boost::chrono::microseconds>(end_time - loop_start_time);
00370         configuration_mutex_.lock();
00371         boost::chrono::microseconds sleep_time = calling_duration_ - execution_duration;
00372         configuration_mutex_.unlock();
00373         if (moving_ && ros::ok())
00374         {
00375           if (sleep_time > boost::chrono::microseconds(0))
00376           {
00377             // interruption point
00378             boost::this_thread::sleep_for(sleep_time);
00379           }
00380           else
00381           {
00382             // provide an interruption point also with 0 or negative sleep_time
00383             boost::this_thread::interruption_point();
00384             ROS_WARN_THROTTLE(1.0, "Calculation needs too much time to stay in the moving frequency! (%f > %f)",
00385                               execution_duration.count()/1000000.0, calling_duration_.count()/1000000.0);
00386           }
00387         }
00388       }
00389     }
00390     catch (const boost::thread_interrupted &ex)
00391     {
00392       // Controller thread interrupted; in most cases we have started a new plan
00393       // Can also be that robot is oscillating or we have exceeded planner patience
00394       ROS_DEBUG_STREAM("Controller thread interrupted!");
00395       publishZeroVelocity();
00396       setState(STOPPED);
00397       condition_.notify_all();
00398       moving_ = false;
00399     }
00400     catch (...)
00401     {
00402       message_ = "Unknown error occurred: " + boost::current_exception_diagnostic_information();
00403       ROS_FATAL_STREAM(message_);
00404       setState(INTERNAL_ERROR);
00405     }
00406   }
00407 
00408 
00409   void AbstractControllerExecution::publishZeroVelocity()
00410   {
00411     geometry_msgs::Twist cmd_vel;
00412     cmd_vel.linear.x = 0;
00413     cmd_vel.linear.y = 0;
00414     cmd_vel.linear.z = 0;
00415     cmd_vel.angular.x = 0;
00416     cmd_vel.angular.y = 0;
00417     cmd_vel.angular.z = 0;
00418     vel_pub_.publish(cmd_vel);
00419   }
00420 
00421 } /* namespace mbf_abstract_nav */


mbf_abstract_nav
Author(s): Sebastian Pütz
autogenerated on Mon Jun 17 2019 20:11:35