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 #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
00073
00074 struct ClientInfo
00075 {
00076 std::vector<TFPair> tf_subscriptions_;
00077 unsigned int client_ID_;
00078 ros::Timer timer_;
00079 };
00080
00081
00082 struct ClientGoalInfo : ClientInfo
00083 {
00084 GoalHandle handle;
00085 };
00086
00087
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
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
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
00200 gh.setAccepted();
00201
00202
00203 const tf2_web_republisher::TFSubscriptionGoal::ConstPtr& goal = gh.getGoal();
00204
00205
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
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
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
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
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);
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
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
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
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
00303 boost::mutex::scoped_lock lock (tf_buffer_mutex_);
00304
00305
00306 transform = tf_buffer_.lookupTransform(it->getTargetFrame(),
00307 it->getSourceFrame(),
00308 ros::Time(0));
00309
00310
00311
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
00323 it->updateTransform(transform);
00324 }
00325 catch (tf2::TransformException ex)
00326 {
00327
00328 if (it->is_okay)
00329 {
00330 it->is_okay = false;
00331 ROS_ERROR("%s", ex.what());
00332 }
00333 }
00334
00335
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
00343 it->transmissionTriggered();
00344
00345
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
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
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 }