urg_stamped.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018 The urg_stamped Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ros/ros.h>
18 #include <sensor_msgs/LaserScan.h>
19 
20 #include <boost/bind/bind.hpp>
21 #include <boost/format.hpp>
22 #include <boost/thread.hpp>
23 
24 #include <algorithm>
25 #include <map>
26 #include <random>
27 #include <string>
28 #include <vector>
29 #include <list>
30 
31 #include <scip2/scip2.h>
32 #include <scip2/walltime.h>
33 
34 #include <first_order_filter.h>
37 
39 {
40 protected:
47 
48  sensor_msgs::LaserScan msg_base_;
49  uint32_t step_min_;
50  uint32_t step_max_;
51 
54 
56 
57  boost::posix_time::ptime time_tm_request;
58  std::list<ros::Duration> communication_delays_;
59  std::list<ros::Time> device_time_origins_;
61  size_t tm_iter_num_;
65 
66  boost::posix_time::ptime time_ii_request;
67  std::vector<ros::Duration> on_scan_communication_delays_;
68 
70 
71  std::default_random_engine random_engine_;
72  std::uniform_real_distribution<double> sync_interval_;
74 
76  {
77  public:
79  double gain_;
80 
82  : gain_(1.0)
83  {
84  }
85  DriftedTime(const ros::Time origin, const float gain)
86  : origin_(origin)
87  , gain_(gain)
88  {
89  }
90  };
92 
98 
100  const boost::posix_time::ptime &time_request,
101  const boost::posix_time::ptime &time_response,
102  const uint64_t &device_timestamp)
103  {
104  const auto delay =
105  ros::Time::fromBoost(time_response) -
106  ros::Time::fromBoost(time_request);
107  const ros::Time time_at_device_timestamp = ros::Time::fromBoost(time_request) + delay * 0.5;
108 
109  return time_at_device_timestamp - ros::Duration().fromNSec(device_timestamp * 1e6);
110  }
112  const boost::posix_time::ptime &time_response,
113  const uint64_t &device_timestamp,
114  ros::Time &time_at_device_timestamp)
115  {
116  time_at_device_timestamp = ros::Time::fromBoost(time_response) - estimated_communication_delay_ * 0.5;
117 
118  return time_at_device_timestamp - ros::Duration().fromNSec(device_timestamp * 1e6);
119  }
120 
121  void cbM(
122  const boost::posix_time::ptime &time_read,
123  const std::string &echo_back,
124  const std::string &status,
125  const scip2::ScanData &scan,
126  const bool has_intensity)
127  {
128  const uint64_t walltime_device = walltime_.update(scan.timestamp_);
129 
130  const auto estimated_timestamp_lf =
131  device_time_origin_.origin_ +
132  ros::Duration().fromNSec(walltime_device * 1e6 * device_time_origin_.gain_) +
133  ros::Duration(msg_base_.time_increment * step_min_);
134 
135  if (t0_ == ros::Time(0))
136  t0_ = estimated_timestamp_lf;
137 
138  const auto receive_time =
139  timestamp_outlier_removal_.update(
140  ros::Time::fromBoost(time_read) -
141  estimated_communication_delay_ * 0.5 -
142  ros::Duration(msg_base_.scan_time));
143 
144  sensor_msgs::LaserScan msg(msg_base_);
145  msg.header.stamp =
146  timestamp_moving_average_.update(
147  t0_ +
149  timestamp_lpf_.update((estimated_timestamp_lf - t0_).toSec()) +
150  timestamp_hpf_.update((receive_time - t0_).toSec())));
151 
152  if (scan.ranges_.size() != step_max_ - step_min_ + 1)
153  {
154  ROS_DEBUG("Size of the received scan data is wrong (expected: %d, received: %lu); refreshing",
155  step_max_ - step_min_ + 1, scan.ranges_.size());
156  scip_->sendCommand(
157  (has_intensity ? "ME" : "MD") +
158  (boost::format("%04d%04d") % step_min_ % step_max_).str() +
159  "00000");
160  return;
161  }
162 
163  msg.ranges.reserve(scan.ranges_.size());
164  for (auto &r : scan.ranges_)
165  msg.ranges.push_back(r * 1e-3);
166  if (has_intensity)
167  {
168  msg.intensities.reserve(scan.intensities_.size());
169  for (auto &r : scan.intensities_)
170  msg.intensities.push_back(r);
171  }
172 
173  pub_scan_.publish(msg);
174  }
175  void cbTMSend(const boost::posix_time::ptime &time_send)
176  {
177  time_tm_request = time_send;
178  }
179  void cbTM(
180  const boost::posix_time::ptime &time_read,
181  const std::string &echo_back,
182  const std::string &status,
183  const scip2::Timestamp &time_device)
184  {
185  if (status != "00")
186  return;
187 
188  timer_try_tm_.stop();
189  switch (echo_back[2])
190  {
191  case '0':
192  {
193  scip_->sendCommand(
194  "TM1",
195  boost::bind(&UrgStampedNode::cbTMSend, this, boost::arg<1>()));
196  break;
197  }
198  case '1':
199  {
200  const uint64_t walltime_device = walltime_.update(time_device.timestamp_);
201 
202  const auto delay =
203  ros::Time::fromBoost(time_read) -
204  ros::Time::fromBoost(time_tm_request);
205  communication_delays_.push_back(delay);
206  if (communication_delays_.size() > tm_median_window_)
207  communication_delays_.pop_front();
208 
209  const auto origin = calculateDeviceTimeOriginByAverage(
210  time_tm_request, time_read, walltime_device);
211  device_time_origins_.push_back(origin);
212  if (device_time_origins_.size() > tm_median_window_)
213  device_time_origins_.pop_front();
214 
215  if (communication_delays_.size() >= tm_iter_num_)
216  {
217  std::vector<ros::Duration> delays(communication_delays_.begin(), communication_delays_.end());
218  std::vector<ros::Time> origins(device_time_origins_.begin(), device_time_origins_.end());
219  sort(delays.begin(), delays.end());
220  sort(origins.begin(), origins.end());
221 
222  if (!estimated_communication_delay_init_)
223  {
224  estimated_communication_delay_ = delays[tm_iter_num_ / 2];
225  device_time_origin_ = DriftedTime(origins[tm_iter_num_ / 2], 1.0);
226  }
227  else
228  {
229  estimated_communication_delay_ =
230  estimated_communication_delay_ * (1.0 - communication_delay_filter_alpha_) +
231  delays[tm_iter_num_ / 2] * communication_delay_filter_alpha_;
232  }
233  estimated_communication_delay_init_ = true;
234  ROS_DEBUG("delay: %0.6f, device timestamp: %ld, device time origin: %0.6f",
235  estimated_communication_delay_.toSec(),
236  walltime_device,
237  origins[tm_iter_num_ / 2].toSec());
238  scip_->sendCommand("TM2");
239  }
240  else
241  {
242  ros::Duration(0.005).sleep();
243  scip_->sendCommand(
244  "TM1",
245  boost::bind(&UrgStampedNode::cbTMSend, this, boost::arg<1>()));
246  }
247  break;
248  }
249  case '2':
250  {
251  scip_->sendCommand(
252  (publish_intensity_ ? "ME" : "MD") +
253  (boost::format("%04d%04d") % step_min_ % step_max_).str() +
254  "00000");
255  timeSync();
256  timestamp_outlier_removal_.reset();
257  timestamp_moving_average_.reset();
258  t0_ = ros::Time();
259  break;
260  }
261  }
262  }
263  void cbPP(
264  const boost::posix_time::ptime &time_read,
265  const std::string &echo_back,
266  const std::string &status,
267  const std::map<std::string, std::string> &params)
268  {
269  const auto amin = params.find("AMIN");
270  const auto amax = params.find("AMAX");
271  const auto dmin = params.find("DMIN");
272  const auto dmax = params.find("DMAX");
273  const auto ares = params.find("ARES");
274  const auto afrt = params.find("AFRT");
275  const auto scan = params.find("SCAN");
276  if (amin == params.end() || amax == params.end() ||
277  dmin == params.end() || dmax == params.end() ||
278  ares == params.end() || afrt == params.end() ||
279  scan == params.end())
280  {
281  ROS_ERROR("PP doesn't have required parameters");
282  return;
283  }
284  step_min_ = std::stoi(amin->second);
285  step_max_ = std::stoi(amax->second);
286  msg_base_.scan_time = 60.0 / std::stoi(scan->second);
287  msg_base_.angle_increment = 2.0 * M_PI / std::stoi(ares->second);
288  msg_base_.time_increment = msg_base_.scan_time / std::stoi(ares->second);
289  msg_base_.range_min = std::stoi(dmin->second) * 1e-3;
290  msg_base_.range_max = std::stoi(dmax->second) * 1e-3;
291  msg_base_.angle_min =
292  (std::stoi(amin->second) - std::stoi(afrt->second)) * msg_base_.angle_increment;
293  msg_base_.angle_max =
294  (std::stoi(amax->second) - std::stoi(afrt->second)) * msg_base_.angle_increment;
295 
296  timestamp_outlier_removal_.setInterval(ros::Duration(msg_base_.scan_time));
297  timestamp_moving_average_.setInterval(ros::Duration(msg_base_.scan_time));
298  delayEstimation();
299  }
300  void cbVV(
301  const boost::posix_time::ptime &time_read,
302  const std::string &echo_back,
303  const std::string &status,
304  const std::map<std::string, std::string> &params)
305  {
306  }
307  void cbIISend(const boost::posix_time::ptime &time_send)
308  {
309  time_ii_request = time_send;
310  }
311  void cbII(
312  const boost::posix_time::ptime &time_read,
313  const std::string &echo_back,
314  const std::string &status,
315  const std::map<std::string, std::string> &params)
316  {
317  const auto delay =
318  ros::Time::fromBoost(time_read) -
319  ros::Time::fromBoost(time_ii_request);
320 
321  if (delay.toSec() < 0.002)
322  {
323  const auto time = params.find("TIME");
324  if (time == params.end())
325  {
326  ROS_DEBUG("II doesn't have timestamp");
327  return;
328  }
329  if (time->second.size() != 6 && time->second.size() != 4)
330  {
331  ROS_DEBUG("Timestamp in II is ill-formatted (%s)", time->second.c_str());
332  return;
333  }
334  const uint32_t time_device =
335  time->second.size() == 6 ?
336  std::stoi(time->second, nullptr, 16) :
337  *(scip2::Decoder<4>(time->second).begin());
338 
339  const uint64_t walltime_device = walltime_.update(time_device);
340 
341  ros::Time time_at_device_timestamp;
342  const auto origin = calculateDeviceTimeOrigin(
343  time_read, walltime_device, time_at_device_timestamp);
344 
345  const auto now = ros::Time::fromBoost(time_read);
346  if (last_sync_time_ == ros::Time(0))
347  last_sync_time_ = now;
348  const double dt = std::min((now - last_sync_time_).toSec(), 10.0);
349  last_sync_time_ = now;
350 
351  const double gain =
352  (time_at_device_timestamp - device_time_origin_.origin_).toSec() /
353  (time_at_device_timestamp - origin).toSec();
354  const double exp_lpf_alpha =
355  dt * (1.0 / 30.0); // 30 seconds exponential LPF
356  const double updated_gain =
357  (1.0 - exp_lpf_alpha) * device_time_origin_.gain_ + exp_lpf_alpha * gain;
358  device_time_origin_.gain_ = updated_gain;
359  device_time_origin_.origin_ +=
360  ros::Duration(exp_lpf_alpha * (origin - device_time_origin_.origin_).toSec());
361 
362  ROS_DEBUG("on scan delay: %0.6f, device timestamp: %ld, device time origin: %0.6f, gain: %0.6f",
363  delay.toSec(),
364  walltime_device,
365  origin.toSec(),
366  updated_gain);
367  }
368  else
369  {
370  ROS_DEBUG("on scan delay (%0.6f) is larger than expected; skipping",
371  delay.toSec());
372  }
373  }
374  void cbQT(
375  const boost::posix_time::ptime &time_read,
376  const std::string &echo_back,
377  const std::string &status)
378  {
379  ROS_DEBUG("Scan data stopped");
380  }
381  void cbConnect()
382  {
383  scip_->sendCommand("PP");
384  device_->startWatchdog(boost::posix_time::seconds(1));
385  }
386 
387  void timeSync(const ros::TimerEvent &event = ros::TimerEvent())
388  {
389  scip_->sendCommand(
390  "II",
391  boost::bind(&UrgStampedNode::cbIISend, this, boost::arg<1>()));
392  timer_sync_ = nh_.createTimer(
393  ros::Duration(sync_interval_(random_engine_)),
394  &UrgStampedNode::timeSync, this, true);
395  }
397  {
398  timer_sync_.stop();
399  ROS_INFO("Starting communication delay estimation");
400  scip_->sendCommand("QT");
401  timer_try_tm_ = nh_.createTimer(
402  ros::Duration(0.05),
403  &UrgStampedNode::tryTM, this);
404  }
405  void tryTM(const ros::TimerEvent &event = ros::TimerEvent())
406  {
407  scip_->sendCommand("QT");
408  scip_->sendCommand("TM0");
409  }
410 
411 public:
413  : nh_("")
414  , pnh_("~")
415  , tm_iter_num_(5)
416  , tm_median_window_(35)
417  , estimated_communication_delay_init_(false)
418  , communication_delay_filter_alpha_(0.3)
419  , last_sync_time_(0)
420  , timestamp_lpf_(20)
421  , timestamp_hpf_(20)
422  , timestamp_outlier_removal_(ros::Duration(0.001), ros::Duration())
423  , timestamp_moving_average_(5, ros::Duration())
424  {
425  std::string ip;
426  int port;
427  double sync_interval_min;
428  double sync_interval_max;
429  double delay_estim_interval;
430 
431  pnh_.param("ip_address", ip, std::string("192.168.0.10"));
432  pnh_.param("ip_port", port, 10940);
433  pnh_.param("frame_id", msg_base_.header.frame_id, std::string("laser"));
434  pnh_.param("publish_intensity", publish_intensity_, true);
435  pnh_.param("sync_interval_min", sync_interval_min, 1.0);
436  pnh_.param("sync_interval_max", sync_interval_max, 1.5);
437  sync_interval_ = std::uniform_real_distribution<double>(sync_interval_min, sync_interval_max);
438  pnh_.param("delay_estim_interval", delay_estim_interval, 20.0);
439 
440  pub_scan_ = nh_.advertise<sensor_msgs::LaserScan>("scan", 10);
441 
442  device_.reset(new scip2::ConnectionTcp(ip, port));
443  device_->registerCloseCallback(ros::shutdown);
444  device_->registerConnectCallback(
445  boost::bind(&UrgStampedNode::cbConnect, this));
446 
447  scip_.reset(new scip2::Protocol(device_));
448  scip_->registerCallback<scip2::ResponsePP>(
449  boost::bind(&UrgStampedNode::cbPP, this,
450  boost::arg<1>(),
451  boost::arg<2>(),
452  boost::arg<3>(),
453  boost::arg<4>()));
455  boost::bind(&UrgStampedNode::cbVV, this,
456  boost::arg<1>(),
457  boost::arg<2>(),
458  boost::arg<3>(),
459  boost::arg<4>()));
461  boost::bind(&UrgStampedNode::cbII, this,
462  boost::arg<1>(),
463  boost::arg<2>(),
464  boost::arg<3>(),
465  boost::arg<4>()));
467  boost::bind(&UrgStampedNode::cbM, this,
468  boost::arg<1>(),
469  boost::arg<2>(),
470  boost::arg<3>(),
471  boost::arg<4>(),
472  false));
474  boost::bind(&UrgStampedNode::cbM, this,
475  boost::arg<1>(),
476  boost::arg<2>(),
477  boost::arg<3>(),
478  boost::arg<4>(),
479  true));
481  boost::bind(&UrgStampedNode::cbTM, this,
482  boost::arg<1>(),
483  boost::arg<2>(),
484  boost::arg<3>(),
485  boost::arg<4>()));
487  boost::bind(&UrgStampedNode::cbQT, this,
488  boost::arg<1>(),
489  boost::arg<2>(),
490  boost::arg<3>()));
491 
492  if (delay_estim_interval > 0.0)
493  {
494  timer_delay_estim_ = nh_.createTimer(
495  ros::Duration(delay_estim_interval), &UrgStampedNode::delayEstimation, this);
496  }
497  }
498  void spin()
499  {
500  boost::thread thread(
501  boost::bind(&scip2::Connection::spin, device_.get()));
502  ros::spin();
503  timer_sync_.stop();
504  scip_->sendCommand("QT");
505  device_->stop();
506  }
507 };
508 
509 int main(int argc, char **argv)
510 {
511  ros::init(argc, argv, "urg_stamped");
512  UrgStampedNode node;
513  node.spin();
514 
515  return 1;
516 }
scip2::Walltime< 24 > walltime_
Definition: urg_stamped.cpp:69
DriftedTime(const ros::Time origin, const float gain)
Definition: urg_stamped.cpp:85
std::shared_ptr< Connection > Ptr
Definition: connection.h:63
void registerCallback(Callback cb)
Definition: parameters.h:92
void publish(const boost::shared_ptr< M > &message) const
ros::NodeHandle nh_
Definition: urg_stamped.cpp:41
std::vector< int32_t > ranges_
Definition: stream.h:35
FirstOrderHPF< double > timestamp_hpf_
Definition: urg_stamped.cpp:95
DriftedTime device_time_origin_
Definition: urg_stamped.cpp:91
void cbVV(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status, const std::map< std::string, std::string > &params)
int main(int argc, char **argv)
bool sleep() const
bool estimated_communication_delay_init_
Definition: urg_stamped.cpp:63
ros::Time update(const ros::Time &stamp)
void stop()
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
std::list< ros::Duration > communication_delays_
Definition: urg_stamped.cpp:58
ros::Timer timer_try_tm_
Definition: urg_stamped.cpp:46
void registerCallback(Callback cb)
Definition: stream.h:101
FLT update(const FLT &in)
void delayEstimation(const ros::TimerEvent &event=ros::TimerEvent())
ros::Time calculateDeviceTimeOrigin(const boost::posix_time::ptime &time_response, const uint64_t &device_timestamp, ros::Time &time_at_device_timestamp)
ros::Timer timer_delay_estim_
Definition: urg_stamped.cpp:45
void cbTMSend(const boost::posix_time::ptime &time_send)
uint32_t timestamp_
Definition: stream.h:34
ros::Publisher pub_scan_
Definition: urg_stamped.cpp:43
void registerCallback(Callback cb)
Definition: time_sync.h:99
void setInterval(const ros::Duration &interval)
scip2::Protocol::Ptr scip_
Definition: urg_stamped.cpp:53
std::default_random_engine random_engine_
Definition: urg_stamped.cpp:71
ros::Time update(const ros::Time &stamp)
void cbII(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status, const std::map< std::string, std::string > &params)
boost::posix_time::ptime time_ii_request
Definition: urg_stamped.cpp:66
uint64_t update(const uint32_t &time_device)
Definition: walltime.h:33
ros::Timer timer_sync_
Definition: urg_stamped.cpp:44
#define ROS_INFO(...)
void cbM(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status, const scip2::ScanData &scan, const bool has_intensity)
scip2::Connection::Ptr device_
Definition: urg_stamped.cpp:52
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ros::Time last_sync_time_
Definition: urg_stamped.cpp:73
std::vector< ros::Duration > on_scan_communication_delays_
Definition: urg_stamped.cpp:67
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
std::uniform_real_distribution< double > sync_interval_
Definition: urg_stamped.cpp:72
TimestampMovingAverage timestamp_moving_average_
Definition: urg_stamped.cpp:97
std::shared_ptr< Protocol > Ptr
Definition: protocol.h:65
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
TimestampOutlierRemover timestamp_outlier_removal_
Definition: urg_stamped.cpp:96
sensor_msgs::LaserScan msg_base_
Definition: urg_stamped.cpp:48
ROSCPP_DECL void spin()
std::vector< int32_t > intensities_
Definition: stream.h:36
void cbIISend(const boost::posix_time::ptime &time_send)
void cbQT(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status)
double communication_delay_filter_alpha_
Definition: urg_stamped.cpp:64
void cbTM(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status, const scip2::Timestamp &time_device)
void setInterval(const ros::Duration &interval)
uint32_t timestamp_
Definition: time_sync.h:32
size_t tm_median_window_
Definition: urg_stamped.cpp:62
bool publish_intensity_
Definition: urg_stamped.cpp:55
void tryTM(const ros::TimerEvent &event=ros::TimerEvent())
ros::Duration estimated_communication_delay_
Definition: urg_stamped.cpp:60
Duration & fromNSec(int64_t t)
void timeSync(const ros::TimerEvent &event=ros::TimerEvent())
static Time fromBoost(const boost::posix_time::ptime &t)
ROSCPP_DECL void shutdown()
FirstOrderLPF< double > timestamp_lpf_
Definition: urg_stamped.cpp:94
boost::posix_time::ptime time_tm_request
Definition: urg_stamped.cpp:57
void cbPP(const boost::posix_time::ptime &time_read, const std::string &echo_back, const std::string &status, const std::map< std::string, std::string > &params)
virtual void spin()=0
size_t tm_iter_num_
Definition: urg_stamped.cpp:61
std::list< ros::Time > device_time_origins_
Definition: urg_stamped.cpp:59
ros::Time t0_
Definition: urg_stamped.cpp:93
#define ROS_ERROR(...)
ros::Time calculateDeviceTimeOriginByAverage(const boost::posix_time::ptime &time_request, const boost::posix_time::ptime &time_response, const uint64_t &device_timestamp)
Definition: urg_stamped.cpp:99
ros::NodeHandle pnh_
Definition: urg_stamped.cpp:42
uint32_t step_min_
Definition: urg_stamped.cpp:49
#define ROS_DEBUG(...)
uint32_t step_max_
Definition: urg_stamped.cpp:50


urg_stamped
Author(s): Atsushi Watanabe
autogenerated on Thu Jun 6 2019 19:55:59