00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <fstream>
00031 #include <yaml-cpp/yaml.h>
00032
00033 #include <tf/tf.h>
00034 #include <tf/transform_listener.h>
00035 #include <ros/ros.h>
00036 #include <geometry_msgs/PoseStamped.h>
00037 #include <geometry_msgs/PointStamped.h>
00038 #include <move_base_msgs/MoveBaseAction.h>
00039 #include <actionlib/client/simple_action_client.h>
00040 #include <visualization_msgs/MarkerArray.h>
00041
00042 #include <yocs_math_toolkit/common.hpp>
00043 #include <yocs_math_toolkit/geometry.hpp>
00044
00045 #ifdef HAVE_NEW_YAMLCPP
00046
00047
00048 template<typename T>
00049 void operator >> (const YAML::Node& node, T& i)
00050 {
00051 i = node.as<T>();
00052 }
00053 #endif
00054
00055 class WaypointsGoalNode
00056 {
00057 public:
00058 WaypointsGoalNode() : mode_(NONE), state_(IDLE), move_base_ac_("move_base", true)
00059 {
00060 }
00061
00062 bool init()
00063 {
00064 ros::NodeHandle nh;
00065 ros::NodeHandle pnh("~");
00066
00067 pnh.param("frequency", frequency_, 1.0);
00068 pnh.param("close_enough", close_enough_, 0.3);
00069 pnh.param("goal_timeout", goal_timeout_, 30.0);
00070 pnh.param("waypoints_file", waypoints_file_, std::string());
00071 pnh.param("robot_frame", robot_frame_, std::string("/base_footprint"));
00072 pnh.param("world_frame", world_frame_, std::string("/map"));
00073
00074
00075 ros::Time t0 = ros::Time::now();
00076 double timeout = 10.0;
00077
00078 while ((move_base_ac_.waitForServer(ros::Duration(1.0)) == false) && (ros::ok() == true))
00079 {
00080 if ((ros::Time::now() - t0).toSec() > timeout/2.0)
00081 ROS_WARN_THROTTLE(3, "Waiting for move_base action server to come up...");
00082
00083 if ((ros::Time::now() - t0).toSec() > timeout)
00084 {
00085 ROS_ERROR("Timeout while waiting for move_base action server to come up");
00086 return false;
00087 }
00088 }
00089
00090 wp_markers_pub_ = nh.advertise<visualization_msgs::MarkerArray>("waypoint_marker", 1, true);
00091
00092 final_goal_sub_ = nh.subscribe("final_goal", 1, &WaypointsGoalNode::finalGoalCB, this);
00093 waypoints_sub_ = nh.subscribe("waypoints", 1, &WaypointsGoalNode::waypointCB, this);
00094
00095 return true;
00096 }
00097
00098 void finalGoalCB(const geometry_msgs::PoseStamped::ConstPtr& goal)
00099 {
00100 if ((state_ > IDLE) && (state_ < COMPLETED))
00101 {
00102 ROS_INFO("Already have a task; cancelling...");
00103 resetWaypoints();
00104 cancelAllGoals();
00105 }
00106 else
00107 {
00108 ROS_INFO("Goal received with %lu waypoints; lets' go!", waypoints_.size());
00109
00110 goal_ = *goal;
00111 mode_ = GOAL;
00112 state_ = START;
00113
00114 waypoints_it_ = waypoints_.begin();
00115 }
00116 }
00117
00118 void waypointCB(const geometry_msgs::PointStamped::ConstPtr& point)
00119 {
00120 if ((state_ > IDLE) && (state_ < COMPLETED))
00121 {
00122 ROS_DEBUG("Already have a task; ignoring additional points");
00123 }
00124 else if ((waypoints_.size() > 1) && (mtk::distance2D(point->point, waypoints_.front().point) < 0.2))
00125 {
00126
00127 ROS_INFO("Waypoints loop closed with %lu points; lets' go!", waypoints_.size());
00128
00129 mode_ = LOOP;
00130 state_ = START;
00131
00132 waypoints_it_ = waypoints_.begin();
00133 }
00134 else
00135 {
00136 ROS_DEBUG("Waypoint added: %s", mtk::point2str(point->point));
00137 waypoints_.push_back(*point);
00138 }
00139 }
00140
00141 bool cancelAllGoals(double timeout = 2.0)
00142 {
00143 actionlib::SimpleClientGoalState goal_state = move_base_ac_.getState();
00144 if ((goal_state != actionlib::SimpleClientGoalState::ACTIVE) &&
00145 (goal_state != actionlib::SimpleClientGoalState::PENDING) &&
00146 (goal_state != actionlib::SimpleClientGoalState::RECALLED) &&
00147 (goal_state != actionlib::SimpleClientGoalState::PREEMPTED))
00148 {
00149
00150 ROS_WARN("Cannot cancel move base goal, as it has %s state!", goal_state.toString().c_str());
00151 return true;
00152 }
00153
00154 ROS_INFO("Canceling move base goal with %s state...", goal_state.toString().c_str());
00155 move_base_ac_.cancelAllGoals();
00156 if (move_base_ac_.waitForResult(ros::Duration(timeout)) == false)
00157 {
00158 ROS_WARN("Cancel move base goal didn't finish after %.2f seconds: %s",
00159 timeout, goal_state.toString().c_str());
00160 return false;
00161 }
00162
00163 ROS_INFO("Cancel move base goal succeed. New state is %s", goal_state.toString().c_str());
00164 return true;
00165 }
00166
00167 void resetWaypoints()
00168 {
00169 ROS_DEBUG("Full reset: clear markers, delete waypoints and goal and set state to IDLE");
00170
00171 publishMarkers(true);
00172 waypoints_.clear();
00173 waypoints_it_ = waypoints_.end();
00174 goal_ = NOWHERE;
00175 mode_ = NONE;
00176 state_ = IDLE;
00177 }
00178
00179 void publishMarkers(bool clear = false)
00180 {
00181 if ((state_ == IDLE) || (wp_markers_pub_.getNumSubscribers() == 0))
00182 return;
00183
00184 visualization_msgs::MarkerArray markers_array;
00185 visualization_msgs::Marker marker, label;
00186
00187 marker.header.frame_id = world_frame_;
00188 marker.header.stamp = ros::Time::now();
00189 marker.scale.x = 0.08;
00190 marker.scale.y = 0.08;
00191 marker.scale.z = 0.01;
00192 marker.pose.position.z = marker.scale.z/2.0;
00193 marker.color.r = 0.8f;
00194 marker.color.g = 0.2f;
00195 marker.color.b = 0.2f;
00196
00197 int index = 0;
00198 std::vector<geometry_msgs::PointStamped>::iterator it;
00199 for (it = waypoints_.begin(); it != waypoints_.end(); it++)
00200 {
00201 std::stringstream name;
00202 name << "WP " << index;
00203 marker.ns = name.str();
00204 marker.id = index;
00205 marker.pose.position.x = it->point.x;
00206 marker.pose.position.y = it->point.y;
00207 marker.type = visualization_msgs::Marker::CYLINDER;
00208 if ((clear == true) || ((mode_ == GOAL) && (it < waypoints_it_)))
00209 marker.action = visualization_msgs::Marker::DELETE;
00210 else
00211 marker.action = visualization_msgs::Marker::ADD;
00212 marker.color.a = (it == waypoints_it_)?1.0f:0.6f;
00213
00214 label = marker;
00215 label.id = marker.id + 1000000;
00216 label.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
00217 label.pose.position.z = marker.pose.position.z + marker.scale.z/2.0 + 0.05;
00218 label.text = marker.ns.substr(3);
00219 label.scale.x = 0.1;
00220 label.scale.y = 0.1;
00221 label.scale.z = 0.1;
00222
00223 markers_array.markers.push_back(marker);
00224 markers_array.markers.push_back(label);
00225
00226 index++;
00227 }
00228
00229 if (mode_ == GOAL)
00230 {
00231 marker.ns = "GOAL";
00232 marker.id = 666666;
00233 marker.type = visualization_msgs::Marker::ARROW;
00234 if (clear == true)
00235 marker.action = visualization_msgs::Marker::DELETE;
00236 else
00237 marker.action = visualization_msgs::Marker::ADD;
00238 marker.scale.x = 0.5;
00239 marker.scale.y = 0.08;
00240 marker.scale.z = 0.001;
00241 marker.pose.orientation = goal_.pose.orientation;
00242 marker.pose.position.x = goal_.pose.position.x;
00243 marker.pose.position.y = goal_.pose.position.y;
00244 marker.pose.position.z = 0.0005;
00245 marker.color.r = 0.8f;
00246 marker.color.g = 0.2f;
00247 marker.color.b = 0.2f;
00248 marker.color.a = 1.0f;
00249
00250 markers_array.markers.push_back(marker);
00251 }
00252
00253 wp_markers_pub_.publish(markers_array);
00254 }
00255
00256 void spin()
00257 {
00258 move_base_msgs::MoveBaseGoal mb_goal;
00259
00260 ros::Rate rate(frequency_);
00261
00262 while (ros::ok())
00263 {
00264 ros::spinOnce();
00265 rate.sleep();
00266
00267 publishMarkers();
00268
00269 if ((state_ <= READY) || (state_ >= COMPLETED))
00270 continue;
00271
00272 if (state_ == ACTIVE)
00273 {
00274 actionlib::SimpleClientGoalState goal_state = move_base_ac_.getState();
00275 if ((goal_state == actionlib::SimpleClientGoalState::ACTIVE) ||
00276 (goal_state == actionlib::SimpleClientGoalState::PENDING) ||
00277 (goal_state == actionlib::SimpleClientGoalState::RECALLED) ||
00278 (goal_state == actionlib::SimpleClientGoalState::PREEMPTED))
00279 {
00280
00281
00282 if ((ros::Time::now() - mb_goal.target_pose.header.stamp).toSec() >= goal_timeout_)
00283 {
00284 ROS_WARN("Cannot reach goal after %.2f seconds; request a new one (current state is %s)",
00285 goal_timeout_, move_base_ac_.getState().toString().c_str());
00286 }
00287 else if (equals(mb_goal.target_pose, goal_) == false)
00288 {
00289
00290
00291 tf::StampedTransform robot_gb, goal_gb;
00292 try
00293 {
00294 tf_listener_.lookupTransform(world_frame_, robot_frame_, ros::Time(0.0), robot_gb);
00295 }
00296 catch (tf::TransformException& e)
00297 {
00298 ROS_WARN("Cannot get tf %s -> %s: %s", world_frame_.c_str(), robot_frame_.c_str(), e.what());
00299 continue;
00300 }
00301
00302 mtk::pose2tf(mb_goal.target_pose, goal_gb);
00303 double distance = mtk::distance2D(robot_gb, goal_gb);
00304 if (distance > close_enough_)
00305 {
00306 continue;
00307 }
00308 else
00309 {
00310 ROS_DEBUG("Close enough to current goal (%.2f <= %.2f m); request a new one", distance, close_enough_);
00311 }
00312 }
00313 else
00314 {
00315
00316 continue;
00317 }
00318 }
00319 else if (equals(mb_goal.target_pose, goal_) == true)
00320 {
00321 resetWaypoints();
00322
00323
00324 if (move_base_ac_.getState() != actionlib::SimpleClientGoalState::SUCCEEDED)
00325 {
00326 ROS_ERROR("Go to goal failed: %s", move_base_ac_.getState().toString().c_str());
00327 }
00328 else
00329 {
00330 ROS_INFO("Go to goal successfully completed: %.2f, %.2f, %.2f",
00331 mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
00332 tf::getYaw(mb_goal.target_pose.pose.orientation));
00333 }
00334
00335 continue;
00336 }
00337 }
00338
00339
00340
00341
00343 if ((waypoints_.size() > 0) && (equals(mb_goal.target_pose.pose.position, waypoints_it_->point) == true))
00344 {
00345 waypoints_it_++;
00346
00347 if (mode_ == LOOP)
00348 {
00349 if (waypoints_it_ == waypoints_.end())
00350 waypoints_it_ = waypoints_.begin();
00351 }
00352 }
00353
00354 if (waypoints_it_ < waypoints_.end())
00355 {
00356 mb_goal.target_pose.header.stamp = ros::Time::now();
00357 mb_goal.target_pose.header.frame_id = world_frame_;
00358 mb_goal.target_pose.pose.position = waypoints_it_->point;
00359 mb_goal.target_pose.pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00360 }
00361 else if (mode_ == GOAL)
00362 {
00363 mb_goal.target_pose = goal_;
00364 mb_goal.target_pose.header.stamp = ros::Time::now();
00365 }
00366 else
00367 {
00368 ROS_ERROR("Impossible situation. M: %d S: %d", mode_, state_);
00369 break;
00370 }
00371
00372 ROS_INFO("New goal: %.2f, %.2f, %.2f",
00373 mb_goal.target_pose.pose.position.x, mb_goal.target_pose.pose.position.y,
00374 tf::getYaw(mb_goal.target_pose.pose.orientation));
00375
00376
00377
00378 int times_sent = 0;
00379 do
00380 {
00381 move_base_ac_.sendGoal(mb_goal);
00382 times_sent++;
00383 } while ((move_base_ac_.waitForResult(ros::Duration(0.1)) == true) &&
00384 (move_base_ac_.getState() == actionlib::SimpleClientGoalState::SUCCEEDED));
00385
00386 if (times_sent > 1)
00387 ROS_WARN("Again the strange case of instantaneous goals... (goal sent %d times)", times_sent);
00388
00389 state_ = ACTIVE;
00390 }
00391 }
00392
00393 private:
00394 const geometry_msgs::PoseStamped NOWHERE;
00395
00396 enum { NONE = 0,
00397 GOAL,
00398 LOOP
00399 } mode_;
00400
00401 enum { IDLE = 0,
00402 READY,
00403 START,
00404 ACTIVE,
00405 COMPLETED
00406 } state_;
00407
00408 double frequency_;
00409 double close_enough_;
00410 double goal_timeout_;
00411 std::string robot_frame_;
00412 std::string world_frame_;
00413
00414 std::string waypoints_file_;
00415
00416 std::vector<geometry_msgs::PointStamped> waypoints_;
00417 std::vector<geometry_msgs::PointStamped>::iterator waypoints_it_;
00418 geometry_msgs::PoseStamped goal_;
00419
00420 tf::TransformListener tf_listener_;
00421 ros::Publisher wp_markers_pub_;
00422 ros::Subscriber final_goal_sub_;
00423 ros::Subscriber waypoints_sub_;
00424
00425 actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> move_base_ac_;
00426
00427
00428 bool readWaypointsFile(std::string file)
00429 {
00430 waypoints_.clear();
00431 try
00432 {
00433
00434
00435
00436 std::ifstream ifs(file.c_str(), std::ifstream::in);
00437 if (ifs.good() == false)
00438 {
00439 ROS_ERROR("Waypoints file not found [%s]", file.c_str());
00440 return false;
00441 }
00442
00443 YAML::Node doc;
00444 #ifdef HAVE_NEW_YAMLCPP
00445 doc = YAML::Load(ifs);
00446
00447 const YAML::Node &wp_node_tmp = doc["waypoints"];
00448 const YAML::Node *wp_node = wp_node_tmp ? &wp_node_tmp : NULL;
00449 #else
00450 YAML::Parser parser(ifs);
00451 parser.GetNextDocument(doc);
00452
00453 const YAML::Node *wp_node = doc.FindValue("waypoints");
00454 #endif
00455 if (wp_node != NULL)
00456 {
00457 for (unsigned int i = 0; i < wp_node->size(); i++)
00458 {
00459
00460 geometry_msgs::PointStamped point;
00461
00462 (*wp_node)[i]["point"]["x"] >> point.point.x;
00463 (*wp_node)[i]["point"]["y"] >> point.point.y;
00464 (*wp_node)[i]["point"]["z"] >> point.point.z;
00465
00466 waypoints_.push_back(point);
00467 }
00468 }
00469 else
00470 {
00471 ROS_DEBUG("No waypoints in file; assuming go to goal mode");
00472 }
00473
00474 #ifdef HAVE_NEW_YAMLCPP
00475 const YAML::Node &fg_node_tmp = doc["final_goal"];
00476 const YAML::Node *fg_node = fg_node_tmp ? &fg_node_tmp : NULL;
00477 #else
00478 const YAML::Node *fg_node = doc.FindValue("final_goal");
00479 #endif
00480 if ((wp_node == NULL) && (fg_node == NULL))
00481 {
00482 ROS_ERROR("Missing required key: final_goal");
00483 return false;
00484 }
00485
00486 if (fg_node != NULL)
00487 {
00488
00489 geometry_msgs::PoseStamped goal;
00490
00491 (*fg_node)["header"]["frame_id"] >> goal.header.frame_id;
00492
00493 (*fg_node)["pose"]["position"]["x"] >> goal.pose.position.x;
00494 (*fg_node)["pose"]["position"]["y"] >> goal.pose.position.y;
00495 (*fg_node)["pose"]["position"]["z"] >> goal.pose.position.z;
00496
00497 (*fg_node)["pose"]["orientation"]["x"] >> goal.pose.orientation.x;
00498 (*fg_node)["pose"]["orientation"]["y"] >> goal.pose.orientation.y;
00499 (*fg_node)["pose"]["orientation"]["z"] >> goal.pose.orientation.z;
00500 (*fg_node)["pose"]["orientation"]["w"] >> goal.pose.orientation.w;
00501
00502 goal_ = goal;
00503 mode_ = GOAL;
00504 }
00505 else
00506 {
00507 ROS_DEBUG("No final goal in file; assuming loop mode");
00508 waypoints_it_ = waypoints_.begin();
00509 mode_ = LOOP;
00510 }
00511
00512 state_ = READY;
00513
00514 ROS_DEBUG("Waypoints file successfully parsed: %s", waypoints_file_.c_str());
00515 }
00516 catch(YAML::ParserException& e)
00517 {
00518 ROS_ERROR("Parse waypoints file failed: %s", e.what());
00519 return false;
00520 }
00521 catch(YAML::RepresentationException& e)
00522 {
00523 ROS_ERROR("Parse waypoints file failed: %s", e.what());
00524 return false;
00525 }
00526
00527 return true;
00528 }
00529
00530 bool equals(const geometry_msgs::PoseStamped& a, const geometry_msgs::PoseStamped& b)
00531 {
00532 return ((a.pose.position.x == b.pose.position.x) &&
00533 (a.pose.position.y == b.pose.position.y) &&
00534 (a.pose.position.z == b.pose.position.z));
00535
00536 }
00537
00538 bool equals(const geometry_msgs::Point& a, const geometry_msgs::Point& b)
00539 {
00540 return ((a.x == b.x) && (a.y == b.y) && (a.z == b.z));
00541 }
00542 };
00543
00544 int main(int argc, char **argv)
00545 {
00546 ros::init(argc, argv, ROS_PACKAGE_NAME);
00547
00548 WaypointsGoalNode eg;
00549 if (eg.init() == false)
00550 {
00551 ROS_ERROR("%s initialization failed", ros::this_node::getName().c_str());
00552 return -1;
00553 }
00554 ROS_INFO("%s initialized", ros::this_node::getName().c_str());
00555 eg.spin();
00556 return 0;
00557 }