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
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 #include <ros/ros.h>
00062 #include <XmlRpc.h>
00063
00064 #include <pthread.h>
00065
00066
00067 #include <actionlib/client/simple_action_client.h>
00068 #include <actionlib/server/simple_action_server.h>
00069 #include <move_base_msgs/MoveBaseAction.h>
00070
00071 #include <geometry_msgs/Twist.h>
00072 #include <geometry_msgs/PoseStamped.h>
00073 #include <nav_msgs/Odometry.h>
00074
00075 #include <tf/transform_listener.h>
00076
00077
00078
00079 class NodeClass
00080 {
00081 public:
00082
00083 ros::NodeHandle nh_;
00084
00085
00086 ros::Publisher topic_pub_command_;
00087
00088 ros::Subscriber goal_sub_, odometry_sub_;
00089
00090
00091 actionlib::SimpleActionServer<move_base_msgs::MoveBaseAction> as_;
00092 move_base_msgs::MoveBaseResult result_;
00093
00094
00095 actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> * action_client_;
00096
00097
00098
00099 NodeClass(std::string name) :
00100 as_(nh_, name, boost::bind(&NodeClass::actionCB, this, _1), false)
00101 {
00102 m_mutex = PTHREAD_MUTEX_INITIALIZER;
00103
00104 ros::NodeHandle private_nh("~");
00105
00106
00107 topic_pub_command_ = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1);
00108
00109
00110
00111
00112 ros::NodeHandle simple_nh("move_base_linear_simple");
00113 goal_sub_ = simple_nh.subscribe<geometry_msgs::PoseStamped>("goal", 1, boost::bind(&NodeClass::topicCB, this, _1));
00114
00115 action_client_ = new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>(nh_, name);
00116
00117
00118 odometry_sub_ = nh_.subscribe<nav_msgs::Odometry>("odom", 1, boost::bind(&NodeClass::odometryCB, this, _1));
00119
00120
00121 if(!private_nh.hasParam("kv")) ROS_WARN("Used default parameter for kv [2.5]");
00122 private_nh.param("kv", kv_, 2.5);
00123
00124 if(!private_nh.hasParam("kp")) ROS_WARN("Used default parameter for kp [3.0]");
00125 private_nh.param("kp", kp_, 3.0);
00126
00127 if(!private_nh.hasParam("virt_mass")) ROS_WARN("Used default parameter for virt_mass [0.5]");
00128 private_nh.param("virt_mass", virt_mass_, 0.5);
00129
00130 if(!private_nh.hasParam("vmax")) ROS_WARN("Used default parameter for vmax [0.3 m/s]");
00131 private_nh.param("vmax", v_max_, 0.3);
00132
00133 if(!private_nh.hasParam("goal_threshold")) ROS_WARN("Used default parameter for goal_threshold [0.03 cm]");
00134 private_nh.param("goal_threshold", goal_threshold_, 0.03);
00135
00136 if(!private_nh.hasParam("speed_threshold")) ROS_WARN("Used default parameter for speed_threshold [0.08 m/s]");
00137 private_nh.param("speed_threshold", speed_threshold_, 0.08);
00138
00139 if(!private_nh.hasParam("kv_rot")) ROS_WARN("Used default parameter for kv_rot [2.0]");
00140 private_nh.param("kv_rot", kv_rot_, 2.0);
00141
00142 if(!private_nh.hasParam("kp_rot")) ROS_WARN("Used default parameter for kp_rot [2.0]");
00143 private_nh.param("kp_rot", kp_rot_, 2.0);
00144
00145 if(!private_nh.hasParam("virt_mass_rot")) ROS_WARN("Used default parameter for virt_mass_rot [0.5]");
00146 private_nh.param("virt_mass_rot", virt_mass_rot_, 0.5);
00147
00148 if(!private_nh.hasParam("vtheta_max")) ROS_WARN("Used default parameter for vtheta_max [0.3 rad/s]");
00149 private_nh.param("vtheta_max", vtheta_max_, 0.3);
00150
00151 if(!private_nh.hasParam("goal_threshold_rot")) ROS_WARN("Used default parameter for goal_threshold_rot [0.08 rad]");
00152 private_nh.param("goal_threshold_rot", goal_threshold_rot_, 0.08);
00153
00154 if(!private_nh.hasParam("speed_threshold_rot")) ROS_WARN("Used default parameter for speed_threshold_rot [0.08 rad/s]");
00155 private_nh.param("speed_threshold_rot", speed_threshold_rot_, 0.08);
00156
00157 if(!private_nh.hasParam("global_frame")) ROS_WARN("Used default parameter for global_frame [/map]");
00158 private_nh.param("global_frame", global_frame_, std::string("/map"));
00159
00160 if(!private_nh.hasParam("robot_frame")) ROS_WARN("Used default parameter for robot_frame [/base_link]");
00161 private_nh.param("robot_frame", robot_frame_, std::string("/base_link"));
00162
00163 if(!private_nh.hasParam("slow_down_distance")) ROS_WARN("Used default parameter for slow_down_distance [0.5m]");
00164 private_nh.param("slow_down_distance", slow_down_distance_, 0.5);
00165
00166 if(!private_nh.hasParam("goal_abortion_time")) ROS_WARN("Used default parameter for goal_abortion_time [5.0s]");
00167 private_nh.param("goal_abortion_time", goal_abortion_time_, 5.0);
00168
00169
00170 zero_pose_.pose.position.x = 0.0;
00171 zero_pose_.pose.position.y = 0.0;
00172 zero_pose_.pose.position.z = 0.0;
00173 zero_pose_.pose.orientation = tf::createQuaternionMsgFromYaw(0.0);
00174 zero_pose_.header.frame_id = robot_frame_;
00175
00176
00177 ros::Time last_error = ros::Time::now();
00178 std::string tf_error;
00179 while(!tf_listener_.waitForTransform(global_frame_, robot_frame_, ros::Time(), ros::Duration(0.1), ros::Duration(0.01), &tf_error)) {
00180 ros::spinOnce();
00181 if(last_error + ros::Duration(5.0) < ros::Time::now()){
00182 ROS_WARN("Waiting on transform from %s to %s to become available before running cob_linear_nav, tf error: %s",
00183 robot_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
00184 last_error = ros::Time::now();
00185 }
00186 }
00187
00188
00189 as_.start();
00190 }
00191
00192 void topicCB(const geometry_msgs::PoseStamped::ConstPtr& goal){
00193 ROS_INFO("In ROS goal callback, wrapping the PoseStamped in the action message and re-sending to the server.");
00194 move_base_msgs::MoveBaseGoal action_goal;
00195
00196 action_goal.target_pose = transformGoalToMap(*goal);
00197
00198 action_client_->sendGoal(action_goal);
00199 action_client_->stopTrackingGoal();
00200 }
00201
00202 void actionCB(const move_base_msgs::MoveBaseGoalConstPtr &goal){
00203
00204 ROS_INFO("In idle mode, new goal accepted");
00205
00206 last_time_moving_ = ros::Time::now().toSec();
00207
00208 getRobotPoseGlobal();
00209 x_last_ = robot_pose_global_.pose.position.x;
00210 y_last_ = robot_pose_global_.pose.position.y;
00211 theta_last_ = tf::getYaw(robot_pose_global_.pose.orientation);
00212 vtheta_last_ = 0.0f;
00213 vx_last_ = 0.0f;
00214 vy_last_ = 0.0f;
00215 last_time_ = ros::Time::now().toSec();
00216
00217 goal_pose_global_ = transformGoalToMap(goal->target_pose);
00218
00219 move_ = true;
00220
00221 ros::Rate loop_rate(100);
00222 while(nh_.ok()) {
00223 loop_rate.sleep();
00224
00225 if(as_.isPreemptRequested()) {
00226 if(as_.isNewGoalAvailable()) {
00227 ROS_INFO("New goal received, updating movement");
00228
00229 move_base_msgs::MoveBaseGoal new_goal = * as_.acceptNewGoal();
00230 goal_pose_global_ = transformGoalToMap(new_goal.target_pose);
00231
00232 last_time_moving_ = ros::Time::now().toSec();
00233 move_ = true;
00234 } else {
00235 ROS_INFO("Preempt requested, aborting movement");
00236
00237 as_.setPreempted();
00238
00239 move_ = false;
00240 stopMovement();
00241 return;
00242 }
00243 }
00244
00245 performControllerStep();
00246
00247 if(finished_) {
00248 as_.setSucceeded(move_base_msgs::MoveBaseResult(), "Goal reached.");
00249 ROS_INFO("Goal reached.");
00250 return;
00251 }
00252
00253 if(!as_.isActive()) {
00254 ROS_INFO("Goal not active anymore. Stop!");
00255 return;
00256 }
00257 }
00258
00259
00260
00261 stopMovement();
00262 as_.setAborted(move_base_msgs::MoveBaseResult(), "Aborting on the goal because the node has been killed");
00263 return;
00264 };
00265
00266
00267 void odometryCB(const nav_msgs::Odometry::ConstPtr &odometry){
00268 geometry_msgs::Vector3Stamped vec_stamped;
00269
00270 vec_stamped.vector = odometry->twist.twist.linear;
00271 vec_stamped.header.frame_id = "/base_footprint";
00272 tf_listener_.transformVector(robot_frame_, vec_stamped, robot_twist_linear_robot_);
00273
00274 vec_stamped.vector = odometry->twist.twist.angular;
00275 vec_stamped.header.frame_id = "/base_footprint";
00276 tf_listener_.transformVector(robot_frame_, vec_stamped, robot_twist_angular_robot_);
00277 }
00278
00279
00280 ~NodeClass()
00281 {
00282 }
00283
00284 private:
00285 tf::TransformListener tf_listener_;
00286 std::string global_frame_, robot_frame_;
00287 geometry_msgs::PoseStamped goal_pose_global_;
00288 geometry_msgs::PoseStamped zero_pose_;
00289 geometry_msgs::PoseStamped robot_pose_global_;
00290 geometry_msgs::Vector3Stamped robot_twist_linear_robot_, robot_twist_angular_robot_;
00291
00292 double slow_down_distance_, goal_abortion_time_;
00293
00294 bool finished_, move_;
00295
00296 pthread_mutex_t m_mutex;
00297
00298
00299 void performControllerStep();
00300 void publishVelocitiesGlobal(double vx, double vy, double theta);
00301 geometry_msgs::PoseStamped transformGoalToMap(geometry_msgs::PoseStamped goal_pose);
00302 geometry_msgs::PoseStamped getRobotPoseGlobal();
00303
00304
00305 double getDistance2d(geometry_msgs::PoseStamped a, geometry_msgs::PoseStamped b);
00306 double getDistance2d(geometry_msgs::Point a, geometry_msgs::Point b);
00307 double getThetaDiffRad(double target, double actual);
00308 double sign(double x);
00309 void stopMovement();
00310 bool notMovingDueToObstacle();
00311
00312
00313 double vx_last_, vy_last_, x_last_, y_last_, theta_last_, vtheta_last_;
00314 double goal_threshold_, speed_threshold_;
00315 double goal_threshold_rot_, speed_threshold_rot_;
00316 double kp_, kv_, virt_mass_;
00317 double kp_rot_, kv_rot_, virt_mass_rot_;
00318 double last_time_;
00319 double v_max_, vtheta_max_;
00320 double last_time_moving_;
00321
00322 };
00323
00324 geometry_msgs::PoseStamped NodeClass::transformGoalToMap(geometry_msgs::PoseStamped goal_pose) {
00325 geometry_msgs::PoseStamped goal_global_;
00326 if(goal_pose.header.frame_id == global_frame_) return goal_pose;
00327 else if(tf_listener_.canTransform(global_frame_, goal_pose.header.frame_id, ros::Time(0), new std::string)) {
00328 tf_listener_.transformPose(global_frame_, ros::Time(0), goal_pose, "/base_link", goal_global_);
00329 return goal_global_;
00330 } else {
00331 ROS_WARN("Can't transform goal to global frame %s", global_frame_.c_str());
00332 return robot_pose_global_;
00333 }
00334 }
00335
00336 geometry_msgs::PoseStamped NodeClass::getRobotPoseGlobal() {
00337 try{
00338 tf_listener_.transformPose(global_frame_, zero_pose_, robot_pose_global_);
00339 }
00340 catch(tf::TransformException& ex){
00341 ROS_WARN("Failed to find robot pose in global frame %s", global_frame_.c_str());
00342 return zero_pose_;
00343 }
00344
00345 return robot_pose_global_;
00346 }
00347
00348 double NodeClass::getDistance2d(geometry_msgs::PoseStamped a, geometry_msgs::PoseStamped b) {
00349 return sqrt( pow(a.pose.position.x - b.pose.position.x,2) + pow(a.pose.position.y - b.pose.position.y,2) );
00350 }
00351
00352 double NodeClass::getDistance2d(geometry_msgs::Point a, geometry_msgs::Point b) {
00353 return sqrt( pow(a.x - b.x,2) + pow(a.y - b.y,2) );
00354 }
00355
00356 double NodeClass::sign(double x) {
00357 if(x >= 0.0f) return 1.0f;
00358 else return -1.0f;
00359 }
00360
00361 double NodeClass::getThetaDiffRad(double target, double actual) {
00362 if(fabs(target - actual) <= M_PI) return (target - actual);
00363 else return sign(target - actual) * -2.0f * M_PI - (target - actual);
00364 }
00365
00366 void NodeClass::publishVelocitiesGlobal(double vx, double vy, double theta) {
00367
00368 geometry_msgs::Vector3Stamped cmd_global, cmd_robot;
00369 geometry_msgs::Twist msg;
00370
00371 cmd_global.header.frame_id = global_frame_;
00372 cmd_global.vector.x = vx;
00373 cmd_global.vector.y = vy;
00374 try { tf_listener_.transformVector(robot_frame_, cmd_global, cmd_robot);
00375 } catch (tf::TransformException ex){
00376 ROS_ERROR("%s",ex.what());
00377 cmd_robot.vector.x = 0.0f;
00378 cmd_robot.vector.y = 0.0f;
00379 }
00380
00381
00382 msg.linear = cmd_robot.vector;
00383 msg.angular.z = theta;
00384 msg.linear.z = 0.0; msg.angular.x = 0.0; msg.angular.y = 0.0;
00385
00386 topic_pub_command_.publish(msg);
00387 }
00388
00389 void NodeClass::stopMovement() {
00390 publishVelocitiesGlobal(0.0f, 0.0f, 0.0f);
00391 vx_last_ = 0.0f;
00392 vy_last_ = 0.0f;
00393 vtheta_last_ = 0.0f;
00394 }
00395
00396 bool NodeClass::notMovingDueToObstacle() {
00397 if (move_ == true &&
00398 finished_ == false &&
00399 fabs(robot_twist_linear_robot_.vector.x) <= 0.01 &&
00400 fabs(robot_twist_linear_robot_.vector.y) <= 0.01 &&
00401 fabs(robot_twist_angular_robot_.vector.z) <= 0.01 &&
00402 ros::Time::now().toSec() - last_time_moving_ > goal_abortion_time_ )
00403 {
00404 return true;
00405 } else if ( fabs(robot_twist_linear_robot_.vector.x) > 0.01 ||
00406 fabs(robot_twist_linear_robot_.vector.y) > 0.01 ||
00407 fabs(robot_twist_angular_robot_.vector.z) > 0.01 )
00408 {
00409 last_time_moving_ = ros::Time::now().toSec();
00410 }
00411
00412 return false;
00413
00414 }
00415
00416 void NodeClass::performControllerStep() {
00417 pthread_mutex_lock(&m_mutex);
00418
00419 double dt;
00420 double F_x, F_y, F_theta;
00421 double distance_to_goal;
00422 double theta, theta_goal;
00423 double cmd_vx, cmd_vy, cmd_vtheta;
00424 double vx_d, vy_d, vtheta_d, v_factor;
00425 double v_max_goal = v_max_;
00426
00427 if(!move_) {
00428 pthread_mutex_unlock(&m_mutex);
00429 return;
00430 }
00431
00432 getRobotPoseGlobal();
00433
00434 distance_to_goal = getDistance2d(robot_pose_global_, goal_pose_global_);
00435 theta = tf::getYaw(robot_pose_global_.pose.orientation);
00436 theta_goal = tf::getYaw(goal_pose_global_.pose.orientation);
00437
00438
00439 if( distance_to_goal <= goal_threshold_ &&
00440 sqrt(vx_last_ * vx_last_ + vy_last_ * vy_last_) <= speed_threshold_ &&
00441 fabs(getThetaDiffRad(theta_goal, theta)) <= goal_threshold_rot_ &&
00442 fabs(vtheta_last_) <= speed_threshold_rot_ )
00443 {
00444 finished_ = true;
00445 move_ = false;
00446 stopMovement();
00447 pthread_mutex_unlock(&m_mutex);
00448 return;
00449 } else if( notMovingDueToObstacle() == true ) {
00450 finished_ = false;
00451 move_ = false;
00452 stopMovement();
00453 as_.setAborted(move_base_msgs::MoveBaseResult(), "Cancel the goal because an obstacle is blocking the path.");
00454 ROS_INFO("Cancel the goal because an obstacle is blocking the path.");
00455 pthread_mutex_unlock(&m_mutex);
00456 return;
00457 } else finished_ = false;
00458
00459 dt = ros::Time::now().toSec() - last_time_;
00460 last_time_ = ros::Time::now().toSec();
00461
00462
00463 if(distance_to_goal < slow_down_distance_) {
00464
00465 double goal_linear_slope = v_max_ / slow_down_distance_;
00466 v_max_goal = distance_to_goal * goal_linear_slope;
00467
00468 if(v_max_goal > v_max_) v_max_goal = v_max_;
00469 else if(v_max_goal < 0.0f) v_max_goal = 0.0f;
00470 }
00471
00472
00473
00474 vx_d = kp_/kv_ * (goal_pose_global_.pose.position.x - robot_pose_global_.pose.position.x);
00475 vy_d = kp_/kv_ * (goal_pose_global_.pose.position.y - robot_pose_global_.pose.position.y);
00476 v_factor = v_max_goal / sqrt(vy_d*vy_d + vx_d*vx_d);
00477
00478 if(v_factor > 1.0) v_factor = 1.0;
00479
00480 F_x = - kv_ * vx_last_ + v_factor * kp_ * (goal_pose_global_.pose.position.x - robot_pose_global_.pose.position.x);
00481 F_y = - kv_ * vy_last_ + v_factor * kp_ * (goal_pose_global_.pose.position.y - robot_pose_global_.pose.position.y);
00482
00483 cmd_vx = vx_last_ + F_x / virt_mass_ * dt;
00484 cmd_vy = vy_last_ + F_y / virt_mass_ * dt;
00485
00486
00487
00488 vtheta_d = kp_rot_ / kv_rot_ * getThetaDiffRad(theta_goal, theta);
00489 v_factor = fabs(vtheta_max_ / vtheta_d);
00490 if(v_factor > 1.0) v_factor = 1.0;
00491
00492 F_theta = - kv_rot_ * vtheta_last_ + v_factor * kp_rot_ * getThetaDiffRad(theta_goal, theta);
00493 cmd_vtheta = vtheta_last_ + F_theta / virt_mass_rot_ * dt;
00494
00495
00496
00497 x_last_ = robot_pose_global_.pose.position.x;
00498 y_last_ = robot_pose_global_.pose.position.y;
00499 theta_last_ = theta;
00500 vx_last_ = cmd_vx;
00501 vy_last_ = cmd_vy;
00502 vtheta_last_ = cmd_vtheta;
00503
00504 publishVelocitiesGlobal(cmd_vx, cmd_vy, cmd_vtheta);
00505 pthread_mutex_unlock(&m_mutex);
00506 }
00507
00508
00509
00510
00511
00512 int main(int argc, char** argv)
00513 {
00514
00515 ros::init(argc, argv, "cob_linear");
00516
00517
00518 NodeClass nodeClass("move_base_linear");
00519
00520 ros::spin();
00521
00522 return 0;
00523 }
00524