safety_limiter.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2017, the neonavigation authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <algorithm>
31 #include <cmath>
32 #include <iostream>
33 #include <limits>
34 #include <memory>
35 #include <random>
36 #include <sstream>
37 #include <string>
38 #include <utility>
39 #include <vector>
40 
41 #include <Eigen/Core>
42 #include <Eigen/Geometry>
43 
44 #include <ros/ros.h>
45 
47 #include <dynamic_reconfigure/server.h>
48 #include <geometry_msgs/Twist.h>
49 #include <safety_limiter_msgs/SafetyLimiterStatus.h>
50 #include <sensor_msgs/PointCloud.h>
51 #include <sensor_msgs/PointCloud2.h>
52 #include <std_msgs/Bool.h>
53 #include <std_msgs/Empty.h>
56 
57 #include <pcl/common/transforms.h>
58 #include <pcl/filters/voxel_grid.h>
59 #include <pcl/kdtree/kdtree_flann.h>
60 #include <pcl/point_cloud.h>
61 #include <pcl/point_types.h>
63 #include <pcl_ros/transforms.h>
64 
66 
67 #include <safety_limiter/SafetyLimiterConfig.h>
68 
69 namespace safety_limiter
70 {
71 pcl::PointXYZ operator-(const pcl::PointXYZ& a, const pcl::PointXYZ& b)
72 {
73  auto c = a;
74  c.x -= b.x;
75  c.y -= b.y;
76  c.z -= b.z;
77  return c;
78 }
79 pcl::PointXYZ operator+(const pcl::PointXYZ& a, const pcl::PointXYZ& b)
80 {
81  auto c = a;
82  c.x += b.x;
83  c.y += b.y;
84  c.z += b.z;
85  return c;
86 }
87 pcl::PointXYZ operator*(const pcl::PointXYZ& a, const float& b)
88 {
89  auto c = a;
90  c.x *= b;
91  c.y *= b;
92  c.z *= b;
93  return c;
94 }
96 {
97  return value.getType() == XmlRpc::XmlRpcValue::TypeInt ||
99 }
100 
102 {
103 protected:
110  std::vector<ros::Subscriber> sub_clouds_;
116  boost::recursive_mutex parameter_server_mutex_;
117  std::unique_ptr<dynamic_reconfigure::Server<SafetyLimiterConfig>> parameter_server_;
118 
119  geometry_msgs::Twist twist_;
121  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_accum_;
123  double hz_;
124  double timeout_;
126  double vel_[2];
127  double acc_[2];
128  double tmax_;
129  double dt_;
130  double d_margin_;
131  double d_escape_;
132  double yaw_margin_;
133  double yaw_escape_;
134  double r_lim_;
135  double max_values_[2];
136  double z_range_[2];
139  std::string fixed_frame_id_;
140  std::string base_frame_id_;
141 
147 
153 
154  constexpr static float EPSILON = 1e-6;
155 
157 
158 public:
160  : nh_()
161  , pnh_("~")
162  , tfl_(tfbuf_)
163  , cloud_accum_(new pcl::PointCloud<pcl::PointXYZ>)
164  , cloud_clear_(false)
165  , last_disable_cmd_(0)
166  , watchdog_stop_(false)
167  , has_cloud_(false)
168  , has_twist_(true)
169  , has_collision_at_now_(false)
170  , stuck_started_since_(ros::Time(0))
171  {
173  pub_twist_ = neonavigation_common::compat::advertise<geometry_msgs::Twist>(
174  nh_, "cmd_vel",
175  pnh_, "cmd_vel_out", 1, true);
176  pub_cloud_ = nh_.advertise<sensor_msgs::PointCloud>("collision", 1, true);
177  pub_status_ = pnh_.advertise<safety_limiter_msgs::SafetyLimiterStatus>("status", 1, true);
179  nh_, "cmd_vel_in",
180  pnh_, "cmd_vel_in", 1, &SafetyLimiterNode::cbTwist, this);
182  nh_, "disable_safety",
183  pnh_, "disable", 1, &SafetyLimiterNode::cbDisable, this);
185  nh_, "watchdog_reset",
186  pnh_, "watchdog_reset", 1, &SafetyLimiterNode::cbWatchdogReset, this);
187 
188  int num_input_clouds;
189  pnh_.param("num_input_clouds", num_input_clouds, 1);
190  if (num_input_clouds == 1)
191  {
193  nh_, "cloud",
194  pnh_, "cloud", 1, &SafetyLimiterNode::cbCloud, this));
195  }
196  else
197  {
198  for (int i = 0; i < num_input_clouds; ++i)
199  {
200  sub_clouds_.push_back(nh_.subscribe(
201  "cloud" + std::to_string(i), 1, &SafetyLimiterNode::cbCloud, this));
202  }
203  }
204 
205  if (pnh_.hasParam("t_margin"))
206  ROS_WARN("safety_limiter: t_margin parameter is obsolated. Use d_margin and yaw_margin instead.");
207  pnh_.param("base_frame", base_frame_id_, std::string("base_link"));
208  pnh_.param("fixed_frame", fixed_frame_id_, std::string("odom"));
209  double watchdog_interval_d;
210  pnh_.param("watchdog_interval", watchdog_interval_d, 0.0);
211  watchdog_interval_ = ros::Duration(watchdog_interval_d);
212  pnh_.param("max_linear_vel", max_values_[0], std::numeric_limits<double>::infinity());
213  pnh_.param("max_angular_vel", max_values_[1], std::numeric_limits<double>::infinity());
214 
215  parameter_server_.reset(
216  new dynamic_reconfigure::Server<SafetyLimiterConfig>(parameter_server_mutex_, pnh_));
217  parameter_server_->setCallback(boost::bind(&SafetyLimiterNode::cbParameter, this, _1, _2));
218 
219  XmlRpc::XmlRpcValue footprint_xml;
220  if (!pnh_.hasParam("footprint"))
221  {
222  ROS_FATAL("Footprint doesn't specified");
223  throw std::runtime_error("Footprint doesn't specified");
224  }
225  pnh_.getParam("footprint", footprint_xml);
226  if (footprint_xml.getType() != XmlRpc::XmlRpcValue::TypeArray || footprint_xml.size() < 3)
227  {
228  ROS_FATAL("Invalid footprint");
229  throw std::runtime_error("Invalid footprint");
230  }
231  footprint_radius_ = 0;
232  for (int i = 0; i < footprint_xml.size(); i++)
233  {
234  if (!XmlRpc_isNumber(footprint_xml[i][0]) ||
235  !XmlRpc_isNumber(footprint_xml[i][1]))
236  {
237  ROS_FATAL("Invalid footprint value");
238  throw std::runtime_error("Invalid footprint value");
239  }
240 
241  vec v;
242  v[0] = static_cast<double>(footprint_xml[i][0]);
243  v[1] = static_cast<double>(footprint_xml[i][1]);
244  footprint_p.v.push_back(v);
245 
246  const float dist = std::hypot(v[0], v[1]);
247  if (dist > footprint_radius_)
248  footprint_radius_ = dist;
249  }
250  footprint_p.v.push_back(footprint_p.v.front());
251  ROS_INFO("footprint radius: %0.3f", footprint_radius_);
252 
255  }
256  void spin()
257  {
258  ros::Timer predict_timer =
260 
261  if (watchdog_interval_ != ros::Duration(0.0))
262  {
265  }
266 
267  ros::spin();
268  }
269 
270 protected:
271  void cbWatchdogReset(const std_msgs::Empty::ConstPtr& msg)
272  {
274  watchdog_stop_ = false;
275  }
276  void cbWatchdogTimer(const ros::TimerEvent& event)
277  {
278  ROS_WARN_THROTTLE(1.0, "safety_limiter: Watchdog timed-out");
279  watchdog_stop_ = true;
280  r_lim_ = 0;
281  geometry_msgs::Twist cmd_vel;
282  pub_twist_.publish(cmd_vel);
283 
285  }
286  void cbPredictTimer(const ros::TimerEvent& event)
287  {
288  if (!has_twist_)
289  return;
290  if (!has_cloud_)
291  return;
292 
294  {
295  ROS_WARN_THROTTLE(1.0, "safety_limiter: PointCloud timed-out");
296  geometry_msgs::Twist cmd_vel;
297  pub_twist_.publish(cmd_vel);
298 
299  cloud_accum_.reset(new pcl::PointCloud<pcl::PointXYZ>);
300  has_cloud_ = false;
301  r_lim_ = 0;
302 
304  return;
305  }
306 
307  ros::Time now = ros::Time::now();
308  const double r_lim_current = predict(twist_);
309 
310  if (r_lim_current < r_lim_)
311  r_lim_ = r_lim_current;
312 
313  if (r_lim_current < 1.0)
314  hold_off_ = now + hold_;
315 
316  cloud_clear_ = true;
317 
319  }
320  void cbParameter(const SafetyLimiterConfig& config, const uint32_t /* level */)
321  {
322  boost::recursive_mutex::scoped_lock lock(parameter_server_mutex_);
323  hz_ = config.freq;
324  timeout_ = config.cloud_timeout;
325  disable_timeout_ = config.disable_timeout;
326  vel_[0] = config.lin_vel;
327  acc_[0] = config.lin_acc;
328  vel_[1] = config.ang_vel;
329  acc_[1] = config.ang_acc;
330  max_values_[0] = config.max_linear_vel;
331  max_values_[1] = config.max_angular_vel;
332  z_range_[0] = config.z_range_min;
333  z_range_[1] = config.z_range_max;
334  dt_ = config.dt;
335  d_margin_ = config.d_margin;
336  d_escape_ = config.d_escape;
337  yaw_margin_ = config.yaw_margin;
338  yaw_escape_ = config.yaw_escape;
339  downsample_grid_ = config.downsample_grid;
340  hold_ = ros::Duration(std::max(config.hold, 1.0 / hz_));
341  allow_empty_cloud_ = config.allow_empty_cloud;
342 
343  tmax_ = 0.0;
344  for (int i = 0; i < 2; i++)
345  {
346  auto t = vel_[i] / acc_[i];
347  if (tmax_ < t)
348  tmax_ = t;
349  }
350  tmax_ *= 1.5;
351  tmax_ += std::max(d_margin_ / vel_[0], yaw_margin_ / vel_[1]);
352  r_lim_ = 1.0;
353  }
354  double predict(const geometry_msgs::Twist& in)
355  {
356  if (cloud_accum_->size() == 0)
357  {
358  if (allow_empty_cloud_)
359  {
360  return 1.0;
361  }
362  ROS_WARN_THROTTLE(1.0, "safety_limiter: Empty pointcloud passed.");
363  return 0.0;
364  }
365 
366  const bool can_transform = tfbuf_.canTransform(
367  base_frame_id_, cloud_accum_->header.frame_id,
368  pcl_conversions::fromPCL(cloud_accum_->header.stamp));
369  const ros::Time stamp =
370  can_transform ? pcl_conversions::fromPCL(cloud_accum_->header.stamp) : ros::Time(0);
371 
372  geometry_msgs::TransformStamped fixed_to_base;
373  try
374  {
375  fixed_to_base = tfbuf_.lookupTransform(
376  base_frame_id_, cloud_accum_->header.frame_id, stamp);
377  }
378  catch (tf2::TransformException& e)
379  {
380  ROS_WARN_THROTTLE(1.0, "safety_limiter: Transform failed: %s", e.what());
381  return 0.0;
382  }
383 
384  const Eigen::Affine3f fixed_to_base_eigen =
385  Eigen::Translation3f(
386  fixed_to_base.transform.translation.x,
387  fixed_to_base.transform.translation.y,
388  fixed_to_base.transform.translation.z) *
389  Eigen::Quaternionf(
390  fixed_to_base.transform.rotation.w,
391  fixed_to_base.transform.rotation.x,
392  fixed_to_base.transform.rotation.y,
393  fixed_to_base.transform.rotation.z);
394  pcl::transformPointCloud(*cloud_accum_, *cloud_accum_, fixed_to_base_eigen);
395 
396  pcl::PointCloud<pcl::PointXYZ>::Ptr pc(new pcl::PointCloud<pcl::PointXYZ>);
397  pcl::VoxelGrid<pcl::PointXYZ> ds;
398  ds.setInputCloud(cloud_accum_);
400  ds.filter(*pc);
401 
402  auto filter_z = [this](pcl::PointXYZ& p)
403  {
404  if (p.z < this->z_range_[0] || this->z_range_[1] < p.z)
405  return true;
406  p.z = 0.0;
407  return false;
408  };
409  pc->erase(std::remove_if(pc->points.begin(), pc->points.end(), filter_z),
410  pc->points.end());
411 
412  if (pc->size() == 0)
413  {
414  if (allow_empty_cloud_)
415  {
416  return 1.0;
417  }
418  ROS_WARN_THROTTLE(1.0, "safety_limiter: Empty pointcloud passed.");
419  return 0.0;
420  }
421 
422  pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
423  kdtree.setInputCloud(pc);
424 
425  Eigen::Affine3f move;
426  Eigen::Affine3f move_inv;
427  Eigen::Affine3f motion =
428  Eigen::AngleAxisf(-twist_.angular.z * dt_, Eigen::Vector3f::UnitZ()) *
429  Eigen::Translation3f(Eigen::Vector3f(-twist_.linear.x * dt_, -twist_.linear.y * dt_, 0.0));
430  Eigen::Affine3f motion_inv =
431  Eigen::Translation3f(Eigen::Vector3f(twist_.linear.x * dt_, twist_.linear.y * dt_, 0.0)) *
432  Eigen::AngleAxisf(twist_.angular.z * dt_, Eigen::Vector3f::UnitZ());
433  move.setIdentity();
434  move_inv.setIdentity();
435  sensor_msgs::PointCloud col_points;
436  col_points.header.frame_id = base_frame_id_;
437  col_points.header.stamp = ros::Time::now();
438 
439  float d_col = 0;
440  float yaw_col = 0;
441  bool has_collision = false;
442  float d_escape_remain = 0;
443  float yaw_escape_remain = 0;
444  has_collision_at_now_ = false;
445  const double linear_vel = std::hypot(twist_.linear.x, twist_.linear.y);
446 
447  for (float t = 0; t < tmax_; t += dt_)
448  {
449  if (t != 0)
450  {
451  d_col += linear_vel * dt_;
452  d_escape_remain -= linear_vel * dt_;
453  yaw_col += twist_.angular.z * dt_;
454  yaw_escape_remain -= std::abs(twist_.angular.z) * dt_;
455  move = move * motion;
456  move_inv = move_inv * motion_inv;
457  }
458 
459  pcl::PointXYZ center;
460  center = pcl::transformPoint(center, move_inv);
461  std::vector<int> indices;
462  std::vector<float> dist;
463  const int num = kdtree.radiusSearch(center, footprint_radius_, indices, dist);
464  if (num == 0)
465  continue;
466 
467  bool colliding = false;
468  for (auto& i : indices)
469  {
470  auto& p = pc->points[i];
471  auto point = pcl::transformPoint(p, move);
472  vec v(point.x, point.y);
473  if (footprint_p.inside(v))
474  {
475  geometry_msgs::Point32 pos;
476  pos.x = p.x;
477  pos.y = p.y;
478  pos.z = p.z;
479  col_points.points.push_back(pos);
480  colliding = true;
481  break;
482  }
483  }
484  if (colliding)
485  {
486  d_col -= linear_vel * dt_;
487  yaw_col -= twist_.angular.z * dt_;
488  if (t == 0)
489  {
490  // The robot is already in collision.
491  // Allow movement under d_escape_ and yaw_escape_
492  d_escape_remain = d_escape_;
493  yaw_escape_remain = yaw_escape_;
494  has_collision_at_now_ = true;
495  }
496  if (d_escape_remain <= 0 || yaw_escape_remain <= 0)
497  {
499  {
500  // It's not possible to escape from collision; stop completely.
501  d_col = yaw_col = 0;
502  }
503 
504  has_collision = true;
505  break;
506  }
507  }
508  }
509  pub_cloud_.publish(col_points);
510 
512  {
515  }
516  else
517  {
520  }
521 
522  if (!has_collision)
523  return 1.0;
524 
525  // delay compensation:
526  // solve for d_compensated: d_compensated = d - delay * sqrt(2 * acc * d_compensated)
527  // d_compensated = d + acc * delay^2 - sqrt((acc * delay^2)^2 + 2 * d * acc * delay^2)
528 
529  const float delay = 1.0 * (1.0 / hz_) + dt_;
530  const float acc_dtsq[2] =
531  {
532  static_cast<float>(acc_[0] * std::pow(delay, 2)),
533  static_cast<float>(acc_[1] * std::pow(delay, 2)),
534  };
535 
536  d_col = std::max<float>(
537  0.0,
538  std::abs(d_col) - d_margin_ + acc_dtsq[0] -
539  std::sqrt(std::pow(acc_dtsq[0], 2) + 2 * acc_dtsq[0] * std::abs(d_col)));
540  yaw_col = std::max<float>(
541  0.0,
542  std::abs(yaw_col) - yaw_margin_ + acc_dtsq[1] -
543  std::sqrt(std::pow(acc_dtsq[1], 2) + 2 * acc_dtsq[1] * std::abs(yaw_col)));
544 
545  float d_r =
546  std::sqrt(std::abs(2 * acc_[0] * d_col)) / linear_vel;
547  float yaw_r =
548  std::sqrt(std::abs(2 * acc_[1] * yaw_col)) / std::abs(twist_.angular.z);
549  if (!std::isfinite(d_r))
550  d_r = 1.0;
551  if (!std::isfinite(yaw_r))
552  yaw_r = 1.0;
553 
554  return std::min(d_r, yaw_r);
555  }
556 
557  geometry_msgs::Twist
558  limit(const geometry_msgs::Twist& in)
559  {
560  auto out = in;
561  if (r_lim_ < 1.0 - EPSILON)
562  {
563  out.linear.x *= r_lim_;
564  out.linear.y *= r_lim_;
565  out.angular.z *= r_lim_;
566  if (std::abs(in.linear.x - out.linear.x) > EPSILON ||
567  std::abs(in.linear.y - out.linear.y) > EPSILON ||
568  std::abs(in.angular.z - out.angular.z) > EPSILON)
569  {
571  1.0, "safety_limiter: (%0.2f, %0.2f, %0.2f)->(%0.2f, %0.2f, %0.2f)",
572  in.linear.x, in.linear.y, in.angular.z,
573  out.linear.x, out.linear.y, out.angular.z);
574  }
575  }
576  return out;
577  }
578 
579  geometry_msgs::Twist
580  limitMaxVelocities(const geometry_msgs::Twist& in)
581  {
582  auto out = in;
583  if (max_values_[0] <= 0.0)
584  {
585  out.linear.x = 0;
586  out.linear.y = 0;
587  }
588  else
589  {
590  const double out_linear_vel = std::hypot(out.linear.x, out.linear.y);
591  if (out_linear_vel > max_values_[0])
592  {
593  const double vel_ratio = max_values_[0] / out_linear_vel;
594  out.linear.x *= vel_ratio;
595  out.linear.y *= vel_ratio;
596  }
597  }
598  out.angular.z = (out.angular.z > 0) ?
599  std::min(out.angular.z, max_values_[1]) :
600  std::max(out.angular.z, -max_values_[1]);
601 
602  return out;
603  }
604 
605  class vec
606  {
607  public:
608  float c[2];
609  vec(const float x, const float y)
610  {
611  c[0] = x;
612  c[1] = y;
613  }
614  vec()
615  {
616  c[0] = c[1] = 0.0;
617  }
618  float& operator[](const int& i)
619  {
620  ROS_ASSERT(i < 2);
621  return c[i];
622  }
623  const float& operator[](const int& i) const
624  {
625  ROS_ASSERT(i < 2);
626  return c[i];
627  }
628  vec operator-(const vec& a) const
629  {
630  vec out = *this;
631  out[0] -= a[0];
632  out[1] -= a[1];
633  return out;
634  }
635  float cross(const vec& a) const
636  {
637  return (*this)[0] * a[1] - (*this)[1] * a[0];
638  }
639  float dot(const vec& a) const
640  {
641  return (*this)[0] * a[0] + (*this)[1] * a[1];
642  }
643  float dist(const vec& a) const
644  {
645  return std::hypot((*this)[0] - a[0], (*this)[1] - a[1]);
646  }
647  float dist_line(const vec& a, const vec& b) const
648  {
649  return (b - a).cross((*this) - a) / b.dist(a);
650  }
651  float dist_linestrip(const vec& a, const vec& b) const
652  {
653  if ((b - a).dot((*this) - a) <= 0)
654  return this->dist(a);
655  if ((a - b).dot((*this) - b) <= 0)
656  return this->dist(b);
657  return std::abs(this->dist_line(a, b));
658  }
659  };
660  class polygon
661  {
662  public:
663  std::vector<vec> v;
664  void move(const float& x, const float& y, const float& yaw)
665  {
666  const float cos_v = cosf(yaw);
667  const float sin_v = sinf(yaw);
668  for (auto& p : v)
669  {
670  const auto tmp = p;
671  p[0] = cos_v * tmp[0] - sin_v * tmp[1] + x;
672  p[1] = sin_v * tmp[0] + cos_v * tmp[1] + y;
673  }
674  }
675  bool inside(const vec& a) const
676  {
677  int cn = 0;
678  for (size_t i = 0; i < v.size() - 1; i++)
679  {
680  auto& v1 = v[i];
681  auto& v2 = v[i + 1];
682  if ((v1[1] <= a[1] && a[1] < v2[1]) ||
683  (v2[1] <= a[1] && a[1] < v1[1]))
684  {
685  float lx;
686  lx = v1[0] + (v2[0] - v1[0]) * (a[1] - v1[1]) / (v2[1] - v1[1]);
687  if (a[0] < lx)
688  cn++;
689  }
690  }
691  return ((cn & 1) == 1);
692  }
693  float dist(const vec& a) const
694  {
695  float dist = std::numeric_limits<float>::max();
696  for (size_t i = 0; i < v.size() - 1; i++)
697  {
698  auto& v1 = v[i];
699  auto& v2 = v[i + 1];
700  auto d = a.dist_linestrip(v1, v2);
701  if (d < dist)
702  dist = d;
703  }
704  return dist;
705  }
706  };
707 
709 
710  void cbTwist(const geometry_msgs::Twist::ConstPtr& msg)
711  {
712  ros::Time now = ros::Time::now();
713 
714  twist_ = *msg;
715  has_twist_ = true;
716 
718  {
720  }
721  else if (!has_cloud_ || watchdog_stop_)
722  {
723  geometry_msgs::Twist cmd_vel;
724  pub_twist_.publish(cmd_vel);
725  }
726  else
727  {
728  geometry_msgs::Twist cmd_vel = limitMaxVelocities(limit(twist_));
729  pub_twist_.publish(cmd_vel);
730 
731  if (now > hold_off_)
732  r_lim_ = 1.0;
733  }
734  }
735 
736  void cbCloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
737  {
738  const bool can_transform = tfbuf_.canTransform(
739  fixed_frame_id_, msg->header.frame_id, msg->header.stamp);
740  const ros::Time stamp =
741  can_transform ? msg->header.stamp : ros::Time(0);
742 
743  sensor_msgs::PointCloud2 cloud_msg_fixed;
744  try
745  {
746  const geometry_msgs::TransformStamped cloud_to_fixed =
747  tfbuf_.lookupTransform(fixed_frame_id_, msg->header.frame_id, stamp);
748  tf2::doTransform(*msg, cloud_msg_fixed, cloud_to_fixed);
749  }
750  catch (tf2::TransformException& e)
751  {
752  ROS_WARN_THROTTLE(1.0, "safety_limiter: Transform failed: %s", e.what());
753  return;
754  }
755 
756  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_fixed(new pcl::PointCloud<pcl::PointXYZ>());
757  cloud_fixed->header.frame_id = fixed_frame_id_;
758  pcl::fromROSMsg(cloud_msg_fixed, *cloud_fixed);
759 
760  if (cloud_clear_)
761  {
762  cloud_clear_ = false;
763  cloud_accum_.reset(new pcl::PointCloud<pcl::PointXYZ>);
764  }
765  *cloud_accum_ += *cloud_fixed;
766  cloud_accum_->header.frame_id = fixed_frame_id_;
767  last_cloud_stamp_ = msg->header.stamp;
768  has_cloud_ = true;
769  }
770  void cbDisable(const std_msgs::Bool::ConstPtr& msg)
771  {
772  if (msg->data)
773  {
775  }
776  }
777 
779  {
780  safety_limiter_msgs::SafetyLimiterStatus status_msg;
781 
782  if (!has_cloud_ || watchdog_stop_)
783  {
784  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Stopped due to data timeout.");
785  }
786  else if (r_lim_ == 1.0)
787  {
788  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "OK");
789  }
790  else if (r_lim_ < EPSILON)
791  {
792  stat.summary(diagnostic_msgs::DiagnosticStatus::WARN,
794  "Cannot escape from collision." :
795  "Trying to avoid collision, but cannot move anymore.");
796  }
797  else
798  {
799  stat.summary(diagnostic_msgs::DiagnosticStatus::OK,
801  "Escaping from collision." :
802  "Reducing velocity to avoid collision.");
803  }
804  stat.addf("Velocity Limit Ratio", "%.2f", r_lim_);
805  stat.add("Pointcloud Availability", has_cloud_ ? "true" : "false");
806  stat.add("Watchdog Timeout", watchdog_stop_ ? "true" : "false");
807 
808  status_msg.limit_ratio = r_lim_;
809  status_msg.is_cloud_available = has_cloud_;
810  status_msg.has_watchdog_timed_out = watchdog_stop_;
811  status_msg.stuck_started_since = stuck_started_since_;
812 
813  pub_status_.publish(status_msg);
814  }
815 };
816 
817 } // namespace safety_limiter
818 
819 int main(int argc, char** argv)
820 {
821  ros::init(argc, argv, "safety_limiter");
822 
824  limiter.spin();
825 
826  return 0;
827 }
XmlRpc::XmlRpcValue::size
int size() const
safety_limiter::XmlRpc_isNumber
bool XmlRpc_isNumber(XmlRpc::XmlRpcValue &value)
Definition: safety_limiter.cpp:95
safety_limiter::SafetyLimiterNode::sub_disable_
ros::Subscriber sub_disable_
Definition: safety_limiter.cpp:111
pcl
safety_limiter::SafetyLimiterNode::twist_
geometry_msgs::Twist twist_
Definition: safety_limiter.cpp:119
safety_limiter::operator-
pcl::PointXYZ operator-(const pcl::PointXYZ &a, const pcl::PointXYZ &b)
Definition: safety_limiter.cpp:71
safety_limiter::SafetyLimiterNode::vec::dist_line
float dist_line(const vec &a, const vec &b) const
Definition: safety_limiter.cpp:647
safety_limiter::SafetyLimiterNode::polygon::v
std::vector< vec > v
Definition: safety_limiter.cpp:663
safety_limiter::SafetyLimiterNode::diag_updater_
diagnostic_updater::Updater diag_updater_
Definition: safety_limiter.cpp:156
safety_limiter::SafetyLimiterNode::vec::operator[]
const float & operator[](const int &i) const
Definition: safety_limiter.cpp:623
safety_limiter::SafetyLimiterNode::parameter_server_
std::unique_ptr< dynamic_reconfigure::Server< SafetyLimiterConfig > > parameter_server_
Definition: safety_limiter.cpp:117
msg
msg
ros::Publisher
safety_limiter::SafetyLimiterNode::pub_status_
ros::Publisher pub_status_
Definition: safety_limiter.cpp:108
safety_limiter::SafetyLimiterNode::base_frame_id_
std::string base_frame_id_
Definition: safety_limiter.cpp:140
diagnostic_updater::DiagnosticStatusWrapper::add
void add(const std::string &key, const bool &b)
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
diagnostic_updater::Updater::force_update
void force_update()
safety_limiter::SafetyLimiterNode::footprint_p
polygon footprint_p
Definition: safety_limiter.cpp:708
safety_limiter::SafetyLimiterNode::sub_twist_
ros::Subscriber sub_twist_
Definition: safety_limiter.cpp:109
safety_limiter::SafetyLimiterNode::acc_
double acc_[2]
Definition: safety_limiter.cpp:127
safety_limiter::SafetyLimiterNode::r_lim_
double r_lim_
Definition: safety_limiter.cpp:134
safety_limiter::SafetyLimiterNode::max_values_
double max_values_[2]
Definition: safety_limiter.cpp:135
ros
ros::NodeHandle::getParam
bool getParam(const std::string &key, bool &b) const
ROS_WARN_THROTTLE
#define ROS_WARN_THROTTLE(period,...)
ros.h
safety_limiter::SafetyLimiterNode::predict
double predict(const geometry_msgs::Twist &in)
Definition: safety_limiter.cpp:354
safety_limiter::SafetyLimiterNode::sub_watchdog_
ros::Subscriber sub_watchdog_
Definition: safety_limiter.cpp:112
safety_limiter::SafetyLimiterNode::vec::operator-
vec operator-(const vec &a) const
Definition: safety_limiter.cpp:628
tf2_sensor_msgs.h
diagnostic_updater::Updater
XmlRpc::XmlRpcValue::TypeInt
TypeInt
PointCloud
sensor_msgs::PointCloud2 PointCloud
safety_limiter::operator*
pcl::PointXYZ operator*(const pcl::PointXYZ &a, const float &b)
Definition: safety_limiter.cpp:87
compatibility.h
safety_limiter::SafetyLimiterNode::nh_
ros::NodeHandle nh_
Definition: safety_limiter.cpp:104
safety_limiter::SafetyLimiterNode::cbTwist
void cbTwist(const geometry_msgs::Twist::ConstPtr &msg)
Definition: safety_limiter.cpp:710
safety_limiter::SafetyLimiterNode::footprint_radius_
float footprint_radius_
Definition: safety_limiter.cpp:137
safety_limiter::SafetyLimiterNode::hz_
double hz_
Definition: safety_limiter.cpp:123
safety_limiter::SafetyLimiterNode::downsample_grid_
double downsample_grid_
Definition: safety_limiter.cpp:138
pcl::fromROSMsg
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
safety_limiter::operator+
pcl::PointXYZ operator+(const pcl::PointXYZ &a, const pcl::PointXYZ &b)
Definition: safety_limiter.cpp:79
safety_limiter::SafetyLimiterNode::watchdog_timer_
ros::Timer watchdog_timer_
Definition: safety_limiter.cpp:113
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
safety_limiter::SafetyLimiterNode::has_collision_at_now_
bool has_collision_at_now_
Definition: safety_limiter.cpp:151
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
safety_limiter::SafetyLimiterNode::vec::vec
vec()
Definition: safety_limiter.cpp:614
transforms.h
diagnostic_updater.h
safety_limiter::SafetyLimiterNode::vec::dot
float dot(const vec &a) const
Definition: safety_limiter.cpp:639
tf2_ros::TransformListener
safety_limiter::SafetyLimiterNode::vel_
double vel_[2]
Definition: safety_limiter.cpp:126
safety_limiter::SafetyLimiterNode::tmax_
double tmax_
Definition: safety_limiter.cpp:128
safety_limiter::SafetyLimiterNode::cbPredictTimer
void cbPredictTimer(const ros::TimerEvent &event)
Definition: safety_limiter.cpp:286
safety_limiter::SafetyLimiterNode::cbParameter
void cbParameter(const SafetyLimiterConfig &config, const uint32_t)
Definition: safety_limiter.cpp:320
neonavigation_common::compat::subscribe
ros::Subscriber subscribe(ros::NodeHandle &nh_new, const std::string &topic_new, ros::NodeHandle &nh_old, const std::string &topic_old, uint32_t queue_size, const boost::function< void(const boost::shared_ptr< M const > &)> &callback, const ros::VoidConstPtr &tracked_object=ros::VoidConstPtr(), const ros::TransportHints &transport_hints=ros::TransportHints())
safety_limiter
Definition: safety_limiter.cpp:69
safety_limiter::SafetyLimiterNode
Definition: safety_limiter.cpp:101
diagnostic_updater::DiagnosticStatusWrapper::summary
void summary(const diagnostic_msgs::DiagnosticStatus &src)
XmlRpc::XmlRpcValue::TypeDouble
TypeDouble
tf2_ros::Buffer::canTransform
virtual bool canTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration timeout, std::string *errstr=NULL) const
pcl_conversions::fromPCL
void fromPCL(const pcl::ModelCoefficients &pcl_mc, pcl_msgs::ModelCoefficients &mc)
safety_limiter::SafetyLimiterNode::pnh_
ros::NodeHandle pnh_
Definition: safety_limiter.cpp:105
safety_limiter::SafetyLimiterNode::EPSILON
constexpr static float EPSILON
Definition: safety_limiter.cpp:154
safety_limiter::SafetyLimiterNode::hold_
ros::Duration hold_
Definition: safety_limiter.cpp:143
safety_limiter::SafetyLimiterNode::cbWatchdogReset
void cbWatchdogReset(const std_msgs::Empty::ConstPtr &msg)
Definition: safety_limiter.cpp:271
safety_limiter::SafetyLimiterNode::z_range_
double z_range_[2]
Definition: safety_limiter.cpp:136
safety_limiter::SafetyLimiterNode::fixed_frame_id_
std::string fixed_frame_id_
Definition: safety_limiter.cpp:139
ros::Timer::setPeriod
void setPeriod(const Duration &period, bool reset=true)
safety_limiter::SafetyLimiterNode::diagnoseCollision
void diagnoseCollision(diagnostic_updater::DiagnosticStatusWrapper &stat)
Definition: safety_limiter.cpp:778
safety_limiter::SafetyLimiterNode::polygon::move
void move(const float &x, const float &y, const float &yaw)
Definition: safety_limiter.cpp:664
safety_limiter::SafetyLimiterNode::has_twist_
bool has_twist_
Definition: safety_limiter.cpp:150
ros::NodeHandle::hasParam
bool hasParam(const std::string &key) const
safety_limiter::SafetyLimiterNode::tfl_
tf2_ros::TransformListener tfl_
Definition: safety_limiter.cpp:115
d
d
ROS_WARN
#define ROS_WARN(...)
safety_limiter::SafetyLimiterNode::timeout_
double timeout_
Definition: safety_limiter.cpp:124
safety_limiter::SafetyLimiterNode::tfbuf_
tf2_ros::Buffer tfbuf_
Definition: safety_limiter.cpp:114
tf2_ros::Buffer
safety_limiter::SafetyLimiterNode::d_margin_
double d_margin_
Definition: safety_limiter.cpp:130
ros::NodeHandle::subscribe
Subscriber subscribe(const std::string &topic, uint32_t queue_size, const boost::function< void(C)> &callback, const VoidConstPtr &tracked_object=VoidConstPtr(), const TransportHints &transport_hints=TransportHints())
ros::TimerEvent
safety_limiter::SafetyLimiterNode::vec::c
float c[2]
Definition: safety_limiter.cpp:608
diagnostic_updater::Updater::setHardwareID
void setHardwareID(const std::string &hwid)
XmlRpc::XmlRpcValue::getType
const Type & getType() const
ROS_FATAL
#define ROS_FATAL(...)
safety_limiter::SafetyLimiterNode::cbWatchdogTimer
void cbWatchdogTimer(const ros::TimerEvent &event)
Definition: safety_limiter.cpp:276
safety_limiter::SafetyLimiterNode::vec::vec
vec(const float x, const float y)
Definition: safety_limiter.cpp:609
safety_limiter::SafetyLimiterNode::limitMaxVelocities
geometry_msgs::Twist limitMaxVelocities(const geometry_msgs::Twist &in)
Definition: safety_limiter.cpp:580
XmlRpc::XmlRpcValue::TypeArray
TypeArray
neonavigation_common::compat::checkCompatMode
void checkCompatMode()
safety_limiter::SafetyLimiterNode::vec::cross
float cross(const vec &a) const
Definition: safety_limiter.cpp:635
transform_listener.h
safety_limiter::SafetyLimiterNode::has_cloud_
bool has_cloud_
Definition: safety_limiter.cpp:149
safety_limiter::SafetyLimiterNode::d_escape_
double d_escape_
Definition: safety_limiter.cpp:131
safety_limiter::SafetyLimiterNode::polygon::inside
bool inside(const vec &a) const
Definition: safety_limiter.cpp:675
safety_limiter::SafetyLimiterNode::last_cloud_stamp_
ros::Time last_cloud_stamp_
Definition: safety_limiter.cpp:120
safety_limiter::SafetyLimiterNode::polygon
Definition: safety_limiter.cpp:660
safety_limiter::SafetyLimiterNode::pub_twist_
ros::Publisher pub_twist_
Definition: safety_limiter.cpp:106
ros::Time
tf2::doTransform
void doTransform(const T &data_in, T &data_out, const geometry_msgs::TransformStamped &transform)
safety_limiter::SafetyLimiterNode::watchdog_stop_
bool watchdog_stop_
Definition: safety_limiter.cpp:148
safety_limiter::SafetyLimiterNode::disable_timeout_
double disable_timeout_
Definition: safety_limiter.cpp:125
safety_limiter::SafetyLimiterNode::spin
void spin()
Definition: safety_limiter.cpp:256
diagnostic_updater::DiagnosticStatusWrapper::addf
void addf(const std::string &key, const char *format,...)
safety_limiter::SafetyLimiterNode::vec::dist_linestrip
float dist_linestrip(const vec &a, const vec &b) const
Definition: safety_limiter.cpp:651
ros::NodeHandle::param
T param(const std::string &param_name, const T &default_val) const
safety_limiter::SafetyLimiterNode::dt_
double dt_
Definition: safety_limiter.cpp:129
diagnostic_updater::DiagnosticStatusWrapper
safety_limiter::SafetyLimiterNode::vec
Definition: safety_limiter.cpp:605
safety_limiter::SafetyLimiterNode::cbDisable
void cbDisable(const std_msgs::Bool::ConstPtr &msg)
Definition: safety_limiter.cpp:770
safety_limiter::SafetyLimiterNode::limit
geometry_msgs::Twist limit(const geometry_msgs::Twist &in)
Definition: safety_limiter.cpp:558
safety_limiter::SafetyLimiterNode::yaw_escape_
double yaw_escape_
Definition: safety_limiter.cpp:133
ros::spin
ROSCPP_DECL void spin()
safety_limiter::SafetyLimiterNode::yaw_margin_
double yaw_margin_
Definition: safety_limiter.cpp:132
safety_limiter::SafetyLimiterNode::pub_cloud_
ros::Publisher pub_cloud_
Definition: safety_limiter.cpp:107
safety_limiter::SafetyLimiterNode::parameter_server_mutex_
boost::recursive_mutex parameter_server_mutex_
Definition: safety_limiter.cpp:116
safety_limiter::SafetyLimiterNode::sub_clouds_
std::vector< ros::Subscriber > sub_clouds_
Definition: safety_limiter.cpp:110
main
int main(int argc, char **argv)
Definition: safety_limiter.cpp:819
safety_limiter::SafetyLimiterNode::vec::operator[]
float & operator[](const int &i)
Definition: safety_limiter.cpp:618
tf2::TransformException
ROS_INFO
#define ROS_INFO(...)
safety_limiter::SafetyLimiterNode::polygon::dist
float dist(const vec &a) const
Definition: safety_limiter.cpp:693
safety_limiter::SafetyLimiterNode::SafetyLimiterNode
SafetyLimiterNode()
Definition: safety_limiter.cpp:159
ROS_ASSERT
#define ROS_ASSERT(cond)
diagnostic_updater::DiagnosticTaskVector::add
void add(const std::string &name, TaskFunction f)
safety_limiter::SafetyLimiterNode::stuck_started_since_
ros::Time stuck_started_since_
Definition: safety_limiter.cpp:152
safety_limiter::SafetyLimiterNode::hold_off_
ros::Time hold_off_
Definition: safety_limiter.cpp:144
ros::NodeHandle::createTimer
Timer createTimer(Duration period, const TimerCallback &callback, bool oneshot=false, bool autostart=true) const
safety_limiter::SafetyLimiterNode::last_disable_cmd_
ros::Time last_disable_cmd_
Definition: safety_limiter.cpp:142
safety_limiter::SafetyLimiterNode::vec::dist
float dist(const vec &a) const
Definition: safety_limiter.cpp:643
safety_limiter::SafetyLimiterNode::cloud_accum_
pcl::PointCloud< pcl::PointXYZ >::Ptr cloud_accum_
Definition: safety_limiter.cpp:121
safety_limiter::SafetyLimiterNode::allow_empty_cloud_
bool allow_empty_cloud_
Definition: safety_limiter.cpp:146
ros::Duration
ros::Timer
safety_limiter::SafetyLimiterNode::watchdog_interval_
ros::Duration watchdog_interval_
Definition: safety_limiter.cpp:145
tf2_ros::Buffer::lookupTransform
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration timeout) const
XmlRpc::XmlRpcValue
safety_limiter::SafetyLimiterNode::cloud_clear_
bool cloud_clear_
Definition: safety_limiter.cpp:122
ros::NodeHandle
ros::Subscriber
ros::Time::now
static Time now()
pcl_conversions.h
move
void move(std::vector< T > &a, std::vector< T > &b)
safety_limiter::SafetyLimiterNode::cbCloud
void cbCloud(const sensor_msgs::PointCloud2::ConstPtr &msg)
Definition: safety_limiter.cpp:736


safety_limiter
Author(s): Atsushi Watanabe
autogenerated on Fri May 16 2025 02:15:16