00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <cob_footprint_observer.h>
00019
00020
00021 FootprintObserver::FootprintObserver() :
00022 times_warned_(0)
00023 {
00024 nh_ = ros::NodeHandle("~");
00025
00026 m_mutex = PTHREAD_MUTEX_INITIALIZER;
00027
00028
00029 topic_pub_footprint_ = nh_.advertise<geometry_msgs::PolygonStamped>("adjusted_footprint",1);
00030
00031
00032 srv_get_footprint_ = nh_.advertiseService("/get_footprint", &FootprintObserver::getFootprintCB, this);
00033
00034
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
00040 ros::NodeHandle footprint_source_nh_(footprint_source);
00041
00042
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
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
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
00063 FootprintObserver::~FootprintObserver()
00064 {}
00065
00066
00067 bool FootprintObserver::getFootprintCB(cob_footprint_observer::GetFootprint::Request &req, cob_footprint_observer::GetFootprint::Response &resp)
00068 {
00069
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
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
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
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
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
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
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
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
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
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
00246 void FootprintObserver::checkFootprint(){
00247
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
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
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
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
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
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
00317 publishFootprint();
00318 }
00319
00320 }
00321
00322
00323 void FootprintObserver::publishFootprint(){
00324
00325
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
00338 topic_pub_footprint_.publish(footprint_poly);
00339
00340 }
00341
00342
00343 double FootprintObserver::sign(double x){
00344 if(x >= 0.0f) return 1.0f;
00345 else return -1.0f;
00346 }
00347
00348
00349
00350 int main(int argc, char** argv)
00351 {
00352
00353 ros::init(argc,argv,"footprint_observer");
00354
00355
00356 FootprintObserver footprintObserver;
00357
00358
00359 ros::Rate loop_rate(30);
00360 while(footprintObserver.nh_.ok()){
00361 footprintObserver.checkFootprint();
00362 ros::spinOnce();
00363 loop_rate.sleep();
00364 }
00365
00366 return 0;
00367 };