controller.cpp
Go to the documentation of this file.
00001 
00015 #include "ucl_drone/controller.h"
00016 
00017 static bool urgency_signal = false;  // true when Ctrl-C (Emergency stop)
00018 
00019 // Constructor
00020 BasicController::BasicController()
00021 {
00022   std::string drone_prefix;
00023   ros::param::get("~drone_prefix", drone_prefix);
00024   /*
00025   List of subscribers and publishers. This node subscribes to pose_estimation to get the real-time
00026   position of the drone. It also subsribes to path_planning to know the new pose that the drone must
00027   reach.
00028 
00029   In order to do that, this node publish in the topic cmd_vel in order to give the computed speed
00030   commands to the drone. It also can send message in order to make the drone taking off or landing
00031   and to change its state (emergency or ok).
00032 
00033   The message reset pose is used to reset the pose when the drone finishes to take off. It allows
00034   the pose_estimation to reset and to erase the drift that happenned during launching.
00035 
00036   */
00037 
00038   // Subscribers
00039   pose_channel = nh.resolveName("pose_estimation");
00040   pose_sub = nh.subscribe(pose_channel, 10, &BasicController::poseCb, this);
00041   poseref_channel = nh.resolveName("path_planning");
00042   poseref_sub = nh.subscribe(poseref_channel, 10, &BasicController::poseRefCb, this);
00043 
00044   // Publishers
00045   control_channel = nh.resolveName(drone_prefix + "cmd_vel");
00046   vel_pub = nh.advertise< geometry_msgs::Twist >(control_channel, 1);
00047 
00048   takeoff_channel = nh.resolveName(drone_prefix + "ardrone/takeoff");
00049   takeoff_pub = nh.advertise< std_msgs::Empty >(takeoff_channel, 1, true);
00050 
00051   land_channel = nh.resolveName(drone_prefix + "ardrone/land");
00052   land_pub = nh.advertise< std_msgs::Empty >(land_channel, 1);
00053 
00054   toggleState_channel = nh.resolveName(drone_prefix + "ardrone/reset");
00055   toggleState_pub = nh.advertise< std_msgs::Empty >(toggleState_channel, 1, true);
00056 
00057   reset_channel = nh.resolveName("reset_pose");
00058   reset_pub = nh.advertise< std_msgs::Empty >(reset_channel, 1, true);
00059 
00060   // Services To launch and to quit control.
00061   startControl_ =
00062       nh.advertiseService("drone_ucl/start_control", &BasicController::startControl, this);
00063 
00064   stopControl_ = nh.advertiseService("drone_ucl/stop_control", &BasicController::stopControl, this);
00065 
00066   // Here are the parameters used before we got the first pose_ref from the path_planning
00067 
00068   // Parameters
00069   alt_ref = 1.0;  // default altitude reference (unit: m)
00070   x_ref = 0.0;
00071   y_ref = 0.0;
00072   yaw_ref = 0.0;
00073   isControlling = false;
00074 
00075   /*Here are the gains used for the PID controller. Those are tuned for an AR.Drone 2.0 with the
00076    * indoor skin.
00077    */
00078   // Initializing the variables we need
00079 
00080   // altitude
00081   Kp_alt = 1.8;
00082   Ki_alt = 0;
00083   Kd_alt = 2;
00084   /*working values inside : Kp_alt=7 & Kd_alt=2
00085   working values dronelab : Kp_alt=1.5 & Kd_alt =2*/
00086 
00087   // yaw
00088   Kp_yaw = 3;
00089   Ki_yaw = 0;
00090   Kd_yaw = 0.3;
00091   /*working values : Kp_yaw=3 */
00092 
00093   // XY
00094   Kp_plan = 0.06;
00095   Ki_plan = 0.002;
00096   Kd_plan = 0.2;
00097   /* working values : Kp_plan=0.06 ki_plan = 0.002 kd_plan = 0.2*/
00098 
00099   // integral_alt_error = 0;
00100   // integral_yaw_error = 0;
00101   // integral_xy_error = 0;
00102   // integral_f_error = 0;
00103   // integral_l_error = 0;
00104 
00105   // Limitation of the rotZ speed in order to stay stable.
00106   anti_windup_yaw = 0.5;
00107 
00108   // Initialization of some variables useful for regulation (see below).
00109   alt_desired_old = 0;
00110   yaw_desired_old = 0;
00111   x_desired_old = 0;
00112   y_desired_old = 0;
00113   old_yaw_desired = 0;
00114 }
00115 
00116 BasicController::~BasicController()
00117 {
00118 }
00119 
00120 // From services
00121 
00122 bool BasicController::startControl(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
00123 {
00124   ROS_INFO("calling service start");
00125   takeoff_pub.publish(std_msgs::Empty());
00126   sendVelToDrone(0, 0, 0, 0,
00127                  true);  // very important: ensure the state at next start is hover mode (see
00128                          // ardrone_autonomy doc)
00129   isControlling = true;
00130   ros::Duration(8).sleep();  // Wait for 8s before to reset the odometry (in order to erase
00131                              // drift).
00132   std_msgs::Empty msg;
00133   reset_pub.publish(msg);
00134   sendVelToDrone(0, 0, 0, 0, true);
00135   ros::Duration(5).sleep();  // Wait for 4s
00136   return true;
00137 }
00138 
00139 bool BasicController::stopControl(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
00140 {
00141   // ROS_INFO("calling service stop");
00142   sendVelToDrone(0, 0, 0, 0);  // very important: ensure the state at next start is hover mode (see
00143                                // ardrone_autonomy doc)
00144   isControlling = false;
00145   land_pub.publish(std_msgs::Empty());
00146   return true;
00147 }
00148 
00149 // Regulation in the XY plane. The X and Y regulator are exactly the same. It is important to know
00150 // that the drone is controlled by Forward-Backward, Left-Right motion in its own repere. So we need
00151 // to use a rotation matrix to match drone command and good drone movement.
00152 
00153 void BasicController::reguXY(double* xvel_cmd, double* yvel_cmd, double x_mes, double y_mes,
00154                              double x_desired, double y_desired, double yaw,
00155                              double regu_new_time_xy)
00156 {
00157   // printf("reguXY x_desired : %lf y_desired : %lf \n", x_desired, y_desired);
00158   double p_term_l;
00159   double d_term_l = 0;  // In case time_diff = 0
00160   double i_term_l;
00161   double p_term_f;
00162   double d_term_f = 0;  // In case time_diff = 0
00163   double i_term_f;
00164 
00165   double time_difference = (regu_new_time_xy - regu_old_time_xy);
00166 
00167   // If navdata has the same timestamp, send the last command (in order to avoid null division)
00168   if (time_difference != 0)
00169   {
00170     if (x_desired_old != x_desired ||
00171         y_desired_old != y_desired)  // If pose_ref has changed, integral error is reset.
00172     {
00173       integral_xy_error = 0;
00174     }
00175     double dist = sqrt(pow((x_mes - x_desired), 2) + pow((y_mes - y_desired), 2));
00176     dist_old = dist;
00177     double delta_x = x_desired - x_mes;
00178     double delta_y = y_desired - y_mes;
00179     regu_old_time_xy = regu_new_time_xy;
00180 
00181     // axis transformation (from absolute axis to drone axis in order to give it velocities
00182     // commands)
00183     double c_theta = cos(yaw);
00184     double s_theta = sin(yaw);
00185 
00186     // Proportional term
00187 
00188     p_term_f = delta_x * c_theta - delta_y * s_theta;
00189     p_term_l = delta_x * s_theta + delta_y * c_theta;
00190 
00191     // Differential term
00192     d_term_f = (p_term_f - p_term_f_old) / time_difference;
00193     d_term_l = (p_term_l - p_term_l_old) / time_difference;
00194     p_term_f_old = p_term_f;
00195     p_term_l_old = p_term_l;
00196 
00197     // Integral term
00198     i_term_f += p_term_f * time_difference;
00199     i_term_l += p_term_l * time_difference;
00200     last_vel_x_command = (Kp_plan * p_term_f + i_term_f * Ki_plan + Kd_plan * d_term_f);
00201     last_vel_y_command = (Kp_plan * p_term_l + i_term_l * Ki_plan + Kd_plan * d_term_l);
00202 
00203     // Velocities command in the drone repere.
00204     *xvel_cmd = last_vel_x_command;
00205     *yvel_cmd = last_vel_y_command;
00206   }
00207 
00208   else
00209   {
00210     *xvel_cmd = last_vel_x_command;
00211     *yvel_cmd = last_vel_y_command;
00212   }
00213 }
00214 
00215 // Regulation in altitude, according to the Z axis.
00216 void BasicController::reguAltitude(double* zvel_cmd, double alt_mes, double alt_desired,
00217                                    double regu_new_time_z)
00218 {
00219   double p_term;
00220   double d_term = 0;  // In case time_diff = 0
00221   double i_term;
00222 
00223   double time_difference = (regu_new_time_z - regu_old_time_z);
00224   // If navdata has the same timestamp, send the last command
00225   if (time_difference != 0)
00226   {
00227     if (alt_desired_old != alt_desired)  // Reset of the integral term if target has changed
00228     {
00229       integral_alt_error = 0;
00230     }
00231 
00232     alt_desired_old = alt_desired;
00233     regu_old_time_z = regu_new_time_z;
00234 
00235     double delta_alt = -alt_mes + alt_desired;
00236 
00237     // Proportional term
00238 
00239     p_term = delta_alt;
00240 
00241     // Differential term
00242     if (time_difference != 0)
00243     {
00244       d_term = (delta_alt - old_delta_alt) / time_difference;
00245       old_delta_alt = delta_alt;
00246     }
00247 
00248     integral_alt_error += delta_alt * time_difference;
00249 
00250     // Integral term
00251 
00252     i_term = integral_alt_error;
00253 
00254     // Z velocity command sent to the drone
00255 
00256     last_vel_z_command = (Kp_alt * p_term + i_term * Ki_alt + Kd_alt * d_term);
00257 
00258     *zvel_cmd = last_vel_z_command;
00259     // printf("zvel_cmd: %lf \n", last_vel_z_command);
00260     /*printf("p_term: %lf\n",p_term);
00261     printf("d_term: %lf\n",d_term);
00262     printf("i_term: %lf\n",i_term);*/
00263   }
00264   else
00265   {
00266     *zvel_cmd = last_vel_z_command;
00267   }
00268 }
00269 
00270 // Regulation of theta, the yaw angle.
00271 void BasicController::reguYaw(double* yawvel_cmd, double yaw_mes, double yaw_desired,
00272                               double regu_new_time)
00273 {
00274   // ROS_INFO_STREAM("Regulating yaw");
00275   double p_term;
00276   double d_term = 0;  // In case time_diff = 0
00277   double i_term;
00278 
00279   double time_difference = (regu_new_time - regu_old_time_yaw);
00280   double new_vel_yaw_cmd;
00281 
00282   // If navdata has the same timestamp, send the last command
00283   if (time_difference != 0)
00284   {
00285     if (yaw_desired_old != yaw_desired)  // Reset of yaw integral error if the target has changed.
00286     {
00287       integral_yaw_error = 0;
00288     }
00289     yaw_desired_old = yaw_desired;
00290     regu_old_time_yaw = regu_new_time;
00291     double delta_yaw = -yaw_mes + yaw_desired;
00292 
00293     // The yaw angle is included in [-pi;pi] interval. In order to make it go from -179° to 179°
00294     // without taking the large side (passing through 0) we implemented a way to make it take the
00295     // shorter way to go to a position. If its proportional term is greater than a half turn, we
00296     // make it take the other side.
00297 
00298     if (delta_yaw > 3.14159)
00299     {
00300       delta_yaw -= 2 * 3.14159;
00301     }
00302     else if (delta_yaw < -3.14159)
00303     {
00304       delta_yaw += 2 * 3.14159;
00305     }
00306     p_term = delta_yaw;
00307     if (time_difference != 0)
00308     {
00309       d_term = (delta_yaw - old_delta_yaw) / time_difference;
00310       old_delta_yaw = delta_yaw;
00311     }
00312     integral_yaw_error += delta_yaw * time_difference;
00313     i_term = integral_yaw_error;
00314     new_vel_yaw_cmd = (Kp_yaw * p_term + i_term * Ki_yaw + Kd_yaw * d_term);
00315 
00316     // rotational speed limitation (wrongly called anti_windup).
00317     if (new_vel_yaw_cmd > anti_windup_yaw)
00318     {
00319       new_vel_yaw_cmd = anti_windup_yaw;
00320     }
00321     else if (new_vel_yaw_cmd < -anti_windup_yaw)
00322     {
00323       new_vel_yaw_cmd = -anti_windup_yaw;
00324     }
00325 
00326     // Velocity sent to the drone.
00327     last_vel_yaw_command = new_vel_yaw_cmd;
00328     *yawvel_cmd = last_vel_yaw_command;
00329   }
00330   else
00331   {
00332     *yawvel_cmd = last_vel_yaw_command;
00333   }
00334 }
00335 
00336 // This function publishes the computed velocities in the topic to the drone.
00337 void BasicController::sendVelToDrone(double pitch, double roll, double yaw_vel, double zvel_cmd,
00338                                      bool force /*=false*/)
00339 {
00340   geometry_msgs::Twist cmdT;
00341   cmdT.angular.z = yaw_vel;
00342   cmdT.linear.z = zvel_cmd;
00343   cmdT.linear.x = pitch;
00344   cmdT.linear.y = roll;
00345 
00346   // printf("cmdT.angular.z : %lf \n", cmdT.angular.z);
00347   // assume that while actively controlling,
00348   // the above for will never be equal to zero, so i will never hover.
00349   cmdT.angular.x = cmdT.angular.y = 0;  // TODO: good idea ?
00350 
00351   if (isControlling || force)
00352   {
00353     vel_pub.publish(cmdT);
00354   }
00355 }
00356 
00357 // This function is called when this node receives a message from the topic "pose_estimation". So it
00358 // takes this message and put it in a variable where it will be used in the other functions.
00359 void BasicController::poseCb(const ucl_drone::Pose3D::ConstPtr posePtr)
00360 {
00361   lastPoseReceived = *posePtr;
00362 }
00363 
00364 // This function is called when this node receives a message from the topic "poseref". So it
00365 // takes this message and put it in a variable where it will be used in the other functions.
00366 void BasicController::poseRefCb(const ucl_drone::PoseRef::ConstPtr poseRefPtr)
00367 {
00368   lastPoseRefReceived = *poseRefPtr;
00369 }
00370 
00371 // This function is really important. It is here that all the regulation functions are called and
00372 // their results are sent to the drone.
00373 void BasicController::controlLoop()
00374 {
00375   double xvel_cmd;
00376   double yvel_cmd;
00377   double yawvel_cmd;
00378   double zvel_cmd;
00379 
00380   reguXY(&xvel_cmd, &yvel_cmd, lastPoseReceived.x, lastPoseReceived.y, lastPoseRefReceived.x,
00381          lastPoseRefReceived.y, lastPoseReceived.rotZ,
00382          lastPoseReceived.header.stamp.sec + lastPoseReceived.header.stamp.nsec / pow(10, 9));
00383   reguYaw(&yawvel_cmd, lastPoseReceived.rotZ, lastPoseRefReceived.rotZ,
00384           lastPoseReceived.header.stamp.sec + lastPoseReceived.header.stamp.nsec / pow(10, 9));
00385   reguAltitude(&zvel_cmd, lastPoseReceived.z, lastPoseRefReceived.z,
00386                lastPoseReceived.header.stamp.sec + lastPoseReceived.header.stamp.nsec / pow(10, 9));
00387   // sendVelToDrone(0,0,0,zvel_cmd,false);                 //Test
00388   sendVelToDrone(xvel_cmd, yvel_cmd, yawvel_cmd, zvel_cmd, false);  // ALL
00389 
00390   // used if pathplanning asks to stop the control of the drone.
00391   if (lastPoseRefReceived.landAndStop)
00392   {
00393     std_srvs::Empty::Request req;
00394     std_srvs::Empty::Response res;
00395     stopControl(req, res);
00396   }
00397   // used if pathplanning asks to takeoff and start control.
00398   if (lastPoseRefReceived.takeoffAndStart && !isControlling)
00399   {
00400     // isControlling = true;
00401     std_srvs::Empty::Request req;
00402     std_srvs::Empty::Response res;
00403     startControl(req, res);
00404   }
00405 
00406   // used when user presses the Ctrl-c keys in order to force landing.
00407   if (urgency_signal)  // force landing when Ctrl-C
00408   {
00409     std_srvs::Empty::Request req;
00410     std_srvs::Empty::Response res;
00411     stopControl(req, res);
00412     ros::shutdown();
00413   }
00414 }
00415 
00417 static void basicSigintHandler(int sig)
00418 {
00419   urgency_signal = true;
00420 }
00421 
00422 // Main function, launching the controLoop function.
00423 int main(int argc, char** argv)
00424 {
00425   ros::init(argc, argv, "controller", ros::init_options::NoSigintHandler);
00426   signal(SIGINT, basicSigintHandler);
00427   BasicController bc;
00428   ROS_INFO_STREAM("controller started!");
00429   while (ros::ok())
00430   {
00431     TIC(control);
00432     bc.controlLoop();
00433     TOC(control, "control");
00434     ros::spinOnce();  // if we dont want this we have to place callback and services in threads
00435   }
00436   return 0;
00437 }


ucl_drone
Author(s): dronesinma
autogenerated on Sat Jun 8 2019 20:51:53