uos_freespace.cpp
Go to the documentation of this file.
00001 /*
00002  * uos_freespace
00003  * Author: Marat "Peter" Purnyn
00004  * Created 11/8/2015 (based on earlier work uos-freespace.cc)
00005  */
00006 #include <uos_freespace/uos_freespace.h>
00007 #include <cmath>
00008 #include <cstdio>
00009 #include <geometry_msgs/Twist.h>
00010 #include <geometry_msgs/QuaternionStamped.h>
00011 
00012 #define EPSILON 0.000001 // arbitrarily small value for cos limit to prevent numerical instability
00013 #define min(a,b) ((a)<(b)?(a):(b)) //carried over from old code, possible cmath is enough
00014 #define max(a,b) ((a)>(b)?(a):(b))
00015 #define LASERSCAN_OPENING_ANGLE_WIDTH 270 //the viewing angle of the laserscanner (possibly only for lms100)
00016 #define LASERSCAN_DEADZONE 45 //the angle range one either side of the scanner to ignore
00017 
00018 FreeSpace::FreeSpace():
00019         turn_state_(0),
00020         scanner_orientation_(0),
00021         turn_omega_(0),
00022         private_nh_("~")
00023 {
00024         //sets the limits for the speed of the navigation
00025         private_nh_.param("max_vel_x_", max_vel_x_, 0.5);
00026         private_nh_.param("max_rotational_vel_", max_rotational_vel_, 0.3);
00027         
00028         turn_state_ = 0; 
00029                                         
00030         scanner_orientation_ = 0; 
00031         
00032         //publishers and subscribers
00033         vel_pub_ = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1);
00034         laser_sub_ = nh_.subscribe("scan", 10, &FreeSpace::autonomousBehaviour, this);
00035         ROS_DEBUG("constructor");
00036 }
00037 
00038 double FreeSpace::calcFreespace(const sensor_msgs::LaserScan::ConstPtr &laserscan){
00039         unsigned int i; //index of a laserscan point
00040         double phi;             //angle of i
00041         double sinSum = 0.0, cosSum = 0.0;
00042         double orientationWeight;
00043         double alpha = 0.0;
00044         
00045         for (i = 0; i < laserscan->ranges.size(); i++) {
00046                 if(laserscan->ranges[i] > 0.1){
00047                         phi = laserscan->angle_min + i * laserscan->angle_increment;
00048                         
00049                         //the arg of cos should not be smaller than phi/2 otherwise 
00050                         //the robot will be unstable when obstacles are close to its side. 
00051                         //The exponential function is used to weight the range information.
00052                         //The farther an object is the larger its weight.
00053                         
00054                         orientationWeight = cos(phi/2) * 1.0/(1.0+exp(-(laserscan->ranges[i]-1)/0.5));
00055                         sinSum += sin(phi) * orientationWeight;
00056                         cosSum += cos(phi) * orientationWeight;
00057                 }
00058         }
00059         if (fabs(cosSum) > EPSILON){
00060                 alpha = atan2(sinSum, cosSum);
00061         }
00062         return alpha;
00063 }
00064 
00065 int FreeSpace::isInvertedScannerCheck(const sensor_msgs::LaserScan::ConstPtr &laserscan){
00066         // To account for lasers that are mounted upside-down, we determine the
00067         // min, max, and increment angles of the laser in the base frame.
00068         tf::Quaternion q;
00069         q.setRPY(0.0, 0.0, laserscan->angle_min);
00070         tf::Stamped<tf::Quaternion> min_q(q, laserscan->header.stamp,laserscan->header.frame_id);
00071         q.setRPY(0.0, 0.0, laserscan->angle_max);
00072         tf::Stamped<tf::Quaternion> max_q(q, laserscan->header.stamp,laserscan->header.frame_id);
00073         
00074         try     {
00075                 //wait for the tf frames to be available then transform the laserscan frame to the base frame
00076                 //this makes it so all scanners data is read as right-side up
00077                 tf_.waitForTransform("base_link", "laser",laserscan->header.stamp,ros::Duration(1.0));
00078                 tf_.transformQuaternion("base_link", min_q, min_q);
00079                 tf_.transformQuaternion("base_link", max_q, max_q);
00080         }
00081         catch(tf::TransformException& e) {
00082                 //if there is an error transforming, keep the orientation unset to try again
00083                 ROS_WARN("Unable to transform min/max laser angles into base frame: %s",e.what());
00084                 return 0;
00085         }
00086         
00087         ROS_DEBUG("Successfully transformed min/max laser angles into base frame");
00088         
00089         //calculate the angle increment of the scan with respect the base frame
00090         double minAngle = tf::getYaw(min_q);
00091         double maxAngle = tf::getYaw(max_q);
00092         double baseFrameAngleIncrement = (maxAngle - minAngle) / laserscan->ranges.size();
00093         
00094         ROS_DEBUG("Laser angles in base frame: min: %.3f max: %.3f inc: %.3f", minAngle, maxAngle, baseFrameAngleIncrement);
00095         if (baseFrameAngleIncrement < 0){
00096                 //upside-down
00097                 ROS_DEBUG("Inverting scan");
00098                 return -1;
00099         }
00100         return 1; //if not return by now, then assume right side up
00101 }
00102 
00103 int FreeSpace::checkRange(
00104         const sensor_msgs::LaserScan::ConstPtr &laserscan, 
00105         double xregion, double yregion,
00106     int *indexOfObstacle, 
00107     double *distanceToObstacle)
00108 {               
00109         unsigned int i; //index of laser point
00110         double generalDistance = 65536.0; //starting max distance
00111         *distanceToObstacle = 65536.0; //starting distance to obstacle
00112         *indexOfObstacle = -1; //starting index of obstacle
00113         
00114         //calculate the number of laserpoints in the deadzone
00115         int deadzonePoints = int(floor((((LASERSCAN_DEADZONE)*(M_PI/180))/laserscan->angle_increment)));
00116         ROS_WARN("deadzonepoints :%d", deadzonePoints);
00117         //The laser scanner detects part of the robot frame.
00118         //ignore laserpoints in deadzone on the left and right of the scan
00119         for (i = fabs(deadzonePoints); i < (laserscan->ranges.size() - fabs(deadzonePoints)); i++) {
00120                 if(laserscan->ranges[i] > 0.1){
00121                         double x = laserscan->ranges[i] * cos(laserscan->angle_min + i * laserscan->angle_increment);
00122                         double y = laserscan->ranges[i] * sin(laserscan->angle_min + i * laserscan->angle_increment);
00123                         if ((x < xregion) && (fabs(y) < (yregion / 2))) { 
00124                                 // Is there something in the region in front of the robot?
00125                                 if (*distanceToObstacle > x) {
00126                                 //Found obstacle, record distance and index
00127                                   *distanceToObstacle = x;      
00128                                   *indexOfObstacle = i;
00129                                   ROS_WARN("set index to obstacle %d", i);
00130                                 }
00131                         } //is there something in the generel direction?
00132                         if ((x > xregion) && (fabs(y) < (yregion / 2))) { 
00133                                 //Is there something ahead of the robot?
00134                                 if (generalDistance > x) {
00135                                 //Set general distance to the closest object infront of the robot
00136                                   generalDistance = x;  
00137                                   //ROS_WARN("set general distance");      
00138                                 }
00139                         }
00140                 }
00141         }
00142         
00143         if (*indexOfObstacle == -1){ 
00144                 //There is no obstacle, so reset the distance.
00145                 *distanceToObstacle = generalDistance;
00146         }
00147         ROS_WARN("distance to obstacle :%f", *distanceToObstacle);
00148         ROS_WARN(" index obstacle :%d", *indexOfObstacle);
00149         return 0;               
00150 }
00151 
00152 // Statemachine
00153 // Drive into the general direction of the freespace infront of the Robot
00154 // If there is an obstacle infront of the robot turn away from it
00155 // X axis is pointing forward
00156 // Y axis is pointing left
00157 // Z axis is pointing up
00158 void FreeSpace::autonomousBehaviour(const sensor_msgs::LaserScan::ConstPtr &laserscan){
00159         ROS_DEBUG("autonomousBehaviour");
00160         // region is a quadilatiral in front of the robot
00161         // define the size
00162         double xregion = 0.5; // x is in forward
00163         double yregion = 0.5;  // y is left
00164         double DTOStopTurning = 1.0; // defines when to start to turn into freespace
00165         double DTOStartTurning = 0.5; // defines when to start to turn away form an obstacle
00166         int indexOfObstacle = -1;
00167         double frontMaxDistance = 0;
00168 
00169         double uTurning = 0.2 * max_vel_x_;    //decrease the speed while turning
00170         double distanceToObstacle = INFINITY;
00171         double omega = calcFreespace(laserscan); // find the freespace infront of the robot and return the angle to the direction of the freespace
00172         double u = max_vel_x_;                 // define maximum velocity of the robot
00173         
00174         //ROS_WARN("behave maxpoints :%d", laserscan->ranges.size());
00175   
00176         for(unsigned int i = 0; i < laserscan->ranges.size(); i++) {
00177                 if(laserscan->ranges[i] > frontMaxDistance) {
00178                         frontMaxDistance = laserscan->ranges[i];
00179                 }
00180         }
00181         
00182         if(scanner_orientation_ == 0) {
00183                 scanner_orientation_ = isInvertedScannerCheck(laserscan);
00184                 ROS_DEBUG("scanner_orientation_: %d", scanner_orientation_);
00185         }
00186 
00187         // look for obstacles infront of the robot
00188         checkRange(laserscan,xregion,yregion,&indexOfObstacle,&distanceToObstacle);
00189 
00190         // slow down if obstacle
00191         if (indexOfObstacle != -1) {
00192                 u = max(distanceToObstacle - 0.2 / (xregion * max_vel_x_),0);
00193         }
00194 
00195         // handle turning mode
00196         // turn if obstacle is infront
00197         if (turn_state_ == 1) {
00198                 ROS_WARN("turn_state: 1");
00199                 if ( (distanceToObstacle > DTOStopTurning)) {
00200                         turn_state_ = 0;                 // end turning
00201                 } 
00202                 else {
00203                         //turn slower if obstacle is infront
00204                                 ROS_WARN("turning away");
00205                 }  
00206           u = min(u, uTurning);          //set velocity to u if its not higher then the maximum velocity
00207         }
00208         // detect end of corridor use StopTurn val
00209         if (((frontMaxDistance < DTOStopTurning) || (distanceToObstacle < DTOStartTurning)) && (turn_state_ == 0) ) {
00210                 // start turning
00211                 // turn away from obstacle
00212                 if (indexOfObstacle > laserscan->ranges.size() / 2) {
00213                         turn_omega_ = -max_rotational_vel_;
00214                         //omega = min(omega,max_rotational_vel_); //omega should not exceed max_rotational_vel_
00215                         ROS_WARN("turning_left");
00216                 }
00217                 else if (indexOfObstacle <= laserscan->ranges.size() / 2 && indexOfObstacle >= 0) {
00218                         turn_omega_ = max_rotational_vel_;
00219                         
00220                         //omega = max(omega,-max_rotational_vel_);
00221                         ROS_WARN("turning_right");
00222                 }
00223                 turn_state_ = 1;
00224                 u = min(u, uTurning);
00225         }
00226         if (turn_state_ == 1){
00227                 omega = turn_omega_;
00228         }
00229         
00230         geometry_msgs::Twist vel;
00231         vel.linear.x = u;
00232         vel.angular.z = scanner_orientation_ * omega;
00233         vel_pub_.publish(vel);
00234 }


uos_freespace
Author(s): Jochen Sprickerhof
autogenerated on Wed May 24 2017 03:03:05