tf2_server.cpp
Go to the documentation of this file.
2 #include <geometry_msgs/TransformStamped.h>
3 #include <tf2_msgs/TFMessage.h>
4 
5 namespace tf2_server
6 {
7 
8 void warnLeadingSlash(const std::string& s)
9 {
10  ROS_WARN_STREAM("Found initial slash in " << s);
11 }
12 
13 std::string stripLeadingSlash(const std::string &s, const bool warn)
14 {
15  if (s.length() > 0 && s[0] == '/')
16  {
17  if (warn) {
19  }
20  return s.substr(1);
21  }
22 
23  return s;
24 }
25 
27 {
28  double buffer_size;
29  this->nh.param("buffer_size", buffer_size, 120.0);
30 
31  bool publish_frame_service;
32  this->nh.param("publish_frame_service", publish_frame_service, false);
33 
34  // Legacy behavior re: #209
35  bool use_node_namespace;
36  this->nh.param("use_node_namespace", use_node_namespace, false);
37  std::string node_name;
38  if (use_node_namespace)
39  {
40  node_name = this->pnh.getNamespace();
41  }
42  else
43  {
44  node_name = "tf2_buffer_server";
45  }
46 
47  this->pnh.setParam("supports_transform_streams", true);
48 
49  double duration;
50  this->pnh.param("initial_streams_wait_time", duration, 1.0);
51  this->initialStreamsWaitTime = ros::Duration(duration);
52 
53  this->pnh.param("transforms_update_period", duration, 1.0);
54  this->transformsUpdatePeriod = ros::Duration(duration);
55 
56  this->buffer =
57  std::make_unique<tf2_ros::Buffer>(ros::Duration(buffer_size), publish_frame_service);
58  this->listener = std::make_unique<tf2_ros::TransformListener>(*buffer, this->nh);
59  this->server = std::make_unique<tf2_ros::BufferServer>(*buffer, node_name, false);
60 
62  pnh.advertiseService("request_transform_stream", &TF2Server::onRequestTransformStream, this);
63 
64  this->buffer->_addTransformsChangedListener(boost::bind(&TF2Server::updateFramesLists, this));
65 
66  if (this->pnh.hasParam("streams"))
67  {
68  XmlRpc::XmlRpcValue initialStreamsParam;
69  this->pnh.getParam("streams", initialStreamsParam);
70  if (initialStreamsParam.getType() != XmlRpc::XmlRpcValue::TypeStruct)
71  {
72  ROS_ERROR("Parameter streams has to be a dictionary");
73  return;
74  }
75 
76  for (const auto initialStream : initialStreamsParam)
77  {
78  const std::string streamName = initialStream.first;
79 
80  if (initialStream.second.getType() != XmlRpc::XmlRpcValue::TypeStruct)
81  {
82  ROS_ERROR("The definition of stream %s has to be a dictionary", streamName.c_str());
83  continue;
84  }
85 
86  auto streamDef = initialStream.second;
87  RequestTransformStreamRequest req;
88  req.requested_topic_name = streamName;
89  req.publisher_queue_size = 10;
90 
91  if (streamDef.hasMember("parent_frame"))
92  req.parent_frame = static_cast<std::string>(streamDef["parent_frame"]);
93  if (streamDef.hasMember("intermediate_frames"))
94  req.intermediate_frames = static_cast<bool>(streamDef["intermediate_frames"]);
95  if (streamDef.hasMember("publication_period"))
96  {
97  double period;
98  if (streamDef["publication_period"].getType() == XmlRpc::XmlRpcValue::TypeInt)
99  period = static_cast<int>(streamDef["publication_period"]);
100  else if (streamDef["publication_period"].getType() == XmlRpc::XmlRpcValue::TypeDouble)
101  period = static_cast<double>(streamDef["publication_period"]);
102  req.publication_period = ros::Duration(period);
103  }
104  if (streamDef.hasMember("publisher_queue_size"))
105  req.publisher_queue_size = static_cast<int>(streamDef["publisher_queue_size"]);
106  if (streamDef.hasMember("allow_transforms_update"))
107  req.allow_transforms_update = static_cast<bool>(streamDef["allow_transforms_update"]);
108  if (streamDef.hasMember("child_frames"))
109  {
110  if (streamDef["child_frames"].getType() != XmlRpc::XmlRpcValue::TypeArray)
111  {
112  ROS_ERROR("child_frames of stream %s have to be an array", streamName.c_str());
113  continue;
114  }
115 
116  for (size_t i = 0; i < streamDef["child_frames"].size(); ++i)
117  {
118  req.child_frames.push_back(streamDef["child_frames"][i]);
119  }
120  }
121 
122  this->initialStreams.push_back(req);
123  }
124  }
125 }
126 
128 {
129  this->server->start();
130  this->started = true;
132 
133  ROS_INFO("TF2 server started.");
134 
135  if (this->initialStreams.size() > 0)
136  {
137  this->initialStreamsTimer = this->pnh.createTimer(
138  this->initialStreamsWaitTime, std::bind(&TF2Server::registerInitialStreams, this), true, true);
139  }
140 }
141 
142 bool operator==(const tf2_msgs::TFMessage& lhs, const tf2_msgs::TFMessage& rhs)
143 {
144  if (lhs.transforms.size() != rhs.transforms.size())
145  return false;
146 
147  for (size_t i = 0; i < lhs.transforms.size(); ++i)
148  {
149  const auto& lt = lhs.transforms[i];
150  const auto& rt = rhs.transforms[i];
151 
152  if (lt.header.stamp != rt.header.stamp) return false;
153  if (lt.header.frame_id != rt.header.frame_id) return false;
154  if (lt.child_frame_id != rt.child_frame_id) return false;
155  if (lt.transform.translation.x != rt.transform.translation.x) return false;
156  if (lt.transform.translation.y != rt.transform.translation.y) return false;
157  if (lt.transform.translation.z != rt.transform.translation.z) return false;
158  if (lt.transform.rotation.x != rt.transform.rotation.x) return false;
159  if (lt.transform.rotation.y != rt.transform.rotation.y) return false;
160  if (lt.transform.rotation.z != rt.transform.rotation.z) return false;
161  if (lt.transform.rotation.w != rt.transform.rotation.w) return false;
162  }
163 
164  return true;
165 }
166 
167 bool operator!=(const tf2_msgs::TFMessage& lhs, const tf2_msgs::TFMessage& rhs)
168 {
169  return !(lhs == rhs);
170 }
171 
172 bool TF2Server::onRequestTransformStream(RequestTransformStreamRequest &req,
173  RequestTransformStreamResponse &resp)
174 {
175  TopicsSpec topics;
176  {
177  std::lock_guard<std::mutex> streamsLock(this->streamsMutex);
178 
179  topics = this->getTopicsNames(req);
180  if (topics.first.empty() || topics.second.empty())
181  return false;
182 
183  this->streams[topics] = req;
184  }
185 
186  const auto topicName = resp.topic_name = topics.first;
187  const auto staticTopicName = resp.static_topic_name = topics.second;
188 
189  std::lock_guard<std::mutex> lock(this->mutex);
190 
191  if (this->frames.find(req) == this->frames.end())
192  {
193  auto framesList = this->getFramesList(req);
194  if (framesList->empty() && !req.allow_transforms_update)
195  throw std::runtime_error("Could not find any child frames of frame " + req.parent_frame);
196 
197  this->frames[req] = std::move(framesList);
198  }
199 
200  // timer creation has to happen before subscribers
201  if (this->timers.find(topics) == this->timers.end())
202  {
203  this->timers[topics] = this->nh.createTimer(req.publication_period,
204  std::bind(&TF2Server::streamTransform, this, std::placeholders::_1, req, topics),
205  false, false);
206  }
207 
208  if (this->publishers.find(topicName) == this->publishers.end())
209  {
210  this->publishers[topicName] =
211  this->nh.advertise<tf2_msgs::TFMessage>(topicName, req.publisher_queue_size,
212  std::bind(&TF2Server::onSubscriberConnected, this, topics),
213  std::bind(&TF2Server::onSubscriberDisconnected, this, topics));
214  }
215 
216  if (this->staticPublishers.find(staticTopicName) == this->staticPublishers.end())
217  {
218  this->staticPublishers[staticTopicName] =
219  this->nh.advertise<tf2_msgs::TFMessage>(staticTopicName, req.publisher_queue_size,
220  std::bind(&TF2Server::onSubscriberConnected, this, topics),
221  std::bind(&TF2Server::onSubscriberDisconnected, this, topics),
222  ros::VoidConstPtr(), true);
223  }
224 
225  return true;
226 }
227 
229  const RequestTransformStreamRequest &request,
230  const TopicsSpec& topics)
231 {
232  std::lock_guard<std::mutex> lock(this->mutex);
233 
234  if (this->frames[request]->empty())
235  return;
236 
237  const ros::Duration timeout(request.publication_period.toSec() * 0.9 / this->frames[request]->size());
238  tf2_msgs::TFMessage msg;
239  tf2_msgs::TFMessage staticMsg;
240 
241  for (const auto& frame : *this->frames[request])
242  {
243  try
244  {
245  const auto transform = this->buffer->lookupTransform(frame.first, frame.second, ros::Time(0), timeout);
246  if (transform.header.stamp != ros::Time(0, 0)) // static transforms are returned with zero timestamp
247  msg.transforms.push_back(transform);
248  else
249  staticMsg.transforms.push_back(transform);
250  }
251  catch (tf2::TransformException &e)
252  {
253  ROS_ERROR_STREAM_THROTTLE(1.0, "Error publishing transform stream: " << e.what());
254  }
255  }
256 
257  this->publishers[topics.first].publish(msg);
258  if (staticMsg != this->lastStaticTransforms[topics.second])
259  {
260  this->staticPublishers[topics.second].publish(staticMsg);
261  this->lastStaticTransforms[topics.second] = staticMsg;
262  }
263 }
264 
265 TF2Server::TopicsSpec TF2Server::getTopicsNames(const RequestTransformStreamRequest &request)
266 {
267  // make sure this->mutex is locked
268 
269  const RequestComparator comp;
270 
271  if (request.requested_topic_name.empty() && !request.requested_static_topic_name.empty())
272  {
273  throw std::runtime_error("when requested_static_topic_name is filled, "
274  "requested_topic_name has to be filled, too");
275  }
276  else if (!request.requested_topic_name.empty())
277  {
278  // if the request requests specific topic names, we have to do a few checks
279 
280  const auto topicName = this->pnh.resolveName(request.requested_topic_name);
281  // if static topic name is not filled, just append static to the topic name
282  const auto staticTopicName = this->pnh.resolveName(
283  !request.requested_static_topic_name.empty() ? request.requested_static_topic_name :
284  ros::names::append(request.requested_topic_name, "static"));
285  const TopicsSpec topics = std::make_pair(topicName, staticTopicName);
286 
287  const auto existingStream = this->streams.find(topics);
288  if (existingStream == this->streams.end())
289  {
290  // no stream exists yet
291  this->topicNames.insert(topicName);
292  this->topicNames.insert(staticTopicName);
293  return topics;
294  }
295  else
296  {
297  // a stream on these topics already exists, check if it is compatible
298  const auto existingRequest = (*existingStream).second;
299  const auto streamsCompatible = comp.equals(request, existingRequest);
300 
301  if (streamsCompatible)
302  return topics;
303  else
304  throw std::runtime_error(std::string("TF stream requested on topics ") + topicName +
305  " and " + staticTopicName + "is not compatible with the already existing stream on these topics.");
306  }
307  }
308 
309  // try to find an existing compatible stream
310  for (const auto& stream : this->streams)
311  {
312  const auto& topics = stream.first;
313  const auto& existingRequest = stream.second;
314 
315  const auto streamsCompatible = comp.equals(request, existingRequest);
316 
317  if (streamsCompatible)
318  return topics;
319  }
320 
321  const auto baseName = this->pnh.resolveName(
322  ros::names::append("streams", stripLeadingSlash(request.parent_frame, true)));
323  std::string error;
324 
325  TopicsSpec topics = std::make_pair(std::string(""), std::string(""));
326 
327  bool topicNameFound = false;
328  for (size_t i = 0; i < 10000; ++i)
329  {
330  topics.first = ros::names::append(baseName, "stream_" + std::to_string(i));
331  error = "";
332  if (ros::names::validate(topics.first, error) && this->topicNames.find(topics.first) == this->topicNames.end())
333  {
334  topicNameFound = true;
335  break;
336  }
337  }
338 
339  if (!topicNameFound)
340  {
341  ROS_ERROR("Could not generate topic name for transform stream. Name validation error of last "
342  "tried name '%s' is: %s", topics.first.c_str(), error.c_str());
343  topics.first = topics.second = "";
344  return topics;
345  }
346 
347  // first try just appending "static"
348  topics.second = ros::names::append(topics.first, "static");
349  if (this->topicNames.find(topics.second) == this->topicNames.end())
350  {
351  this->topicNames.insert(topics.first);
352  this->topicNames.insert(topics.second);
353  return topics;
354  }
355 
356  // otherwise, try again searching for the name iteratively
357 
358  bool staticTopicNameFound = false;
359  for (size_t i = 0; i < 10000; ++i)
360  {
361  topics.second = ros::names::append(baseName, ros::names::append("stream_" + std::to_string(i), "static"));
362  error = "";
363  if (ros::names::validate(topics.second, error) && this->topicNames.find(topics.second) == this->topicNames.end())
364  {
365  staticTopicNameFound = true;
366  break;
367  }
368  }
369 
370  if (!staticTopicNameFound)
371  {
372  ROS_ERROR("Could not generate static topic name for transform stream. Name validation error of last "
373  "tried name '%s' is: %s", topics.second.c_str(), error.c_str());
374  topics.first = topics.second = "";
375  return topics;
376  }
377 
378  this->topicNames.insert(topics.first);
379  this->topicNames.insert(topics.second);
380 
381  return topics;
382 }
383 
384 std::unique_ptr<TF2Server::FramesList> TF2Server::getFramesList(const RequestTransformStreamRequest &req) const
385 {
386  auto result = std::make_unique<TF2Server::FramesList>();
387 
388  // if parent frame doesn't exist, we either wait for it (if it's allowed), or fail immediately
389  if (!this->buffer->_frameExists(req.parent_frame))
390  {
391  if (!req.allow_transforms_update)
392  throw tf2::LookupException("Frame " + req.parent_frame + " doesn't exist.");
393  else
394  return result;
395  }
396 
397  if (!req.intermediate_frames)
398  { // if intermediate frames are not requested, we just publish pairs of parent and all children
399  result->reserve(req.child_frames.size());
400  for (const auto& child : req.child_frames)
401  {
402  if (!this->buffer->_frameExists(child))
403  {
404  if (!req.allow_transforms_update)
405  ROS_WARN("Frame %s doesn't exist, it won't be streamed.", child.c_str());
406  continue;
407  }
408  result->emplace_back(req.parent_frame, child);
409  }
410  }
411  else
412  { // otherwise, we need to get a whole subtree
413  std::vector<std::string> chainFrames;
414  std::vector<std::string> childFrames;
415 
416  if (!req.child_frames.empty())
417  { // only subtree to specific children is requested
418  for (const auto& child : req.child_frames)
419  {
420  if (!this->buffer->_frameExists(child))
421  {
422  if (!req.allow_transforms_update)
423  ROS_WARN("Frame %s doesn't exist, it won't be streamed.", child.c_str());
424  continue;
425  }
426  childFrames.push_back(child);
427  }
428  }
429  else
430  { // whole subtree is requested; we don't know what children it has, so we have to find them
431  std::vector<std::string> allFrames;
432  this->buffer->_getFrameStrings(allFrames);
433 
434  std::string parentParent; // parent frame of req.parent_frame
435  if (!this->buffer->_getParent(req.parent_frame, ros::Time(0), parentParent))
436  parentParent = ""; // if req.parent_frame is the topmost frame
437 
438  for (const auto& frame : allFrames)
439  {
440  if (frame == req.parent_frame)
441  continue;
442 
443  chainFrames.clear();
444  try
445  {
446  this->buffer->_chainAsVector(frame, ros::Time(0), req.parent_frame, ros::Time(0), req.parent_frame, chainFrames);
447  }
448  catch (tf2::ExtrapolationException& e)
449  {
450  // the frame is stale, ignore it
451  continue;
452  }
453  catch (tf2::TransformException& e)
454  {
455  ROS_ERROR("Error while searching TF tree: %s", e.what());
456  continue;
457  }
458 
459  // if chainFrames[1] is parentParent, it means that the path to the given frame does not
460  // descend into req.parent_frame's subtree, but it goes above; we throw away such paths
461  if (chainFrames[1] != parentParent)
462  childFrames.push_back(frame);
463  }
464  }
465 
466  // collect all unique pairs of parent-child in the requested subtree
467 
468  std::set<TF2Server::FrameSpec> framePairs;
469  for (const auto& child : childFrames)
470  {
471  chainFrames.clear();
472  this->buffer->_chainAsVector(child, ros::Time(0), req.parent_frame, ros::Time(0), req.parent_frame, chainFrames);
473 
474  if (chainFrames.size() <= 1)
475  continue;
476 
477  for (size_t i = 1; i < chainFrames.size(); ++i)
478  framePairs.emplace(chainFrames[i-1], chainFrames[i]);
479  }
480 
481  result->insert(result->begin(), framePairs.begin(), framePairs.end());
482  }
483  return result;
484 }
485 
487 {
488  // check update frequency
490  return;
492 
493  {
494  std::lock_guard<std::mutex> lock(this->mutex);
495 
496  // update all frames that allow it
497  for (auto &frame : this->frames)
498  {
499  const auto &req = frame.first;
500  if (req.allow_transforms_update)
501  {
502  this->frames[req] = std::move(this->getFramesList(req));
503  }
504  }
505  }
506 }
507 
509 {
510  std::lock_guard<std::mutex> lock(this->subscriberMutex);
511 
512  this->subscriberNumbers[topics] = this->subscriberNumbers[topics] + 1;
513  if (this->subscriberNumbers[topics] == 1)
514  ROS_INFO("Started streaming %s, %s", topics.first.c_str(), topics.second.c_str());
515 
516  this->timers[topics].start();
517 }
518 
520 {
521  std::lock_guard<std::mutex> lock(this->subscriberMutex);
522 
523  this->subscriberNumbers[topics] = this->subscriberNumbers[topics] - 1;
524  if (this->subscriberNumbers[topics] == 0)
525  {
526  ROS_INFO("Stopped streaming %s, %s", topics.first.c_str(), topics.second.c_str());
527  this->timers[topics].stop();
528  }
529 }
530 
532 {
533  for (auto& req : this->initialStreams)
534  {
535  try
536  {
537  RequestTransformStreamResponse resp;
538  const auto succeeded = this->onRequestTransformStream(req, resp);
539  if (succeeded)
540  ROS_INFO("Stream %s, %s ready", resp.topic_name.c_str(), resp.static_topic_name.c_str());
541  else
542  ROS_ERROR("There was an error setting up transform stream %s", req.requested_topic_name.c_str());
543  }
544  catch (std::runtime_error &e)
545  {
546  ROS_ERROR("There was an error setting up transform stream %s: %s", req.requested_topic_name.c_str(), e.what());
547  }
548  }
549 }
550 
552  const tf2_server::RequestTransformStreamRequest &r1,
553  const tf2_server::RequestTransformStreamRequest &r2) const
554 {
555  if (r1.intermediate_frames != r2.intermediate_frames)
556  return r1.intermediate_frames < r2.intermediate_frames;
557  else if (r1.allow_transforms_update != r2.allow_transforms_update)
558  return r1.allow_transforms_update < r2.allow_transforms_update;
559  else if (r1.parent_frame != r2.parent_frame)
560  return r1.parent_frame < r2.parent_frame;
561  else if (r1.child_frames.size() != r2.child_frames.size())
562  return r1.child_frames.size() < r2.child_frames.size();
563  else
564  {
565  for (size_t i = 0; i < r1.child_frames.size(); ++i)
566  {
567  if (r1.child_frames[i] != r2.child_frames[i])
568  return r1.child_frames[i] < r2.child_frames[i];
569  }
570  return false;
571  }
572 }
573 
575  const tf2_server::RequestTransformStreamRequest &r1,
576  const tf2_server::RequestTransformStreamRequest &r2) const
577 {
578  return !this->operator()(r1, r2) && !this->operator()(r2, r1);
579 }
580 
582  const tf2_server::RequestTransformStreamRequest &r1,
583  const tf2_server::RequestTransformStreamRequest &r2) const
584 {
585  if (r1.intermediate_frames != r2.intermediate_frames)
586  return r1.intermediate_frames < r2.intermediate_frames;
587  else if (r1.allow_transforms_update != r2.allow_transforms_update)
588  return r1.allow_transforms_update < r2.allow_transforms_update;
589  else if (r1.publication_period != r2.publication_period)
590  return r1.publication_period < r2.publication_period;
591  else if (r1.publisher_queue_size != r2.publisher_queue_size)
592  return r1.publisher_queue_size < r2.publisher_queue_size;
593  else if (r1.parent_frame != r2.parent_frame)
594  return r1.parent_frame < r2.parent_frame;
595  else if (r1.child_frames.size() != r2.child_frames.size())
596  return r1.child_frames.size() < r2.child_frames.size();
597  else
598  {
599  for (size_t i = 0; i < r1.child_frames.size(); ++i)
600  {
601  if (r1.child_frames[i] != r2.child_frames[i])
602  return r1.child_frames[i] < r2.child_frames[i];
603  }
604  return false;
605  }
606  // requested_topic_name and requested_static_topic_name are omitted intentionally
607 }
608 
610  const tf2_server::RequestTransformStreamRequest &r1,
611  const tf2_server::RequestTransformStreamRequest &r2) const
612 {
613  return !this->operator()(r1, r2) && !this->operator()(r2, r1);
614 }
615 
616 }
std::map< TopicsSpec, size_t > subscriberNumbers
Definition: tf2_server.h:59
std::string stripLeadingSlash(const std::string &s, const bool warn)
Definition: tf2_server.cpp:13
boost::shared_ptr< void const > VoidConstPtr
#define ROS_ERROR_STREAM_THROTTLE(period, args)
bool operator()(const tf2_server::RequestTransformStreamRequest &r1, const tf2_server::RequestTransformStreamRequest &r2) const
Definition: tf2_server.cpp:581
bool operator!=(const tf2_msgs::TFMessage &lhs, const tf2_msgs::TFMessage &rhs)
Definition: tf2_server.cpp:167
std::unique_ptr< tf2_ros::Buffer > buffer
Definition: tf2_server.h:39
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
std::map< RequestTransformStreamRequest, std::unique_ptr< FramesList >, RequestComparatorByFrames > frames
Definition: tf2_server.h:55
virtual void onSubscriberDisconnected(const TopicsSpec &topics)
Definition: tf2_server.cpp:519
std::map< TopicsSpec, ros::Timer > timers
Definition: tf2_server.h:58
std::unique_ptr< tf2_ros::TransformListener > listener
Definition: tf2_server.h:41
ROSCPP_DECL bool validate(const std::string &name, std::string &error)
TF2Server(ros::NodeHandle &nh, ros::NodeHandle &pnh)
Definition: tf2_server.cpp:26
virtual void start()
Definition: tf2_server.cpp:127
virtual std::unique_ptr< FramesList > getFramesList(const RequestTransformStreamRequest &req) const
Definition: tf2_server.cpp:384
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
void warnLeadingSlash(const std::string &s)
Definition: tf2_server.cpp:8
std::mutex streamsMutex
Definition: tf2_server.h:44
#define ROS_WARN(...)
ros::Duration initialStreamsWaitTime
Definition: tf2_server.h:63
std::vector< RequestTransformStreamRequest > initialStreams
Definition: tf2_server.h:62
bool operator==(const tf2_msgs::TFMessage &lhs, const tf2_msgs::TFMessage &rhs)
Definition: tf2_server.cpp:142
ros::Timer initialStreamsTimer
Definition: tf2_server.h:64
bool equals(const tf2_server::RequestTransformStreamRequest &r1, const tf2_server::RequestTransformStreamRequest &r2) const
Definition: tf2_server.cpp:609
bool param(const std::string &param_name, T &param_val, const T &default_val) const
Type const & getType() const
std::map< TopicsSpec, RequestTransformStreamRequest > streams
Definition: tf2_server.h:60
bool operator()(const tf2_server::RequestTransformStreamRequest &r1, const tf2_server::RequestTransformStreamRequest &r2) const
Definition: tf2_server.cpp:551
virtual void updateFramesLists()
Definition: tf2_server.cpp:486
ros::NodeHandle & pnh
Definition: tf2_server.h:37
#define ROS_INFO(...)
bool getParam(const std::string &key, std::string &s) const
ros::Duration transformsUpdatePeriod
Definition: tf2_server.h:67
virtual TopicsSpec getTopicsNames(const RequestTransformStreamRequest &request)
Definition: tf2_server.cpp:265
std::map< std::string, ros::Publisher > staticPublishers
Definition: tf2_server.h:50
std::string resolveName(const std::string &name, bool remap=true) const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool equals(const tf2_server::RequestTransformStreamRequest &r1, const tf2_server::RequestTransformStreamRequest &r2) const
Definition: tf2_server.cpp:574
#define ROS_WARN_STREAM(args)
bool hasParam(const std::string &key) const
ros::ServiceServer requestTransformStreamServer
Definition: tf2_server.h:46
std::pair< std::string, std::string > TopicsSpec
Definition: tf2_server.h:57
ros::Time lastTransformsUpdateTime
Definition: tf2_server.h:66
std::map< std::string, ros::Publisher > publishers
Definition: tf2_server.h:49
const std::string & getNamespace() const
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
virtual void onSubscriberConnected(const TopicsSpec &topics)
Definition: tf2_server.cpp:508
std::unordered_set< std::string > topicNames
Definition: tf2_server.h:48
virtual void streamTransform(const ros::TimerEvent &event, const RequestTransformStreamRequest &request, const TopicsSpec &topics)
Definition: tf2_server.cpp:228
ROSCPP_DECL std::string append(const std::string &left, const std::string &right)
static Time now()
std::map< std::string, tf2_msgs::TFMessage > lastStaticTransforms
Definition: tf2_server.h:51
std::unique_ptr< tf2_ros::BufferServer > server
Definition: tf2_server.h:40
ros::NodeHandle & nh
Definition: tf2_server.h:36
virtual bool onRequestTransformStream(RequestTransformStreamRequest &req, RequestTransformStreamResponse &resp)
Definition: tf2_server.cpp:172
std::mutex subscriberMutex
Definition: tf2_server.h:43
#define ROS_ERROR(...)


tf2_server
Author(s): Martin Pecka
autogenerated on Sun Jun 12 2022 02:10:45