00001
00038 #include <actionlib/client/simple_action_client.h>
00039 #include <actionlib/client/terminal_state.h>
00040 #include <actionlib/server/simple_action_server.h>
00041 #include <boost/date_time/posix_time/posix_time.hpp>
00042 #include <boost/filesystem.hpp>
00043 #include <boost/foreach.hpp>
00044 #include <boost/thread/thread.hpp>
00045 #include <bwi_tools/resource_resolver.h>
00046 #include <message_filters/subscriber.h>
00047 #include <move_base_msgs/MoveBaseAction.h>
00048 #include <multi_level_map_msgs/ChangeCurrentLevel.h>
00049 #include <multi_level_map_msgs/LevelMetaData.h>
00050 #include <multi_level_map_msgs/MultiLevelMapData.h>
00051 #include <multi_level_map_utils/utils.h>
00052 #include <map_msgs/OccupancyGridUpdate.h>
00053 #include <nav_msgs/Odometry.h>
00054 #include <ros/ros.h>
00055 #include <tf/message_filter.h>
00056 #include <tf/transform_datatypes.h>
00057 #include <tf/transform_listener.h>
00058
00059 #include <bwi_msgs/LogicalNavigationAction.h>
00060 #include <segbot_logical_translator/segbot_logical_translator.h>
00061
00062 using bwi_planning_common::PlannerAtom;
00063 using bwi_planning_common::NO_DOOR_IDX;
00064
00065 class SegbotLogicalNavigator : public segbot_logical_translator::SegbotLogicalTranslator {
00066
00067 public:
00068
00069 typedef actionlib::SimpleActionServer<bwi_msgs::LogicalNavigationAction> LogicalNavActionServer;
00070
00071 SegbotLogicalNavigator();
00072 void execute(const bwi_msgs::LogicalNavigationGoalConstPtr &goal);
00073
00074 protected:
00075
00076 void senseState(std::vector<PlannerAtom>& observations,
00077 size_t door_idx = NO_DOOR_IDX);
00078 bool approachDoor(const std::string& door_name,
00079 std::vector<PlannerAtom>& observations,
00080 std::string& error_message, bool gothrough = false);
00081 bool senseDoor(const std::string& door_name,
00082 std::vector<PlannerAtom>& observations,
00083 std::string& error_message);
00084
00085 bool approachObject(const std::string& object_name,
00086 std::vector<PlannerAtom>& observations,
00087 std::string& error_message);
00088
00089 bool changeFloor(const std::string& new_room,
00090 const std::string& facing_door,
00091 std::vector<PlannerAtom>& observations,
00092 std::string& error_message);
00093
00094 bool executeNavigationGoal(const geometry_msgs::PoseStamped& pose);
00095 void odometryHandler(const nav_msgs::Odometry::ConstPtr& odom);
00096
00097 void currentLevelHandler(const multi_level_map_msgs::LevelMetaData::ConstPtr& current_level);
00098 void multimapHandler(const multi_level_map_msgs::MultiLevelMapData::ConstPtr& multimap);
00099
00100 void publishNavigationMap(bool publish_map_with_doors = false,
00101 bool wait_for_costmap_change = false);
00102
00103 float robot_x_;
00104 float robot_y_;
00105 float robot_yaw_;
00106 std::string current_level_id_;
00107
00108 double door_proximity_distance_;
00109
00110 boost::shared_ptr<LogicalNavActionServer> execute_action_server_;
00111 bool execute_action_server_started_;
00112
00113 boost::shared_ptr<actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> > robot_controller_;
00114
00115 boost::shared_ptr<tf::TransformListener> tf_;
00116 boost::shared_ptr<tf::MessageFilter<nav_msgs::Odometry> > tf_filter_;
00117 boost::shared_ptr<message_filters::Subscriber<nav_msgs::Odometry> > odom_subscriber_;
00118
00119 ros::Subscriber current_level_subscriber_;
00120 ros::Subscriber multimap_subscriber_;
00121 ros::ServiceClient change_level_client_;
00122 bool change_level_client_available_;
00123 std::vector<multi_level_map_msgs::LevelMetaData> all_levels_;
00124 std::map<std::string, std::vector<bwi_planning_common::Door> > level_to_doors_map_;
00125 std::map<std::string, std::vector<std::string> > level_to_loc_names_map_;
00126 std::map<std::string, std::vector<int32_t> > level_to_loc_map_;
00127
00128 ros::Publisher navigation_map_publisher_;
00129 bool last_map_published_with_doors_;
00130
00131
00132 void costmapSubscriber(const nav_msgs::OccupancyGrid::ConstPtr& costmap);
00133 void costmapUpdatesSubscriber(const map_msgs::OccupancyGridUpdate::ConstPtr& costmap_updates);
00134 ros::Subscriber costmap_subscriber_;
00135 ros::Subscriber costmap_updates_subscriber_;
00136 bool full_global_costmap_update_;
00137 int global_costmap_width_;
00138
00139 bool robot_controller_available_;
00140
00141 };
00142
00143 SegbotLogicalNavigator::SegbotLogicalNavigator() :
00144 robot_x_(0), robot_y_(0), robot_yaw_(0), current_level_id_(""), execute_action_server_started_(false),
00145 change_level_client_available_(false), robot_controller_available_(false) {
00146
00147 ROS_INFO("SegbotLogicalNavigator: Advertising services!");
00148
00149 ros::param::param("~door_proximity_distance", door_proximity_distance_, 2.0);
00150
00151
00152 ros::NodeHandle private_nh("~");
00153 navigation_map_publisher_ = private_nh.advertise<nav_msgs::OccupancyGrid>("map", 1, true);
00154
00155 robot_controller_.reset(
00156 new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>(
00157 "move_base", true));
00158
00159 tf_.reset(new tf::TransformListener);
00160 odom_subscriber_.reset(new message_filters::Subscriber<nav_msgs::Odometry>);
00161 odom_subscriber_->subscribe(*nh_, "odom", 5);
00162 tf_filter_.reset(new tf::MessageFilter<nav_msgs::Odometry>(
00163 *odom_subscriber_, *tf_, global_frame_id_, 5));
00164 tf_filter_->registerCallback(
00165 boost::bind(&SegbotLogicalNavigator::odometryHandler, this, _1));
00166
00167 execute_action_server_.reset(new LogicalNavActionServer(*nh_,
00168 "execute_logical_goal",
00169 boost::bind(&SegbotLogicalNavigator::execute, this, _1),
00170 false));
00171
00172 current_level_subscriber_ = nh_->subscribe("level_mux/current_level",
00173 1,
00174 &SegbotLogicalNavigator::currentLevelHandler,
00175 this);
00176
00177 multimap_subscriber_ = nh_->subscribe("map_metadata",
00178 1,
00179 &SegbotLogicalNavigator::multimapHandler,
00180 this);
00181
00182
00183 costmap_subscriber_ = nh_->subscribe("move_base/global_costmap/costmap",
00184 1,
00185 &SegbotLogicalNavigator::costmapSubscriber,
00186 this);
00187 costmap_updates_subscriber_ = nh_->subscribe("move_base/global_costmap/costmap_updates",
00188 1,
00189 &SegbotLogicalNavigator::costmapUpdatesSubscriber,
00190 this);
00191 global_costmap_width_ = -1;
00192 full_global_costmap_update_ = false;
00193 }
00194
00195 void SegbotLogicalNavigator::currentLevelHandler(const multi_level_map_msgs::LevelMetaData::ConstPtr& current_level) {
00196 if (current_level_id_ != current_level->level_id) {
00197 std::string resolved_map_file = bwi_tools::resolveRosResource(current_level->map_file);
00198 ros::param::set("~map_file", resolved_map_file);
00199 std::string resolved_data_directory = bwi_tools::resolveRosResource(current_level->data_directory);
00200 ros::param::set("~data_directory", resolved_data_directory);
00201 if (SegbotLogicalTranslator::initialize()) {
00202 publishNavigationMap();
00203
00204 current_level_id_ = current_level->level_id;
00205 }
00206 }
00207 }
00208
00209 void SegbotLogicalNavigator::costmapSubscriber(const nav_msgs::OccupancyGrid::ConstPtr& costmap) {
00210 global_costmap_width_ = costmap->info.width;
00211 }
00212
00213 void SegbotLogicalNavigator::costmapUpdatesSubscriber(const map_msgs::OccupancyGridUpdate::ConstPtr& costmap_updates) {
00214 if (global_costmap_width_ != -1 && costmap_updates->width == global_costmap_width_) {
00215 full_global_costmap_update_ = true;
00216 }
00217 }
00218
00219 void SegbotLogicalNavigator::multimapHandler(const multi_level_map_msgs::MultiLevelMapData::ConstPtr& multimap) {
00220
00221
00222 BOOST_FOREACH(const multi_level_map_msgs::LevelMetaData &level, multimap->levels) {
00223 std::string resolved_data_directory = bwi_tools::resolveRosResource(level.data_directory);
00224 std::string doors_file = resolved_data_directory + "/doors.yaml";
00225 std::string locations_file = resolved_data_directory + "/locations.yaml";
00226 bwi_planning_common::readDoorFile(doors_file, level_to_doors_map_[level.level_id]);
00227 bwi_planning_common::readLocationFile(locations_file,
00228 level_to_loc_names_map_[level.level_id],
00229 level_to_loc_map_[level.level_id]);
00230 }
00231
00232
00233 if (!change_level_client_available_) {
00234 change_level_client_available_ = ros::service::waitForService("level_mux/change_current_level", ros::Duration(5.0));
00235 if (change_level_client_available_) {
00236 change_level_client_ = nh_->serviceClient<multi_level_map_msgs::ChangeCurrentLevel>("level_mux/change_current_level");
00237 }
00238 }
00239
00240 }
00241
00242 void SegbotLogicalNavigator::publishNavigationMap(bool publish_map_with_doors, bool wait_for_costmap_change) {
00243 if (wait_for_costmap_change) {
00244 full_global_costmap_update_ = false;
00245 }
00246 if (publish_map_with_doors) {
00247 navigation_map_publisher_.publish(map_with_doors_);
00248 last_map_published_with_doors_ = true;
00249 } else {
00250 navigation_map_publisher_.publish(map_);
00251 last_map_published_with_doors_ = false;
00252 }
00253 if (wait_for_costmap_change) {
00254 int counter = 0;
00255 while (!full_global_costmap_update_ && counter < 100) {
00256 boost::this_thread::sleep(boost::posix_time::milliseconds(20));
00257 counter++;
00258 }
00259 if (counter == 100) {
00260 ROS_WARN_STREAM("segbot_logical_navigator: Waited 2 seconds for move_base global costmap to change, but it did not. I'll move forward with the assumption that the map changed and I missed the notification.");
00261 }
00262 }
00263 }
00264
00265 void SegbotLogicalNavigator::senseState(std::vector<PlannerAtom>& observations, size_t door_idx) {
00266
00267 PlannerAtom at;
00268 at.name = "at";
00269 size_t location_idx = getLocationIdx(bwi::Point2f(robot_x_, robot_y_));
00270 at.value.push_back(getLocationString(location_idx));
00271 observations.push_back(at);
00272
00273 size_t num_doors = getNumDoors();
00274 bwi::Point2f robot_loc(robot_x_, robot_y_);
00275 bool beside_found = false;
00276 size_t beside_idx = NO_DOOR_IDX;
00277 size_t facing_idx = NO_DOOR_IDX;
00278
00279 for (size_t door = 0; door < num_doors; ++door) {
00280
00281 if (door_idx != NO_DOOR_IDX && door != door_idx) {
00282
00283 continue;
00284 }
00285
00286 if ((doors_[door].approach_names[0] != getLocationString(location_idx)) &&
00287 (doors_[door].approach_names[1] != getLocationString(location_idx))) {
00288
00289 continue;
00290 }
00291
00292 bool facing_door =
00293 isRobotFacingDoor(robot_loc, robot_yaw_, door_proximity_distance_, door);
00294 bool beside_door = !beside_found &&
00295 isRobotBesideDoor(robot_loc, robot_yaw_, door_proximity_distance_, door);
00296
00297 if (facing_door) {
00298 facing_idx = door;
00299 beside_idx = door;
00300 break;
00301 }
00302
00303 if (beside_door) {
00304 beside_idx = door;
00305 beside_found = true;
00306 }
00307 }
00308
00309 for (size_t door = 0; door < num_doors; ++door) {
00310
00311 if ((doors_[door].approach_names[0] != getLocationString(location_idx)) &&
00312 (doors_[door].approach_names[1] != getLocationString(location_idx))) {
00313
00314 continue;
00315 }
00316
00317 PlannerAtom beside_door;
00318 beside_door.value.push_back(getDoorString(door));
00319 beside_door.name = "beside";
00320 if (beside_idx != door) {
00321 beside_door.name = "-" + beside_door.name;
00322 }
00323 observations.push_back(beside_door);
00324
00325 PlannerAtom facing_door;
00326 facing_door.value.push_back(getDoorString(door));
00327 facing_door.name = "facing";
00328 if (facing_idx != door) {
00329 facing_door.name = "-" + facing_door.name;
00330 }
00331 observations.push_back(facing_door);
00332 }
00333
00334
00335 if (facing_idx != NO_DOOR_IDX) {
00336 PlannerAtom door_open;
00337 door_open.value.push_back(getDoorString(facing_idx));
00338 if (isDoorOpen(facing_idx)) {
00339 door_open.name = "open";
00340 observations.push_back(door_open);
00341 } else {
00342 door_open.name = "-open";
00343 observations.push_back(door_open);
00344 }
00345 }
00346 }
00347
00348 bool SegbotLogicalNavigator::executeNavigationGoal(
00349 const geometry_msgs::PoseStamped& pose) {
00350
00351 if (!robot_controller_available_) {
00352 ROS_INFO("SegbotLogicalNavigator: Need to send command to robot. Waiting for move_base server...");
00353 robot_controller_->waitForServer();
00354 ROS_INFO("SegbotLogicalNavigator: move_base server found!");
00355 }
00356
00357 move_base_msgs::MoveBaseGoal goal;
00358 goal.target_pose = pose;
00359 robot_controller_->sendGoal(goal);
00360 bool navigation_request_complete = false;
00361 while (!navigation_request_complete) {
00362 if (execute_action_server_->isPreemptRequested() || !ros::ok()) {
00363 ROS_INFO("SegbotLogicalNavigator: Got pre-empted. Cancelling low level navigation task...");
00364 robot_controller_->cancelGoal();
00365 break;
00366 }
00367 navigation_request_complete = robot_controller_->waitForResult(ros::Duration(0.5));
00368 }
00369
00370 if (navigation_request_complete) {
00371 actionlib::SimpleClientGoalState state = robot_controller_->getState();
00372 return state == actionlib::SimpleClientGoalState::SUCCEEDED;
00373 }
00374
00375
00376 return false;
00377 }
00378
00379 void SegbotLogicalNavigator::odometryHandler(
00380 const nav_msgs::Odometry::ConstPtr& odom) {
00381 geometry_msgs::PoseStamped pose_in, pose_out;
00382 pose_in.header = odom->header;
00383 pose_in.pose = odom->pose.pose;
00384 tf_->transformPose(global_frame_id_, pose_in, pose_out);
00385 robot_x_ = pose_out.pose.position.x;
00386 robot_y_ = pose_out.pose.position.y;
00387
00388
00389 robot_yaw_ = tf::getYaw(pose_out.pose.orientation);
00390
00391 if(!execute_action_server_started_) {
00392 execute_action_server_->start();
00393 execute_action_server_started_ = true;
00394 }
00395 }
00396
00397 bool SegbotLogicalNavigator::approachDoor(const std::string& door_name,
00398 std::vector<PlannerAtom>& observations,
00399 std::string& error_message, bool gothrough) {
00400 error_message = "";
00401
00402 size_t door_idx = getDoorIdx(door_name);
00403 if (door_idx == NO_DOOR_IDX) {
00404
00405 error_message = "Could not resolve argument: " + door_name;
00406 return false;
00407 } else {
00408
00409
00410 bwi::Point2f approach_pt;
00411 float approach_yaw = 0;
00412 bool door_approachable = false;
00413
00414 if (!gothrough) {
00415 publishNavigationMap(true, true);
00416 door_approachable = getApproachPoint(door_idx, bwi::Point2f(robot_x_, robot_y_), approach_pt, approach_yaw);
00417 } else {
00418 publishNavigationMap(false, true);
00419 door_approachable = getThroughDoorPoint(door_idx, bwi::Point2f(robot_x_, robot_y_), approach_pt, approach_yaw);
00420 }
00421
00422 if (door_approachable) {
00423 geometry_msgs::PoseStamped pose;
00424 pose.header.stamp = ros::Time::now();
00425 pose.header.frame_id = global_frame_id_;
00426 pose.pose.position.x = approach_pt.x;
00427 pose.pose.position.y = approach_pt.y;
00428
00429 tf::quaternionTFToMsg(
00430 tf::createQuaternionFromYaw(approach_yaw), pose.pose.orientation);
00431 bool success = executeNavigationGoal(pose);
00432
00433
00434
00435 publishNavigationMap(false, true);
00436 senseState(observations, door_idx);
00437
00438 return success;
00439 } else {
00440
00441 error_message = "Cannot interact with " + door_name + " from here.";
00442 return false;
00443 }
00444 }
00445 }
00446
00447 bool SegbotLogicalNavigator::approachObject(const std::string& object_name,
00448 std::vector<PlannerAtom>& observations,
00449 std::string& error_message) {
00450
00451 error_message = "";
00452 observations.clear();
00453
00454 if (object_approach_map_.find(object_name) != object_approach_map_.end()) {
00455
00456 if (!isObjectApproachable(object_name, bwi::Point2f(robot_x_, robot_y_))) {
00457 error_message = "Cannot interact with " + object_name + " from the robot's current location.";
00458 return false;
00459 }
00460
00461 publishNavigationMap(true);
00462
00463 geometry_msgs::PoseStamped pose;
00464 pose.header.stamp = ros::Time::now();
00465 pose.header.frame_id = global_frame_id_;
00466 pose.pose = object_approach_map_[object_name];
00467 bool success = executeNavigationGoal(pose);
00468
00469
00470 senseState(observations, NO_DOOR_IDX);
00471 PlannerAtom closeto;
00472 closeto.name = "closeto";
00473 closeto.value.push_back(object_name);
00474 if (!success) {
00475 closeto.name = "-closeto";
00476 }
00477 observations.push_back(closeto);
00478 return success;
00479 }
00480
00481 error_message = object_name + " does not exist.";
00482 return false;
00483 }
00484
00485 bool SegbotLogicalNavigator::changeFloor(const std::string& new_room,
00486 const std::string& facing_door,
00487 std::vector<PlannerAtom>& observations,
00488 std::string& error_message) {
00489
00490 error_message = "";
00491 observations.clear();
00492
00493
00494 std::string floor_name;
00495 if (!change_level_client_available_) {
00496 error_message = "SegbotLogicalNavigator has not received the multimap. Cannot change floors!";
00497 return false;
00498 } else {
00499 bool new_room_found = false;
00500 typedef std::pair<std::string, std::vector<std::string> > Level2LocNamesPair;
00501 BOOST_FOREACH(const Level2LocNamesPair& level_to_loc, level_to_loc_names_map_) {
00502 if (std::find(level_to_loc.second.begin(), level_to_loc.second.end(), new_room) != level_to_loc.second.end()) {
00503 new_room_found = true;
00504 floor_name = level_to_loc.first;
00505 break;
00506 }
00507 }
00508
00509 if (!new_room_found) {
00510 error_message = "Location " + new_room + " has not been defined on any floor!";
00511 return false;
00512 }
00513 }
00514
00515 if (current_level_id_ == floor_name) {
00516 error_message = "The robot is already on " + floor_name + " (in which " + new_room + " exists)!";
00517 return false;
00518 }
00519
00520
00521 bool door_found;
00522 bwi_planning_common::Door door;
00523 BOOST_FOREACH(const bwi_planning_common::Door& floor_door, level_to_doors_map_[floor_name]) {
00524 if (floor_door.name == facing_door) {
00525 door_found = true;
00526 door = floor_door;
00527 break;
00528 }
00529 }
00530
00531 if (!door_found) {
00532 error_message = "Door " + facing_door + " has not been defined on floor " + floor_name + "!";
00533 return false;
00534 }
00535
00536 bwi::Point2f approach_pt;
00537 float approach_yaw = 0;
00538 if (door.approach_names[0] == new_room) {
00539 approach_pt = door.approach_points[0];
00540 approach_yaw = door.approach_yaw[0];
00541 } else if (door.approach_names[1] == new_room) {
00542 approach_pt = door.approach_points[1];
00543 approach_yaw = door.approach_yaw[1];
00544 } else {
00545 error_message = "Door " + facing_door + " does not connect location " + new_room + "!";
00546 return false;
00547 }
00548
00549 multi_level_map_msgs::ChangeCurrentLevel srv;
00550 srv.request.new_level_id = floor_name;
00551 srv.request.publish_initial_pose = true;
00552
00553 geometry_msgs::PoseWithCovarianceStamped &pose = srv.request.initial_pose;
00554 pose.header.stamp = ros::Time::now();
00555 pose.header.frame_id = global_frame_id_;
00556 pose.pose.pose.position.x = approach_pt.x;
00557 pose.pose.pose.position.y = approach_pt.y;
00558 tf::quaternionTFToMsg( tf::createQuaternionFromYaw(approach_yaw), pose.pose.pose.orientation);
00559 pose.pose.covariance[0] = 0.1;
00560 pose.pose.covariance[7] = 0.1f;
00561 pose.pose.covariance[35] = 0.25f;
00562
00563 if (change_level_client_.call(srv)) {
00564 if (!srv.response.success) {
00565 error_message = srv.response.error_message;
00566 return false;
00567 }
00568
00569
00570 ros::Rate r(1);
00571 while (current_level_id_ != floor_name) r.sleep();
00572
00573
00574 senseState(observations);
00575 return true;
00576 } else {
00577 error_message = "ChangeCurrentLevel service call failed for unknown reason.";
00578 return false;
00579 }
00580 }
00581
00582 bool SegbotLogicalNavigator::senseDoor(const std::string& door_name,
00583 std::vector<PlannerAtom>& observations,
00584 std::string& error_message) {
00585 error_message = "";
00586 size_t door_idx =
00587 bwi_planning_common::resolveDoor(door_name, doors_);
00588 if (door_idx == bwi_planning_common::NO_DOOR_IDX) {
00589 error_message = "Door " + door_name + " does not exist!";
00590 return false;
00591 }
00592 bool door_open = isDoorOpen(door_idx);
00593 PlannerAtom open;
00594 open.name = "open";
00595 if (!door_open) {
00596 open.name = "-" + open.name;
00597 }
00598 open.value.push_back(door_name);
00599 observations.push_back(open);
00600 return true;
00601 }
00602
00603 void SegbotLogicalNavigator::execute(const bwi_msgs::LogicalNavigationGoalConstPtr& goal) {
00604
00605 bwi_msgs::LogicalNavigationResult res;
00606 res.observations.clear();
00607
00608 if (goal->command.name == "approach") {
00609 res.success = approachDoor(goal->command.value[0], res.observations, res.status, false);
00610 } else if (goal->command.name == "gothrough") {
00611 res.success = approachDoor(goal->command.value[0], res.observations,
00612 res.status, true);
00613 } else if (goal->command.name == "sensedoor") {
00614 res.success = senseDoor(goal->command.value[0], res.observations,
00615 res.status);
00616 } else if (goal->command.name == "goto") {
00617 res.success = approachObject(goal->command.value[0], res.observations,
00618 res.status);
00619 } else if (goal->command.name == "changefloor") {
00620 res.success = changeFloor(goal->command.value[0], goal->command.value[1], res.observations, res.status);
00621 } else {
00622 res.success = true;
00623 res.status = "";
00624 senseState(res.observations);
00625 }
00626
00627 if (res.success) {
00628 execute_action_server_->setSucceeded(res);
00629 } else if (execute_action_server_->isPreemptRequested()) {
00630 execute_action_server_->setPreempted(res);
00631 } else {
00632 execute_action_server_->setAborted(res);
00633 }
00634
00635 }
00636
00637 int main(int argc, char *argv[]) {
00638
00639 ros::init(argc, argv, "segbot_logical_translator");
00640 ros::NodeHandle nh;
00641
00642 ROS_INFO("SegbotLogicalNavigator: Starting up node...");
00643 SegbotLogicalNavigator handler;
00644 ros::MultiThreadedSpinner spinner(2);
00645 spinner.spin();
00646 ROS_INFO("SegbotLogicalNavigator: Stopping node.");
00647
00648 return 0;
00649 }