strategy.cpp
Go to the documentation of this file.
00001 /*
00002  *  This file is part of ucl_drone 2016.
00003  *  For more information, refer
00004  *  to the corresponding header file.
00005  *
00006  * This file receives information from Strategy and the Pose_estimation and publishes to the
00007  * Controller.
00008  * It tells the controller where the drone must go as function of the strategy and the position of
00009  * the drone.
00010  *
00011  *  \authors Julien Gérardy & Félicien Schiltz
00012  *  \date 2016
00013  *
00014  */
00015 
00016 #include "ucl_drone/strategy.h"
00017 
00018 // Constructor
00019 Strategy::Strategy()
00020 {
00021   // drone prefix and name from the launch file.
00022   std::string drone_prefix;
00023   ros::param::get("~drone_prefix", drone_prefix);
00024 
00025   ros::param::get("~drone_name", drone_name);
00026 
00027   // List of subscribers and publishers. This node subscribes to target detection from drone 4,
00028   // drone 5 and from the drone to which it belongs. This allows this node to always know wich drone
00029   // sees the target. It is also subscribed to the Multi_drone in order to know which drone is the
00030   // master and which one is the slave. This gives information of what strategy to chose. The
00031   // subscription to the master drone navdata gives the information about its battery. This is the
00032   // only way for the secondary drone to know when it has to start. The subscription to the pose
00033   // estimation to the secondary drone tells to the main drone when it may go back to the base
00034   // because the secondary drone is close enough to replace it.
00035   //
00036   // This node only publish in the topic strategy that is read from the path_planning.
00037 
00038   // Subscribers
00039 
00040   target_channel = nh.resolveName("ucl_drone/target_detected/");
00041   target_sub = nh.subscribe(target_channel, 10, &Strategy::targetDetectedCb, this);
00042 
00043   /* ** Other group's comments: ** */
00044   // TODO: subscribe to channels in funtion of the role ...
00045   // TODO: do not use absolute path to get the target_detected channel of the current drone
00046   // TODO: get the other drone name from the DroneRoles message sent by swarm_initialization
00047 
00048   target_channel_from_master = nh.resolveName("/ucl_drone_5/ucl_drone/target_detected/");
00049   target_sub_from_master =
00050       nh.subscribe(target_channel_from_master, 10, &Strategy::targetDetectedFromMasterCb, this);
00051 
00052   target_channel_from_slave = nh.resolveName("/ucl_drone_4/ucl_drone/target_detected/");
00053   target_sub_from_slave =
00054       nh.subscribe(target_channel_from_slave, 10, &Strategy::targetDetectedFromSlaveCb, this);
00055 
00056   multi_strategy_channel = nh.resolveName("/drones_roles");
00057   multi_strategy_sub = nh.subscribe(multi_strategy_channel, 10, &Strategy::multi_strategyCb, this);
00058 
00059   navdata_channel = nh.resolveName("/ucl_drone_5/motherboard1/ardrone/navdata");
00060   navdata_sub = nh.subscribe(navdata_channel, 10, &Strategy::navdataCb, this);
00061 
00062   pose_from_slave_channel = nh.resolveName("ucl_drone_4/pose_estimation");
00063   pose_from_slave_sub = nh.subscribe(pose_from_slave_channel, 10, &Strategy::poseFromSlaveCb, this);
00064 
00065   // Publishers
00066 
00067   strategy_channel = nh.resolveName("strategy");
00068   strategy_pub = nh.advertise< ucl_drone::StrategyMsg >(strategy_channel, 1);
00069 
00070   /* ** Other group's comments: ** */
00071   // TODO: Publisher to send a "ready state" message with the drone_name
00072   // to the swarm_initialization (multistrategy)
00073   // the topic name is "ready"
00074   // the message type is ucl_drone::DroneRole
00075   // fill the drone_name only
00076 
00077   // Initialization of some parameters.
00078 
00079   Intheair = false;
00080   StrategyCbreceived = false;
00081   TargetDetectedFromMaster = false;
00082   TargetDetectedFromSlave = false;
00083   backupCalled = false;
00084   batteryLeft = 100;
00085   oldbatteryLeft = 101;
00086 }
00087 
00088 // Destructor
00089 Strategy::~Strategy()
00090 {
00091 }
00092 
00093 // each strategy is corresponding to a number. This number will be sent (with a pose if needed) to
00094 // the pathplanning.
00095 
00096 void Strategy::reset()
00097 {
00098   strategychosen = 0.0;
00099   oldstrategychosen = 0.0;
00100 }
00101 
00102 void Strategy::Takeoff()
00103 {
00104   oldstrategychosen = strategychosen;
00105   strategychosen = 1.0;
00106 }
00107 
00108 void Strategy::Seek()
00109 {
00110   oldstrategychosen = strategychosen;
00111   strategychosen = 2.0;
00112 }
00113 
00114 void Strategy::Goto()
00115 {
00116   oldstrategychosen = strategychosen;
00117   strategychosen = 3.0;
00118 }
00119 
00120 void Strategy::Land()
00121 {
00122   oldstrategychosen = strategychosen;
00123   strategychosen = 4.0;
00124 }
00125 
00126 void Strategy::Follow()
00127 {
00128   oldstrategychosen = strategychosen;
00129   strategychosen = 5.0;
00130 }
00131 
00132 void Strategy::BackToBase()
00133 {
00134   oldstrategychosen = strategychosen;
00135   strategychosen = 6.0;
00136 }
00137 
00138 // This function is used to find the role of the drone, i.e. if it the main or the secondary drone.
00139 int Strategy::FindRole()
00140 {
00141   i = 0;
00142   while (ros::ok() && i < 2)  // 2 car 2 drones pour le moment
00143   {
00144     if (lastDronesrolesreceived.roles[i].name == drone_name)
00145     {
00146       return lastDronesrolesreceived.roles[i].role;
00147     }
00148     i++;
00149   }
00150   /* ** Other group's comments: ** */
00151   // TODO: the drone should wait until a role is attributed ...
00152   printf("I did not find my role");
00153   return 0.0;
00154 }
00155 
00156 // This function give the position chosen to the object of this function.
00157 void Strategy::SetXYChosen(double xchosen, double ychosen)
00158 {
00159   this->xchosen = xchosen;
00160   this->ychosen = ychosen;
00161 }
00162 
00163 // This function sent the strategy number and the position chosen to the path_planning.
00164 void Strategy::publish_strategy()
00165 {
00166   // instantiate the strategy message
00167   ucl_drone::StrategyMsg strategy_msg;
00168 
00169   strategy_msg.type = strategychosen;
00170   strategy_msg.x = xchosen;
00171   strategy_msg.y = ychosen;
00172 
00173   // publish
00174   strategy_pub.publish(strategy_msg);
00175 }
00176 
00177 // This function is called when the topic of the target_detected of this drone publishes something.
00178 void Strategy::targetDetectedCb(const ucl_drone::TargetDetected::ConstPtr targetDetectedPtr)
00179 {
00180   lastTargetDetected = *targetDetectedPtr;
00181   oldstrategychosen = strategychosen;
00182   // xchosen = lastTargetDetectedFromSlave.world_point.x;
00183   // ychosen = lastTargetDetectedFromMaster.world_point.y;
00184 }
00185 
00186 // This function is called when the topic of the target_detected of the drone master publishes
00187 // something. The place of the target become the x and y chosen that will be sent to the
00188 // pathplanning.
00189 void Strategy::targetDetectedFromMasterCb(
00190     const ucl_drone::TargetDetected::ConstPtr lastTargetDetectedPtr)
00191 {
00192   lastTargetDetectedFromMaster = *lastTargetDetectedPtr;
00193   xchosen = lastTargetDetectedFromMaster.world_point.x;
00194   ychosen = lastTargetDetectedFromMaster.world_point.y;
00195   TargetDetectedFromMaster = true;
00196 
00197   printf("target detected from master \n");
00198 }
00199 
00200 // This function is called when the topic of the target_detected of the secondary drone publishes
00201 // something. The position of the target on the real playground will be set in the x and y chosen
00202 // and in the x and y dectectedbyslave variables.
00203 
00204 void Strategy::targetDetectedFromSlaveCb(
00205     const ucl_drone::TargetDetected::ConstPtr lastTargetDetectedPtr)
00206 {
00207   lastTargetDetectedFromSlave = *lastTargetDetectedPtr;
00208   xDetectedBySlave = lastTargetDetectedFromSlave.world_point.x;
00209   yDetectedBySlave = lastTargetDetectedFromSlave.world_point.y;
00210   xchosen = lastTargetDetectedFromSlave.world_point.x;
00211   ychosen = lastTargetDetectedFromSlave.world_point.y;
00212   TargetDetectedFromSlave = true;
00213   ROS_INFO("Slave detected the target");
00214 }
00215 
00216 // This function is executed when the multi strategy publishes something. This message contains the
00217 // roles (main/master, secondary/slave) of the drones.
00218 void Strategy::multi_strategyCb(const ucl_drone::DroneRoles::ConstPtr drones_rolesPtr)
00219 {
00220   lastDronesrolesreceived = *drones_rolesPtr;
00221   StrategyCbreceived = true;
00222   ROS_DEBUG("strategy received multistrategy message");
00223 }
00224 
00225 // This function is called when a message is published in the navdata channel. Navdata channel
00226 // contains a lot of information but, in this case, the intersting one is the percentage of the
00227 // master drone. This will allow to call the secondary drone if the latter is to low.
00228 void Strategy::navdataCb(const ardrone_autonomy::Navdata::ConstPtr navdataPtr)
00229 {
00230   lastNavdataReceived = *navdataPtr;
00231   batteryLeft = lastNavdataReceived.batteryPercent;
00232 
00233   if (drone_name == "ucl_drone_4" && batteryLeft != oldbatteryLeft)
00234   {
00235     ROS_INFO("BatteryLeft read from drone 2 only %lf:", batteryLeft);
00236     // ROS_INFO("BackupCalled is %d", backupCalled);
00237     oldbatteryLeft = batteryLeft;
00238   }
00239   if (batteryLeft < 75)
00240   {
00241     backupCalled = true;
00242   }
00243 }
00244 
00245 // This function is called when the drone slave published its pose.
00246 void Strategy::poseFromSlaveCb(const ucl_drone::Pose3D::ConstPtr posePtr)
00247 {
00248   lastPoseReceivedFromSlave = *posePtr;
00249 }
00250 
00251 // This is the main function where the strategy to sent to the path_planning will be chosen as a
00252 // function of all above data.
00253 int main(int argc, char** argv)
00254 {
00255   ROS_INFO_STREAM("strategy started");
00256   ros::init(argc, argv, "strategy");
00257   printf("Main de la strategy");
00258 
00259   /*  if (ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug))
00260     {
00261       ros::console::notifyLoggerLevelsChanged();
00262     }*/
00263 
00264   Strategy myStrategy;
00265   ros::Rate r(20);  // This function refreshes every 1/20 second.
00266   ROS_DEBUG("Strategy started");
00267   myStrategy.reset();
00268   myStrategy.publish_strategy();
00269   ros::spinOnce();
00270   r.sleep();
00271 
00272   // while strategy hasn't receive the roles from the multi_strategy, it does nothing and keep
00273   // listening.
00274   while (!myStrategy.StrategyCbreceived)
00275   {
00276     // printf("Waiting for strategy to send me something to do");
00277     ros::spinOnce();
00278     r.sleep();
00279   }
00280 
00281   // If the role received is 4, that means this node belongs to the master drone. So, strategy 1
00282   // (takeoff) is sent to the pathplanning.
00283   // Then, the drone waits 10 seconds (time to take off and get stabilized). After that, the
00284   // strategy 2 (Seek/explore) is sent to the path_planning.
00285   //
00286   // ros::spinOnce(); means that ros can see if it got a message from a topic.
00287   if (myStrategy.FindRole() == 4)
00288   {
00289     myStrategy.Takeoff();
00290     myStrategy.publish_strategy();
00291     ros::spinOnce();
00292     r.sleep();
00293     ros::Duration(10).sleep();  // Wait for 10s
00294     myStrategy.Seek();
00295     myStrategy.publish_strategy();
00296     ros::spinOnce();
00297     r.sleep();
00298 
00299     while (ros::ok())
00300     {
00301       TIC(stratego);
00302       // The strategy stays in Seek/exploration mode until the drones find the target. Then the
00303       // strategy becomes "Follow" the target.
00304       if (myStrategy.TargetDetectedFromMaster && !myStrategy.TargetDetectedFromSlave)
00305       {
00306         myStrategy.strategychosen = 5.0;
00307         // ROS_INFO("Strategy: Follow");
00308       }
00309       // If the backup is called and that the secondary drone is near from the main drone, so the
00310       // strategy back to base is sent to the pathplanning node.
00311       if ((myStrategy.lastPoseReceivedFromSlave.x - myStrategy.xchosen) *
00312                       (myStrategy.lastPoseReceivedFromSlave.x - myStrategy.xchosen) +
00313                   ((myStrategy.lastPoseReceivedFromSlave.y - 0.5) - myStrategy.ychosen) *
00314                       ((myStrategy.lastPoseReceivedFromSlave.y - 0.5) - myStrategy.ychosen) <
00315               16 &&
00316           myStrategy.strategychosen == 5.0 && myStrategy.backupCalled)
00317       {
00318         myStrategy.BackToBase();
00319       }
00320       myStrategy.publish_strategy();
00321       //  }
00322       ros::spinOnce();
00323 
00324       TOC(stratego, "stratego");
00325       r.sleep();
00326     }
00327   }
00328 
00329   // if the messages got from the multi_drone node contains role = 2, so this node belongs to the
00330   // drone slave. So the strategy GOTO is selected but not yet sent to the pathplanning node.
00331   else if (myStrategy.FindRole() == 2)
00332   {
00333     myStrategy.strategychosen = 3.0;
00334 
00335     // While the backup, i.e. this drone, is not called, Ros does nothing and read its topics. So,
00336     // the strategy is still not sent and nothing happen to the secondary drone.If
00337     // backup is called, we go to the next loop.
00338     while (ros::ok() && !myStrategy.backupCalled)
00339     {
00340       // Waiting for master to reach its critical battery.
00341       ros::spinOnce();
00342       r.sleep();
00343     }
00344 
00345     while (ros::ok())
00346     {
00347       // if the backup is called (previous loop) and the drone has not yest took off, the strategy
00348       // take off is sent to the pathplanning. Then the drone waits 10 seconds to stabilized.
00349       // Then,
00350       // the strategy goto is sent to the pathplanning and the drone is considred as "intheair" so
00351       // this loop will not be called anymore.
00352       if (myStrategy.strategychosen == 3.0 && !myStrategy.Intheair)
00353       {
00354         myStrategy.Takeoff();
00355         myStrategy.publish_strategy();
00356         ros::spinOnce();
00357         r.sleep();
00358         ros::Duration(10).sleep();  // Wait for 10s
00359         myStrategy.Goto();  // Goto is called and x,y from the TargetDetectedFromMaster are the
00360                             // pose_ref
00361         myStrategy.publish_strategy();
00362         ros::spinOnce();
00363         r.sleep();
00364         myStrategy.Intheair = true;
00365       }
00366 
00367       // When the slave detects the target, the strategy follow is sent to the pathplanning.The
00368       // coordinates from the strategy message are the position of the target read from the
00369       // targetDetectedFromSlaveCb message.
00370       if (myStrategy.TargetDetectedFromSlave)
00371       {
00372         myStrategy.strategychosen = 5.0;
00373         myStrategy.publish_strategy();
00374         // ROS_INFO("Slave must follow the target!");
00375         ros::spinOnce();
00376         r.sleep();
00377       }
00378 
00379       // If the drone must land and is still in the air, this strategy is sent to the pathplanning
00380       // and the variable Intheair is reset to false. Land is never called by the IA for the
00381       // secondary drone. So, this function is only true when we ask for an emergency stop.
00382       if (myStrategy.strategychosen == 4.0 && myStrategy.Intheair)
00383       {
00384         myStrategy.publish_strategy();
00385         ros::spinOnce();
00386         r.sleep();
00387         myStrategy.Intheair = false;
00388       }
00389       ros::spinOnce();
00390       r.sleep();
00391     }
00392   }
00393   return 0;
00394 }


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