tf_web_republisher.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  *  Copyright (c) 2014, Willow Garage, Inc.
00004  *  All rights reserved.
00005  *
00006  *  Redistribution and use in source and binary forms, with or without
00007  *  modification, are permitted provided that the following conditions
00008  *  are met:
00009  *
00010  *   * Redistributions of source code must retain the above copyright
00011  *     notice, this list of conditions and the following disclaimer.
00012  *   * Redistributions in binary form must reproduce the above
00013  *     copyright notice, this list of conditions and the following
00014  *     disclaimer in the documentation and/or other materials provided
00015  *     with the distribution.
00016  *   * Neither the name of the Willow Garage nor the names of its
00017  *     contributors may be used to endorse or promote products derived
00018  *     from this software without specific prior written permission.
00019  *
00020  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031  *  POSSIBILITY OF SUCH DAMAGE.
00032 
00033  *  Author: Julius Kammerl (jkammerl@willowgarage.com)
00034  *
00035  */
00036 
00037 #include <sstream>
00038 
00039 #include <ros/ros.h>
00040 #include <tf2_ros/transform_listener.h>
00041 
00042 #include <boost/thread/mutex.hpp>
00043 #include "boost/thread.hpp"
00044 
00045 #include <tf/tfMessage.h>
00046 #include <tf/transform_datatypes.h>
00047 #include <geometry_msgs/TransformStamped.h>
00048 
00049 #include <actionlib/server/simple_action_server.h>
00050 #include <tf2_web_republisher/TFSubscriptionAction.h>
00051 
00052 #include <tf2_web_republisher/RepublishTFs.h>
00053 #include <tf2_web_republisher/TFArray.h>
00054 
00055 #include "tf_pair.h"
00056 
00057 class TFRepublisher
00058 {
00059 protected:
00060   typedef actionlib::ActionServer<tf2_web_republisher::TFSubscriptionAction> TFTransformServer;
00061   typedef TFTransformServer::GoalHandle GoalHandle;
00062 
00063   typedef tf2_web_republisher::RepublishTFs::Request Request;
00064   typedef tf2_web_republisher::RepublishTFs::Response Response;
00065 
00066   ros::NodeHandle nh_;
00067   ros::NodeHandle priv_nh_;
00068 
00069   TFTransformServer as_;
00070   ros::ServiceServer tf_republish_service_;
00071 
00072   // base struct that holds information about the TFs
00073   // a client (either Service or Action) has subscribed to
00074   struct ClientInfo
00075   {
00076     std::vector<TFPair> tf_subscriptions_;
00077     unsigned int client_ID_;
00078     ros::Timer  timer_;
00079   };
00080 
00081   // struct for Action client info
00082   struct ClientGoalInfo : ClientInfo
00083   {
00084     GoalHandle handle;
00085   };
00086 
00087   // struct for Service client info
00088   struct ClientRequestInfo : ClientInfo
00089   {
00090     ros::Publisher pub_;
00091     ros::Duration unsub_timeout_;
00092     ros::Timer unsub_timer_;
00093   };
00094 
00095   std::list<boost::shared_ptr<ClientGoalInfo> > active_goals_;
00096   boost::mutex goals_mutex_;
00097 
00098   std::list<boost::shared_ptr<ClientRequestInfo> > active_requests_;
00099   boost::mutex requests_mutex_;
00100 
00101   // tf2 buffer and transformer
00102 #if ROS_VERSION_MINOR < 10
00103   tf2::Buffer tf_buffer_;
00104   tf2::TransformListener tf_listener_;
00105 #else
00106   tf2_ros::Buffer tf_buffer_;
00107   tf2_ros::TransformListener tf_listener_;
00108 #endif
00109   boost::mutex tf_buffer_mutex_;
00110 
00111   unsigned int client_ID_count_;
00112 
00113 public:
00114 
00115   TFRepublisher(const std::string& name) :
00116     nh_(),
00117     as_(nh_,
00118         name,
00119         boost::bind(&TFRepublisher::goalCB, this, _1),
00120         boost::bind(&TFRepublisher::cancelCB, this, _1),
00121         false),
00122     priv_nh_("~"),
00123     tf_buffer_(),
00124     tf_listener_(tf_buffer_),
00125     client_ID_count_(0)
00126   {
00127     tf_republish_service_ = nh_.advertiseService("republish_tfs",
00128                                                  &TFRepublisher::requestCB,
00129                                                  this);
00130     as_.start();
00131   }
00132 
00133   ~TFRepublisher() {}
00134 
00135 
00136 
00137   void cancelCB(GoalHandle gh)
00138   {
00139     boost::mutex::scoped_lock l(goals_mutex_);
00140 
00141     ROS_DEBUG("GoalHandle canceled");
00142 
00143     // search for goal handle and remove it from active_goals_ list
00144     for(std::list<boost::shared_ptr<ClientGoalInfo> >::iterator it = active_goals_.begin(); it != active_goals_.end();)
00145     {
00146       ClientGoalInfo& info = **it;
00147       if(info.handle == gh)
00148       {
00149         it = active_goals_.erase(it);
00150         info.timer_.stop();
00151         info.handle.setCanceled();
00152         return;
00153       }
00154       else
00155         ++it;
00156     }
00157   }
00158 
00159   const std::string cleanTfFrame( const std::string frame_id ) const
00160   {
00161     if ( frame_id[0] == '/' )
00162     {
00163       return frame_id.substr(1);
00164     }
00165     return frame_id;
00166   }
00167 
00172   void setSubscriptions(boost::shared_ptr<ClientInfo> info,
00173                         const std::vector<std::string>& source_frames,
00174                         const std::string& target_frame_,
00175                         float angular_thres,
00176                         float trans_thres) const
00177   {
00178     std::size_t request_size_ = source_frames.size();
00179     info->tf_subscriptions_.resize(request_size_);
00180 
00181     for (std::size_t i=0; i<request_size_; ++i )
00182     {
00183       TFPair& tf_pair = info->tf_subscriptions_[i];
00184 
00185       std::string source_frame = cleanTfFrame(source_frames[i]);
00186       std::string target_frame = cleanTfFrame(target_frame_);
00187 
00188       tf_pair.setSourceFrame(source_frame);
00189       tf_pair.setTargetFrame(target_frame);
00190       tf_pair.setAngularThres(angular_thres);
00191       tf_pair.setTransThres(trans_thres);
00192     }
00193   }
00194 
00195   void goalCB(GoalHandle gh)
00196   {
00197     ROS_DEBUG("GoalHandle request received");
00198 
00199     // accept new goals
00200     gh.setAccepted();
00201 
00202     // get goal from handle
00203     const tf2_web_republisher::TFSubscriptionGoal::ConstPtr& goal = gh.getGoal();
00204 
00205     // generate goal_info struct
00206     boost::shared_ptr<ClientGoalInfo> goal_info = boost::make_shared<ClientGoalInfo>();
00207     goal_info->handle = gh;
00208     goal_info->client_ID_ = client_ID_count_++;
00209 
00210     // add the tf_subscriptions to the ClientGoalInfo object
00211     setSubscriptions(goal_info,
00212                      goal->source_frames,
00213                      goal->target_frame,
00214                      goal->angular_thres,
00215                      goal->trans_thres);
00216 
00217     goal_info->timer_ = nh_.createTimer(ros::Duration(1.0 / goal->rate),
00218                                         boost::bind(&TFRepublisher::processGoal, this, goal_info, _1));
00219 
00220     {
00221       boost::mutex::scoped_lock l(goals_mutex_);
00222       // add new goal to list of active goals/clients
00223       active_goals_.push_back(goal_info);
00224     }
00225 
00226   }
00227 
00228   bool requestCB(Request& req, Response& res)
00229   {
00230     ROS_DEBUG("RepublishTF service request received");
00231     // generate request_info struct
00232     boost::shared_ptr<ClientRequestInfo> request_info = boost::make_shared<ClientRequestInfo>();
00233 
00234     request_info->client_ID_ = client_ID_count_;
00235     std::stringstream topicname;
00236     topicname << "tf_repub_" << client_ID_count_++;
00237 
00238     request_info->pub_ = priv_nh_.advertise<tf2_web_republisher::TFArray>(topicname.str(), 10, true);
00239 
00240     // add the tf_subscriptions to the ClientGoalInfo object
00241     setSubscriptions(request_info,
00242                      req.source_frames,
00243                      req.target_frame,
00244                      req.angular_thres,
00245                      req.trans_thres);
00246 
00247     request_info->unsub_timeout_ = req.timeout;
00248     request_info->unsub_timer_ = nh_.createTimer(request_info->unsub_timeout_,
00249                                                  boost::bind(&TFRepublisher::unadvertiseCB, this, request_info, _1),
00250                                                  true); // only fire once
00251 
00252     request_info->timer_ = nh_.createTimer(ros::Duration(1.0 / req.rate),
00253                                            boost::bind(&TFRepublisher::processRequest, this, request_info, _1));
00254 
00255     {
00256       boost::mutex::scoped_lock l(requests_mutex_);
00257       // add new request to list of active requests
00258       active_requests_.push_back(request_info);
00259     }
00260     res.topic_name = request_info->pub_.getTopic();
00261     ROS_INFO_STREAM("Publishing requested TFs on topic " << res.topic_name);
00262 
00263     return true;
00264   }
00265 
00266   void unadvertiseCB(boost::shared_ptr<ClientRequestInfo> request_info, const ros::TimerEvent&)
00267   {
00268     ROS_INFO_STREAM("No subscribers on tf topic for request "
00269                      << request_info->client_ID_
00270                      << " for " << request_info->unsub_timeout_.toSec()
00271                      << " seconds. Unadvertising topic:"
00272                      << request_info->pub_.getTopic());
00273     request_info->pub_.shutdown();
00274     request_info->unsub_timer_.stop();
00275     request_info->timer_.stop();
00276 
00277     // search for ClientRequestInfo struct and remove it from active_requests_ list
00278     for(std::list<boost::shared_ptr<ClientRequestInfo> >::iterator it = active_requests_.begin(); it != active_requests_.end(); ++it)
00279     {
00280       ClientRequestInfo& info = **it;
00281       if(info.pub_ == request_info->pub_)
00282       {
00283         active_requests_.erase(it);
00284         return;
00285       }
00286     }
00287   }
00288 
00289   void updateSubscriptions(std::vector<TFPair>& tf_subscriptions,
00290                            std::vector<geometry_msgs::TransformStamped>& transforms)
00291   {
00292     // iterate over tf_subscription vector
00293     std::vector<TFPair>::iterator it ;
00294     std::vector<TFPair>::const_iterator end = tf_subscriptions.end();
00295 
00296     for (it=tf_subscriptions.begin(); it!=end; ++it)
00297     {
00298       geometry_msgs::TransformStamped transform;
00299 
00300       try
00301       {
00302         // protecting tf_buffer
00303         boost::mutex::scoped_lock lock (tf_buffer_mutex_);
00304 
00305         // lookup transformation for tf_pair
00306         transform = tf_buffer_.lookupTransform(it->getTargetFrame(),
00307                                                it->getSourceFrame(),
00308                                                ros::Time(0));
00309 
00310         // If the transform broke earlier, but worked now (we didn't get
00311         // booted into the catch block), tell the user all is well again
00312         if (!it->is_okay)
00313         {
00314           it->is_okay = true;
00315           ROS_INFO_STREAM("Transform from "
00316                           << it->getSourceFrame()
00317                           << " to "
00318                           << it->getTargetFrame()
00319                           << " is working again at time "
00320                           << transform.header.stamp.toSec());
00321         }
00322         // update tf_pair with transformtion
00323         it->updateTransform(transform);
00324       }
00325       catch (tf2::TransformException ex)
00326       {
00327         // Only log an error if the transform was okay before
00328         if (it->is_okay)
00329         {
00330           it->is_okay = false;
00331           ROS_ERROR("%s", ex.what());
00332         }
00333       }
00334 
00335       // check angular and translational thresholds
00336       if (it->updateNeeded())
00337       {
00338         transform.header.stamp = ros::Time::now();
00339         transform.header.frame_id = it->getTargetFrame();
00340         transform.child_frame_id = it->getSourceFrame();
00341 
00342         // notify tf_subscription that a network transmission has been triggered
00343         it->transmissionTriggered();
00344 
00345         // add transform to the array
00346         transforms.push_back(transform);
00347       }
00348     }
00349   }
00350 
00351   void processGoal(boost::shared_ptr<ClientGoalInfo> goal_info, const ros::TimerEvent& )
00352   {
00353     tf2_web_republisher::TFSubscriptionFeedback feedback;
00354 
00355     updateSubscriptions(goal_info->tf_subscriptions_,
00356                         feedback.transforms);
00357 
00358     if (feedback.transforms.size() > 0)
00359     {
00360       // publish feedback
00361       goal_info->handle.publishFeedback(feedback);
00362       ROS_DEBUG("Client %d: TF feedback published:", goal_info->client_ID_);
00363     } else
00364     {
00365       ROS_DEBUG("Client %d: No TF frame update needed:", goal_info->client_ID_);
00366     }
00367   }
00368 
00369   void processRequest(boost::shared_ptr<ClientRequestInfo> request_info, const ros::TimerEvent& )
00370   {
00371     if (request_info->pub_.getNumSubscribers() == 0)
00372     {
00373       request_info->unsub_timer_.start();
00374     }
00375     else
00376     {
00377       request_info->unsub_timer_.stop();
00378     }
00379 
00380     tf2_web_republisher::TFArray array_msg;
00381     updateSubscriptions(request_info->tf_subscriptions_,
00382                         array_msg.transforms);
00383 
00384     if (array_msg.transforms.size() > 0)
00385     {
00386       // publish TFs
00387       request_info->pub_.publish(array_msg);
00388       ROS_DEBUG("Request %d: TFs published:", request_info->client_ID_);
00389     }
00390     else
00391     {
00392       ROS_DEBUG("Request %d: No TF frame update needed:", request_info->client_ID_);
00393     }
00394   }
00395 };
00396 
00397 int main(int argc, char **argv)
00398 {
00399   ros::init(argc, argv, "tf2_web_republisher");
00400 
00401   TFRepublisher tf2_web_republisher(ros::this_node::getName());
00402   ros::spin();
00403 
00404   return 0;
00405 }


tf2_web_republisher
Author(s): Julius Kammer
autogenerated on Thu Jun 6 2019 20:34:26