crossing_goer.cpp
Go to the documentation of this file.
00001 #include <goto_crossing/crossing_goer.h>
00002 
00003 namespace goto_crossing
00004 {
00005 
00006 CrossingGoer::CrossingGoer() :
00007   kp_v_(0.1),
00008   kp_w_(0.2),
00009   ki_v_(0),
00010   ki_w_(0),
00011   min_linear_velocity_(0),
00012   max_linear_velocity_(0),
00013   min_angular_velocity_(0),
00014   max_angular_velocity_(0),
00015   reach_distance_(0.1),
00016   dtheta_force_left_(0),
00017   threshold_w_only_(0.35),
00018   max_sum_v_(10),
00019   max_sum_w_(30),
00020   last_t_(ros::Time::now()),
00021   sum_v_(0),
00022   sum_w_(0)
00023 {
00024   ros::NodeHandle private_nh("~");
00025   private_nh.getParam("kp_v", kp_v_);
00026   private_nh.getParam("kp_w", kp_w_);
00027   private_nh.getParam("ki_v", ki_v_);
00028   private_nh.getParam("ki_w", ki_w_);
00029   private_nh.getParam("min_linear_velocity", min_linear_velocity_);
00030   private_nh.getParam("max_linear_velocity", max_linear_velocity_);
00031   private_nh.getParam("min_angular_velocity", min_angular_velocity_);
00032   private_nh.getParam("max_angular_velocity", max_angular_velocity_);
00033   private_nh.getParam("reach_distance", reach_distance_);
00034   private_nh.getParam("dtheta_force_left", dtheta_force_left_);
00035   private_nh.getParam("threshold_w_only", threshold_w_only_);
00036   private_nh.getParam("max_sum_v", max_sum_v_);
00037   private_nh.getParam("max_sum_w", max_sum_w_);
00038 
00039   twist_publisher_ = private_nh.advertise<geometry_msgs::Twist>("cmd_vel", 1);
00040   goal_reached_publisher_ = private_nh.advertise<std_msgs::Bool>("goal_reached", 1);
00041   
00042   reset_integral_server_ = private_nh.advertiseService("reset_integrals", &CrossingGoer::callback_resetIntegrals, this);
00043 }
00044 
00045 /* Compute the twist needed to reach the goal
00046  *
00047  * Return true if the goal is reached, false otherwise.
00048  *
00049  * crossing[in] crossing descriptor relative to the robot
00050  * twist[out] twist to apply (all null except linear.x and angular.z).
00051  */
00052 bool CrossingGoer::goto_crossing(const lama_msgs::Crossing& crossing, geometry_msgs::Twist& twist)
00053 {
00054   geometry_msgs::Point goal;
00055 
00056   // Can only reach the goal if at least 3 exits.
00057   bool can_reach = (crossing.frontiers.size() >= 3);
00058 
00059   if (crossing.frontiers.size() == 0)
00060   {
00061     // Move straight forward.
00062     goal.x = crossing.radius;
00063   }
00064   else if (crossing.frontiers.size() == 1)
00065   {
00066     // Move to the sole frontier.
00067     // DOES NOT WORK VERY WELL.
00068     // goal.x = (crossing.frontiers[0].p1.x + crossing.frontiers[0].p2.x) / 2;
00069     // goal.y = (crossing.frontiers[0].p1.y + crossing.frontiers[0].p2.y) / 2;
00070     goal.x = std::cos(crossing.frontiers[0].angle) * crossing.radius;
00071     goal.y = std::sin(crossing.frontiers[0].angle) * crossing.radius;
00072   }
00073   else if (crossing.frontiers.size() == 2)
00074   {
00075     // Move to the frontier that is the closest to forward.
00076     double dtheta0 = angles::normalize_angle(crossing.frontiers[0].angle);
00077     double dtheta1 = angles::normalize_angle(crossing.frontiers[1].angle);
00078     
00079     if (std::abs(dtheta0) < std::abs(dtheta1))
00080     {
00081       // DOES NOT WORK VERY WELL.
00082       // goal.x = (crossing.frontiers[0].p1.x + crossing.frontiers[0].p2.x) / 2;
00083       // goal.y = (crossing.frontiers[0].p1.y + crossing.frontiers[0].p2.y) / 2;
00084       goal.x = std::cos(dtheta0) * crossing.radius;
00085       goal.y = std::sin(dtheta0) * crossing.radius;
00086     }
00087     else
00088     {
00089       // DOES NOT WORK VERY WELL.
00090       // goal.x = (crossing.frontiers[1].p1.x + crossing.frontiers[1].p2.x) / 2;
00091       // goal.y = (crossing.frontiers[1].p1.y + crossing.frontiers[1].p2.y) / 2;
00092       goal.x = std::cos(dtheta1) * crossing.radius;
00093       goal.y = std::sin(dtheta1) * crossing.radius;
00094     }
00095   }
00096   else
00097   {
00098     // Move to the crossing center.
00099     goal.x = crossing.center.x;
00100     goal.y = crossing.center.y;
00101   }
00102   ROS_DEBUG("goal: (%.3f, %.3f)", goal.x, goal.y);
00103   return goToGoal(goal, twist) && can_reach;
00104 }
00105 
00106 /* Callback for the Crossing topic.
00107  *
00108  * Compute and publish the Twist and Bool messages.
00109  */
00110 void CrossingGoer::callback_goto_crossing(const lama_msgs::Crossing& crossing)
00111 {
00112   // TODO: add a timeout mechanism for Crossing reception and set twist to 0.
00113   geometry_msgs::Twist twist;
00114   bool goal_reached = goto_crossing(crossing, twist);
00115 
00116   twist_publisher_.publish(twist);
00117   std_msgs::Bool goal_reached_msg;
00118   goal_reached_msg.data = goal_reached;
00119   goal_reached_publisher_.publish(goal_reached_msg);
00120 }
00121 
00122 bool CrossingGoer::callback_resetIntegrals(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
00123 {
00124   resetIntegrals();
00125   return true;
00126 }
00127 
00128 /* Return the twist to reach the given goal pose
00129  * 
00130  * There is no cycle in this function, so it should be called periodically by class instances.
00131  *
00132  * goal[in] position of the goal relative to the robot.
00133  * twist[out] twist to apply (all null except linear.x and angular.z).
00134  */
00135 bool CrossingGoer::goToGoal(const geometry_msgs::Point& goal, geometry_msgs::Twist& twist)
00136 {
00137   // TODO: use dynamic_reconfigure instead of getParamCached.
00138   ros::NodeHandle private_nh("~");
00139   private_nh.getParamCached("kp_v", kp_v_);
00140   private_nh.getParamCached("kp_w", kp_w_);
00141   private_nh.getParamCached("ki_v", ki_v_);
00142   private_nh.getParamCached("ki_w", ki_w_);
00143   private_nh.getParamCached("min_linear_velocity", min_linear_velocity_);
00144   private_nh.getParamCached("max_linear_velocity", max_linear_velocity_);
00145   private_nh.getParamCached("min_angular_velocity", min_angular_velocity_);
00146   private_nh.getParamCached("max_angular_velocity", max_angular_velocity_);
00147   private_nh.getParamCached("reach_distance", reach_distance_);
00148   private_nh.getParamCached("dtheta_force_left", dtheta_force_left_);
00149   private_nh.getParamCached("threshold_w_only", threshold_w_only_);
00150   private_nh.getParamCached("max_sum_v", max_sum_v_);
00151   private_nh.getParamCached("max_sum_w", max_sum_w_);
00152 
00153   double distance = std::sqrt(goal.x * goal.x + goal.y * goal.y);
00154 
00155   if (distance < reach_distance_)
00156   {
00157     ROS_DEBUG("%s: Goal (%f, %f) reached", ros::this_node::getName().c_str(), goal.x, goal.y);
00158     twist = geometry_msgs::Twist();
00159     // Return true to indicate that the goal is reached.
00160     return true;
00161   }
00162 
00163   double dtheta = std::atan2(goal.y, goal.x);
00164 
00165   if (std::abs(dtheta) > threshold_w_only_)
00166   {
00167     // Do no go forward because the goal is not well in front of the robot.
00168     ROS_DEBUG("%s: Goal angle too large, just turning...", ros::this_node::getName().c_str());
00169     distance = 0.0;
00170   }
00171 
00172   if ((M_PI - dtheta_force_left_ < dtheta) && (dtheta < M_PI))
00173   {
00174     // If the goal is behind the robot, force the robot to turn left.
00175     dtheta -= 2 * M_PI;
00176   }
00177 
00178   // Compute the integrals.
00179   const ros::Time t = ros::Time::now();
00180   const double dt = (t - last_t_).toSec();
00181   sum_v_ += distance * dt;
00182   sum_w_ += dtheta * dt;
00183 
00184   // Anti wind-up.
00185   if (sum_v_ < -max_sum_v_)
00186   {
00187     sum_v_ = -max_sum_v_;
00188   }
00189   else if (sum_v_ > max_sum_v_)
00190   {
00191     sum_v_ = max_sum_v_;
00192   }
00193   if (sum_w_ < -max_sum_w_)
00194   {
00195     sum_w_ = -max_sum_w_;
00196   }
00197   else if (sum_w_ > max_sum_w_)
00198   {
00199     sum_w_ = max_sum_w_;
00200   }
00201   
00202   // TODO: add a parameter "allow_backward" so that we don't need to turn 180 deg.
00203   
00204   double vx = kp_v_ * distance + ki_v_ * sum_v_; 
00205   double wz = kp_w_ * dtheta + ki_w_ * sum_w_;
00206 
00207   // Linear velocity throttle depending on dtheta (full velocity
00208   // when dtheta = 0, 0 when dtheta = threshold_w_only_).
00209   const double vx_throttle_dtheta = std::max(std::min(1.0, 1.0 - std::abs(dtheta) / threshold_w_only_), 0.0);
00210   vx = vx * vx_throttle_dtheta;
00211 
00212   // Dead-zone management (not needed if ki_v_ and ki_w_ non null).
00213   if ((ki_v_ < 1e-10) && (vx < min_linear_velocity_) &&
00214       (std::abs(distance) > 1e-10) && (std::abs(wz) < min_angular_velocity_))
00215   {
00216     vx = min_linear_velocity_;
00217   }
00218   if ((ki_w_ < 1e-10) && (wz > 0) && (wz < min_angular_velocity_) && (vx < min_linear_velocity_))
00219   {
00220     wz = min_angular_velocity_;
00221   }
00222   else if ((ki_w_ < 1e-10) && (wz < 0) && (wz > -min_angular_velocity_) && (vx < min_linear_velocity_))
00223   {
00224     wz = -min_angular_velocity_;
00225   }
00226 
00227   // velocity throttle.
00228   if (max_linear_velocity_ > 1e-10)
00229   {
00230     // Only throttle if max_linear_velocity_ is set.
00231     if (vx > max_linear_velocity_)
00232     {
00233       vx = max_linear_velocity_;
00234     }
00235   }
00236   if (max_angular_velocity_ > 1e-10)
00237   {
00238     // Only throttle if max_angular_velocity_ is set.
00239     if (wz > max_angular_velocity_)
00240     {
00241       wz = max_angular_velocity_;
00242     }
00243     else if (wz < -max_angular_velocity_)
00244     {
00245       wz = -max_angular_velocity_;
00246     }
00247   }
00248 
00249   ROS_DEBUG("distance to goal: %f, dtheta to goal: %f, twist: (%f, %f)",
00250       distance, dtheta, vx, wz);
00251 
00252   ROS_DEBUG_NAMED("superdebug", "sum_v_: %f", sum_v_);
00253   ROS_DEBUG_NAMED("superdebug", "sum_w_: %f", sum_w_);
00254 
00255   twist.linear.x = vx;
00256   twist.angular.z = wz;
00257 
00258   last_t_ = t;
00259   return false;
00260 }
00261 
00262 } // namespace goto_crossing


goto_crossing
Author(s): Gaƫl Ecorchard
autogenerated on Thu Jun 6 2019 22:02:00