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
00031
00032
00033
00034
00035
00036
00037
00038 #include <ros/ros.h>
00039 #include <geometry_msgs/PoseStamped.h>
00040 #include <geometry_msgs/Twist.h>
00041 #include <nav_msgs/Odometry.h>
00042 #include <nav_msgs/OccupancyGrid.h>
00043 #include <tf/tf.h>
00044 #include <tf/transform_listener.h>
00045 #include <tf/message_filter.h>
00046 #include <message_filters/subscriber.h>
00047 #include <angles/angles.h>
00048 #include <visualization_msgs/Marker.h>
00049
00050 #include <actionlib/server/simple_action_server.h>
00051 #include <move_base_msgs/MoveBaseAction.h>
00052
00053
00054 typedef actionlib::SimpleActionServer<move_base_msgs::MoveBaseAction> MoveBaseActionServer;
00055
00056 MoveBaseActionServer * as;
00057
00058 ros::NodeHandle * n_ptr;
00059 ros::Publisher * cmd_vel_pub_ptr;
00060 ros::Publisher * marker_pub_ptr;
00061
00062 nav_msgs::OccupancyGrid map;
00063
00064
00065 typedef enum _SimpleNavigationState {
00066
00067 SN_STOPPED = 1,
00068 SN_MOVING = 2,
00069 SN_ROTATING = 3,
00070 SN_MOVING_AS = 4,
00071 SN_ROTATING_AS = 5
00072
00073 } SimpleNavigationState;
00074
00075
00076 SimpleNavigationState state;
00077
00078
00079 tf::TransformListener * tf_listener;
00080
00081 double distance_travelled;
00082
00083
00084 std::string global_frame_id;
00085 double rate;
00086 double in_place_angular_velocity;
00087 double max_linear_velocity;
00088 double min_linear_velocity;
00089 double alpha;
00090 double attraction_coefficient;
00091 double repulsion_coefficient_robots;
00092 double repulsion_coefficient_obstacles;
00093 double goal_tolerance;
00094 double angular_threshold;
00095 double repulsion_threshold_robots;
00096 double repulsion_threshold_obstacles;
00097 ros::Duration delay;
00098 double max_distance;
00099 ros::Duration goal_timeout;
00100
00101 bool visualization;
00102
00103 int simple_goal = 0;
00104
00105
00106 class Robot
00107 {
00108 public:
00109 Robot(int id) : pose(), prefix()
00110 {
00111 this->id = id;
00112
00113 prefix = "/robot_";
00114 prefix.append<int>(1, 48+id);
00115 };
00116
00117 Robot(const Robot& r) : pose(), prefix()
00118 {
00119 id = r.id;
00120
00121 pose = r.pose;
00122
00123 odom_sub = r.odom_sub;
00124 tf_filter = r.tf_filter;
00125
00126 prefix = r.prefix;
00127 };
00128
00129
00130 int id;
00131
00132
00133 geometry_msgs::PoseStamped pose;
00134
00135
00136 message_filters::Subscriber<nav_msgs::Odometry> * odom_sub;
00137 tf::MessageFilter<nav_msgs::Odometry> * tf_filter;
00138
00139 std::string prefix;
00140 };
00141
00142
00143 std::vector<Robot> robots;
00144
00145 int me;
00146
00147 bool rotate_in_place;
00148
00149 double calculateVelocities(double current_x, double current_y, double current_orientation, double goal_x, double goal_y, double * linear_velocity, double * angular_velocity)
00150 {
00151
00152 double G_attr_x = -attraction_coefficient*(current_x - goal_x);
00153 double G_attr_y = -attraction_coefficient*(current_y - goal_y);
00154
00155
00156 double G_rep_x = 0.0;
00157 double G_rep_y = 0.0;
00158
00159
00160 for(int i=0 ; i<robots.size() ; i++)
00161 {
00162 if(i==me) continue;
00163
00164 double distance = sqrt(pow(current_x-robots[i].pose.pose.position.x,2)+pow(current_y-robots[i].pose.pose.position.y,2));
00165 if(distance <= repulsion_threshold_robots)
00166 {
00167 G_rep_x += -repulsion_coefficient_robots*(current_x-robots[i].pose.pose.position.x)*(1/pow(distance,2)-repulsion_threshold_robots/pow(distance,3));
00168 G_rep_y += -repulsion_coefficient_robots*(current_y-robots[i].pose.pose.position.y)*(1/pow(distance,2)-repulsion_threshold_robots/pow(distance,3));
00169 }
00170 }
00171
00172
00173 int current_x_index = current_x/map.info.resolution;
00174 int current_y_index = current_y/map.info.resolution;
00175
00176 int rep = ceil(repulsion_threshold_obstacles/map.info.resolution)+1;
00177 for(int i=(current_x_index-rep<0 ? 0 : current_x_index-rep) ; i<(current_x_index+rep>map.info.width ? map.info.width : current_x_index+rep) ; i++)
00178 {
00179 for(int j=(current_y_index-rep<0 ? 0 : current_y_index-rep) ; j<(current_y_index+rep>map.info.height ? map.info.height : current_y_index+rep) ; j++)
00180 {
00181 double distance = sqrt(pow(current_x-i*map.info.resolution,2)+pow(current_y-j*map.info.resolution,2));
00182 if(map.data[j*map.info.width+i] != 0 && distance <= repulsion_threshold_obstacles)
00183 {
00184 G_rep_x += -repulsion_coefficient_obstacles*(current_x-i*map.info.resolution)*(1/pow(distance,2)-repulsion_threshold_obstacles/pow(distance,3));
00185 G_rep_y += -repulsion_coefficient_obstacles*(current_y-j*map.info.resolution)*(1/pow(distance,2)-repulsion_threshold_obstacles/pow(distance,3));
00186 }
00187 }
00188 }
00189
00190 double G_x = G_attr_x + G_rep_x;
00191 double G_y = G_attr_y + G_rep_y;
00192
00193 double target_orientation = atan2(G_y, G_x);
00194
00195 *linear_velocity = sqrt(G_x*G_x + G_y*G_y);
00196 if(fabs(*linear_velocity) > max_linear_velocity) *linear_velocity = (*linear_velocity > 0 ? max_linear_velocity : -max_linear_velocity);
00197 if(fabs(*linear_velocity) < min_linear_velocity) *linear_velocity = (*linear_velocity > 0 ? min_linear_velocity : -min_linear_velocity);
00198
00199 double delta_yaw = angles::shortest_angular_distance(target_orientation, current_orientation);
00200
00201 *angular_velocity = -alpha*(delta_yaw);
00202
00203 if(visualization)
00204 {
00205 visualization_msgs::Marker marker;
00206
00207 std::string frame_id = robots[me].prefix;
00208 frame_id.append("/base_link");
00209 marker.header.frame_id = frame_id;
00210 marker.header.stamp = ros::Time::now();
00211
00212
00213
00214 marker.ns = "attraction_repulsion_vectors";
00215 marker.id = robots[me].id;
00216
00217
00218 marker.type = visualization_msgs::Marker::ARROW;
00219
00220
00221 marker.action = visualization_msgs::Marker::ADD;
00222
00223
00224 marker.pose.position.x = 0.10;
00225 marker.pose.position.y = 0;
00226 marker.pose.position.z = 0.10;
00227 marker.pose.orientation = tf::createQuaternionMsgFromYaw(-1*delta_yaw);
00228
00229
00230 marker.scale.x = 0.4;
00231 marker.scale.y = 0.4;
00232 marker.scale.z = fabs(*linear_velocity)*0.5/max_linear_velocity;
00233
00234
00235 marker.color.r = 1.0f;
00236 marker.color.g = 1.0f;
00237 marker.color.b = 0.0f;
00238 marker.color.a = 1.0;
00239
00240 marker.lifetime = ros::Duration();
00241
00242
00243 marker_pub_ptr->publish(marker);
00244 }
00245
00246 return delta_yaw;
00247 }
00248
00249 void displayGoal(double goal_x, double goal_y)
00250 {
00251 visualization_msgs::Marker goal_marker;
00252
00253 goal_marker.header.frame_id = global_frame_id;
00254 goal_marker.header.stamp = ros::Time::now();
00255
00256
00257
00258 goal_marker.ns = "goals";
00259 goal_marker.id = robots[me].id;
00260
00261
00262 goal_marker.type = visualization_msgs::Marker::CYLINDER;
00263
00264
00265 goal_marker.action = visualization_msgs::Marker::ADD;
00266
00267
00268 goal_marker.pose.position.x = goal_x;
00269 goal_marker.pose.position.y = goal_y;
00270 goal_marker.pose.position.z = 0.0;
00271 goal_marker.pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00272
00273
00274 goal_marker.scale.x = goal_tolerance*2.0;
00275 goal_marker.scale.y = goal_tolerance*2.0;
00276 goal_marker.scale.z = 0.01;
00277
00278
00279 goal_marker.color.r = 0.0f;
00280 goal_marker.color.g = 1.0f;
00281 goal_marker.color.b = 0.0f;
00282 goal_marker.color.a = 0.5;
00283
00284 goal_marker.lifetime = ros::Duration();
00285
00286
00287 marker_pub_ptr->publish(goal_marker);
00288 }
00289
00290 void publishVelocities(double linear_velocity, double angular_velocity)
00291 {
00292
00293 geometry_msgs::Twist cmd_vel;
00294
00295 cmd_vel.linear.x = linear_velocity;
00296 cmd_vel.angular.z = angular_velocity;
00297 cmd_vel_pub_ptr->publish(cmd_vel);
00298 }
00299
00300
00301 void goalReceived(const geometry_msgs::PoseStamped::ConstPtr& msg)
00302 {
00303 ros::Time start_time = ros::Time::now();
00304 simple_goal++;
00305
00306 if(state == SN_ROTATING_AS || state == SN_MOVING_AS)
00307 {
00308 ROS_ERROR("MoveBase Simple - %s - Ignoring goal because there is already an ActionServer goal in progress!", __FUNCTION__);
00309 return;
00310 }
00311
00312 if(msg->header.frame_id != global_frame_id)
00313 {
00314 ROS_ERROR("MoveBase Simple - %s - Ignoring goal because frame_id is not the global frame!", __FUNCTION__);
00315 return;
00316 }
00317
00318 geometry_msgs::PoseStamped goal = *msg;
00319
00320 displayGoal(goal.pose.position.x, goal.pose.position.y);
00321
00322 state = SN_MOVING;
00323 distance_travelled = 0.0;
00324
00325 ros::Rate r(rate);
00326 while(n_ptr->ok())
00327 {
00328 if(state == SN_ROTATING_AS || state == SN_MOVING_AS || simple_goal>1) return;
00329
00330 double linear_velocity;
00331 double angular_velocity;
00332
00333 double current_orientation = tf::getYaw(robots[me].pose.pose.orientation);
00334 double current_x = robots[me].pose.pose.position.x;
00335 double current_y = robots[me].pose.pose.position.y;
00336
00337
00338 if(ros::Time::now() - start_time > goal_timeout)
00339 {
00340 publishVelocities(0.0, 0.0);
00341 state = SN_STOPPED;
00342 return;
00343 }
00344
00345
00346 if(sqrt((current_x-goal.pose.position.x)*(current_x-goal.pose.position.x)+(current_y-goal.pose.position.y)*(current_y-goal.pose.position.y)) < goal_tolerance)
00347 {
00348 publishVelocities(0.0, 0.0);
00349 state = SN_STOPPED;
00350 simple_goal--;
00351 return;
00352 }
00353
00354
00355 if(distance_travelled > max_distance)
00356 {
00357 publishVelocities(0.0, 0.0);
00358 distance_travelled = 0.0;
00359 delay.sleep();
00360 }
00361
00362 double delta_yaw = calculateVelocities(current_x, current_y, current_orientation, goal.pose.position.x, goal.pose.position.y, &linear_velocity, &angular_velocity);
00363
00364
00365 if(state == SN_MOVING && fabs(delta_yaw) > angular_threshold)
00366 {
00367 state = SN_ROTATING;
00368 distance_travelled = 0.0;
00369 delay.sleep();
00370 }
00371
00372
00373 if(state == SN_ROTATING)
00374 {
00375 linear_velocity = 0.0;
00376 angular_velocity = (angular_velocity < 0 ? -in_place_angular_velocity : in_place_angular_velocity);
00377 if(fabs(delta_yaw) < angular_threshold) angular_velocity = 0.0;
00378 }
00379
00380 publishVelocities(linear_velocity, angular_velocity);
00381 if(state == SN_ROTATING && fabs(delta_yaw) < angular_threshold)
00382 {
00383 state = SN_MOVING;
00384 distance_travelled = 0.0;
00385 delay.sleep();
00386 }
00387
00388 ros::spinOnce();
00389 r.sleep();
00390 }
00391 }
00392
00393
00394
00395 void asCallback(const move_base_msgs::MoveBaseGoalConstPtr& move_base_goal)
00396 {
00397 ros::Time start_time = ros::Time::now();
00398
00399 if(move_base_goal->target_pose.header.frame_id.compare(global_frame_id) != 0)
00400 {
00401 as->setAborted(move_base_msgs::MoveBaseResult(), "Aborting on goal because frame_id is not the global frame!");
00402 return;
00403 }
00404
00405 geometry_msgs::PoseStamped goal = move_base_goal->target_pose;
00406
00407 displayGoal(goal.pose.position.x, goal.pose.position.y);
00408
00409 state = SN_MOVING_AS;
00410 distance_travelled = 0.0;
00411
00412 ros::Rate r(rate);
00413 while(n_ptr->ok())
00414 {
00415 if(as->isPreemptRequested())
00416 {
00417 publishVelocities(0.0, 0.0);
00418 as->setPreempted();
00419 state = SN_STOPPED;
00420 return;
00421 }
00422
00423 double linear_velocity;
00424 double angular_velocity;
00425
00426 double current_orientation = tf::getYaw(robots[me].pose.pose.orientation);
00427 double current_x = robots[me].pose.pose.position.x;
00428 double current_y = robots[me].pose.pose.position.y;
00429
00430
00431 if(ros::Time::now() - start_time > goal_timeout)
00432 {
00433 publishVelocities(0.0, 0.0);
00434 as->setAborted();
00435 state = SN_STOPPED;
00436 return;
00437 }
00438
00439
00440 if(sqrt((current_x-goal.pose.position.x)*(current_x-goal.pose.position.x)+(current_y-goal.pose.position.y)*(current_y-goal.pose.position.y)) < goal_tolerance)
00441 {
00442 publishVelocities(0.0, 0.0);
00443 as->setSucceeded();
00444 state = SN_STOPPED;
00445 return;
00446 }
00447
00448
00449 if(distance_travelled > max_distance)
00450 {
00451 publishVelocities(0.0, 0.0);
00452 distance_travelled = 0.0;
00453 delay.sleep();
00454 }
00455
00456 double delta_yaw = calculateVelocities(current_x, current_y, current_orientation, goal.pose.position.x, goal.pose.position.y, &linear_velocity, &angular_velocity);
00457
00458
00459 if(state == SN_MOVING_AS && fabs(delta_yaw) > angular_threshold)
00460 {
00461 state = SN_ROTATING_AS;
00462 distance_travelled = 0.0;
00463 delay.sleep();
00464 }
00465
00466
00467 if(state == SN_ROTATING_AS)
00468 {
00469 linear_velocity = 0.0;
00470 angular_velocity = (angular_velocity < 0 ? -in_place_angular_velocity : in_place_angular_velocity);
00471 if(fabs(delta_yaw) < angular_threshold) angular_velocity = 0.0;
00472 }
00473
00474
00475 move_base_msgs::MoveBaseFeedback feedback;
00476
00477 feedback.base_position = robots[me].pose;
00478 as->publishFeedback(feedback);
00479
00480 publishVelocities(linear_velocity, angular_velocity);
00481 if(state == SN_ROTATING_AS && fabs(delta_yaw) < angular_threshold)
00482 {
00483 state = SN_MOVING_AS;
00484 distance_travelled = 0.0;
00485 delay.sleep();
00486 }
00487
00488 ros::spinOnce();
00489 r.sleep();
00490 }
00491 }
00492
00493
00494 void odomCallback(int index, const boost::shared_ptr<const nav_msgs::Odometry>& msg)
00495 {
00496 geometry_msgs::PoseStamped pose;
00497 pose.header.frame_id = msg->header.frame_id;
00498 pose.header.stamp = msg->header.stamp;
00499 pose.pose = msg->pose.pose;
00500
00501 geometry_msgs::PoseStamped global_frame_id_pose;
00502
00503 try
00504 {
00505
00506 tf_listener->transformPose(global_frame_id, pose, global_frame_id_pose);
00507 }
00508 catch(tf::TransformException &ex)
00509 {
00510 ROS_ERROR("MoveBase Simple - %s - Error: %s", __FUNCTION__, ex.what());
00511 return;
00512 }
00513
00514 if(index == me && max_distance > 0.0) distance_travelled += sqrt(pow(robots[index].pose.pose.position.x-global_frame_id_pose.pose.position.x, 2)+pow(robots[index].pose.pose.position.y-global_frame_id_pose.pose.position.y, 2));
00515
00516 robots[index].pose = global_frame_id_pose;
00517 }
00518
00519 void mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg)
00520 {
00521 map = *msg;
00522 }
00523
00524 int main(int argc, char** argv)
00525 {
00526 ros::init(argc, argv, "move_base_simple");
00527
00528 ROS_INFO("Move Base Simple for ROS");
00529
00530 ros::NodeHandle n;
00531 ros::NodeHandle pn("~");
00532
00533 n_ptr = &n;
00534
00535 tf_listener = new tf::TransformListener();
00536
00537 as = new MoveBaseActionServer(ros::NodeHandle(), "move_base", boost::bind(asCallback, _1), false);
00538
00539
00540 pn.param<std::string>("global_frame_id", global_frame_id, "/map");
00541
00542
00543 XmlRpc::XmlRpcValue list_of_ids;
00544 if( n.getParam("/list_of_ids", list_of_ids) )
00545 {
00546 int my_id;
00547 if( !pn.getParam("my_id", my_id) )
00548 {
00549 ROS_FATAL("MoveBase Simple -- A list of ids was defined but my_id was not!!!");
00550 ROS_BREAK();
00551 }
00552
00553 ROS_ASSERT(list_of_ids.getType() == XmlRpc::XmlRpcValue::TypeArray);
00554
00555 for(int i=0 ; i<list_of_ids.size() ; ++i)
00556 {
00557 ROS_ASSERT(list_of_ids[i].getType() == XmlRpc::XmlRpcValue::TypeInt);
00558
00559 robots.push_back(Robot(static_cast<int>(list_of_ids[i])));
00560
00561 robots[i].odom_sub = new message_filters::Subscriber<nav_msgs::Odometry>();
00562 std::string topic = robots[i].prefix;
00563 topic.append("/odom");
00564 robots[i].odom_sub->subscribe(n, topic, 20);
00565 robots[i].tf_filter = new tf::MessageFilter<nav_msgs::Odometry>(*robots[i].odom_sub, *tf_listener, global_frame_id, 20);
00566 robots[i].tf_filter->registerCallback( boost::bind(odomCallback, i, _1) );
00567
00568
00569 if(robots[i].id == my_id) me = i;
00570 }
00571 }
00572 else
00573
00574 {
00575 ROS_FATAL("MoveBase Simple -- A list of ids was not defined!!!");
00576 ROS_BREAK();
00577 }
00578
00579 pn.param("rate", rate, 3.0);
00580 pn.param("in_place_angular_velocity", in_place_angular_velocity, 3.0);
00581 pn.param("max_linear_velocity", max_linear_velocity, 0.2);
00582 pn.param("min_linear_velocity", min_linear_velocity, 0.05);
00583 pn.param("alpha", alpha, 0.5);
00584 pn.param("attraction_coefficient", attraction_coefficient, 0.5);
00585 pn.param("repulsion_coefficient_robots", repulsion_coefficient_robots, 0.5);
00586 pn.param("repulsion_coefficient_obstacles", repulsion_coefficient_obstacles, 0.5);
00587 pn.param("goal_tolerance", goal_tolerance, 0.10);
00588 pn.param("angular_threshold", angular_threshold, 0.4);
00589 pn.param("repulsion_threshold_robots", repulsion_threshold_robots, 0.5);
00590 pn.param("repulsion_threshold_obstacles", repulsion_threshold_obstacles, 0.0);
00591 double delay_sec = 0.0;
00592 pn.param("delay", delay_sec, 0.0);
00593 delay = ros::Duration(delay_sec);
00594 pn.param("visualization", visualization, false);
00595 pn.param("max_distance", max_distance, 0.5);
00596 double goal_timeout_sec = 0.0;
00597 pn.param("goal_timeout", goal_timeout_sec, 60.0);
00598 goal_timeout = ros::Duration(goal_timeout_sec);
00599
00600 if(angular_threshold == 0.0)
00601 {
00602 rotate_in_place = false;
00603 ROS_INFO("MoveBase Simple -- Not using in-place rotations.");
00604 }
00605 else
00606 {
00607 rotate_in_place = true;
00608 ROS_INFO("MoveBase Simple -- Using in-place rotations.");
00609 }
00610
00611 ROS_INFO("Using %s as the global frame.", global_frame_id.c_str());
00612
00613
00614 std::string topic;
00615
00616 topic = robots[me].prefix;
00617 topic.append("/cmd_vel");
00618 ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>(topic, 10);
00619 cmd_vel_pub_ptr = &cmd_vel_pub;
00620
00621 ros::Publisher marker_pub;
00622 if(visualization)
00623 {
00624 marker_pub = n.advertise<visualization_msgs::Marker>("/simple_navigation_markers", 10);
00625 marker_pub_ptr = &marker_pub;
00626 }
00627
00628 topic = robots[me].prefix;
00629 topic.append("/move_base_simple/goal");
00630 ros::Subscriber goal_sub = n.subscribe<geometry_msgs::PoseStamped>(topic, 10, goalReceived);
00631
00632 ros::Subscriber map_sub = n.subscribe<nav_msgs::OccupancyGrid>("/map", 1, mapCallback);
00633
00634 ROS_INFO("idx:%d id:%d %s", me, robots[me].id, topic.c_str());
00635
00636 state = SN_STOPPED;
00637
00638
00639 as->start();
00640
00641 ros::spin();
00642
00643 return(0);
00644 }
00645
00646
00647