cob_footprint_observer.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *   http://www.apache.org/licenses/LICENSE-2.0
00009 
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 
00018 #include <cob_footprint_observer.h>
00019 
00020 // Constructor
00021 FootprintObserver::FootprintObserver() :
00022   times_warned_(0)
00023 {
00024   nh_ = ros::NodeHandle("~");
00025 
00026   m_mutex = PTHREAD_MUTEX_INITIALIZER;
00027 
00028   // publish footprint
00029   topic_pub_footprint_ = nh_.advertise<geometry_msgs::PolygonStamped>("adjusted_footprint",1);
00030 
00031   // advertise service
00032   srv_get_footprint_ = nh_.advertiseService("/get_footprint", &FootprintObserver::getFootprintCB, this);
00033 
00034   // read footprint_source parameter
00035   std::string footprint_source;
00036   if(!nh_.hasParam("footprint_source")) ROS_WARN("Checking default location (/local_costmap_node/costmap) for initial footprint parameter.");
00037   nh_.param("footprint_source", footprint_source, std::string("/local_costmap_node/costmap"));
00038 
00039   // node handle to get footprint from parameter server
00040   ros::NodeHandle footprint_source_nh_(footprint_source);
00041 
00042   // load the robot footprint from the parameter server if its available in the local costmap namespace
00043   robot_footprint_ = loadRobotFootprint(footprint_source_nh_);
00044   if(robot_footprint_.size() > 4)
00045     ROS_WARN("You have set more than 4 points as robot_footprint, cob_footprint_observer can deal only with rectangular footprints so far!");
00046 
00047   // get parameter sepcifying minimal changes of footprint that are accepted. (smaller changes are neglected)
00048   if(!nh_.hasParam("epsilon"))
00049     ROS_WARN("No epsilon value specified. Changes in footprint smaller than epsilon are neglected. Using default [0.01m]!");
00050   nh_.param("epsilon", epsilon_, 0.01);
00051 
00052   // get the frames for which to check the footprint
00053   if(!nh_.hasParam("frames_to_check")) ROS_WARN("No frames to check for footprint observer. Only using initial footprint!");
00054   nh_.param("frames_to_check", frames_to_check_, std::string(""));
00055 
00056   if(!nh_.hasParam("robot_base_frame")) ROS_WARN("No parameter robot_base_frame on parameter server. Using default [/base_link].");
00057   nh_.param("robot_base_frame", robot_base_frame_, std::string("/base_link"));
00058 
00059   last_tf_missing_ = ros::Time::now();
00060 }
00061 
00062 // Destructor
00063 FootprintObserver::~FootprintObserver()
00064 {}
00065 
00066 // GetFootprint Service callback
00067 bool FootprintObserver::getFootprintCB(cob_footprint_observer::GetFootprint::Request &req, cob_footprint_observer::GetFootprint::Response &resp)
00068 {
00069   // create Polygon message for service call
00070   geometry_msgs::PolygonStamped footprint_poly;
00071   footprint_poly.header.frame_id = robot_base_frame_;
00072   footprint_poly.header.stamp = ros::Time::now();
00073 
00074   footprint_poly.polygon.points.resize(robot_footprint_.size());
00075   for(unsigned int i=0; i<robot_footprint_.size(); ++i) {
00076     footprint_poly.polygon.points[i].x = robot_footprint_[i].x;
00077     footprint_poly.polygon.points[i].y = robot_footprint_[i].y;
00078     footprint_poly.polygon.points[i].z = robot_footprint_[i].z;
00079   }
00080 
00081   resp.footprint = footprint_poly;
00082   resp.success.data = true;
00083 
00084   return true;
00085 }
00086 
00087 // load robot footprint from costmap_2d_ros to keep same footprint format
00088 std::vector<geometry_msgs::Point> FootprintObserver::loadRobotFootprint(ros::NodeHandle node){
00089   std::vector<geometry_msgs::Point> footprint;
00090   geometry_msgs::Point pt;
00091   double padding;
00092 
00093   std::string padding_param, footprint_param;
00094   if(!node.searchParam("footprint_padding", padding_param))
00095     padding = 0.01;
00096   else
00097     node.param(padding_param, padding, 0.01);
00098 
00099   //grab the footprint from the parameter server if possible
00100   XmlRpc::XmlRpcValue footprint_list;
00101   std::string footprint_string;
00102   std::vector<std::string> footstring_list;
00103   if(node.searchParam("footprint", footprint_param)){
00104     node.getParam(footprint_param, footprint_list);
00105     if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString) {
00106       footprint_string = std::string(footprint_list);
00107 
00108       //if there's just an empty footprint up there, return
00109       if(footprint_string == "[]" || footprint_string == "")
00110         return footprint;
00111 
00112       boost::erase_all(footprint_string, " ");
00113 
00114       boost::char_separator<char> sep("[]");
00115       boost::tokenizer<boost::char_separator<char> > tokens(footprint_string, sep);
00116       footstring_list = std::vector<std::string>(tokens.begin(), tokens.end());
00117     }
00118     //make sure we have a list of lists
00119     if(!(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeArray && footprint_list.size() > 2)
00120        && !(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString && footstring_list.size() > 5)){
00121       ROS_FATAL("The footprint must be specified as list of lists on the parameter server, %s was specified as %s",
00122                 footprint_param.c_str(), std::string(footprint_list).c_str());
00123       throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least 3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
00124     }
00125 
00126     if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeArray) {
00127       for(int i = 0; i < footprint_list.size(); ++i){
00128         //make sure we have a list of lists of size 2
00129         XmlRpc::XmlRpcValue point = footprint_list[i];
00130         if(!(point.getType() == XmlRpc::XmlRpcValue::TypeArray && point.size() == 2)){
00131           ROS_FATAL("The footprint must be specified as list of lists on the parameter server eg: [[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
00132           throw std::runtime_error("The footprint must be specified as list of lists on the parameter server eg: [[x1, y1], [x2, y2], ..., [xn, yn]], but this spec is not of that form");
00133         }
00134 
00135         //make sure that the value we're looking at is either a double or an int
00136         if(!(point[0].getType() == XmlRpc::XmlRpcValue::TypeInt || point[0].getType() == XmlRpc::XmlRpcValue::TypeDouble)){
00137           ROS_FATAL("Values in the footprint specification must be numbers");
00138           throw std::runtime_error("Values in the footprint specification must be numbers");
00139         }
00140         pt.x = point[0].getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(point[0]) : (double)(point[0]);
00141         pt.x += sign(pt.x) * padding;
00142 
00143         //make sure that the value we're looking at is either a double or an int
00144         if(!(point[1].getType() == XmlRpc::XmlRpcValue::TypeInt || point[1].getType() == XmlRpc::XmlRpcValue::TypeDouble)){
00145           ROS_FATAL("Values in the footprint specification must be numbers");
00146           throw std::runtime_error("Values in the footprint specification must be numbers");
00147         }
00148         pt.y = point[1].getType() == XmlRpc::XmlRpcValue::TypeInt ? (int)(point[1]) : (double)(point[1]);
00149         pt.y += sign(pt.y) * padding;
00150 
00151         footprint.push_back(pt);
00152 
00153         node.deleteParam(footprint_param);
00154         std::ostringstream oss;
00155         bool first = true;
00156         BOOST_FOREACH(geometry_msgs::Point p, footprint) {
00157           if(first) {
00158             oss << "[[" << p.x << "," << p.y << "]";
00159             first = false;
00160           }
00161           else {
00162             oss << ",[" << p.x << "," << p.y << "]";
00163           }
00164         }
00165         oss << "]";
00166         node.setParam(footprint_param, oss.str().c_str());
00167         node.setParam("footprint", oss.str().c_str());
00168       }
00169     }
00170 
00171     else if(footprint_list.getType() == XmlRpc::XmlRpcValue::TypeString) {
00172       std::vector<geometry_msgs::Point> footprint_spec;
00173       bool valid_foot = true;
00174       BOOST_FOREACH(std::string t, footstring_list) {
00175         if( t != "," ) {
00176           boost::erase_all(t, " ");
00177           boost::char_separator<char> pt_sep(",");
00178           boost::tokenizer<boost::char_separator<char> > pt_tokens(t, pt_sep);
00179           std::vector<std::string> point(pt_tokens.begin(), pt_tokens.end());
00180 
00181           if(point.size() != 2) {
00182             ROS_WARN("Each point must have exactly 2 coordinates");
00183             valid_foot = false;
00184             break;
00185           }
00186 
00187           std::vector<double>tmp_pt;
00188           BOOST_FOREACH(std::string p, point) {
00189             std::istringstream iss(p);
00190             double temp;
00191             if(iss >> temp) {
00192               tmp_pt.push_back(temp);
00193             }
00194             else {
00195               ROS_WARN("Each coordinate must convert to a double.");
00196               valid_foot = false;
00197               break;
00198             }
00199           }
00200           if(!valid_foot)
00201             break;
00202 
00203           geometry_msgs::Point pt;
00204           pt.x = tmp_pt[0];
00205           pt.y = tmp_pt[1];
00206 
00207           footprint_spec.push_back(pt);
00208         }
00209       }
00210       if (valid_foot) {
00211         footprint = footprint_spec;
00212         node.setParam("footprint", footprint_string);
00213       }
00214       else {
00215         ROS_FATAL("This footprint is not vaid it must be specified as a list of lists with at least 3 points, you specified %s", footprint_string.c_str());
00216         throw std::runtime_error("The footprint must be specified as list of lists on the parameter server with at least 3 points eg: [[x1, y1], [x2, y2], ..., [xn, yn]]");
00217       }
00218     }
00219   }
00220 
00221   footprint_right_ = 0.0f; footprint_left_ = 0.0f; footprint_front_ = 0.0f; footprint_rear_ = 0.0f;
00222   //extract rectangular borders for simplifying:
00223   for(unsigned int i=0; i<footprint.size(); i++) {
00224     if(footprint[i].x > footprint_front_) footprint_front_ = footprint[i].x;
00225     if(footprint[i].x < footprint_rear_) footprint_rear_ = footprint[i].x;
00226     if(footprint[i].y > footprint_left_) footprint_left_ = footprint[i].y;
00227     if(footprint[i].y < footprint_right_) footprint_right_ = footprint[i].y;
00228   }
00229   ROS_DEBUG("Extracted rectangular footprint for cob_footprint_observer: Front: %f, Rear %f, Left: %f, Right %f",
00230             footprint_front_, footprint_rear_, footprint_left_, footprint_right_);
00231 
00232   if ( fabs(footprint_front_ - footprint_rear_) == 0.0 || fabs(footprint_right_ - footprint_left_) == 0.0){
00233     ROS_WARN("Footprint has no physical dimension!");
00234   }
00235 
00236   // save initial footprint
00237   footprint_front_initial_ = footprint_front_;
00238   footprint_rear_initial_ = footprint_rear_;
00239   footprint_left_initial_ = footprint_left_;
00240   footprint_right_initial_ = footprint_right_;
00241 
00242   return footprint;
00243 }
00244 
00245 // checks if footprint has to be adjusted and does so if necessary
00246 void FootprintObserver::checkFootprint(){
00247   // check each frame
00248   std::string frame;
00249   std::stringstream ss;
00250   ss << frames_to_check_;
00251 
00252   double x_rear, x_front, y_left, y_right;
00253   x_front = footprint_front_initial_;
00254   x_rear = footprint_rear_initial_;
00255   y_left = footprint_left_initial_;
00256   y_right = footprint_right_initial_;
00257 
00258   bool missing_frame_exists = false;
00259   while(ss >> frame){
00260     // get transform between robot base frame and frame
00261     if(tf_listener_.canTransform(robot_base_frame_, frame, ros::Time(0))) {
00262       tf::StampedTransform transform;
00263       tf_listener_.lookupTransform(robot_base_frame_, frame, ros::Time(0), transform);
00264 
00265       tf::Vector3 frame_position = transform.getOrigin();
00266 
00267       // check if frame position is outside of current footprint
00268       if(frame_position.x() > x_front) x_front = frame_position.x();
00269       if(frame_position.x() < x_rear) x_rear = frame_position.x();
00270       if(frame_position.y() > y_left) y_left = frame_position.y();
00271       if(frame_position.y() < y_right) y_right = frame_position.y();
00272     } else if ( (ros::Time::now() - last_tf_missing_).toSec() > 10.0
00273                 && times_warned_ < 3 ) {
00274       ++times_warned_;
00275       missing_frame_exists = true;
00276       ROS_WARN("Footprint Observer: Transformation for %s not available! Frame %s not considered in adjusted footprint!",
00277                frame.c_str(), frame.c_str());
00278     }
00279   }
00280   if (missing_frame_exists)
00281     last_tf_missing_ = ros::Time::now();
00282 
00283   // check if footprint has changed
00284   if ( fabs( footprint_front_ - x_front ) > epsilon_
00285        || fabs( footprint_rear_ - x_rear ) > epsilon_
00286        || fabs( footprint_left_ - y_left ) > epsilon_
00287        || fabs( footprint_right_ - y_right ) > epsilon_ )
00288   {
00289     pthread_mutex_lock(&m_mutex);
00290     // adjust footprint
00291     footprint_front_ = x_front;
00292     footprint_rear_ = x_rear;
00293     footprint_left_ = y_left;
00294     footprint_right_ = y_right;
00295     pthread_mutex_unlock(&m_mutex);
00296 
00297     // create new footprint vector
00298     geometry_msgs::Point point;
00299     std::vector<geometry_msgs::Point> points;
00300 
00301     point.x = footprint_front_;
00302     point.y = footprint_left_;
00303     point.z = 0;
00304     points.push_back(point);
00305     point.y = footprint_right_;
00306     points.push_back(point);
00307     point.x = footprint_rear_;
00308     points.push_back(point);
00309     point.y = footprint_left_;
00310     points.push_back(point);
00311 
00312     pthread_mutex_lock(&m_mutex);
00313     robot_footprint_ = points;
00314     pthread_mutex_unlock(&m_mutex);
00315 
00316     // publish the adjusted footprint
00317     publishFootprint();
00318   }
00319 
00320 }
00321 
00322 // publishes the adjusted footprint
00323 void FootprintObserver::publishFootprint(){
00324 
00325   // create Polygon message
00326   geometry_msgs::PolygonStamped footprint_poly;
00327   footprint_poly.header.frame_id = robot_base_frame_;
00328   footprint_poly.header.stamp = ros::Time::now();
00329 
00330   footprint_poly.polygon.points.resize(robot_footprint_.size());
00331   for(unsigned int i=0; i<robot_footprint_.size(); ++i) {
00332     footprint_poly.polygon.points[i].x = robot_footprint_[i].x;
00333     footprint_poly.polygon.points[i].y = robot_footprint_[i].y;
00334     footprint_poly.polygon.points[i].z = robot_footprint_[i].z;
00335   }
00336 
00337   // publish adjusted footprint
00338   topic_pub_footprint_.publish(footprint_poly);
00339 
00340 }
00341 
00342 // compute the sign of x
00343 double FootprintObserver::sign(double x){
00344   if(x >= 0.0f) return 1.0f;
00345   else return -1.0f;
00346 }
00347 
00348 //#######################
00349 //#### main programm ####
00350 int main(int argc, char** argv)
00351 {
00352   // initialize ROS, specify name of node
00353   ros::init(argc,argv,"footprint_observer");
00354 
00355   // create observer
00356   FootprintObserver footprintObserver;
00357 
00358   // run footprint observer periodically until node has been shut down
00359   ros::Rate loop_rate(30); // Hz
00360   while(footprintObserver.nh_.ok()){
00361     footprintObserver.checkFootprint();
00362     ros::spinOnce();
00363     loop_rate.sleep();
00364   }
00365 
00366   return 0;
00367 };


cob_footprint_observer
Author(s): Matthias Gruhler
autogenerated on Thu Jun 6 2019 21:19:01